name: Post-publish verify on: workflow_call: inputs: image_ref: description: Exact registry repository to verify required: true type: string image_tag: description: Exact published release tag required: true type: string image_digest: description: Exact published multi-arch manifest digest required: true type: string build_sha: description: Expected short source commit SHA required: true type: string workflow_dispatch: inputs: image_ref: description: Exact registry repository to verify required: true type: string image_tag: description: Exact published release tag required: true type: string image_digest: description: Exact published multi-arch manifest digest required: true type: string build_sha: description: Expected short source commit SHA required: true type: string permissions: contents: read packages: read jobs: verify: name: Pull and smoke-test exact published image runs-on: ubuntu-latest timeout-minutes: 10 env: IMAGE_REF: ${{ inputs.image_ref }} IMAGE_TAG: ${{ inputs.image_tag }} IMAGE_DIGEST: ${{ inputs.image_digest }} BUILD_SHA: ${{ inputs.build_sha }} DATABASE_URL: postgresql://healthlog:healthlog@localhost:5432/healthlog?schema=public services: postgres: image: postgres:16-alpine env: POSTGRES_USER: healthlog POSTGRES_PASSWORD: healthlog POSTGRES_DB: healthlog ports: - 5432:5432 options: >- --health-cmd "pg_isready -U healthlog" --health-interval 5s --health-timeout 5s --health-retries 10 steps: - name: Log in to GHCR uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Pull exact image run: | TAG_REF="${IMAGE_REF}:${IMAGE_TAG}" DIGEST_REF="${IMAGE_REF}@${IMAGE_DIGEST}" echo "Pulling published tag $TAG_REF" docker pull "$TAG_REF" resolved_digest="$(docker buildx imagetools inspect "$TAG_REF" --format '{{json .Manifest.Digest}}' | tr -d '"')" if [ "$resolved_digest" != "$IMAGE_DIGEST" ]; then echo "::error::Published tag digest mismatch: $TAG_REF resolved to '$resolved_digest', expected '$IMAGE_DIGEST'." >&2 exit 1 fi echo "Pulling immutable image $DIGEST_REF" docker pull "$DIGEST_REF" - name: Run migrations run: | DIGEST_REF="${IMAGE_REF}@${IMAGE_DIGEST}" docker run --rm \ --network host \ --entrypoint node \ -e DATABASE_URL="$DATABASE_URL" \ "$DIGEST_REF" \ /opt/prisma-cli/node_modules/prisma/build/index.js migrate deploy - name: Start exact image run: | DIGEST_REF="${IMAGE_REF}@${IMAGE_DIGEST}" docker run -d --name healthlog-verify \ --network host \ -e DATABASE_URL="$DATABASE_URL" \ -e ENCRYPTION_KEYS='{"v1":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}' \ -e ENCRYPTION_ACTIVE_KEY_ID=v1 \ -e API_TOKEN_HMAC_KEY=cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc \ -e AUTH_RP_NAME=HealthLog \ -e AUTH_RP_ID=localhost \ -e AUTH_RP_ORIGIN=http://localhost:3000 \ -e NODE_ENV=production \ -e PORT=3000 \ "$DIGEST_REF" - name: Verify exact version run: | body="" for i in $(seq 1 30); do if body="$(curl -sf http://localhost:3000/api/version)"; then echo "Version endpoint ready after $((i * 2))s" break fi sleep 2 done if [ -z "$body" ]; then echo "::error::Exact image did not expose /api/version within 60 seconds." >&2 docker logs healthlog-verify || true exit 1 fi EXPECTED_VERSION="${IMAGE_TAG#v}" echo "$body" | jq . if ! jq -e --arg expected "$EXPECTED_VERSION" '.data.version == $expected' <<< "$body" >/dev/null; then actual="$(jq -r '.data.version // ""' <<< "$body")" echo "::error::Version mismatch: exact image reports '$actual', expected '$EXPECTED_VERSION' (release tag '$IMAGE_TAG')." >&2 exit 1 fi if ! jq -e --arg expected "$BUILD_SHA" '.data.buildSha == $expected' <<< "$body" >/dev/null; then actual="$(jq -r '.data.buildSha // ""' <<< "$body")" echo "::error::Build SHA mismatch: exact image reports '$actual', expected '$BUILD_SHA'." >&2 exit 1 fi - name: Verify health run: | for i in $(seq 1 30); do if body="$(curl -sf http://localhost:3000/api/health)" && jq -e '.status == "ok"' <<< "$body" >/dev/null; then echo "$body" | jq . echo "Health endpoint ready after $((i * 2))s" exit 0 fi sleep 2 done echo "::error::Exact image did not report healthy within 60 seconds." >&2 docker logs healthlog-verify || true exit 1 - name: Container logs if: always() continue-on-error: true run: docker logs healthlog-verify - name: Tear down if: always() continue-on-error: true run: docker rm -f healthlog-verify