mirror of
https://github.com/react/react-native.git
synced 2026-09-28 13:23:09 +08:00
Add PR body validation to analyze-pr workflow (#55382)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55382 This adds a `validatePRBody.js` script that checks pull request descriptions for required sections as part of a new `analyze-pr.yml` workflow that will replace part of the Danger-pr workflow. The validation includes: - **Description length check**: Fails if the PR body is missing or less than 50 characters - **Summary section check**: Warns if the PR lacks a "## Summary" section - **Test plan check**: Warns if the PR lacks a "## Test Plan" section - **Changelog validation**: failing if missing or invalid Key behaviors: - Phabricator-sourced PRs (detected via "Differential Revision:" in body) skip summary, test plan, and changelog validation since these are enforced differently - Validation messages use GitHub's `[!WARNING]` and `[!CAUTION]` callout syntax for clear visual feedback - The workflow fails (via `core.setFailed`) when any required check fails - Messages are passed to `postPRComment` alongside API changes for consolidated PR comments This aims to mimic relevant parts of dangerfile.js Changelog: [Internal] Reviewed By: huntie Differential Revision: D91158803 fbshipit-source-id: 2304251d18f9fc8bf9a29e536fff2d979573bd86
This commit is contained in:
committed by
meta-codesync[bot]
parent
6a9d792f9a
commit
5e41043fb5
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const changelogRegex =
|
||||
/\[\s?(ANDROID|GENERAL|IOS|INTERNAL)\s?\]\s?\[\s?(BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY)\s?\]\s*?-?\s*?(.*)/i;
|
||||
|
||||
const internalChangelogRegex = /\[\s?(INTERNAL)\s?\].*/gi;
|
||||
|
||||
function validateChangelog(commitMsg) {
|
||||
if (!commitMsg.toLowerCase().includes('changelog:')) {
|
||||
return 'missing';
|
||||
}
|
||||
const hasValidChangelog = changelogRegex.test(commitMsg);
|
||||
const hasValidInternalChangelog = internalChangelogRegex.test(commitMsg);
|
||||
|
||||
if (hasValidChangelog || hasValidInternalChangelog) {
|
||||
return 'valid';
|
||||
}
|
||||
|
||||
return 'invalid';
|
||||
}
|
||||
|
||||
function validatePRBody(prBody) {
|
||||
const body = prBody ?? '';
|
||||
const bodyLower = body.toLowerCase();
|
||||
const isFromPhabricator = bodyLower.includes('differential revision:');
|
||||
|
||||
const messages = [];
|
||||
let hasFail = false;
|
||||
|
||||
function addWarning(title, text) {
|
||||
messages.push(`> [!WARNING]
|
||||
> **${title}**
|
||||
>
|
||||
> ${text}`);
|
||||
}
|
||||
|
||||
function addFail(title, text) {
|
||||
hasFail = true;
|
||||
messages.push(`> [!CAUTION]
|
||||
> **${title}**
|
||||
>
|
||||
> ${text}`);
|
||||
}
|
||||
|
||||
if (!body || body.length < 50) {
|
||||
addFail('Missing Description', 'This pull request needs a description.');
|
||||
} else {
|
||||
const hasSummary =
|
||||
bodyLower.includes('## summary') || bodyLower.includes('summary:');
|
||||
if (!hasSummary && body.split('\n').length <= 2 && !isFromPhabricator) {
|
||||
addWarning(
|
||||
'Missing Summary',
|
||||
'Please add a "## Summary" section to your PR description. This is a good place to explain the motivation for making this change.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isFromPhabricator) {
|
||||
const hasTestPlan = ['## test plan', 'test plan:', 'tests:', 'test:'].some(
|
||||
t => bodyLower.includes(t),
|
||||
);
|
||||
if (!hasTestPlan) {
|
||||
addWarning(
|
||||
'Missing Test Plan',
|
||||
'Please add a "## Test Plan" section to your PR description. A Test Plan lets us know how these changes were tested.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isFromPhabricator) {
|
||||
const status = validateChangelog(body);
|
||||
const link =
|
||||
'https://reactnative.dev/contributing/changelogs-in-pull-requests';
|
||||
if (status === 'missing') {
|
||||
addFail(
|
||||
'Missing Changelog',
|
||||
`Please add a Changelog to your PR description. See [Changelog format](${link})`,
|
||||
);
|
||||
} else if (status === 'invalid') {
|
||||
addFail(
|
||||
'Invalid Changelog Format',
|
||||
`Please verify your Changelog format. See [Changelog format](${link})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
message: messages.join('\n\n'),
|
||||
status: hasFail ? 'FAIL' : 'PASS',
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = validatePRBody;
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Analyze Pull Request
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
analyze-pr:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'facebook/react-native'
|
||||
steps:
|
||||
- name: Check out main branch
|
||||
uses: actions/checkout@v6
|
||||
- name: Setup Node.js
|
||||
uses: ./.github/actions/setup-node
|
||||
- name: Run yarn install
|
||||
uses: ./.github/actions/yarn-install
|
||||
- name: Check PR body
|
||||
id: check-pr-body
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const validatePRBody = require('./.github/workflow-scripts/validatePRBody.js');
|
||||
const {message, status} = validatePRBody(context.payload.pull_request.body);
|
||||
core.setOutput('message', message);
|
||||
core.setOutput('status', status);
|
||||
- name: Post PR comment
|
||||
uses: ./.github/actions/post-pr-comment
|
||||
with:
|
||||
marker: '<!-- analyze-pr -->'
|
||||
sections: '[${{ toJSON(steps.check-pr-body.outputs.message) }}]'
|
||||
- name: Fail if validation errors
|
||||
if: steps.check-pr-body.outputs.status == 'FAIL'
|
||||
run: exit 1
|
||||
Reference in New Issue
Block a user