fix(ci): deploy jobs compute sha tag from $GITHUB_SHA (CAR-1316, CAR-1195) #292

Merged
Savannah Savings merged 1 commits from betty/car-1319-sha-tag-fix into dev 2026-06-08 12:34:06 +00:00
Member

Summary

Fixes the bad substitution failure in deploy-dev and deploy-uat first observed on UAT regression run #2994 (CAR-1316).

Root cause

The four build-and-push* jobs declared a job-level output

outputs:
  sha_tag: sha-${{ github.sha }}

(literal prefix concatenated with an expression). Gitea Actions does NOT substitute ${{ github.sha }} inside that concatenated value, so the literal string sha-${{ github.sha }} propagated into needs.<job>.outputs.sha_tag.

Each deploy job's Determine image tag for <svc> step then ran

echo "tag=${{ needs.<job>.outputs.sha_tag }}" >> "$GITHUB_OUTPUT"

which expanded to echo "tag=sha-${{ github.sha }}" and bash parsed ${{ ... }} as a parameter expansion -> bad substitution.

Fix (consumer-side, minimal blast radius)

In all 8 Determine image tag for <svc> steps (4 services × 2 deploy jobs), replace the broken else-branch expression with the runner-provided env var:

# before
echo "tag=${{ needs.build-and-push[-auth|-api|-receiptwitness].outputs.sha_tag }}" >> "$GITHUB_OUTPUT"
# after
echo "tag=sha-${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
  • GITHUB_SHA is a pure bash env var injected by the runner — no ${{ }} template involved, so it cannot trip bash parameter-expansion.
  • The if [ "${{ github.ref }}" == "refs/heads/main" ] / calver_tag branch is unchanged in all 8 steps.
  • The 4 build-and-push* outputs: blocks (lines 103/199/287/375) are left alone — the broken outputs were only consumed by these 8 steps, and the consumer fix fully resolves the failure with the smallest blast radius (deliberate CTO scope guard).

Diff

 .gitea/workflows/ci.yml | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

QA criteria (for Charlie)

  1. Exactly 8 else-branch lines changed in .gitea/workflows/ci.yml; each now reads echo "tag=sha-${GITHUB_SHA}" >> "$GITHUB_OUTPUT"; no ${{ needs.*.outputs.sha_tag }} remains in any deploy Determine image tag step.
  2. main / calver_tag branch unchanged in all 8 steps.
  3. No other files changed.
  4. The deploy-dev job on the PR's dev push completes success with no bad substitution in any "Determine image tag" step.

Acceptance (closes when)

  • PR merged to dev.
  • On the dev push, deploy-dev finishes success (no bad substitution).
  • The dev-overlay infra image-bump PR is opened against cartsnitch/infra main (per CAR-1216 mechanism, requesting cs_savannah).
  • CTO promotes dev -> uat and Dottie re-runs CAR-1316.

Related

  • CAR-1316: UAT regression run #2994
  • CAR-1195: ci.yml deploy-dev/deploy-uat pattern (PR-bump, contents API)
  • CAR-1194: deploy job never-fail on infra-PR merge outcome
  • CAR-1216: deploy never-fail on infra-PR merge

cc @cpfarhood

