Pin each release's rust base image by digest#20
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0d14120002
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
This PR updates the release/build pipeline to pin Rust base images per stellar-cli release by inlining each base as <label>@sha256:<digest> in builds.json, and to encode a short digest fragment (and shortened stellar-cli ref) into published image tags to preserve tag immutability across base rebuilds.
Changes:
- Replace the shared
rust_image_digestsmap with per-releaserust_versions[]pins (<label>@sha256:<digest>) and update the JSON schema + validation accordingly. - Introduce
scripts/refresh.pyas the unified “append pins + resolve refs” mechanism and refactor release preparation to delegate to it. - Thread the base digest into tag composition, matrix generation, build/publish workflows, and update docs/tests to match the new tag scheme.
Reviewed changes
Copilot reviewed 35 out of 35 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
tests/unit/test_validate_json.py |
Updates schema validation tests; adds cases for pinned entries/uniqueness. |
tests/unit/test_tag_names.py |
Updates tag composition expectations to include ref15/digest15 and required digest arg. |
tests/unit/test_publish_manifests.py |
Updates manifest ref composition to include digest fragment in tags. |
tests/unit/test_publish_aliases.py |
Adjusts fixture data to use pinned rust_versions[] entries. |
tests/unit/test_newest_pair.py |
Updates expectation: newest rust output is now a full pin string. |
tests/unit/test_builds.py |
Adds pin parsing helpers and updates default-rust selection semantics. |
tests/unit/test_build_image.py |
Updates local build command expectations to require/passthrough base digest. |
tests/integration/test_release_prepare.py |
Refactors integration tests for release_prepare delegating to refresh.py. |
tests/integration/test_refresh.py |
New integration coverage for refresh append-only pin behavior + ref resolution. |
tests/integration/test_refresh_stellar_cli_digests.py |
Removed (functionality folded into refresh.py). |
tests/integration/test_refresh_rust_digests.py |
Removed (functionality folded into refresh.py). |
tests/fixtures/builds_unsorted_keys.json |
Updates fixture to reflect new top-level schema (no digest map). |
tests/fixtures/builds_unpinned_refs.json |
Updates fixture to use pinned rust entries and note refresh’s new role. |
tests/fixtures/builds_orphan_rust.json |
Removed (orphan-map constraint no longer exists). |
tests/fixtures/builds_multi_cli.json |
Updates fixture to inline per-entry pins. |
tests/fixtures/builds_minimal.json |
Updates minimal fixture to inline per-entry pins. |
tests/fixtures/builds_invalid_entry.json |
Updates fixture to represent a bare-label schema violation. |
scripts/validate_json.py |
Drops cross-field constraints; validates sorted keys + JSON schema only. |
scripts/tag_names.py |
Requires base digest input; truncates ref/digest and appends digest fragment into tags. |
scripts/resolve_matrix.py |
Switches from label→digest map lookup to splitting pins into (label,digest). |
scripts/release_prepare.py |
Becomes orchestrator over refresh.py + validation + “no-op” guard. |
scripts/release_pr_body.py |
Updates generated PR body text to describe pinning behavior. |
scripts/refresh.py |
New unified refresh script: resolves refs/digests and appends pins without rewriting old ones. |
scripts/publish_manifests.py |
Updates manifest creation to derive label+digest from pins and pass digest to tag composer. |
scripts/publish_aliases.py |
Uses default pin; splits into (label,digest) for tag composition. |
scripts/lib/builds.py |
Replaces rust_image_digest() with pin helpers (split_entry, label_of, digest_of). |
scripts/build_image.py |
Requires base digest as input; asserts the full pin is declared and uses canonical tag naming. |
RELEASE.md |
Updates documentation for new tag scheme and “rebuilt base” adoption workflow. |
README.md |
Updates user-facing docs for pinned base + digest-bearing tags and new refresh script. |
builds.schema.json |
Removes shared digest map; enforces pinned entry format and per-entry uniqueness. |
builds.json |
Migrates to inline rust base pins per release entry. |
.github/workflows/publish.yml |
Threads base digest into tag computation for publish pipeline. |
.github/workflows/build.yml |
Updates CI smoke-build flow to extract label+digest from the newest pin and pass digest onward. |
Comments suppressed due to low confidence (2)
scripts/resolve_matrix.py:42
build_matrix()emitsrust_base_keyas the label (not the full pin). Sincerust_versions[]can contain the same label multiple times with different digests, this can produce matrix rows that are identical in(stellar_cli_version, rust_base_key, arch)but differ only inrust_image_digest. Downstream workflows/artifact naming that key offrust_base_keywill collide/overwrite; consider including a unique per-pin field in the matrix (e.g.rust_pinand/orrust_digest15) and using it everywhere a unique identifier is required.
for pin in entry["rust_versions"]:
label, digest = builds.split_entry(pin)
parsed = rust_keys.parse(label)
for arch in ARCHES:
rows.append(
{
"arch": arch,
"platform": f"linux/{arch}",
"rust_base_key": label,
"rust_base_suffix": parsed.suffix,
"rust_image_digest": digest,
"rust_version": parsed.version,
"stellar_cli_ref": ref,
"stellar_cli_version": cli,
}
.github/workflows/publish.yml:87
- Because the tag now includes a digest fragment and
builds.jsoncan declare the samerust_base_keylabel multiple times with differentrust_image_digestvalues, this workflow needs to treat(label,digest)as the unique unit. Right now the build job name, SBOM/provenance/metadata filenames, and uploaded artifact names are keyed only by${{ matrix.rust_base_key }}+ arch, so two pins with the same label will collide and overwrite each other’s outputs. Consider incorporating a per-pin identifier into filenames/artifact names (e.g.${{ steps.tag.outputs.tag }}or adigest15/rust_pinfield from the matrix).
- name: resolve tag
id: tag
run: |
tag="$(./scripts/tag_names.py \
--stellar-cli-version ${{ matrix.stellar_cli_version }} \
--rust-version ${{ matrix.rust_base_key }} \
--rust-image-digest ${{ matrix.rust_image_digest }} \
--platform ${{ matrix.platform }} \
--stellar-cli-ref ${{ matrix.stellar_cli_ref }})"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "image=$REGISTRY:$tag" >> "$GITHUB_OUTPUT"
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3f59d79a23
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b790370c39
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
What
Replaces the shared
rust_image_digestsmap inbuilds.jsonwith a per-release base-image pin. Eachrust_versions[]entry is fully-qualified as<label>@sha256:<digest>(e.g.1.94.0-slim-trixie@sha256:…). The published image tag is the plain, human-readable<cli>-rust<label>[-<arch>]; the base digest pins the build'sFROMbut is not encoded in the tag.Why
Closes #18. The old model keyed digests by label in one shared map, so a new CLI version couldn't adopt a freshly-rebuilt base without dragging older versions onto the same digest. Making the
(release, label, digest)triple the unit that owns a digest decouples releases — a different cli version is a different tag, andbuilds.jsonrecords the exact base each release was built against.Tags
<cli>-rust<label>[-<arch>](e.g.26.1.0-rust1.95.0-slim-trixie-amd64), plus the:<cli>and:latestmoving aliases.bldimgis the per-arch content digest (@sha256:…).builds.jsonis append-only (a full ledger of every base a release was built against);resolve_matrixpublishes only the newest pin per label (last in the list wins), so the mutable tag deterministically resolves to the latest base.Notable details
refresh.py(merging the two old refresh scripts) resolves the cli ref + each base's current digest and appendslabel@digestpins;release_prepare.pyis a thin orchestrator over it.FROMby digest and cross-checks it against the image'sorg.opencontainers.image.base.digestlabel in the smoke test; the matrix keeps a digest-bearingrust_base_idfor collision-free per-image artifact names.ruff checkandruff format --checkclean.Validation
Validated end-to-end on a fork (publishing to
fnando/stellar-cli-experimental); the publish run was green across every job:refreshappended the new1.96.0-slim-trixiepin to26.1.0alongside1.95.0, and the published Docker Hub tags confirm the scheme: