test: Phase 4B GitHub API Integration Tests (118 tests) #717
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: Branch Name Validation | |
| on: | |
| pull_request: | |
| types: [opened, reopened, synchronize] | |
| permissions: | |
| pull-requests: write | |
| checks: write | |
| jobs: | |
| validate-branch-name: | |
| name: Validate Branch Name | |
| runs-on: ubuntu-latest | |
| permissions: | |
| pull-requests: write | |
| checks: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version-file: ".nvmrc" | |
| cache: "npm" | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Validate branch name | |
| id: validate | |
| env: | |
| BRANCH_NAME: ${{ github.head_ref }} | |
| TARGET_BRANCH: ${{ github.base_ref }} | |
| PR_AUTHOR: ${{ github.event.pull_request.user.login }} | |
| run: | | |
| # Check exemptions: release/* and hotfix/* are allowed on main | |
| if [[ "$TARGET_BRANCH" == "main" ]] && [[ "$BRANCH_NAME" =~ ^(release|hotfix)/ ]]; then | |
| echo "valid=true" >> $GITHUB_OUTPUT | |
| echo "message=Branch is exempt (release or hotfix on main)" | |
| exit 0 | |
| fi | |
| # Exempt dependabot and renovate PRs from branch name validation | |
| if [[ "$PR_AUTHOR" =~ ^(dependabot\[bot\]|app/dependabot|renovate\[bot\]|app/renovate)$ ]] || [[ "$BRANCH_NAME" =~ ^(dependabot|renovate)/ ]]; then | |
| echo "valid=true" >> $GITHUB_OUTPUT | |
| echo "message=Branch is exempt (automated bot)" | |
| exit 0 | |
| fi | |
| # Run validation script | |
| if node scripts/validation/validate-branch-name.cjs "$BRANCH_NAME" 2>&1 | tee validation-output.txt; then | |
| echo "valid=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "valid=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Post validation result | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require('fs'); | |
| const branch = '${{ github.head_ref }}'; | |
| const valid = '${{ steps.validate.outputs.valid }}' === 'true'; | |
| const targetBranch = '${{ github.base_ref }}'; | |
| // Read validation output if validation failed | |
| let validationOutput = ''; | |
| if (!valid && fs.existsSync('validation-output.txt')) { | |
| validationOutput = fs.readFileSync('validation-output.txt', 'utf8'); | |
| } | |
| // Build status message | |
| const validationResults = { | |
| conclusion: valid ? 'success' : 'failure', | |
| output: { | |
| title: valid ? '✅ Branch Name Valid' : '❌ Branch Name Invalid', | |
| summary: valid | |
| ? `Branch \`${branch}\` follows the LightSpeed branching strategy.` | |
| : `Branch \`${branch}\` does not follow the required naming pattern.`, | |
| text: !valid | |
| ? `## Required Format\n\n\`\`\`\n{type}/{scope}-{short-title}\n\`\`\`\n\n### Validation Error\n\n${validationOutput}\n\n### Examples of Valid Branch Names\n\n- \`feat/branch-naming-enforcement\`\n- \`fix/validation-script-bug\`\n- \`chore/update-dependencies\`\n- \`docs/branching-strategy-guide\`\n- \`hotfix/critical-security-patch\`\n\n### More Information\n\nSee [docs/BRANCHING_STRATEGY.md](https://github.com/${{ github.repository }}/blob/${{ github.base_ref }}/docs/BRANCHING_STRATEGY.md) for the complete branching guide.` | |
| : '', | |
| }, | |
| }; | |
| // Create or update check run | |
| const checkRuns = await github.rest.checks.listForRef({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: context.payload.pull_request.head.sha, | |
| check_name: 'Branch Name Validation', | |
| }); | |
| const existingCheckRun = checkRuns.data.check_runs.find( | |
| (check) => check.name === 'Branch Name Validation' | |
| ); | |
| if (existingCheckRun) { | |
| await github.rest.checks.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| check_run_id: existingCheckRun.id, | |
| status: 'completed', | |
| conclusion: validationResults.conclusion, | |
| output: validationResults.output, | |
| }); | |
| } else { | |
| await github.rest.checks.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| name: 'Branch Name Validation', | |
| head_sha: context.payload.pull_request.head.sha, | |
| status: 'completed', | |
| conclusion: validationResults.conclusion, | |
| output: validationResults.output, | |
| }); | |
| } | |
| // Post a comment on failure | |
| if (!valid) { | |
| const commentBody = [ | |
| '## ❌ Branch Name Validation Failed', | |
| '', | |
| `The branch name \`${branch}\` does not follow the LightSpeed branching strategy.`, | |
| '', | |
| '### Required Format', | |
| '', | |
| '```', | |
| '{type}/{scope}-{short-title}', | |
| '```', | |
| '', | |
| '- **type**: one of the allowed prefixes (lowercase)', | |
| '- **scope**: lowercase, hyphens only (no underscores or uppercase)', | |
| '- **title**: lowercase, hyphens only (no underscores or uppercase)', | |
| '', | |
| '### Allowed Branch Types', | |
| '', | |
| '`feat`, `fix`, `hotfix`, `release`, `refactor`, `chore`, `docs`, `test`, `perf`, `ci`, `build`, `deps`, `security`, `revert`, `research`, `design`, `a11y`, `ux`, `i18n`, `ops`, `proto`, `ds`, `api`, `schema`, `telemetry`, `content`, `seo`, `config`, `migrate`, `qa`, `uat`, `audit`, `codex`', | |
| '', | |
| '### Valid Examples', | |
| '', | |
| '- ✓ `feat/branch-naming-enforcement`', | |
| '- ✓ `fix/validation-script-bug`', | |
| '- ✓ `chore/update-dependencies`', | |
| '- ✓ `docs/branching-strategy-guide`', | |
| '- ✓ `hotfix/critical-security-patch`', | |
| '', | |
| '### Invalid Examples', | |
| '', | |
| '- ✗ `claude/my-branch` (type "claude" not allowed)', | |
| '- ✗ `Feature/MyBranch` (uppercase not allowed)', | |
| '- ✗ `fix-bug` (missing type prefix)', | |
| '- ✗ `feat/my_feature` (underscores not allowed)', | |
| '- ✗ `feat/MyFeature` (uppercase not allowed)', | |
| '', | |
| '### Solution', | |
| '', | |
| 'Rename your branch to follow the pattern and update the PR.', | |
| '', | |
| 'For more information, see [docs/BRANCHING_STRATEGY.md](https://github.com/${{ github.repository }}/blob/${{ github.base_ref }}/docs/BRANCHING_STRATEGY.md).', | |
| ].join('\n'); | |
| // Check if comment already exists to avoid duplicates | |
| const comments = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const existingComment = comments.data.find( | |
| (comment) => comment.body.includes('Branch Name Validation Failed') && comment.user.type === 'Bot' | |
| ); | |
| if (existingComment) { | |
| // Update existing comment | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existingComment.id, | |
| body: commentBody, | |
| }); | |
| } else { | |
| // Create new comment | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: commentBody, | |
| }); | |
| } | |
| } | |
| - name: Set status check status | |
| if: always() | |
| env: | |
| VALID: ${{ steps.validate.outputs.valid }} | |
| run: | | |
| if [[ "$VALID" == "true" ]]; then | |
| echo "✅ Branch name validation passed" | |
| exit 0 | |
| else | |
| echo "❌ Branch name validation failed" | |
| exit 1 | |
| fi |