From 3f78fa953abb138c9ba29d2fdc06b59ee02924f0 Mon Sep 17 00:00:00 2001 From: kacperkapusciak Date: Fri, 21 Jul 2023 08:46:03 -0700 Subject: [PATCH] Fix checkForReproducer action not adding Missing Repro comment (#38531) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: This PR fixes a bug where GitHub Actions bot didn't add a comment when "Needs: Repro" label was present in an issue. When a maintainer adds the label manually the bot comments with "Missing Reproductible Example" as normally. It seems like the problem occurred because of a difference in a sandbox repository and the proper facebook/react-native repo environment. My sandbox that I used to test https://github.com/facebook/react-native/pull/38338 had an "bot" account with Personal Access Token setup to reply to issues. Turns out that bots using PAT have more permissions and can trigger one action from the other. **The solution is to send the comment directly from the `checkForReproducer` action.** This won't collide with other actions but sadly will duplicate the sending logic into two actions. This PR also makes the bot respect when a maintainer removes and adds a label by hand and won't alter the maintainer decision. Related to ☂️ https://github.com/facebook/react-native/issues/35591 ## Changelog: [INTERNAL] [FIXED] - Message Pull Request resolved: https://github.com/facebook/react-native/pull/38531 Test Plan: ![image](https://github.com/facebook/react-native/assets/39658211/b956416f-9834-4c61-981f-fe6c17a5eec5) Reviewed By: cipolleschi Differential Revision: D47666922 Pulled By: cortinico fbshipit-source-id: 4a6a471cb11c6ed9b48263d18bf8e283577a14bb --- .github/ISSUE_TEMPLATE/bug_report.yml | 4 +- .../workflow-scripts/checkForReproducer.js | 59 ++++++++++++++----- .github/workflows/check-for-reproducer.yml | 2 +- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 37f64f0dcbf9cf..d923854d7b2a73 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 72f5f0cac69303..1b8973b3c01407 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?`, + 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 d15628a20585a4..8ef450fc1aea74 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