diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 37f64f0dcbf..d923854d7b2 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -38,9 +38,9 @@ body: - type: textarea id: extra attributes: - label: Snack, code example, screenshot, or link to a repository + label: Snack, screenshot, or link to a repository description: | - Please provide a Snack (https://snack.expo.dev/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem. + Please provide a Snack (https://snack.expo.dev/), a link to a repository on GitHub that reproduces the problem. You may provide a screenshot of the application if you think it is relevant to your bug report. Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve Please note that a reproducer is **mandatory**. Issues without reproducer are more likely to stall and will be closed. diff --git a/.github/workflow-scripts/checkForReproducer.js b/.github/workflow-scripts/checkForReproducer.js index 72f5f0cac69..1b8973b3c01 100644 --- a/.github/workflow-scripts/checkForReproducer.js +++ b/.github/workflow-scripts/checkForReproducer.js @@ -8,7 +8,11 @@ */ const NEEDS_REPRO_LABEL = 'Needs: Repro'; -const NEEDS_REPRO_MESSAGE = '| Missing Reproducible Example |'; +const NEEDS_REPRO_HEADER = 'Missing Reproducible Example'; +const NEEDS_REPRO_MESSAGE = + `| :warning: | Missing Reproducible Example |\n` + + `| --- | --- |\n` + + `| :information_source: | We could not detect a reproducible example in your issue report. Please provide either:
|`; module.exports = async (github, context) => { const issueData = { @@ -20,30 +24,35 @@ module.exports = async (github, context) => { const issue = await github.rest.issues.get(issueData); const comments = await github.rest.issues.listComments(issueData); - const botComment = comments.data.find(comment => - comment.body.includes(NEEDS_REPRO_MESSAGE), + const author = issue.data.user.login; + + const maintainerChangedLabel = await hasMaintainerChangedLabel( + github, + issueData, + author, ); - let commentBodies = comments.data.map(comment => comment.body); - if (botComment) { - commentBodies = commentBodies.filter(body => body !== botComment.body); + if (maintainerChangedLabel) { + return; } - const issueAndComments = [issue.data.body, ...commentBodies]; - const issueAndCommentsUniq = [...new Set(issueAndComments)]; + const botComment = comments.data.find(comment => + comment.body.includes(NEEDS_REPRO_HEADER), + ); - const user = issue.data.user.login; + const entities = [issue.data, ...comments.data]; - const hasValidReproducer = issueAndCommentsUniq.some(body => { + // Look for Snack or a GH repo associated with the user that added an issue or comment + const hasValidReproducer = entities.some(entity => { const hasExpoSnackLink = containsPattern( - body, + entity.body, `https?:\\/\\/snack\\.expo\\.dev\\/[^\\s)\\]]+`, ); - const hasGithubRepoLink = containsPattern( - body, - `https?:\\/\\/github\\.com\\/(${user})\\/[^/]+\\/?\\s?`, - ); + const hasGithubRepoLink = containsPattern( + entity.body, + `https?:\\/\\/github\\.com\\/(${entity.user.login})\\/[^/]+\\/?\\s?`, + ); return hasExpoSnackLink || hasGithubRepoLink; }); @@ -70,6 +79,13 @@ module.exports = async (github, context) => { ...issueData, labels: [NEEDS_REPRO_LABEL], }); + + if (botComment) return; + + await github.rest.issues.createComment({ + ...issueData, + body: NEEDS_REPRO_MESSAGE, + }); } }; @@ -77,3 +93,18 @@ function containsPattern(body, pattern) { const regexp = new RegExp(pattern, 'gm'); return body.search(regexp) !== -1; } + +// Prevents the bot from responding when maintainer has changed Needs: Repro the label +async function hasMaintainerChangedLabel(github, issueData, author) { + const timeline = await github.rest.issues.listEventsForTimeline(issueData); + + const labeledEvents = timeline.data.filter( + event => event.event === 'labeled' || event.event === 'unlabeled', + ); + const userEvents = labeledEvents.filter(event => event.actor.type !== 'Bot'); + + return userEvents.some( + event => + event.actor.login !== author && event.label.name === NEEDS_REPRO_LABEL, + ); +} diff --git a/.github/workflows/check-for-reproducer.yml b/.github/workflows/check-for-reproducer.yml index d15628a2058..8ef450fc1ae 100644 --- a/.github/workflows/check-for-reproducer.yml +++ b/.github/workflows/check-for-reproducer.yml @@ -10,7 +10,7 @@ on: jobs: check-for-reproducer: runs-on: ubuntu-latest - if: github.repository == 'facebook/react-native' && github.event.issue.pull_request == null + if: github.repository == 'facebook/react-native' && github.event.issue.pull_request == null && github.event.issue.state == 'open' steps: - uses: actions/checkout@v3 - uses: actions/github-script@v6