👷 ci: add PR quality gates, commit message enforcement, and QA ICU fix #5
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Commit Message Check | |
| on: | |
| pull_request: | |
| types: [opened, edited, reopened, synchronize] | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| jobs: | |
| pr-title: | |
| name: Validate PR title (required) | |
| runs-on: ubuntu-latest | |
| if: github.event.pull_request.draft == false | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Validate PR title | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const { validateCommitMessage } = require('./.github/scripts/commit-message-check.js'); | |
| const pr = context.payload.pull_request; | |
| const labels = (pr.labels || []).map((l) => l.name); | |
| if (labels.includes('commit-message-exception')) { | |
| core.notice('Bypassing PR title check — label: commit-message-exception'); | |
| return; | |
| } | |
| const result = validateCommitMessage(pr.title); | |
| if (!result.valid) { | |
| const lines = [ | |
| '## PR title does not follow conventional-commit format', | |
| '', | |
| 'Expected: `<emoji> <type>[(<scope>)][!]: <description>`', | |
| '', | |
| '**Errors:**', | |
| ...result.errors.map((e) => `- ${e}`), | |
| '', | |
| 'See `.github/prompts/conventional-commit.prompt.md` for the full spec.', | |
| ]; | |
| core.setFailed(lines.join('\n')); | |
| } | |
| commit-messages: | |
| name: Validate commit messages (hygiene) | |
| runs-on: ubuntu-latest | |
| if: github.event.pull_request.draft == false | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Validate commits | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const { validateCommitMessage } = require('./.github/scripts/commit-message-check.js'); | |
| const pr = context.payload.pull_request; | |
| const labels = (pr.labels || []).map((l) => l.name); | |
| if (labels.includes('commit-message-exception')) { | |
| core.notice('Bypassing commit message check — label: commit-message-exception'); | |
| return; | |
| } | |
| const commits = await github.paginate(github.rest.pulls.listCommits, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr.number, | |
| per_page: 100, | |
| }); | |
| const failures = []; | |
| for (const c of commits) { | |
| const msg = c.commit.message; | |
| const result = validateCommitMessage(msg, {requireEmoji: false}); | |
| if (result.skipped) continue; | |
| if (!result.valid) { | |
| const sha = c.sha.slice(0, 7); | |
| failures.push(`\`${sha}\`: ${result.errors.join('; ')}`); | |
| } | |
| } | |
| if (failures.length > 0) { | |
| const lines = [ | |
| `## ${failures.length} commit message(s) violate conventional-commit format`, | |
| '', | |
| ...failures.map((f) => `- ${f}`), | |
| '', | |
| '> This is a hygiene gate. Fix via interactive rebase or amend.', | |
| '> Add label `commit-message-exception` to bypass.', | |
| ]; | |
| core.setFailed(lines.join('\n')); | |
| } | |
| commit-message-tests: | |
| name: Unit tests for commit-message-check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' | |
| - name: Run tests | |
| run: node --test .github/scripts/commit-message-check.test.js |