From e1dcad4bc5b0707daf714c03242bbc986ca2a7ae Mon Sep 17 00:00:00 2001 From: Saadi Myftija Date: Thu, 13 Aug 2026 17:49:04 +0200 Subject: [PATCH 1/3] feat(base-images): push an immutable snapshot-dated tag per publish Mutable tags move on every republish, leaving previously published digests untagged and exposed to any future untagged-manifest cleanup, while shipped CLI releases pin those digests. A dated tag per publish keeps every digest tag-referenced permanently and doubles as a durable record of what was published when. --- .github/workflows/base-images.yml | 12 ++++++++++-- base-images/README.md | 5 ++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/base-images.yml b/.github/workflows/base-images.yml index 13abecdad7..c4c863db8e 100644 --- a/.github/workflows/base-images.yml +++ b/.github/workflows/base-images.yml @@ -39,6 +39,7 @@ jobs: build_packages: ${{ steps.config.outputs.build_packages }} suite: ${{ steps.config.outputs.suite }} snapshot: ${{ steps.config.outputs.snapshot }} + snapshot_date: ${{ steps.config.outputs.snapshot_date }} source_date_epoch: ${{ steps.config.outputs.source_date_epoch }} push: ${{ steps.config.outputs.push }} steps: @@ -89,6 +90,7 @@ jobs: echo "build_packages=$BUILD_PACKAGES" echo "suite=$SUITE" echo "snapshot=$SNAPSHOT" + echo "snapshot_date=${SNAPSHOT:0:8}" echo "source_date_epoch=$EPOCH" echo "push=$PUSH" } >> "$GITHUB_OUTPUT" @@ -164,7 +166,11 @@ jobs: platforms: linux/amd64,linux/arm64 provenance: false outputs: type=image,push=true,rewrite-timestamp=true - tags: triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }} + # The dated tag is immutable and keeps every published digest + # tag-referenced forever; shipped CLI releases pin these digests + tags: | + triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }} + triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }}-${{ needs.setup.outputs.snapshot_date }} build-args: | BASE_IMAGE=${{ matrix.image.base }} DEBIAN_SNAPSHOT=${{ needs.setup.outputs.snapshot }} @@ -187,7 +193,9 @@ jobs: platforms: linux/amd64,linux/arm64 provenance: false outputs: type=image,push=true,rewrite-timestamp=true - tags: triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }}-build + tags: | + triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }}-build + triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }}-build-${{ needs.setup.outputs.snapshot_date }} build-args: | BASE_IMAGE=${{ matrix.image.base }} DEBIAN_SNAPSHOT=${{ needs.setup.outputs.snapshot }} diff --git a/base-images/README.md b/base-images/README.md index 78f63a552a..9e1e48f5ea 100644 --- a/base-images/README.md +++ b/base-images/README.md @@ -14,7 +14,10 @@ images derived from these behave like their upstream bases. ## Tags and pinning Tags are mutable and rebuilt in place on demand; each rebuild picks up Debian -security updates published up to its snapshot date. The +security updates published up to its snapshot date. Every publish also pushes +an immutable snapshot-dated tag (e.g. `22-bookworm-20260812`) so previously +published digests stay tag-referenced; never delete these, since shipped CLI +releases pin their digests. The runtime itself (the node or bun binaries from the upstream base) only moves when the base digests in `images.json` are bumped. When bumping a base digest, keep the snapshot at least as new as the upstream image's own archive From 8bcb764b00704db4964d6a40c90fd14180d6cf06 Mon Sep 17 00:00:00 2001 From: Saadi Myftija Date: Thu, 13 Aug 2026 17:50:54 +0200 Subject: [PATCH 2/3] fix(base-images): disambiguate the immutable tag with the commit Two same-day publishes share a snapshot timestamp, so a date-only tag would be overwritten and orphan the first publish's digests; snapshot date plus commit matches exactly what determines the digests, and a re-run of the same commit re-tags the identical digest. --- .github/workflows/base-images.yml | 9 +++++---- base-images/README.md | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/base-images.yml b/.github/workflows/base-images.yml index c4c863db8e..91709081d4 100644 --- a/.github/workflows/base-images.yml +++ b/.github/workflows/base-images.yml @@ -39,7 +39,7 @@ jobs: build_packages: ${{ steps.config.outputs.build_packages }} suite: ${{ steps.config.outputs.suite }} snapshot: ${{ steps.config.outputs.snapshot }} - snapshot_date: ${{ steps.config.outputs.snapshot_date }} + publish_id: ${{ steps.config.outputs.publish_id }} source_date_epoch: ${{ steps.config.outputs.source_date_epoch }} push: ${{ steps.config.outputs.push }} steps: @@ -53,6 +53,7 @@ jobs: SNAPSHOT_INPUT: ${{ inputs.debian_snapshot }} EVENT_NAME: ${{ github.event_name }} REF: ${{ github.ref }} + SHA: ${{ github.sha }} run: | PACKAGES="$(jq -er '.packages' base-images/images.json)" BUILD_PACKAGES="$(jq -er '.buildPackages' base-images/images.json)" @@ -90,7 +91,7 @@ jobs: echo "build_packages=$BUILD_PACKAGES" echo "suite=$SUITE" echo "snapshot=$SNAPSHOT" - echo "snapshot_date=${SNAPSHOT:0:8}" + echo "publish_id=${SNAPSHOT:0:8}-${SHA:0:7}" echo "source_date_epoch=$EPOCH" echo "push=$PUSH" } >> "$GITHUB_OUTPUT" @@ -170,7 +171,7 @@ jobs: # tag-referenced forever; shipped CLI releases pin these digests tags: | triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }} - triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }}-${{ needs.setup.outputs.snapshot_date }} + triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }}-${{ needs.setup.outputs.publish_id }} build-args: | BASE_IMAGE=${{ matrix.image.base }} DEBIAN_SNAPSHOT=${{ needs.setup.outputs.snapshot }} @@ -195,7 +196,7 @@ jobs: outputs: type=image,push=true,rewrite-timestamp=true tags: | triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }}-build - triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }}-build-${{ needs.setup.outputs.snapshot_date }} + triggerdotdev/${{ matrix.image.repo }}:${{ matrix.image.tag }}-build-${{ needs.setup.outputs.publish_id }} build-args: | BASE_IMAGE=${{ matrix.image.base }} DEBIAN_SNAPSHOT=${{ needs.setup.outputs.snapshot }} diff --git a/base-images/README.md b/base-images/README.md index 9e1e48f5ea..cf4521e30c 100644 --- a/base-images/README.md +++ b/base-images/README.md @@ -15,7 +15,7 @@ images derived from these behave like their upstream bases. Tags are mutable and rebuilt in place on demand; each rebuild picks up Debian security updates published up to its snapshot date. Every publish also pushes -an immutable snapshot-dated tag (e.g. `22-bookworm-20260812`) so previously +an immutable per-publish tag (snapshot date plus commit, e.g. `22-bookworm-20260812-45444a7`) so previously published digests stay tag-referenced; never delete these, since shipped CLI releases pin their digests. The runtime itself (the node or bun binaries from the upstream base) only moves From ed9f3da7fdba89f1fda618e6ce96424388d64e6a Mon Sep 17 00:00:00 2001 From: Saadi Myftija Date: Thu, 13 Aug 2026 18:01:52 +0200 Subject: [PATCH 3/3] fix(base-images): include the snapshot time in the immutable tag The snapshot timestamp, not just its date, determines the published digests; a same-commit dispatch with an intra-day snapshot time could otherwise overwrite the tag and orphan the earlier digests. --- .github/workflows/base-images.yml | 2 +- base-images/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/base-images.yml b/.github/workflows/base-images.yml index 91709081d4..2c23246de3 100644 --- a/.github/workflows/base-images.yml +++ b/.github/workflows/base-images.yml @@ -91,7 +91,7 @@ jobs: echo "build_packages=$BUILD_PACKAGES" echo "suite=$SUITE" echo "snapshot=$SNAPSHOT" - echo "publish_id=${SNAPSHOT:0:8}-${SHA:0:7}" + echo "publish_id=${SNAPSHOT:0:8}-${SNAPSHOT:9:6}-${SHA:0:7}" echo "source_date_epoch=$EPOCH" echo "push=$PUSH" } >> "$GITHUB_OUTPUT" diff --git a/base-images/README.md b/base-images/README.md index cf4521e30c..88e568e50a 100644 --- a/base-images/README.md +++ b/base-images/README.md @@ -15,7 +15,7 @@ images derived from these behave like their upstream bases. Tags are mutable and rebuilt in place on demand; each rebuild picks up Debian security updates published up to its snapshot date. Every publish also pushes -an immutable per-publish tag (snapshot date plus commit, e.g. `22-bookworm-20260812-45444a7`) so previously +an immutable per-publish tag (snapshot timestamp plus commit, e.g. `22-bookworm-20260812-000000-45444a7`) so previously published digests stay tag-referenced; never delete these, since shipped CLI releases pin their digests. The runtime itself (the node or bun binaries from the upstream base) only moves