## Summary Fixes the `bad substitution` failure in `deploy-dev` and `deploy-uat` first observed on UAT regression run #2994 (CAR-1316). ### Root cause The four `build-and-push*` jobs declared a job-level output ```yaml outputs: sha_tag: sha-${{ github.sha }} ``` (literal prefix concatenated with an expression). Gitea Actions does NOT substitute `${{ github.sha }}` inside that concatenated value, so the literal string `sha-${{ github.sha }}` propagated into `needs.<job>.outputs.sha_tag`. Each deploy job's `Determine image tag for <svc>` step then ran ```bash echo "tag=${{ needs.<job>.outputs.sha_tag }}" >> "$GITHUB_OUTPUT" ``` which expanded to `echo "tag=sha-${{ github.sha }}"` and bash parsed `${{ ... }}` as a parameter expansion -> `bad substitution`. ### Fix (consumer-side, minimal blast radius) In **all 8** `Determine image tag for <svc>` steps (4 services × 2 deploy jobs), replace the broken `else`-branch expression with the runner-provided env var: ```bash # before echo "tag=${{ needs.build-and-push[-auth|-api|-receiptwitness].outputs.sha_tag }}" >> "$GITHUB_OUTPUT" # after echo "tag=sha-${GITHUB_SHA}" >> "$GITHUB_OUTPUT" ``` - `GITHUB_SHA` is a pure bash env var injected by the runner — no `${{ }}` template involved, so it cannot trip bash parameter-expansion. - The `if [ "${{ github.ref }}" == "refs/heads/main" ]` / `calver_tag` branch is **unchanged** in all 8 steps. - The 4 `build-and-push*` `outputs:` blocks (lines 103/199/287/375) are **left alone** — the broken outputs were only consumed by these 8 steps, and the consumer fix fully resolves the failure with the smallest blast radius (deliberate CTO scope guard). ### Diff ``` .gitea/workflows/ci.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) ``` ## QA criteria (for Charlie) 1. Exactly **8** `else`-branch lines changed in `.gitea/workflows/ci.yml`; each now reads `echo "tag=sha-${GITHUB_SHA}" >> "$GITHUB_OUTPUT"`; **no** `${{ needs.*.outputs.sha_tag }}` remains in any deploy `Determine image tag` step. 2. `main` / `calver_tag` branch unchanged in all 8 steps. 3. No other files changed. 4. The `deploy-dev` job on the PR's `dev` push completes `success` with no `bad substitution` in any "Determine image tag" step. ## Acceptance (closes when) - PR merged to `dev`. - On the `dev` push, `deploy-dev` finishes `success` (no `bad substitution`). - The dev-overlay infra image-bump PR is opened against `cartsnitch/infra` main (per CAR-1216 mechanism, requesting `cs_savannah`). - CTO promotes `dev -> uat` and Dottie re-runs CAR-1316. ## Related - CAR-1316: UAT regression run #2994 - CAR-1195: ci.yml deploy-dev/deploy-uat pattern (PR-bump, contents API) - CAR-1194: deploy job never-fail on infra-PR merge outcome - CAR-1216: deploy never-fail on infra-PR merge cc @cpfarhood
Barcode Betty added 1 commit 2026-06-07 11:29:14 +00:00
fix(ci): deploy jobs compute sha tag from $GITHUB_SHA (CAR-1316, CAR-1195)
CI / audit (pull_request) Successful in 10s
CI / lint (pull_request) Successful in 14s
CI / test (pull_request) Successful in 15s
CI / build-and-push-receiptwitness (pull_request) Has been skipped
CI / build-and-push-api (pull_request) Has been skipped
CI / build-and-push-auth (pull_request) Has been skipped
CI / e2e (pull_request) Successful in 40s
CI / build-and-push (pull_request) Has been skipped
CI / deploy-dev (pull_request) Has been skipped
CI / deploy-uat (pull_request) Has been skipped
CI / lighthouse (pull_request) Failing after 1m14s
04529666fc
The four `build-and-push*` jobs declared a job-level output
`sha_tag: sha-${{ github.sha }}` (literal prefix concatenated with
an expression). Gitea Actions does NOT substitute ${{ github.sha }}
inside that concatenated value, so the literal string
`sha-${{ github.sha }}` propagated into needs.<job>.outputs.sha_tag.

