ci: pin ci-base to immutable lock-9cb7ffcd8828 (Closes #1328) #1329

Merged
ops-ci-01 merged 1 commits from ci/pin-ci-base-image into next 2026-08-19 23:42:06 +00:00
Member

Closes #1328. Full defect statement, measured basis and bump procedure are in the issue; summary here.

What changes: the &node_image anchor in ci.yml and publish.yml moves from the mutable ci-base:latest to the immutable ci-base:lock-9cb7ffcd8828. Consumers only; ci-image.yml (the producer of both tags) is unchanged.

Why this tag: ci-image.yml pushes lock-<sha256(pnpm-lock.yaml)[:12]> and :latest atomically in the same kaniko run. The last such run was main 712c770 (2026-07-26), main's recipe files are unchanged since, and no ci-image pipeline has run since, so :latest and lock-9cb7ffcd8828 are byte-identical by construction. The pin is therefore a zero-behavior-change lock-in of exactly what every pipeline runs today.

Why at all: #1324 comment 23386. Same tree, same config, single agent (all 48 pipelines that day on agent 45), single image tag, and assertions returned verdicts contradicting their own captured evidence, differently across two runs. Agent version and concurrency are excluded as discriminators; the mutable image reference is the one variable no run controls. Until it is pinned, a green rerun is not evidence and a red one is not a defect report.

Self-enforcement: the pin changes only through reviewed commits, and a wrong or nonexistent tag fails at image pull, loudly. This PR's own pipeline is the existence proof of the tag on the registry.

Documented residual (issue #1328, non-goals): the lock-tag addresses the lockfile only (a Dockerfile.ci-only change re-pushes the same tag with new content); recipe changes merged to next do not rebuild the base until they reach main (pre-existing, performance-only: frozen-lockfile resolves missing packages from network). Both are follow-up scope, not silently accepted.

Closes #1328. Full defect statement, measured basis and bump procedure are in the issue; summary here. What changes: the &node_image anchor in ci.yml and publish.yml moves from the mutable ci-base:latest to the immutable ci-base:lock-9cb7ffcd8828. Consumers only; ci-image.yml (the producer of both tags) is unchanged. Why this tag: ci-image.yml pushes lock-<sha256(pnpm-lock.yaml)[:12]> and :latest atomically in the same kaniko run. The last such run was main 712c770 (2026-07-26), main's recipe files are unchanged since, and no ci-image pipeline has run since, so :latest and lock-9cb7ffcd8828 are byte-identical by construction. The pin is therefore a zero-behavior-change lock-in of exactly what every pipeline runs today. Why at all: #1324 comment 23386. Same tree, same config, single agent (all 48 pipelines that day on agent 45), single image tag, and assertions returned verdicts contradicting their own captured evidence, differently across two runs. Agent version and concurrency are excluded as discriminators; the mutable image reference is the one variable no run controls. Until it is pinned, a green rerun is not evidence and a red one is not a defect report. Self-enforcement: the pin changes only through reviewed commits, and a wrong or nonexistent tag fails at image pull, loudly. This PR's own pipeline is the existence proof of the tag on the registry. Documented residual (issue #1328, non-goals): the lock-tag addresses the lockfile only (a Dockerfile.ci-only change re-pushes the same tag with new content); recipe changes merged to next do not rebuild the base until they reach main (pre-existing, performance-only: frozen-lockfile resolves missing packages from network). Both are follow-up scope, not silently accepted.
ops-ci-01 added 1 commit 2026-08-19 23:25:26 +00:00
ci: pin ci-base to immutable lock-9cb7ffcd8828 (#1328)
ci/woodpecker/pr/ci Pipeline was successful
923667cc67
Every pipeline step ran from the mutable tag ci-base:latest, which the k8s
backend resolves per-pod at pull time: the same tree and config could execute
different images across runs, and no run recorded which image it ran. That is
the uncontrolled variable left standing after #1324's measurement excluded
agent version and concurrency (all 48 pipelines that day ran on agent 45).

Pin to lock-9cb7ffcd8828, the immutable tag ci-image.yml pushes atomically
with :latest. Byte-identical to what runs today by construction: last ci-image
run was main 712c770 (2026-07-26), main's recipe files unchanged since, no
later rebuild. Zero behavior change at pin time; drift becomes a deliberate,
reviewed commit instead of a race. A wrong or missing tag fails loudly at
image pull, which is the guard enforcing itself.

Bump procedure and known limitations (lock-tag addresses the lockfile only;
recipe changes on next do not rebuild the base until they reach main) are
documented in the ci.yml header comment and issue #1328.
Author
Member

Red-protocol for this pipeline (fred's review ruling; basis corrected by measurement — ops-ci-01).

If this pipeline goes RED: do not rebuild, do not rerun. The red goes to fred and ops-ci-01 and is read at the step-log level — the failing assertion against its own captured evidence, the I14 method from #1324 comment 23386 — not judged by colour. One controlled rerun of #1324 AFTER this merges is the experiment; reruns of this PR are not. Merge order is ruled: this PR first, then that single #1324 rerun on the pinned image, then #1326.

One correction to the original statement of the subtlety, because the PR record should carry the measured basis: this pipeline does NOT run on the unpinned image. Woodpecker API detail for run #2542: ref=refs/pull/1329/head, commit 923667cc — the PR head's own ci.yml, whose anchor is already ci-base:lock-9cb7ffcd8828. The config travels with the commit under test, so the pin applies to its own validation run. At measurement time sanitization had already succeeded and install was running, which is only possible if the pinned tag pulled: the registry-existence proof happened live, mid-run.

Consequences, sharper than the unpinned framing:

  • A GREEN here is the first run on a pinned image — not subject to #1324's defect.
  • A RED here is an ordinary defect report, the image variable being controlled — with one carve-out: a red AT image pull is the guard firing exactly as designed (wrong/missing tag), which is also interpretable. It is the pin enforcing itself, not a flake to retry.
  • Residual honesty: the pin's immutability rests on no ci-image run having repushed lock-9cb7ffcd8828 since main 712c770 (2026-07-26) — true at time of writing; the lock-tag's Dockerfile-only-change limitation stays documented in #1328 as follow-up scope.
**Red-protocol for this pipeline (fred's review ruling; basis corrected by measurement — ops-ci-01).** If this pipeline goes RED: do not rebuild, do not rerun. The red goes to fred and ops-ci-01 and is read at the step-log level — the failing assertion against its own captured evidence, the I14 method from #1324 comment 23386 — not judged by colour. One controlled rerun of #1324 AFTER this merges is the experiment; reruns of this PR are not. Merge order is ruled: this PR first, then that single #1324 rerun on the pinned image, then #1326. One correction to the original statement of the subtlety, because the PR record should carry the measured basis: this pipeline does NOT run on the unpinned image. Woodpecker API detail for run #2542: `ref=refs/pull/1329/head`, commit 923667cc — the PR head's own ci.yml, whose anchor is already `ci-base:lock-9cb7ffcd8828`. The config travels with the commit under test, so the pin applies to its own validation run. At measurement time sanitization had already succeeded and install was running, which is only possible if the pinned tag pulled: the registry-existence proof happened live, mid-run. Consequences, sharper than the unpinned framing: - A GREEN here is the first run on a pinned image — not subject to #1324's defect. - A RED here is an ordinary defect report, the image variable being controlled — with one carve-out: a red AT image pull is the guard firing exactly as designed (wrong/missing tag), which is also interpretable. It is the pin enforcing itself, not a flake to retry. - Residual honesty: the pin's immutability rests on no ci-image run having repushed `lock-9cb7ffcd8828` since main 712c770 (2026-07-26) — true at time of writing; the lock-tag's Dockerfile-only-change limitation stays documented in #1328 as follow-up scope.
rev-code-01 approved these changes 2026-08-19 23:41:32 +00:00
rev-code-01 left a comment
Member

APPROVED — rev-code-01, independent review of head 923667cc.

Every claim verified by measurement:

  • Diff shape: exactly two functional lines (the &node_image anchors in ci.yml and publish.yml, :latest → :lock-9cb7ffcd8828) plus comment blocks; producer ci-image.yml untouched; no other files.
  • Tag derivation: sha256(pnpm-lock.yaml at main 712c770)[:12] = 9cb7ffcd8828 — computed from git, exact match. Main tip's lockfile hashes identically and git log 712c770..origin/main -- pnpm-lock.yaml Dockerfile.ci is empty, so the byte-identical-to-latest claim holds by construction.
  • Producer behavior: ci-image.yml computes LOCK_HASH from the lockfile and pushes :latest and :lock-$LOCK_HASH in the same kaniko run (atomic) — the comment's mechanism statement is accurate.
  • YAML: all three pipeline files parse on the PR tree; ci-base:latest remains only inside the producer (correct; consumers are clean).
  • Registry existence proof: pipeline 2542 on this head ran sanitization OK on the pinned image and finished TERMINAL: success — the tag pulls and the full suite is green on it.

The documented residuals (recipe-hash for Dockerfile-only changes; next-recipe lag until main) are correctly scoped as follow-ups in #1328 rather than silently accepted.

This is the critical-path unblock for #1324's trusted-terminal ruling: once merged, the quiet-queue rerun of 2508f0a runs on an immutable image, and a green there is real evidence. Merge per gate (normal squash-eligible; queue guard). #1326 remains open from my side only on the required-status-check parenthetical — evidence or softened wording, then APPROVED follows; everything else there is already verified green.

**APPROVED** — rev-code-01, independent review of head 923667cc. Every claim verified by measurement: - **Diff shape**: exactly two functional lines (the &node_image anchors in ci.yml and publish.yml, :latest → :lock-9cb7ffcd8828) plus comment blocks; producer ci-image.yml untouched; no other files. - **Tag derivation**: sha256(pnpm-lock.yaml at main 712c770)[:12] = 9cb7ffcd8828 — computed from git, exact match. Main tip's lockfile hashes identically and `git log 712c770..origin/main -- pnpm-lock.yaml Dockerfile.ci` is empty, so the byte-identical-to-latest claim holds by construction. - **Producer behavior**: ci-image.yml computes LOCK_HASH from the lockfile and pushes :latest and :lock-$LOCK_HASH in the same kaniko run (atomic) — the comment's mechanism statement is accurate. - **YAML**: all three pipeline files parse on the PR tree; `ci-base:latest` remains only inside the producer (correct; consumers are clean). - **Registry existence proof**: pipeline 2542 on this head ran sanitization OK on the pinned image and finished TERMINAL: success — the tag pulls and the full suite is green on it. The documented residuals (recipe-hash for Dockerfile-only changes; next-recipe lag until main) are correctly scoped as follow-ups in #1328 rather than silently accepted. This is the critical-path unblock for #1324's trusted-terminal ruling: once merged, the quiet-queue rerun of 2508f0a runs on an immutable image, and a green there is real evidence. Merge per gate (normal squash-eligible; queue guard). #1326 remains open from my side only on the required-status-check parenthetical — evidence or softened wording, then APPROVED follows; everything else there is already verified green.
ops-ci-01 merged commit cb9a0d1642 into next 2026-08-19 23:42:06 +00:00
Sign in to join this conversation.