Skip to content

ADR-0012: Frozen base image tags, SHA-tag preview deploys, and dedicated preview registries

Date: 2026-07-01 Status: accepted Deciders: ClearRoute platform team

Context

Every application release required bumping the image tag in four places — the three base/ deployment manifests and the dev + prod overlays — even though base/ is never consumed with its literal tag (dev/prod override via images.newTag; prod additionally pins base at ?ref=<sha>). The only real consumer of the base/ tag was the preview overlay, which reconstructed the CI image tag by keeping the version prefix from base/ and swapping only the SHA segment via fragile per-app delimiter: '-' / index-5/6/7 counting. This coupling forced the redundant base bump and carried a latent bug: a PR branched off an older release tag reconstructed a <newer-version>-<sha> tag that never existed in ECR. The root cause is that the ArgoCD pullRequest generator only exposes head_short_sha_7 — never a semver — so preview could only rebuild a <version>-<sha> tag if the version was hardcoded in the repo. Separately, preview (PR-built, from unmerged code) images were pushed into the dev ECR, mixing ephemeral untrusted artifacts with the dev/prod supply chain and competing with dev images for the registry's retention window.

Decision

Make base/ image references a frozen :latest placeholder that is never version-bumped. Have CI (constellation-core docker-publish.yaml) publish an additional commit-SHA-only tag :<short7sha> on every build, and route PR builds to dedicated <app>-<component>-preview ECRs (dev account). Preview overlays rewrite the repo to -preview via images.newName and replace the whole tag from preview-values.pr-git-sha with one uniform rule (delimiter: ':', index: 1). dev/prod overlays are unchanged — they keep a Renovate-comparable newTag (semver X.Y.Z, or 0.0.0-<sha> for continuous apps), preserving Renovate as the dev/prod bump mechanism.

Alternatives Considered

Keep bumping base/ every release (status quo)

  • Pros: No change; familiar.
  • Cons: Redundant edits in 3 base files per release; preview reconstruction stays fragile (branch-ancestry bug); no isolation of preview images.
  • Why not: The base bump is pure dead weight for dev/prod and actively fragile for preview.

Inject a semver into the preview render (teach the AppSet a version source)

  • Pros: Preview could keep a <version>-<sha> tag without hardcoding it in base/.
  • Cons: The pullRequest generator has no semver; we'd have to encode a version in a git-tracked file or PR label and thread it through the AppSet.
  • Why not: Large machinery to preserve a tag shape preview doesn't need — a bare SHA is sufficient and unique.

ArgoCD Image Updater for preview/dev bumps

  • Pros: Auto-tracks new images; no manual tag edits.
  • Cons: Git write-back commits directly to a branch — no PR, bypassing the review gate; ordering of <semver>-<sha> tags is unreliable.
  • Why not: The repo is review-gated GitOps; losing the PR step is a downgrade. Renovate already opens reviewable bump PRs.

Keep preview images in the dev ECR (status quo registry)

  • Pros: No new Terraform/IAM.
  • Cons: Untrusted PR artifacts share the dev/prod supply chain; preview churn evicts dev images from the retention window; no independent lifecycle.
  • Why not: Isolation and a dedicated aggressive lifecycle are cheap given the <app>-<component>-<env> naming scheme.

Consequences

Positive

  • Releases become overlay-only; base/ is never touched again.
  • Preview pulls the exact commit image (-preview:<short7sha>), fixing the branch-ancestry bug.
  • One uniform preview replacement (: / index 1) replaces per-app segment counting.
  • Preview images are isolated in dedicated registries with their own lifecycle (untagged 1d / any 14d), off the dev/prod supply chain.
  • Renovate's dev/prod flow is untouched (dev/prod overlays and renovate.json unchanged).

Negative

  • More ECR repos + IAM push roles (one set per component, dev account only).
  • Preview overlays carry an extra images.newName block per component.
  • The change spans three surfaces (Terraform, constellation-core CI, iac overlays) with a required merge order.

Risks

  • Merge ordering: the -preview ECRs + push roles (Terraform) and the CI :<sha> tag must exist before the iac preview changes merge, or preview builds fail to push / pull. Mitigation: documented rollout order (Terraform apply → core PR #20 → iac).
  • IMMUTABLE tags + rebuilds: rebuilding the same commit fails to re-push its immutable :<sha> tag (pre-existing behaviour for :<version>-<sha>); acceptable.
  • Deferred CD-model change: routing main → dev (<semver>-<sha>) / tag → prod (<semver>) with a sortable timestamp suffix so Renovate can still order dev tags is not included here; it will be its own ADR/PR.