diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index d11f96458d0656..37f64f0dcbf9cf 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -16,9 +16,9 @@ body: - type: input id: version attributes: - label: Version - description: What react-native version does this appear on? Bear in mind that only issues on [supported versions](https://github.com/reactwg/react-native-releases#which-versions-are-currently-supported) will be looked into. - placeholder: ex. 0.70.0 + label: React Native Version + description: What is the latest version of react-native that this issue reproduces on? Please only list the highest version you tested. Bear in mind that only issues on [supported versions](https://github.com/reactwg/react-native-releases#which-versions-are-currently-supported) will be looked into. + placeholder: ex. 0.71.0 validations: required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/new_architecture_bug_report.yml b/.github/ISSUE_TEMPLATE/new_architecture_bug_report.yml index 6459e4794a2d76..f0ac968d80757c 100644 --- a/.github/ISSUE_TEMPLATE/new_architecture_bug_report.yml +++ b/.github/ISSUE_TEMPLATE/new_architecture_bug_report.yml @@ -21,9 +21,9 @@ body: - type: input id: version attributes: - label: Version - description: What react-native version does this appear on? Please test against the latest stable version. Bug reports against older versions are more likely to stall. - placeholder: ex. 0.68.0 + label: React Native Version + description: What is the latest version of react-native that this issue reproduces on? Please test against the latest stable version, and list only the highest version you tested. Bug reports against older versions are more likely to stall. + placeholder: ex. 0.71.0 validations: required: true - type: textarea diff --git a/.github/workflows/actOnLabel.js b/.github/workflows/actOnLabel.js new file mode 100644 index 00000000000000..5073523ee46893 --- /dev/null +++ b/.github/workflows/actOnLabel.js @@ -0,0 +1,137 @@ +/** + * 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 + */ + +module.exports = async (github, context, label) => { + const closeIssue = async () => { + await github.rest.issues.update({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + state: 'closed', + }); + }; + + const addComment = async comment => { + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment, + }); + }; + + const requestAuthorFeedback = async () => { + // Remove the triage label if it exists (ignore the 404 if not; it's not expected to always be there) + try { + await github.rest.issues.removeLabel({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + name: 'Needs: Triage :mag:', + }); + } catch {} + + await github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['Needs: Author Feedback'], + }); + }; + + switch (label) { + case 'Type: Invalid': + await addComment( + `| :warning: | Issue is Invalid |\n` + + `| --- | --- |\n` + + `| :information_source: | This issue doesn't match any of the expected types for this repository - closing. |`, + ); + await closeIssue(); + return; + case 'Type: Question': + await addComment( + `| :warning: | Issue is a Question |\n` + + `| --- | --- |\n` + + `| :information_source: | We are using GitHub issues exclusively to track bugs in React Native. GitHub may not be the ideal place to ask a question, but you can try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native), or on [Reactiflux](https://www.reactiflux.com/). |`, + ); + await closeIssue(); + return; + case 'Type: Docs': + await addComment( + `| :warning: | Documentation Issue |\n` + + `| --- | --- |\n` + + `| :information_source: | Please report documentation issues in the [react-native-website](https://github.com/facebook/react-native-website/issues) repository. |`, + ); + await closeIssue(); + return; + case 'Resolution: For Stack Overflow': + await addComment( + `| :warning: | Issue is a Question |\n` + + `| --- | --- |\n` + + `| :information_source: | We are using GitHub issues exclusively to track bugs in the core React Native library. Please try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native) as it is better suited for this type of question. |`, + ); + await closeIssue(); + return; + case 'Type: Expo': + await addComment( + `| :warning: | Issue is Related to Expo |\n` + + `| --- | --- |\n` + + `| :information_source: | It looks like your issue is related to Expo and not React Native core. Please open your issue in [Expo's repository](https://github.com/expo/expo/issues/new). If you are able to create a repro that showcases that this issue is also happening in React Native vanilla, we will be happy to re-open. |`, + ); + await closeIssue(); + return; + case 'Needs: Issue Template': + await addComment( + `| :warning: | Missing Required Fields |\n` + + `| --- | --- |\n` + + `| :information_source: | It looks like your issue may be missing some necessary information. GitHub provides an example template whenever a [new issue is created](https://github.com/facebook/react-native/issues/new?template=bug_report.md). Could you go back and make sure to fill out the template? You may edit this issue, or close it and open a new one. |`, + ); + await requestAuthorFeedback(); + return; + case 'Needs: Environment Info': + await addComment( + `| :warning: | Missing Environment Information |\n` + + `| --- | --- |\n` + + `| :information_source: | Your issue may be missing information about your development environment. You can obtain the missing information by running react-native info in a console. |`, + ); + await requestAuthorFeedback(); + return; + case 'Needs: Verify on Latest Version': + await addComment( + `| :warning: | Newer Version of React Native is Available! |\n` + + `| --- | --- |\n` + + `| :information_source: | You are on a supported minor version, but it looks like there's a newer patch available. Please [upgrade](https://reactnative.dev/docs/upgrading) to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases. |`, + ); + return; + case 'Needs: Version Info': + await addComment( + `| :warning: | Add or Reformat Version Info |\n` + + `| --- | --- |\n` + + `| :information_source: | We could not find or parse the version number of React Native in your issue report. Please use the template, and report your version including major, minor, and patch numbers - e.g. 0.70.2 |`, + ); + await requestAuthorFeedback(); + return; + case 'Needs: Repro': + await addComment( + `| :warning: | Missing Reproducible Example |\n` + + `| --- | --- |\n` + + `| :information_source: | It looks like your issue is missing a reproducible example. Please provide a [Snack](https://snack.expo.dev) or a repository that demonstrates the issue you are reporting in a [minimal, complete, and reproducible](https://stackoverflow.com/help/minimal-reproducible-example) manner. |`, + ); + await requestAuthorFeedback(); + return; + case 'Type: Unsupported Version': + await addComment( + `| :warning: | Unsupported Version of React Native |\n` + + `| --- | --- |\n` + + `| :information_source: | It looks like your issue or the example you provided uses an [unsupported version of React Native](https://github.com/reactwg/react-native-releases/blob/main/README.md#releases-support-policy). Due to the number of issues we receive, we're currently only accepting new issues against one of the supported versions. Please [upgrade](https://reactnative.dev/docs/upgrading) to latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If you cannot upgrade, please open your issue on [StackOverflow](https://stackoverflow.com/questions/tagged/react-native) to get further community support. |`, + ); + await requestAuthorFeedback(); + return; + } +}; diff --git a/.github/workflows/on-issue-labeled.yml b/.github/workflows/on-issue-labeled.yml index ae55455cfd1316..72e7efbb1ac487 100644 --- a/.github/workflows/on-issue-labeled.yml +++ b/.github/workflows/on-issue-labeled.yml @@ -9,195 +9,37 @@ permissions: issues: write jobs: - type-invalid: + # Runs automatic checks on issues labeled with "Needs: Triage", + # then invokes actOnLabel to react to any added labels + triage-issue: runs-on: ubuntu-latest - if: "${{ contains(github.event.label.name, 'Type: Invalid') }}" + if: "${{ contains(github.event.label.name, 'Needs: Triage :mag:') }}" steps: + - uses: actions/checkout@v3 - uses: actions/github-script@v6 with: script: | - await github.rest.issues.update({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - state: "closed", - }) - type-question: - runs-on: ubuntu-latest - if: "${{ contains(github.event.label.name, 'Type: Question') }}" - steps: - - uses: actions/github-script@v6 - with: - script: | - await github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `We are using GitHub issues exclusively to track bugs in React Native. GitHub may not be the ideal place to ask a question, but you can try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native), or on [Reactiflux](https://www.reactiflux.com/).`, - }) - await github.rest.issues.update({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - state: "closed", - }) - type-docs: - runs-on: ubuntu-latest - if: "${{ contains(github.event.label.name, 'Type: Docs') }}" - steps: - - uses: actions/github-script@v6 - with: - script: | - await github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `Please report documentation issues in the [react-native-website](https://github.com/facebook/react-native-website/issues) repository.`, - }) - await github.rest.issues.update({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - state: "closed", - }) - resolution-for-stack-overflow: - runs-on: ubuntu-latest - if: "${{ contains(github.event.label.name, 'Resolution: For Stack Overflow') }}" - steps: - - uses: actions/github-script@v6 - with: - script: | - await github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `We are using GitHub issues exclusively to track bugs in the core React Native library. Please try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native) as it is better suited for this type of question.`, - }) - await github.rest.issues.update({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - state: "closed", - }) - needs-issue-template: - runs-on: ubuntu-latest - if: "${{ contains(github.event.label.name, 'Needs: Issue Template') }}" - steps: - - uses: actions/github-script@v6 - with: - script: | - await github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `| :warning: | Missing Required Fields | - | --- | --- | - | :information_source: | It looks like your issue may be missing some necessary information. GitHub provides an example template whenever a [new issue is created](https://github.com/facebook/react-native/issues/new?template=bug_report.md). Could you go back and make sure to fill out the template? You may edit this issue, or close it and open a new one. |`, - }) - await github.rest.issues.addLabels({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: ['Needs: Author Feedback'] - }) - needs-environment-info: - runs-on: ubuntu-latest - if: "${{ contains(github.event.label.name, 'Needs: Environment Info') }}" - steps: - - uses: actions/github-script@v6 - with: - script: | - await github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `| :warning: | Missing Environment Information | - | --- | --- | - | :information_source: | Your issue may be missing information about your development environment. You can obtain the missing information by running react-native info in a console. |`, - }) - await github.rest.issues.addLabels({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: ['Needs: Author Feedback'] - }) - needs-verify-on-latest-version: - runs-on: ubuntu-latest - if: "${{ contains(github.event.label.name, 'Needs: Verify on Latest Version') }}" - steps: - - uses: actions/github-script@v6 - with: - script: | - await github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `| :warning: | Using Old Version | - | --- | --- | - | :information_source: | It looks like you are using an older version of React Native. Please [upgrade](https://reactnative.dev/docs/upgrading) to the latest version, and verify if the issue persists. If it does not, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the current release. |`, - }) - await github.rest.issues.addLabels({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: ['Needs: Author Feedback'] - }) - needs-repro: - runs-on: ubuntu-latest - if: "${{ contains(github.event.label.name, 'Needs: Repro') }}" - steps: - - uses: actions/github-script@v6 - with: - script: | - await github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `| :warning: | Missing Reproducible Example | - | --- | --- | - | :information_source: | It looks like your issue is missing a reproducible example. Please provide a [Snack](https://snack.expo.dev) or a repository that demonstrates the issue you are reporting in a [minimal, complete, and reproducible](https://stackoverflow.com/help/minimal-reproducible-example) manner. |`, - }) - await github.rest.issues.addLabels({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: ['Needs: Author Feedback'] - }) - unsupported-version: - runs-on: ubuntu-latest - if: "${{ contains(github.event.label.name, 'Type: Unsupported Version') }}" - steps: - - uses: actions/github-script@v6 - with: - script: | - await github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `It looks like your issue or the example you provided uses an [unsupported version of React Native](https://github.com/reactwg/react-native-releases/blob/main/README.md#releases-support-policy). Due to the amount of issues we receive, we're currently accepting only new issues against one of the supported version. Please open your issue on [StackOverflow](https://stackoverflow.com/questions/tagged/react-native) to get further community support.`, - }) - await github.rest.issues.addLabels({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: ['Needs: Author Feedback'] - }) - type-expo: + const verifyVersion = require('./.github/workflows/verifyVersion.js') + const labelToAdd = await verifyVersion(github, context); + + if(labelToAdd) { + await github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: [labelToAdd] + }) + + const actOnLabel = require('./.github/workflows/actOnLabel.js') + await actOnLabel(github, context, labelToAdd) + } + # Reacts to the label that triggered this workflow (added manually or via other workflows) + act-on-label: runs-on: ubuntu-latest - if: "${{ contains(github.event.label.name, 'Type: Expo') }}" steps: + - uses: actions/checkout@v3 - uses: actions/github-script@v6 with: script: | - await github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `It looks like your issue is related to Expo and not React Native core. Please open your issue on an [Expo's repository](https://github.com/expo/expo/issues/new). If you are able to create a repro that showcases that this issue is also happening in React Native vanilla, we will be happy to re-open.`, - }) - await github.rest.issues.update({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - state: "closed", - }) + const actOnLabel = require('./.github/workflows/actOnLabel.js') + await actOnLabel(github, context, context.payload.label.name) diff --git a/.github/workflows/verifyVersion.js b/.github/workflows/verifyVersion.js new file mode 100644 index 00000000000000..da66a1f2f0f700 --- /dev/null +++ b/.github/workflows/verifyVersion.js @@ -0,0 +1,117 @@ +/** + * 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 + */ + +module.exports = async (github, context) => { + const issue = context.payload.issue; + + // Ignore issues using upgrade template (they use a special label) + if (issue.labels.find(label => label.name == 'Type: Upgrade Issue')) { + return; + } + + const issueVersionUnparsed = + getReactNativeVersionFromIssueBodyIfExists(issue); + const issueVersion = parseVersionFromString(issueVersionUnparsed); + + // Nightly versions are always supported + if (reportedVersionIsNightly(issueVersionUnparsed, issueVersion)) return; + + if (!issueVersion) { + return 'Needs: Version Info'; + } + + // Ensure the version matches one we support + const recentReleases = ( + await github.rest.repos.listReleases({ + owner: context.repo.owner, + repo: context.repo.repo, + }) + ).data.map(release => release.name); + + const latestRelease = ( + await github.rest.repos.getLatestRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + }) + ).data; + const latestVersion = parseVersionFromString(latestRelease.name); + + if (!isVersionSupported(issueVersion, latestVersion)) { + return 'Type: Unsupported Version'; + } + + // We want to encourage users to repro the issue on the highest available patch for the given minor. + const latestPatchForVersion = getLatestPatchForVersion( + issueVersion, + recentReleases, + ); + if (latestPatchForVersion > issueVersion.patch) { + return 'Needs: Verify on Latest Version'; + } +}; + +// We support N-2 minor versions, and the latest major. +function isVersionSupported(actualVersion, latestVersion) { + return ( + actualVersion.major >= latestVersion.major && + actualVersion.minor >= latestVersion.minor - 2 + ); +} + +// Assumes that releases are sorted in the order of recency (i.e. most recent releases are earlier in the list) +// This enables us to stop looking as soon as we find the first release with a matching major/minor version, since +// we know it's the most recent release, therefore the highest patch available. +function getLatestPatchForVersion(version, releases) { + for (releaseName of releases) { + const release = parseVersionFromString(releaseName); + if ( + release && + release.major == version.major && + release.minor == version.minor + ) { + return release.patch; + } + } +} + +function getReactNativeVersionFromIssueBodyIfExists(issue) { + if (!issue || !issue.body) return; + const rnVersionRegex = /React Native Version[\r\n]+(?.+)[\r\n]*/; + const rnVersionMatch = issue.body.match(rnVersionRegex); + if (!rnVersionMatch || !rnVersionMatch.groups.version) return; + return rnVersionMatch.groups.version; +} + +function reportedVersionIsNightly(unparsedVersionString, version) { + if (!unparsedVersionString && !version) return false; + const nightlyRegex = /nightly/i; + const nightlyMatch = unparsedVersionString.match(nightlyRegex); + const versionIsNightly = nightlyMatch && nightlyMatch[0]; + + const versionIsZero = + version && version.major == 0 && version.minor == 0 && version.patch == 0; + + return versionIsZero || versionIsNightly; +} + +function parseVersionFromString(version) { + if (!version) return; + // This will match the standard x.x.x semver format, as well as the non-standard prerelease x.x.x-rc.x + const semverRegex = + /(?\d+)\.(?\d+)\.(?\d+)(-[rR]{1}[cC]{1}\.(?\d+))?/; + const versionMatch = version.match(semverRegex); + if (!versionMatch) return; + const {major, minor, patch, prerelease} = versionMatch.groups; + return { + major: parseInt(major), + minor: parseInt(minor), + patch: parseInt(patch), + prerelease: parseInt(prerelease), + }; +}