Restore visible focus outlines for keyboard accessibility #299
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: PR Category Check | |
| # Ensures every PR has at least one category checkbox checked. | |
| # Automatically applies matching GitHub labels to the PR. | |
| # Creates labels with custom colors if they don't exist yet. | |
| # Uses pull_request_target so it works for fork PRs too (runs in base repo context). | |
| # This is safe because we only read the PR body from the event payload — no fork code is checked out or executed. | |
| on: | |
| pull_request_target: | |
| types: [opened, edited, synchronize, reopened] | |
| permissions: | |
| pull-requests: write | |
| contents: read | |
| jobs: | |
| check-pr-category: | |
| name: Validate PR Category & Auto-Label | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check categories and apply labels | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const prBody = context.payload.pull_request.body || ''; | |
| const prNumber = context.payload.pull_request.number; | |
| // Category checkboxes mapped to their GitHub label names, colors, and descriptions | |
| const categories = [ | |
| { | |
| label: 'bug fix', | |
| color: 'D73A4A', | |
| description: 'Fixes a bug or incorrect behavior', | |
| displayName: 'Bug Fix', | |
| pattern: /-\s*\[x\]\s*Bug Fix/i, | |
| }, | |
| { | |
| label: 'feature', | |
| color: '9333EA', | |
| description: 'Adds new functionality', | |
| displayName: 'Feature', | |
| pattern: /-\s*\[x\]\s*Feature/i, | |
| }, | |
| { | |
| label: 'performance', | |
| color: 'F97316', | |
| description: 'Improves performance (load time, memory, rendering)', | |
| displayName: 'Performance', | |
| pattern: /-\s*\[x\]\s*Performance/i, | |
| }, | |
| { | |
| label: 'tests', | |
| color: '3B82F6', | |
| description: 'Adds or updates test coverage', | |
| displayName: 'Tests', | |
| pattern: /-\s*\[x\]\s*Tests/i, | |
| }, | |
| { | |
| label: 'documentation', | |
| color: '10B981', | |
| description: 'Updates to docs, comments, or README', | |
| displayName: 'Documentation', | |
| pattern: /-\s*\[x\]\s*Documentation/i, | |
| }, | |
| ]; | |
| const checkedCategories = categories.filter(cat => cat.pattern.test(prBody)); | |
| const uncheckedCategories = categories.filter(cat => !cat.pattern.test(prBody)); | |
| // --- Step 1: Fail CI if no category is selected --- | |
| if (checkedCategories.length === 0) { | |
| const message = [ | |
| '## PR Category Required', | |
| '', | |
| 'This pull request does not have any **PR Category** selected.', | |
| 'Please edit your PR description and check **at least one** category checkbox:', | |
| '', | |
| '| Category | Description |', | |
| '|----------|-------------|', | |
| '| Bug Fix | Fixes a bug or incorrect behavior |', | |
| '| Feature | Adds new functionality |', | |
| '| Performance | Improves performance |', | |
| '| Tests | Adds or updates test coverage |', | |
| '| Documentation | Updates to docs, comments, or README |', | |
| '', | |
| 'Example: Change `- [ ] Bug Fix` to `- [x] Bug Fix`', | |
| '', | |
| '> **Tip:** You can select multiple categories if your PR spans several areas.', | |
| ].join('\n'); | |
| core.setFailed(message); | |
| return; | |
| } | |
| // --- Step 2: Ensure labels exist with proper colors --- | |
| const { data: existingLabels } = await github.rest.issues.listLabelsForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| per_page: 100, | |
| }); | |
| const existingLabelNames = existingLabels.map(l => l.name); | |
| for (const cat of categories) { | |
| if (!existingLabelNames.includes(cat.label)) { | |
| try { | |
| await github.rest.issues.createLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| name: cat.label, | |
| color: cat.color, | |
| description: cat.description, | |
| }); | |
| core.info(`Created label: "${cat.label}" with color #${cat.color}`); | |
| } catch (error) { | |
| core.warning(`Could not create label "${cat.label}". Error: ${error.message}`); | |
| } | |
| } | |
| } | |
| // --- Step 3: Auto-apply labels for checked categories --- | |
| const labelsToAdd = checkedCategories.map(cat => cat.label); | |
| const labelsToRemove = uncheckedCategories.map(cat => cat.label); | |
| // Get current labels on the PR | |
| const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| }); | |
| const currentLabelNames = currentLabels.map(l => l.name); | |
| // Add labels that are checked but not yet on the PR | |
| for (const label of labelsToAdd) { | |
| if (!currentLabelNames.includes(label)) { | |
| try { | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| labels: [label], | |
| }); | |
| core.info(`Added label: "${label}"`); | |
| } catch (error) { | |
| core.warning(`Could not add label "${label}". Error: ${error.message}`); | |
| } | |
| } | |
| } | |
| // Remove labels that are unchecked but still on the PR | |
| for (const label of labelsToRemove) { | |
| if (currentLabelNames.includes(label)) { | |
| try { | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| name: label, | |
| }); | |
| core.info(`Removed label: "${label}"`); | |
| } catch (error) { | |
| core.warning(`Could not remove label "${label}". Error: ${error.message}`); | |
| } | |
| } | |
| } | |
| const selected = checkedCategories.map(c => c.displayName).join(', '); | |
| core.info(`PR categories selected: ${selected}`); | |
| core.info(`Labels synced: ${labelsToAdd.join(', ')}`); |