diff --git a/.github/workflows/branch-protection.yml b/.github/workflows/branch-protection.yml index 8dbb1b010..637da8bee 100644 --- a/.github/workflows/branch-protection.yml +++ b/.github/workflows/branch-protection.yml @@ -19,7 +19,7 @@ jobs: : "${BRANCH_NAME:?github.head_ref is required}" # Valid patterns: bugfix/, hotfix/, feature/, infrastructure/, maintenance/, content/ - if [[ ! $BRANCH_NAME =~ ^(bugfix|hotfix|feature|infrastructure|maintenance|content)/[a-z0-9-]+$ ]]; then + if [[ ! $BRANCH_NAME =~ ^(bugfix|hotfix|feature|infrastructure|maintenance|content|dependabot)/[a-z0-9-]+$ ]]; then echo "❌ Invalid branch name: $BRANCH_NAME" echo "" echo "Branch names must follow one of these patterns:" @@ -29,6 +29,7 @@ jobs: echo " - infrastructure/ (e.g., infrastructure/setup-ci)" echo " - maintenance/ (e.g., maintenance/update-dependencies)" echo " - content/ (e.g., content/update-about-page)" + echo " - dependabot/ (machine generated)" echo "" echo "Use lowercase letters, numbers, and hyphens only." exit 1 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8f7d74ed8..81c2d7e0a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,6 +12,7 @@ on: jobs: analyze: name: Analyze Code + if: github.event_name != 'pull_request' || !startsWith(github.head_ref, 'hotfix/') runs-on: ubuntu-latest permissions: actions: read diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 6084c7c99..16f5602ac 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -12,6 +12,7 @@ permissions: pull-requests: write issues: write deployments: write + checks: write jobs: verify-ci: @@ -25,6 +26,14 @@ jobs: with: script: | const runId = context.payload.workflow_run.id; + const workflowRun = context.payload.workflow_run; + const branch = workflowRun?.head_branch ?? ''; + const isHotfix = branch.startsWith('hotfix/'); + if (workflowRun?.event === 'pull_request' && isHotfix) { + core.notice('Hotfix branch: skipping CI verification requirements.'); + return; + } + const requiredJobs = ['Lint', 'Unit Tests']; const { data } = await github.rest.actions.listJobsForWorkflowRun({ owner: context.repo.owner, @@ -50,7 +59,11 @@ jobs: if: >- needs.verify-ci.result == 'success' && github.event.workflow_run.conclusion == 'success' && - github.event.workflow_run.event == 'pull_request' + github.event.workflow_run.event == 'pull_request' && + !startsWith(github.event.workflow_run.head_branch, 'hotfix/') + + outputs: + previewUrl: ${{ steps.vercel-preview.outputs.preview-url }} steps: - name: Checkout repository @@ -257,6 +270,99 @@ jobs: body: `❌ Preview deployment failed.${linkLine}\n\nPlease review the Vercel build logs for details.` }); + publish-preview-status: + name: Publish Preview Status + runs-on: ubuntu-latest + needs: + - verify-ci + - deploy-preview + if: always() && github.event.workflow_run.event == 'pull_request' + steps: + - name: Publish required check run + uses: actions/github-script@v8 + env: + VERIFY_RESULT: ${{ needs.verify-ci.result }} + DEPLOY_RESULT: ${{ needs.deploy-preview.result }} + PREVIEW_URL: ${{ needs.deploy-preview.outputs.previewUrl }} + with: + script: | + const workflowRun = context.payload.workflow_run; + const sha = workflowRun?.head_sha; + if (!sha) { + core.warning('Missing workflow_run.head_sha; cannot publish check run.'); + return; + } + + const branch = workflowRun?.head_branch ?? ''; + const isHotfix = branch.startsWith('hotfix/'); + const verifyResult = process.env.VERIFY_RESULT || 'unknown'; + const deployResult = process.env.DEPLOY_RESULT || 'unknown'; + const previewUrl = (process.env.PREVIEW_URL || '').trim(); + + const checkName = 'Deploy Preview to Vercel'; + const detailsUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + + let conclusion = 'success'; + let summary = ''; + + if (isHotfix) { + conclusion = 'success'; + summary = 'Hotfix branch: preview deploy intentionally skipped.'; + } else if (verifyResult !== 'success') { + conclusion = 'failure'; + summary = `CI verification failed (${verifyResult}).`; + } else if (deployResult === 'success') { + conclusion = 'success'; + summary = previewUrl ? `Preview deployed: ${previewUrl}` : 'Preview deployed.'; + } else { + conclusion = 'failure'; + summary = `Preview deployment failed (${deployResult}).`; + } + + const output = { + title: checkName, + summary: `${summary}\n\nDetails: ${detailsUrl}` + }; + + const owner = context.repo.owner; + const repo = context.repo.repo; + const completed_at = new Date().toISOString(); + + const { data } = await github.rest.checks.listForRef({ + owner, + repo, + ref: sha, + filter: 'latest', + per_page: 100 + }); + + const existing = (data.check_runs || []).find((run) => run.name === checkName); + if (existing) { + await github.rest.checks.update({ + owner, + repo, + check_run_id: existing.id, + status: 'completed', + conclusion, + completed_at, + output, + details_url: detailsUrl + }); + return; + } + + await github.rest.checks.create({ + owner, + repo, + name: checkName, + head_sha: sha, + status: 'completed', + conclusion, + completed_at, + output, + details_url: detailsUrl + }); + deploy-production: name: Deploy to Production (Vercel) runs-on: ubuntu-latest diff --git a/.husky/scripts/check-branch-name.sh b/.husky/scripts/check-branch-name.sh index 0cb9d9f8e..a624f1f95 100755 --- a/.husky/scripts/check-branch-name.sh +++ b/.husky/scripts/check-branch-name.sh @@ -4,7 +4,7 @@ branch=$(git symbolic-ref --short HEAD) # Define valid branch name patterns -valid_patterns="^(bugfix|hotfix|feature|infrastructure|content|maintenance)\/[a-z0-9-]+$" +valid_patterns="^(bugfix|hotfix|feature|infrastructure|content|maintenance|dependabot)\/[a-z0-9-]+$" # Allow main and develop branches (for merges) if [ "$branch" = "main" ] || [ "$branch" = "develop" ]; then @@ -22,6 +22,7 @@ if ! echo "$branch" | grep -Eq "$valid_patterns"; then echo " - infrastructure/ (e.g., infrastructure/setup-ci)" echo " - maintenance/ (e.g., maintenance/update-dependencies)" echo " - content/ (e.g., content/update-about-page)" + echo " - dependabot/ (machine generated)" echo "" echo "Use lowercase letters, numbers, and hyphens only." echo ""