diff --git a/.github/workflows/release-traceability.yml b/.github/workflows/release-traceability.yml index cdc7343..5549813 100644 --- a/.github/workflows/release-traceability.yml +++ b/.github/workflows/release-traceability.yml @@ -47,7 +47,13 @@ jobs: runs-on: ubuntu-latest needs: deploy steps: + - uses: actions/checkout@v4 + + - name: Verify traceability script cases + run: bash ./scripts/github-actions/test-validate-release-traceability.sh + - name: Validate runtime traceability env: SERVICE_IMAGE_REF: ${{ needs.build.outputs.service_image_ref }} + RUNTIME_PING_URL: https://accounts.svc.plus/api/ping run: bash ./scripts/github-actions/validate-release-traceability.sh diff --git a/docs/api.md b/docs/api.md index 3c4f254..e0f3e76 100644 --- a/docs/api.md +++ b/docs/api.md @@ -9,6 +9,14 @@ upstream and downstream interfaces it depends on. Returns the runtime image identity exposed by the running container. +Validation notes: + +- release traceability checks this endpoint with `GET`, not `HEAD` +- `accounts.svc.plus/api/ping` is the current production validation target for + release identity +- empty `image`, `tag`, `commit`, or `version` means runtime image identity was + not injected correctly and should fail deployment validation + Example response: ```json diff --git a/docs/release-traceability.md b/docs/release-traceability.md index faeddff..dc86560 100644 --- a/docs/release-traceability.md +++ b/docs/release-traceability.md @@ -16,12 +16,22 @@ release identity. - Build must produce `service_image_ref` only from the full `GITHUB_SHA`. - Deploy must consume `service_image_ref` and pass it through as the runtime image identity. +- Validate must use `GET https://accounts.svc.plus/api/ping`. - Validate must derive `tag` and `commit` from `service_image_ref` and compare them against `/api/ping`. +- Validate must fail when runtime `image`, `tag`, `commit`, or `version` is + empty. +- Validate must compare `version` against the derived full commit, not a + synthetic fallback. ## External playbook alignment The external `playbooks/deploy_billing_service.yml` playbook should accept `IMAGE_REF` (or an equivalent full image reference variable), derive any repo/tag helpers from it, and inject `IMAGE=` into the running -container environment. +container environment for the service exposed through +`https://accounts.svc.plus/api/ping`. + +If `accounts.svc.plus/api/ping` keeps returning empty runtime metadata, treat +that as a deployment contract failure: the runtime did not receive the full +`IMAGE` value and release traceability is broken. diff --git a/scripts/github-actions/test-validate-release-traceability.sh b/scripts/github-actions/test-validate-release-traceability.sh new file mode 100644 index 0000000..a113f22 --- /dev/null +++ b/scripts/github-actions/test-validate-release-traceability.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +script_path="${repo_root}/scripts/github-actions/validate-release-traceability.sh" +tmpdir="$(mktemp -d)" +trap 'rm -rf "${tmpdir}"' EXIT + +service_image_ref="ghcr.io/x-evor/billing-service:sha-0123456789abcdef0123456789abcdef01234567" + +run_success_case() { + local response_path="${tmpdir}/success.json" + cat > "${response_path}" <<'EOF' +{"image":"ghcr.io/x-evor/billing-service:sha-0123456789abcdef0123456789abcdef01234567","tag":"sha-0123456789abcdef0123456789abcdef01234567","commit":"0123456789abcdef0123456789abcdef01234567","version":"0123456789abcdef0123456789abcdef01234567","status":"ok"} +EOF + + SERVICE_IMAGE_REF="${service_image_ref}" \ + RUNTIME_PING_URL="file://${response_path}" \ + bash "${script_path}" +} + +run_failure_case() { + local name="$1" + local response_path="$2" + + if SERVICE_IMAGE_REF="${service_image_ref}" \ + RUNTIME_PING_URL="file://${response_path}" \ + bash "${script_path}"; then + echo "expected ${name} to fail" >&2 + exit 1 + fi +} + +cat > "${tmpdir}/empty.json" <<'EOF' +{"image":"","tag":"","commit":"","version":"","status":"ok"} +EOF + +cat > "${tmpdir}/mismatch.json" <<'EOF' +{"image":"ghcr.io/x-evor/billing-service:sha-fedcba98765432100123456789abcdef01234567","tag":"sha-fedcba98765432100123456789abcdef01234567","commit":"fedcba98765432100123456789abcdef01234567","version":"fedcba98765432100123456789abcdef01234567","status":"ok"} +EOF + +run_success_case +run_failure_case "empty runtime metadata" "${tmpdir}/empty.json" +run_failure_case "mismatched runtime metadata" "${tmpdir}/mismatch.json" diff --git a/scripts/github-actions/validate-release-traceability.sh b/scripts/github-actions/validate-release-traceability.sh index b1bb474..112ab13 100644 --- a/scripts/github-actions/validate-release-traceability.sh +++ b/scripts/github-actions/validate-release-traceability.sh @@ -2,11 +2,21 @@ set -euo pipefail service_image_ref="${SERVICE_IMAGE_REF:?SERVICE_IMAGE_REF is required}" +runtime_ping_url="${RUNTIME_PING_URL:?RUNTIME_PING_URL is required}" tag="${service_image_ref##*:}" commit="${tag#sha-}" -curl -fsS "https://billing-service.example.com/api/ping" | jq -e \ +curl -fsS "${runtime_ping_url}" | jq -e \ --arg image "${service_image_ref}" \ --arg tag "${tag}" \ --arg commit "${commit}" \ - '.image == $image and .tag == $tag and .commit == $commit' + ' + (.image | type == "string" and length > 0) and + (.tag | type == "string" and length > 0) and + (.commit | type == "string" and length > 0) and + (.version | type == "string" and length > 0) and + .image == $image and + .tag == $tag and + .commit == $commit and + .version == $commit + ' >/dev/null