|
1 | 1 | name: Sitemap Parity |
2 | 2 |
|
3 | | -# Fires whenever a deployment status changes on any commit — Netlify posts one |
4 | | -# of these every time it finishes a build (both PR previews and prod). |
5 | | -# We only act on successful `deploy-preview` deployments and check every URL |
6 | | -# listed in the current production sitemap against the freshly-deployed |
7 | | -# preview. If a page in prod has become unreachable on the preview, the check |
8 | | -# fails and blocks the PR. |
| 3 | +# Ensures every URL currently listed in the production sitemap is still |
| 4 | +# reachable on the deployment that will be produced by this branch. Runs on: |
| 5 | +# - pull_request -> waits for the Netlify deploy-preview, then checks |
| 6 | +# - push to main -> waits for the Netlify production deploy, then checks |
| 7 | +# |
| 8 | +# In both cases the job blocks until the Netlify deployment for the exact |
| 9 | +# commit under test reports success (via GitHub's Deployments API), then runs |
| 10 | +# the check against that deploy's target URL. If any prod URL is unreachable |
| 11 | +# on the new deploy, the check fails and blocks the merge. |
| 12 | + |
9 | 13 | on: |
10 | | - deployment_status: |
| 14 | + pull_request: |
| 15 | + branches: ['main'] |
| 16 | + push: |
| 17 | + branches: ['main'] |
11 | 18 |
|
12 | 19 | permissions: |
13 | 20 | contents: read |
14 | | - # Needed so failed check runs show up on the PR conversation timeline. |
15 | | - statuses: read |
| 21 | + deployments: read |
16 | 22 |
|
17 | 23 | concurrency: |
18 | | - group: sitemap-parity-${{ github.event.deployment.sha }} |
| 24 | + group: sitemap-parity-${{ github.event.pull_request.head.sha || github.sha }} |
19 | 25 | cancel-in-progress: true |
20 | 26 |
|
21 | 27 | jobs: |
22 | 28 | check: |
23 | | - # Only run when a Netlify deploy preview has just succeeded. |
24 | | - if: >- |
25 | | - github.event.deployment_status.state == 'success' && |
26 | | - startsWith(github.event.deployment.environment, 'deploy-preview') |
27 | 29 | runs-on: ubuntu-latest |
28 | | - timeout-minutes: 10 |
| 30 | + timeout-minutes: 20 |
29 | 31 |
|
30 | 32 | steps: |
31 | | - - name: Checkout code at the deployed commit |
| 33 | + - name: Checkout repository |
32 | 34 | uses: actions/checkout@v4 |
33 | 35 | with: |
34 | | - ref: ${{ github.event.deployment.sha }} |
| 36 | + # For PRs, check out the PR head commit so the script matches what |
| 37 | + # Netlify actually deployed. For pushes, this is the pushed commit. |
| 38 | + ref: ${{ github.event.pull_request.head.sha || github.sha }} |
35 | 39 |
|
36 | 40 | - name: Setup Node.js |
37 | 41 | uses: actions/setup-node@v4 |
38 | 42 | with: |
39 | 43 | node-version: '20' |
40 | 44 |
|
41 | | - - name: Check sitemap parity |
| 45 | + - name: Determine expected Netlify environment |
| 46 | + id: env |
| 47 | + run: | |
| 48 | + if [ "${{ github.event_name }}" = "pull_request" ]; then |
| 49 | + echo "expected=deploy-preview" >> "$GITHUB_OUTPUT" |
| 50 | + echo "sha=${{ github.event.pull_request.head.sha }}" >> "$GITHUB_OUTPUT" |
| 51 | + else |
| 52 | + echo "expected=production" >> "$GITHUB_OUTPUT" |
| 53 | + echo "sha=${{ github.sha }}" >> "$GITHUB_OUTPUT" |
| 54 | + fi |
| 55 | +
|
| 56 | + - name: Wait for Netlify deployment |
| 57 | + id: wait |
42 | 58 | env: |
43 | | - PREVIEW_URL: ${{ github.event.deployment_status.target_url }} |
| 59 | + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 60 | + SHA: ${{ steps.env.outputs.sha }} |
| 61 | + EXPECTED_ENV_PREFIX: ${{ steps.env.outputs.expected }} |
| 62 | + # Total wait budget in seconds (default 15 min) and poll interval. |
| 63 | + WAIT_TIMEOUT: '900' |
| 64 | + WAIT_INTERVAL: '15' |
44 | 65 | run: | |
45 | | - if [ -z "$PREVIEW_URL" ]; then |
46 | | - echo "::error::No preview URL available on this deployment_status event." |
| 66 | + set -euo pipefail |
| 67 | + echo "Waiting for a successful Netlify deployment on commit $SHA ..." |
| 68 | + echo "Expected environment prefix: $EXPECTED_ENV_PREFIX" |
| 69 | +
|
| 70 | + deadline=$(( $(date +%s) + WAIT_TIMEOUT )) |
| 71 | + preview_url="" |
| 72 | +
|
| 73 | + while [ "$(date +%s)" -lt "$deadline" ]; do |
| 74 | + # List deployments GitHub knows about for this SHA. Netlify posts |
| 75 | + # one per build (deploy-preview-<PR#> for PRs, production for main). |
| 76 | + deployments_json=$(gh api \ |
| 77 | + "repos/${{ github.repository }}/deployments?sha=$SHA&per_page=100") |
| 78 | +
|
| 79 | + ids=$(printf '%s' "$deployments_json" \ |
| 80 | + | jq -r --arg prefix "$EXPECTED_ENV_PREFIX" \ |
| 81 | + '.[] | select(.environment | startswith($prefix)) | .id') |
| 82 | +
|
| 83 | + for id in $ids; do |
| 84 | + status_json=$(gh api \ |
| 85 | + "repos/${{ github.repository }}/deployments/$id/statuses?per_page=100") |
| 86 | + latest_state=$(printf '%s' "$status_json" | jq -r '.[0].state // ""') |
| 87 | + latest_target=$(printf '%s' "$status_json" | jq -r '.[0].target_url // ""') |
| 88 | + latest_env_url=$(printf '%s' "$status_json" | jq -r '.[0].environment_url // ""') |
| 89 | +
|
| 90 | + if [ "$latest_state" = "success" ]; then |
| 91 | + # Prefer environment_url (canonical site URL) over target_url |
| 92 | + # (which Netlify sometimes sets to the deploy log page). |
| 93 | + if [ -n "$latest_env_url" ] && [ "$latest_env_url" != "null" ]; then |
| 94 | + preview_url="$latest_env_url" |
| 95 | + else |
| 96 | + preview_url="$latest_target" |
| 97 | + fi |
| 98 | + if [ -n "$preview_url" ] && [ "$preview_url" != "null" ]; then |
| 99 | + echo "Found successful deployment: $id -> $preview_url" |
| 100 | + break 2 |
| 101 | + fi |
| 102 | + elif [ "$latest_state" = "failure" ] || [ "$latest_state" = "error" ]; then |
| 103 | + echo "::error::Netlify deployment $id reported state=$latest_state for commit $SHA." |
| 104 | + exit 1 |
| 105 | + fi |
| 106 | + done |
| 107 | +
|
| 108 | + echo "No successful deployment yet, sleeping ${WAIT_INTERVAL}s ..." |
| 109 | + sleep "$WAIT_INTERVAL" |
| 110 | + done |
| 111 | +
|
| 112 | + if [ -z "$preview_url" ]; then |
| 113 | + echo "::error::Timed out after ${WAIT_TIMEOUT}s waiting for a Netlify deployment on $SHA." |
47 | 114 | exit 1 |
48 | 115 | fi |
| 116 | +
|
| 117 | + echo "preview_url=$preview_url" >> "$GITHUB_OUTPUT" |
| 118 | +
|
| 119 | + - name: Check sitemap parity |
| 120 | + env: |
| 121 | + PREVIEW_URL: ${{ steps.wait.outputs.preview_url }} |
| 122 | + run: | |
49 | 123 | echo "Preview URL: $PREVIEW_URL" |
50 | 124 | node scripts/check-sitemap-parity.mjs \ |
51 | 125 | --prod-base=https://open-elements.com \ |
|
0 commit comments