Skip to content

fix(deploy): let the deploy artifacts carry the version Cargo.toml declares - #303

Merged
argszero merged 1 commit into
mainfrom
fix/deploy-artifact-version-follows-cargo-toml
Sep 26, 2026
Merged

argszero merged 1 commit into
mainfrom
fix/deploy-artifact-version-follows-cargo-toml

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

docker-compose.yml and Dockerfile each copy two facts out of Cargo.toml, and nothing in the repository asserted either copy:

  • the release version — 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;
  • the minimum Rust version — FROM rust:<v>-slim (Dockerfile:16).

Cross-format files cannot import, so a copy is unavoidable here — the same situation catalog_gate.rs handles for MODEL_COUNT. What is avoidable is a copy with no enforcer. These were written in a51b3ba (#99, v0.6.6) and never touched again (docker-compose.yml was edited once since, by 7852277 #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:37 recommends: docker compose up -d --build tags the image it builds aitokenpool:0.6.6, while the binary inside reports 0.7.28 — the /healthz version comes from env!("CARGO_PKG_VERSION"), i.e. from the manifest. docker compose pull would additionally look for a library/aitokenpool:0.6.6 on 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.28 in the header remark and in the image: field.

  • Dockerfile — v0.6.6 → v0.7.28 in the header remark and in the two aitokenpool:<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 via include_str!). Expectations are derived from Cargo.toml, not snapshotted:

    shape example rule
    image tag image: aitokenpool:<v> <v> must equal the declared version
    version remark (v<v>) / (v<v>) <v> must equal the declared version
    builder image FROM rust:<v>-slim <v> must be at least the declared rust-version

    Three 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 the E0658-shaped failure measured when the msrv job was added in ci(msrv): enforce the declared rust-version = "1.86" #302.
    • moving tags such as latest (what the READMEs use) are not version claims and are skipped; a YAML service key (aitokenpool:) is skipped for the same reason.
    • the manifest reader is scoped to the [package] section, because the dependency tables also contain version = "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.toml is untouched and already consistent with src/config.rs's Default impls (checked while scoping this).

Tests

  • cargo test all pass — 399 passed / 0 failed (baseline 396; this PR adds 3 tests).
  • cargo fmt --check passes (rc 0).
  • cargo clippy --all-targets -- -D warnings passes (rc 0).
  • New tests added: the rule itself, a positive control, and a teeth test.

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:

docker-compose.yml:19: 镜像 tag 是 0.6.6,而 Cargo.toml 声明的是 0.7.28(副本没跟上)
docker-compose.yml:1: 注释标注的发行版是 v0.6.6,而 Cargo.toml 声明的是 0.7.28
Dockerfile:3: 镜像 tag 是 0.6.6,而 Cargo.toml 声明的是 0.7.28(副本没跟上)
Dockerfile:6: 镜像 tag 是 0.6.6,而 Cargo.toml 声明的是 0.7.28(副本没跟上)
Dockerfile:1: 注释标注的发行版是 v0.6.6,而 Cargo.toml 声明的是 0.7.28

Both arms of the rule were then exercised in this tree, one at a time, and restored:

Dockerfile:16  FROM rust:1.85-slim        -> red: rust:1.85 低于 Cargo.toml 声明的 rust-version 1.86
Cargo.toml     version = "0.7.29"         -> red: the same five sites, now quoting 0.7.29

The positive control pins what the scanners actually see (docker-compose.yml 1 tag + 1 remark, Dockerfile 2 tags + 1 remark + 1 FROM rust:, READMEs zero version tags), so a future rename or path change cannot make the rule pass by scanning an empty set — the failure mode the_scanner_actually_finds_the_settings_it_guards already 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 only latest). The prose mentions of rust-version in CONTRIBUTING.md:7 and docs/architecture.md:18 are 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:19 is 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.

…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.
@argszero
argszero merged commit b7032dc into main Sep 26, 2026
2 checks passed
@argszero
argszero deleted the fix/deploy-artifact-version-follows-cargo-toml branch September 26, 2026 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant