How to Verify the Deployed Version Matches the Git SHA in GitHub Actions
A deploy can report success while an old image still serves traffic, so verify a /version endpoint returns the same commit SHA the workflow built.
Bake the commit SHA into the build, expose it at /version, then in a verify step fetch that value and assert it equals github.sha before promoting.
Steps
- Inject
github.shainto the build (env var or build arg). - Serve it from a
/versionJSON endpoint. - After deploy, fetch
/versionand compare against${{ github.sha }}.
Workflow
- name: Verify deployed commit
env:
BASE: ${{ vars.DEPLOY_URL }}
EXPECTED: ${{ github.sha }}
run: |
ACTUAL=$(curl -sf "$BASE/version" | jq -r .sha)
echo "expected=$EXPECTED actual=$ACTUAL"
if [ "$ACTUAL" != "$EXPECTED" ]; then
echo "deployed version does not match; aborting"; exit 1
fiGotchas
- A CDN or reverse proxy may cache
/version; request with a cache-busting header orCache-Control: no-cache. - If the SHA does not match, do not promote; roll back to the known-good version.
Verify it actually works
A workflow that runs is not a workflow that works. Confirm the behaviour on a real event rather than on a manual dispatch, because trigger conditions, permissions, and context values all differ between the two.
# 1. validate the file before pushing
docker run --rm -v "$(pwd):/repo" --workdir /repo rhysd/actionlint:latest -color
# 2. trigger the real event, not workflow_dispatch
git commit --allow-empty -m "ci: verify trigger" && git push
# 3. watch it and read the conclusion, not just the colour
gh run watch
gh run view --log-failedWhat usually goes wrong first
- The workflow file must exist on the default branch before scheduled or dispatch triggers appear at all.
GITHUB_TOKENpermissions default to read-only in many organisations. Declare apermissions:block listing every scope the job needs.- Fork pull requests get a read-only token and no access to secrets, regardless of workflow configuration.
actions/checkoutgives you depth 1 on a detached HEAD, so anything needing history or a branch name needsfetch-depth: 0.