feat: add custom theme mode foundation wiring (R552) #823
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: validate-pr | |
| on: | |
| pull_request: | |
| types: | |
| - opened | |
| - edited | |
| - reopened | |
| - synchronize | |
| - ready_for_review | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| jobs: | |
| validate-pr: | |
| name: validate-pr | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Validate PR title and body | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const pr = context.payload.pull_request; | |
| const errors = []; | |
| const title = pr.title || ""; | |
| const body = pr.body || ""; | |
| const conventionalTitle = | |
| /^(feat|fix|docs|chore|refactor|test|build|ci|perf|revert)(\([^)]+\))?:\s.+\(R\d+\)$/i; | |
| if (!conventionalTitle.test(title)) { | |
| errors.push("PR title must follow Conventional Commits and include ticket ID (e.g. `fix: short summary (R123)`)."); | |
| } | |
| if (/\b(wip|dnm|do not merge)\b/i.test(title)) { | |
| errors.push("PR title cannot contain WIP/DNM markers."); | |
| } | |
| const normalizedBody = body | |
| .replace(/<!--[\s\S]*?-->/g, "") | |
| .trim(); | |
| if (normalizedBody.length < 50) { | |
| errors.push("PR description is too short. Please use the template."); | |
| } | |
| const requiredSections = [ | |
| "## Objective", | |
| "## Scope", | |
| "## Verification", | |
| "## Risk", | |
| ]; | |
| for (const section of requiredSections) { | |
| if (!normalizedBody.includes(section)) { | |
| errors.push(`PR description is missing required section: ${section}`); | |
| } | |
| } | |
| const closesIssuePattern = | |
| /\b(close[sd]?|fix(e[sd])?|resolve[sd]?)\s+((?:[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)?#\d+|https?:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]\/issues\/\d+)\b/i; | |
| if (!closesIssuePattern.test(normalizedBody)) { | |
| errors.push( | |
| "PR description must include an issue auto-close reference (for example: `Closes #123`).", | |
| ); | |
| } | |
| const highRiskPattern = /\b(Priority:\s*P0|Risk\s*Tier:\s*T3)\b/i; | |
| if (highRiskPattern.test(normalizedBody)) { | |
| const rollbackSection = normalizedBody.split("## Rollback")[1] || ""; | |
| const rollbackContent = rollbackSection.split("##")[0].trim(); | |
| if ( | |
| rollbackContent.length < 20 || | |
| rollbackContent.includes("How to safely disable or revert") | |
| ) { | |
| errors.push( | |
| "High-risk PRs (P0 or T3) must include detailed rollback notes in the ## Rollback section.", | |
| ); | |
| } | |
| } | |
| if (errors.length > 0) { | |
| core.setFailed(errors.map((e) => `- ${e}`).join("\n")); | |
| } else { | |
| core.info("PR policy checks passed."); | |
| } |