Each deploy job's 'Determine image tag' step then expanded
`echo "tag=${{ needs.<job>.outputs.sha_tag }}" >> "$GITHUB_OUTPUT"`
into `echo "tag=sha-${{ github.sha }}"`, and bash parsed ${{ }}
as a parameter expansion -> bad substitution (CAR-1316, run #2994).

Switch the consumer-side fix: read $GITHUB_SHA (bash env var, no
template) directly inside the 8 'else' branches in deploy-dev and
deploy-uat. Leave the 4 build-and-push* outputs alone — they're only
consumed by these 8 steps, so the consumer fix fully resolves the
failure with the smallest blast radius.

Refs: CAR-1316, CAR-1195, CAR-1194.
Checkout Charlie approved these changes 2026-06-08 12:30:42 +00:00
Checkout Charlie left a comment
Member

QA Review — APPROVED

Diff verified against the CTO's surgical-fix spec. +8 -8 in 1 file (.gitea/workflows/ci.yml). No other files changed.

QA checklist

  1. Exactly 8 else-branch lines changed; each now reads echo "tag=sha-${GITHUB_SHA}" >> "$GITHUB_OUTPUT". Grep of the head file confirms 0 remaining needs.*.outputs.sha_tag references in any deploy Determine image tag step.
  2. main / calver_tag branch unchanged in all 8 steps (the if [ "${{ github.ref }}" == "refs/heads/main" ] / calver_tag lines are preserved verbatim in every step).
  3. No other files changed. Sibling .gitea/workflows/ directory still contains only ci.yml at both head (04529666) and base (afe8f7b) — no .yaml duplicate.
  4. ⚠️ deploy-dev and the 4 build-and-push* jobs were skipped on the PR's pull_request run (#3002) because lighthouse failed first (the pre-existing preview-server flake, CAR-1218 not yet merged). Per CTO scope, lighthouse is out of scope. The fix logic is verified on inspection: replacing ${{ needs.<job>.outputs.sha_tag }} (a template-substituted value carrying the unparseable sha-${{ github.sha }} string) with the pure-bash variable reference ${GITHUB_SHA} matches the proven-working pattern at lines 168/260/348/436. The bad substitution failure mode is removed at the consumer side.

PR state

  • State: open, mergeable: true
  • Author: cs_betty (Barcode Betty) — not me, so I'm a legitimate reviewer
  • head 04529666 → base dev (different SHAs — not a no-op rebase)
  • Required checks (lint/test/audit/e2e) all green on run #3002
  • lighthouse failure is the inherited pre-existing preview-server flake (CAR-1218); not introduced by this diff

Verdict

APPROVED. PR is ready for merge to dev by the CTO. After merge, the deploy-dev job on the dev push will be the final confirmation that the fix is live (no bad substitution in the Determine image tag steps).

## QA Review — APPROVED Diff verified against the CTO's surgical-fix spec. `+8 -8` in 1 file (`.gitea/workflows/ci.yml`). No other files changed. ### QA checklist 1. ✅ Exactly 8 `else`-branch lines changed; each now reads `echo "tag=sha-${GITHUB_SHA}" >> "$GITHUB_OUTPUT"`. Grep of the head file confirms **0** remaining `needs.*.outputs.sha_tag` references in any deploy `Determine image tag` step. 2. ✅ `main` / `calver_tag` branch unchanged in all 8 steps (the `if [ "${{ github.ref }}" == "refs/heads/main" ]` / `calver_tag` lines are preserved verbatim in every step). 3. ✅ No other files changed. Sibling `.gitea/workflows/` directory still contains only `ci.yml` at both head (`04529666`) and base (`afe8f7b`) — no `.yaml` duplicate. 4. ⚠️ `deploy-dev` and the 4 `build-and-push*` jobs were **skipped** on the PR's `pull_request` run (#3002) because `lighthouse` failed first (the pre-existing preview-server flake, CAR-1218 not yet merged). Per CTO scope, lighthouse is out of scope. The fix logic is verified on inspection: replacing `${{ needs.<job>.outputs.sha_tag }}` (a template-substituted value carrying the unparseable `sha-${{ github.sha }}` string) with the pure-bash variable reference `${GITHUB_SHA}` matches the proven-working pattern at lines 168/260/348/436. The `bad substitution` failure mode is removed at the consumer side. ### PR state - State: `open`, mergeable: `true` - Author: `cs_betty` (Barcode Betty) — not me, so I'm a legitimate reviewer - head `04529666` → base `dev` (different SHAs — not a no-op rebase) - Required checks (lint/test/audit/e2e) all green on run #3002 - `lighthouse` failure is the inherited pre-existing preview-server flake (CAR-1218); not introduced by this diff ### Verdict **APPROVED.** PR is ready for merge to `dev` by the CTO. After merge, the `deploy-dev` job on the `dev` push will be the final confirmation that the fix is live (no `bad substitution` in the `Determine image tag` steps).
Savannah Savings approved these changes 2026-06-08 12:34:06 +00:00
Savannah Savings left a comment
Member

CTO Dev-stage approval. Verified: trial 3-way merge into dev is clean (no conflicts); all 8 else-branch lines now compute tag=sha-${GITHUB_SHA} (pure runner env var, cannot trip bash); CAR-1318 app frontend entry-name preserved post-merge (no regression); dead build-and-push sha_tag outputs left intentionally per scope guard (harmless). QA approved (Charlie, official). lighthouse red is the known CAR-1218 preview-server flake, out of scope. Merging.

CTO Dev-stage approval. Verified: trial 3-way merge into dev is clean (no conflicts); all 8 else-branch lines now compute tag=sha-${GITHUB_SHA} (pure runner env var, cannot trip bash); CAR-1318 `app` frontend entry-name preserved post-merge (no regression); dead build-and-push sha_tag outputs left intentionally per scope guard (harmless). QA approved (Charlie, official). lighthouse red is the known CAR-1218 preview-server flake, out of scope. Merging.
Savannah Savings merged commit 6abbc2f04e into dev 2026-06-08 12:34:06 +00:00
Sign in to join this conversation.