diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml new file mode 100644 index 000000000..65c488648 --- /dev/null +++ b/.github/workflows/validate-pr.yml @@ -0,0 +1,59 @@ +name: Validate PR + +on: + pull_request: + types: [opened, edited, synchronize, labeled, unlabeled] + +jobs: + validate: + runs-on: ubuntu-latest + permissions: + pull-requests: read + steps: + - name: Ensure PR has a valid type + uses: actions/github-script@v7 + with: + script: | + const title = context.payload.pull_request.title; + const prefixMatch = title.match(/^(\w+)(?:\([^)]*\))?(!)?:/); + const prefix = prefixMatch?.[1]?.toLowerCase(); + + const validPrefixes = [ + 'feat', 'fix', 'perf', 'test', 'docs', + 'build', 'ci', 'refactor', 'task', 'chore', 'style', + ]; + + const hasValidPrefix = validPrefixes.includes(prefix); + const isBreaking = prefixMatch?.[2] === '!'; + + const { data: labels } = await github.rest.issues.listLabelsOnIssue({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + }); + + const typeLabels = [ + '[Type] Enhancement', + '[Type] Bug', + '[Type] Performance', + '[Type] Automated Testing', + '[Type] Developer Documentation', + '[Type] Build Tooling', + '[Type] Task', + '[Type] Breaking Change', + '[Type] Regression', + ]; + + const hasTypeLabel = labels.some((l) => typeLabels.includes(l.name)); + + if (hasValidPrefix || hasTypeLabel) { + const method = hasValidPrefix ? 'Conventional Commits prefix' : 'type label'; + core.info(`✅ PR has a valid type via ${method}`); + return; + } + + core.setFailed( + 'PR must have either a Conventional Commits title prefix (e.g. feat:, fix:, docs:) ' + + 'or a [Type] label applied manually.\n\n' + + 'See docs/code/developer-workflows.md for details.' + ); diff --git a/docs/releases.md b/docs/releases.md index 4b0118b27..dd82f1068 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -39,18 +39,5 @@ The script: After the release is created, it is ready for integration into the WordPress app. -## Release Notes - -GitHub automatically generates release notes when a release is created. Notes are organized into the following categories based on PR labels: - -- **Breaking Changes** — `[Type] Breaking Change` -- **Features & Enhancements** — `[Type] Enhancement` -- **Bug Fixes** — `[Type] Bug`, `[Type] Regression` -- **Other Changes** — everything else (excludes `[Type] Automated Testing`, `[Type] Build Tooling`, `[Type] Task`, `[Type] Developer Documentation`, and `dependencies`) - -Dependabot dependency bump PRs are excluded automatically. - -PRs are labeled automatically based on their Conventional Commits title prefix. See the [Developer Workflows](./code/developer-workflows.md) guide for details on the labeling rules. - [^1]: We increment the version before building and without tagging so that (1) the correct version number is included in the build's [error reporting metadata](https://github.com/wordpress-mobile/GutenbergKit/blob/8195901ec8883125dcfa102abf2b6a2a3962af3e/src/utils/exception-parser.js#L99) and (2) the Git tag includes the latest build output. [^2]: CI tasks create new Android builds for each commit. However, such infrastructure is not yet in place for iOS. Therefore, we must manually create and commit the iOS build. diff --git a/test-pr-423-invalid-prefix.txt b/test-pr-423-invalid-prefix.txt new file mode 100644 index 000000000..e69de29bb