Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions .github/workflows/verify-release-pointer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Verify release pointer

# Validates edits to version.yaml — the release pointer that selects the
# published image to deploy. Runs on every pull request (no paths filter) so it
# always reports a status; the job itself no-ops when version.yaml is untouched.
on: pull_request

permissions:
contents: read
id-token: write

env:
AWS_REGION: eu-central-1
ECR_REPOSITORY: wallets-list
# Read-only ECR role, assumable only from pull_request events of this
# repository. Fork PRs cannot mint the OIDC token, so pointer changes from
# forks fail closed and must be re-raised by a maintainer.
ROLE_ARN: arn:aws:iam::990678687129:role/gha-ecr-read-wallets-list

jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # full history for ancestry checks

- name: Detect version.yaml change
id: changed
env:
BASE_REF: ${{ github.base_ref }} # pass via env, never inline in run:
run: |
git fetch --no-tags origin "$BASE_REF"
if [ -n "$(git diff --name-only "origin/$BASE_REF...HEAD" -- version.yaml)" ]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "version.yaml untouched; nothing to verify."
echo "changed=false" >> "$GITHUB_OUTPUT"
fi

- name: Validate schema
if: steps.changed.outputs.changed == 'true'
run: |
# Exactly one top-level key `release`, with exactly `branch` + `sha`.
KEYS=$(yq -r '. | keys | join(",")' version.yaml)
[ "$KEYS" = "release" ] || { echo "::error::version.yaml must have exactly one top-level key 'release' (got: $KEYS)"; exit 1; }
RKEYS=$(yq -r '.release | keys | sort | join(",")' version.yaml)
[ "$RKEYS" = "branch,sha" ] || { echo "::error::release must have exactly 'branch' and 'sha' (got: $RKEYS)"; exit 1; }

BRANCH=$(yq -r '.release.branch' version.yaml)
SHA=$(yq -r '.release.sha' version.yaml)
echo "$BRANCH" | grep -Eq '^(main|release/.+)$' || { echo "::error::release.branch must match ^(main|release/.+)$ (got: $BRANCH)"; exit 1; }
echo "$SHA" | grep -Eq '^[0-9a-f]{7,40}$' || { echo "::error::release.sha must be a 7-40 char hex commit id (got: $SHA)"; exit 1; }

# Export for downstream steps.
{
echo "RELEASE_BRANCH=$BRANCH"
echo "RELEASE_SHA=$SHA"
} >> "$GITHUB_ENV"

- name: Verify provenance
if: steps.changed.outputs.changed == 'true'
run: |
# The pointed sha must resolve to a commit AND be reachable from its branch.
git rev-parse --verify "${RELEASE_SHA}^{commit}" >/dev/null 2>&1 \
|| { echo "::error::sha not reachable from branch"; exit 1; }
git fetch --no-tags origin "${RELEASE_BRANCH}"
git merge-base --is-ancestor "$RELEASE_SHA" "origin/${RELEASE_BRANCH}" \
|| { echo "::error::sha not reachable from branch"; exit 1; }

- name: Configure AWS credentials
if: steps.changed.outputs.changed == 'true'
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}

- name: Verify published image exists
if: steps.changed.outputs.changed == 'true'
run: |
# Load-bearing gate: the release pointer must reference an image that
# has actually been published. Tag is the exact sha string from the file.
aws ecr describe-images \
--repository-name "$ECR_REPOSITORY" \
--image-ids imageTag="$RELEASE_SHA" >/dev/null 2>&1 \
|| { echo "::error::no published image for this sha"; exit 1; }
echo "Published image found for $RELEASE_SHA."
Loading