From 42a2898617da1d7a98ef574a5b9e500681c8f738 Mon Sep 17 00:00:00 2001 From: kacperkapusciak Date: Wed, 19 Jul 2023 02:38:28 -0700 Subject: [PATCH] Add check-for-reproducer action (#38338) Summary: This PR adds a "check-for-reproducer" GitHub action proposed in https://github.com/facebook/react-native/issues/35591. This GitHub action automatically labels an issue with when no link to GitHub repository under the authors name or link to Snack is present either in the issue body or in the comments. ## Changelog: [INTERNAL] [ADDED] - Add check-for-reproducer GitHub action Pull Request resolved: https://github.com/facebook/react-native/pull/38338 Test Plan: This action was tested on a private repro mimicking on how GitHub actions are set-up in `facebook/react-native` repository. If you'd want to play around with the action on this private repo just ask for access - provided you're a maintainer. ### Regarding issue body 1. Labels "Needs: Repro" when no link to Expo Snack or a Github repo under the author's name is present in the issue body: ![image](https://github.com/facebook/react-native/assets/39658211/b8e6d4aa-bc9a-471d-99b8-3b551f2c46c6) 2. Removes the "Needs: Repro" label and deletes the Missing Repro comment when the author edits the issue and provides a link to GitHub repo under their name ![image](https://github.com/facebook/react-native/assets/39658211/2dc13fae-b6ca-432f-88a2-f5c340f28c86) 3. Removes the "Needs: Repro" label and deletes the Missing Repro comment when the author edits the issue and provides a valid link to Expo Snack ![image](https://github.com/facebook/react-native/assets/39658211/e82e744e-b2fb-4979-84de-08f95686feb3) ### Regarding comments 3. Removes the "Needs: Repro" label and deletes the Missing Repro comment when there's a comment with a link to reproduction under issue author's name ![image](https://github.com/facebook/react-native/assets/39658211/eefb5978-29d3-4486-8bd7-878fb2c07c05) 4. Removes the "Needs: Repro" label and deletes the Missing Repro comment when there's a comment with a link to Expo Snack ![image](https://github.com/facebook/react-native/assets/39658211/5769ff77-0d9f-4dac-bb36-6d257e6c2a35) ### Regarding false-positives 5. Adds a "Needs: Repro" label when a link to reprository isn't under the issue author's name ![image](https://github.com/facebook/react-native/assets/39658211/01c93ea3-88b9-4920-a8f8-bcd123d214de) 6. Adds a "Needs: Repro" label when a link to Expo Snack's homepage was sent ![image](https://github.com/facebook/react-native/assets/39658211/568c9824-67af-402a-b102-c1c355651d21) 7. Adds a "Needs: Repro" label when a random link was sent ![image](https://github.com/facebook/react-native/assets/39658211/23bb58b3-cc07-4284-a562-6651f2077a3c) Reviewed By: NickGerleman Differential Revision: D47511745 Pulled By: cortinico fbshipit-source-id: 2c0e5a989f52b4e50992a3954283f122b14153e0 --- .github/workflow-scripts/actOnLabel.js | 2 +- .../workflow-scripts/checkForReproducer.js | 79 +++++++++++++++++++ .github/workflows/check-for-reproducer.yml | 21 +++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 .github/workflow-scripts/checkForReproducer.js create mode 100644 .github/workflows/check-for-reproducer.yml diff --git a/.github/workflow-scripts/actOnLabel.js b/.github/workflow-scripts/actOnLabel.js index 25903e72bb0331..7556e122bee631 100644 --- a/.github/workflow-scripts/actOnLabel.js +++ b/.github/workflow-scripts/actOnLabel.js @@ -121,7 +121,7 @@ module.exports = async (github, context, labelWithContext) => { await addComment( `| :warning: | Missing Reproducible Example |\n` + `| --- | --- |\n` + - `| :information_source: | It looks like your issue is missing a reproducible example. Please provide either:
|`, + `| :information_source: | We could not detect a reproducible example in your issue report. Please provide either:
|`, ); await requestAuthorFeedback(); return; diff --git a/.github/workflow-scripts/checkForReproducer.js b/.github/workflow-scripts/checkForReproducer.js new file mode 100644 index 00000000000000..72f5f0cac69303 --- /dev/null +++ b/.github/workflow-scripts/checkForReproducer.js @@ -0,0 +1,79 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +const NEEDS_REPRO_LABEL = 'Needs: Repro'; +const NEEDS_REPRO_MESSAGE = '| Missing Reproducible Example |'; + +module.exports = async (github, context) => { + const issueData = { + issue_number: context.payload.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }; + + 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), + ); + + let commentBodies = comments.data.map(comment => comment.body); + if (botComment) { + commentBodies = commentBodies.filter(body => body !== botComment.body); + } + + const issueAndComments = [issue.data.body, ...commentBodies]; + const issueAndCommentsUniq = [...new Set(issueAndComments)]; + + const user = issue.data.user.login; + + const hasValidReproducer = issueAndCommentsUniq.some(body => { + const hasExpoSnackLink = containsPattern( + body, + `https?:\\/\\/snack\\.expo\\.dev\\/[^\\s)\\]]+`, + ); + const hasGithubRepoLink = containsPattern( + body, + `https?:\\/\\/github\\.com\\/(${user})\\/[^/]+\\/?\\s?`, + ); + + return hasExpoSnackLink || hasGithubRepoLink; + }); + + if (hasValidReproducer) { + try { + await github.rest.issues.removeLabel({ + ...issueData, + name: NEEDS_REPRO_LABEL, + }); + } catch (error) { + if (!/Label does not exist/.test(error.message)) { + throw error; + } + } + + if (!botComment) return; + + await github.rest.issues.deleteComment({ + ...issueData, + comment_id: botComment.id, + }); + } else { + await github.rest.issues.addLabels({ + ...issueData, + labels: [NEEDS_REPRO_LABEL], + }); + } +}; + +function containsPattern(body, pattern) { + const regexp = new RegExp(pattern, 'gm'); + return body.search(regexp) !== -1; +} diff --git a/.github/workflows/check-for-reproducer.yml b/.github/workflows/check-for-reproducer.yml new file mode 100644 index 00000000000000..76d347cfa6bfad --- /dev/null +++ b/.github/workflows/check-for-reproducer.yml @@ -0,0 +1,21 @@ +name: Check for reproducer +# This workflow is triggered when issue is created or edited. +# Also, when a comment is added, edited or deleted. +on: + issues: + types: [opened, edited] + issue_comment: + types: [created, edited, deleted] + +jobs: + check-for-reproducer: + runs-on: ubuntu-latest + if: github.repository == 'facebook/react-native' + steps: + - uses: actions/checkout@v3 + - uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const checkForReproducer = require('./.github/workflow-scripts/checkForReproducer.js') + await checkForReproducer(github, context)