fix(deploy): let the deploy artifacts carry the version Cargo.toml declares - #303
Merged
Merged
Conversation
…clares `docker-compose.yml` and `Dockerfile` each copy two facts out of `Cargo.toml` — the release version (`image: aitokenpool:<v>`, the copy-pasteable `docker build -t aitokenpool:<v> .`, the `(v<v>)` header) and the minimum Rust version (`FROM rust:<v>-slim`). Cross-format files cannot import, so a copy is unavoidable; the problem is that nothing asserted it. The copy was written in a51b3ba (#99, v0.6.6) and never touched again, so by now it was 22 releases stale: the `docker compose up -d --build` that README recommends tagged the image `aitokenpool:0.6.6` while the binary inside it reports 0.7.28 (the `/healthz` version comes from `env!("CARGO_PKG_VERSION")`). Fix the five stale literals, and give the copies an executor in the existing `src/deploy_gate.rs`: expectations are derived from `Cargo.toml` (no snapshots), every `aitokenpool:<v>` tag and `(v<v>)` remark must equal the declared version, and every `FROM rust:<v>` must be at least the declared `rust-version` (building with a newer toolchain is legitimate — the guarded direction is "the declaration was raised and the Dockerfile did not follow"). Moving tags such as `latest` are not version claims and are skipped, and the scanner is scoped to the `[package]` section so the `version = "0.7"` entries of the dependency tables cannot stand in for it. Measured on this tree: before, the rule reports exactly the five stale sites (compose :1/:19, Dockerfile :1/:3/:6); lowering the builder image to rust:1.85 and bumping the manifest to 0.7.29 each turn it red. `cargo test` 396 -> 399. Consequence, deliberate: a release PR now also has to update these two files, and the gate fails loudly if it does not.
This was referenced Sep 27, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
docker-compose.ymlandDockerfileeach copy two facts out ofCargo.toml, and nothing in the repository asserted either copy:image: aitokenpool:<v>(docker-compose.yml:19, a live field), the copy-pasteable# Build: docker build -t aitokenpool:<v> .and the# … (v<v>)header of both files;FROM rust:<v>-slim(Dockerfile:16).Cross-format files cannot import, so a copy is unavoidable here — the same situation
catalog_gate.rshandles forMODEL_COUNT. What is avoidable is a copy with no enforcer. These were written ina51b3ba(#99,v0.6.6) and never touched again (docker-compose.ymlwas edited once since, by7852277#235, and that changed only the master key), so by now the version copy was 22 releases stale.The consequence is user-visible on the very path
README.md:37recommends:docker compose up -d --buildtags the image it buildsaitokenpool:0.6.6, while the binary inside reports0.7.28— the/healthzversion comes fromenv!("CARGO_PKG_VERSION"), i.e. from the manifest.docker compose pullwould additionally look for alibrary/aitokenpool:0.6.6on Docker Hub.This PR updates the five stale literals and gives the copies an executor in the existing
src/deploy_gate.rs.Related Issue
None: no open issue covers this, and it is a self-contained change. (Recorded so the reviewer knows why no issue is linked and the linked-issue field stays empty by design.)
Changes
docker-compose.yml—v0.6.6→v0.7.28in the header remark and in theimage:field.Dockerfile—v0.6.6→v0.7.28in the header remark and in the twoaitokenpool:<v>references.src/deploy_gate.rs— a second rule in the existing deploy gate (test-only module, zero new dependencies, everything read at compile time viainclude_str!). Expectations are derived fromCargo.toml, not snapshotted:image: aitokenpool:<v><v>must equal the declared version(v<v>)/(v<v>)<v>must equal the declared versionFROM rust:<v>-slim<v>must be at least the declaredrust-versionThree details that are deliberate:
FROM rust:<v>is>=, not==: building with a newer toolchain is legitimate; the guarded direction is "the declaration was raised and the Dockerfile did not follow" — which is exactly theE0658-shaped failure measured when themsrvjob was added in ci(msrv): enforce the declared rust-version = "1.86" #302.latest(what the READMEs use) are not version claims and are skipped; a YAML service key (aitokenpool:) is skipped for the same reason.[package]section, because the dependency tables also containversion = "0.7"entries and a global match would let one of them stand in as the expectation.No change to config or data structures;
config/config.example.tomlis untouched and already consistent withsrc/config.rs'sDefaultimpls (checked while scoping this).Tests
cargo testall pass — 399 passed / 0 failed (baseline 396; this PR adds 3 tests).cargo fmt --checkpasses (rc 0).cargo clippy --all-targets -- -D warningspasses (rc 0).The rule fires on the pre-fix tree (measured, not asserted)
Run with the fix reverted (i.e. against
2ce954f+ the gate only), it reports exactly the five stale sites and nothing else:Both arms of the rule were then exercised in this tree, one at a time, and restored:
The positive control pins what the scanners actually see (
docker-compose.yml1 tag + 1 remark,Dockerfile2 tags + 1 remark + 1FROM rust:, READMEs zero version tags), so a future rename or path change cannot make the rule pass by scanning an empty set — the failure modethe_scanner_actually_finds_the_settings_it_guardsalready guards against in the first rule.Scope boundary, stated on purpose
The rule covers the deploy artifacts (
docker-compose.yml,Dockerfile, and the READMEs, which currently contain onlylatest). The prose mentions ofrust-versioninCONTRIBUTING.md:7anddocs/architecture.md:18are not covered: they have no runtime consequence, and this repository's own convention for docs is to not copy such numbers at all (docs/plan-api-matrix.md§4 says so explicitly). They are flagged here rather than silently left out.A consequence a reviewer should see
A
chore(release)PR now also has to update these two files, and the gate fails loudly if it does not. That is the point of the change, but it does add two files to the release surface, so it is called out rather than buried:docker-compose.yml:19is the field that decides what the built image is named, and the alternative — dropping the version from the image name — would silently change the tag every existing self-hosted deployment already has locally.