You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(cargo-anvil): let a repository with no root toolchain file build the container image (#161)
* fix(cargo-anvil): supply the declared MSRV to the container image build
`anvil-msrv-test-setup` resolves the MSRV by scanning the root `Cargo.toml`,
and `just anvil-setup` reaches it through the PR tier. The container image is
built with no repository source in its context by design, so that scan threw
and the install layer failed. No image was produced, which left every
`anvil-container` recipe unusable after a cold build.
The MSRV is the one version anvil installs that is declared in the source
rather than pinned in `versions.just`, and the image already carries the
repository's other toolchain declaration, `rust-toolchain.toml`. Close that
asymmetry by making the MSRV an image input: `anvil-container-tag` resolves the
declared value through a new `_anvil-resolve-stable root-msrv` action and hashes
it, the build passes it as `ARG ANVIL_ROOT_MSRV`, and the resolver reads it only
when no root manifest is present, so it can never shadow a real declaration.
The value is carried rather than the manifest holding it. Copying `Cargo.toml`
into the context would rename the image on every dependency edit, obliging a
publisher to rebuild and republish for changes that cannot alter a byte the
image contains, while `rust-version` moves perhaps once. A repository that
declares no MSRV sends `none`, which is an answer; an unset variable is not, so
a build that drops the argument stops rather than producing an image silently
missing a toolchain it claims to install.
Validated with a cold image build on Docker 29.7.1: the build completes and
`rustup toolchain list` in the resulting image reports the declared MSRV
alongside the pinned stable and nightlies. Contract tests cover manifest
precedence over the override, the `none` declaration, the refusal when nothing
is declared, and that the tag follows the MSRV while ignoring unrelated
manifest edits.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80f12d44-5a83-4650-9806-d3e247175858
* refactor(cargo-anvil): let the image read the MSRV instead of being told it
The setup region installs the toolchain named by the repository's declared
MSRV, which lives in the root `Cargo.toml` rather than in `versions.just`.
That value reached the build as `ARG ANVIL_ROOT_MSRV`, resolved on the host
and passed as a build argument, with the resolver reading the variable
whenever it found no manifest.
That channel is fragile in a way the composed Dockerfile makes likely. The
setup region is a documented replacement point, and the `ARG` sat inside it,
so a repository substituting its own region kept receiving the build argument
while silently losing the declaration that receives it. The resulting failure
is `failed to install MSRV toolchain '1.92'`, which sends the reader hunting a
toolchain that is not the problem.
Admit the root manifest to the build context and copy it to `/opt/anvil`
instead. That directory is already the root the recipes resolve against: it is
`justfile_directory()`, and it holds `justfiles/` and the toolchain pin.
Copying the manifest alongside them completes it for the one question the
setup asks, and the resolver reads it there exactly as it does on a
developer's machine.
`tools.just` therefore returns to what it was, minus the new `root-msrv`
action the tag still needs. The variable, its `none` sentinel, its error
message and the `--build-arg` all go away, and with them a general resolver's
knowledge of containers. Dropping the `COPY` now fails with `Cargo.toml not
found at repository root`, which names the missing thing.
The workspace members the manifest lists stay out of the context: they are a
checkout, and the image is not one. The one path that would need them,
workspace MSRV validation, returns early whenever a root toolchain file
selects the compiler, which this image requires and copies.
The tag continues to hash the resolved value rather than the file. The
manifest is the busiest file in a workspace while `rust-version` moves perhaps
once in a repository's life, so hashing it would rename the image, and oblige
a publisher to rebuild and republish, for a stream of edits that cannot alter
a byte the image contains.
Contracts follow the mechanism: the context admits the manifest, the setup
region copies it, and no build argument survives anywhere.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2963084a-ef08-4cf7-adc6-e78e61556a84
* docs(cargo-anvil): trim the MSRV commentary to what is not inferable
The prose ran about four lines per line of code, well past what the change needs. Keep the reasons a reader cannot recover from the code -- why the value is hashed rather than the manifest, why the members stay out of the context, why the declared value rather than the mapped one -- and drop the restatement around them.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2963084a-ef08-4cf7-adc6-e78e61556a84
* test(cargo-anvil): pin the toolchain-file precondition the MSRV design rests on
Workspace MSRV validation reads every member manifest, and the build context carries none. It stays out of reach only because a root toolchain file selects the compiler and makes the resolver return early -- which holds today by accident, since the unconditional COPY means a repository without one cannot build an image at all.
Making that COPY conditional is a reasonable fix for those repositories, and it would silently put the branch back in reach of a partial workspace. State the dependency so that change has to confront this one.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2963084a-ef08-4cf7-adc6-e78e61556a84
* fix(cargo-anvil): let a repository with no root toolchain file build the image
The setup region named `rust-toolchain.toml` in a COPY and the tag recipe listed it as a required input, so a repository that pins its compiler by other means could not build an image at all: the build failed at the first COPY, and `anvil-container-tag` refused before that. `microsoft/oxidizer` is such a repository.
No engine anvil supports offers a portable COPY of a path that may not exist, so the region names no input at all and copies the context whole. The ignore file already scopes that context to precisely the image's inputs, so what it admits and what the image contains become the same set. It now admits both toolchain-file spellings: naming only the TOML would leave a repository that pins with the extensionless file building an image whose compiler silently disagreed with its own checkout.
The tag discovers the file rather than requiring it, in both spellings. Absence is one fewer record in the digest, so the two states cannot share a reference, and the file's mode still comes from the index. The ignore file stays a named, required input for the reason it always was.
The design note this replaces claimed the toolchain file kept workspace MSRV validation out of reach of a memberless context. It does not: that validation hangs off the `-validate-prereqs` recipes, and `anvil-setup` -- the only thing the image runs -- depends on none of them. `just --dry-run anvil-setup binstall` reaches `_anvil-resolve-stable install-msrv` and nothing else, so no resolver change is needed and the doc and contract test now state the real reason.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(cargo-anvil): delete the copied manifest once the setup has read it
The tag hashes the declared MSRV rather than the manifest, so an edit that leaves rust-version alone computes the same tag. With the manifest left in place that tag named two different filesystems, and a reused or published image carried a Cargo.toml matching no checkout.
Deleting it in the same RUN that reads it makes the identity exact again, and restores the design note's claim that those edits cannot alter a byte the image contains. Three statements in containers.md that still described the old three-path context are corrected alongside it, including the replacement rule a downstream catalog reads verbatim.
Also drops an env_remove for ANVIL_ROOT_MSRV: nothing on this branch reads that variable, so the call could not affect what a fixture resolves and its comment described a mechanism that no longer exists.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2963084a-ef08-4cf7-adc6-e78e61556a84
* docs(cargo-anvil): trim the container commentary to what is not inferable
The comments added with the previous commit ran well past the change. Several restated the assertion message directly below them; others explained what the code already says.
Keep the reasons a reader cannot recover: why the context is copied whole, why both toolchain-file spellings are admitted and neither required, and why the tag discovers the file rather than requiring it. Drop the rest. No behaviour changes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 80f12d44-5a83-4650-9806-d3e247175858
Copilot-Session: 2963084a-ef08-4cf7-adc6-e78e61556a84
Copy file name to clipboardExpand all lines: crates/cargo-anvil/README.md
+5-3Lines changed: 5 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -348,8 +348,10 @@ just anvil-container cargo build
348
348
```
349
349
350
350
The image contains the same tool versions used by the generated checks.
351
-
Its tag is derived from the Dockerfile, toolchain, and generated recipe
352
-
tree, so changes to those inputs select a different image. Docker is
351
+
Its tag is derived from the Dockerfile and its ignore file, the generated
352
+
recipe tree, the declared root MSRV, and a root `rust-toolchain` file where
353
+
the repository owns one, so changes to those inputs select a different
354
+
image. Docker is
353
355
supported; Podman is available on a best-effort basis. Repositories that
354
356
need private feeds can add a host-side credential hook without embedding
355
357
credentials in the image or command line. See
@@ -398,7 +400,7 @@ More detailed design and operational guidance is available in the
398
400
This crate was developed as part of <ahref="../..">The Oxidizer Project</a>. Browse this crate's <ahref="https://github.com/microsoft/ox-tools/tree/main/crates/cargo-anvil">source code</a>.
Copy file name to clipboardExpand all lines: crates/cargo-anvil/docs/design/containers.md
+47-25Lines changed: 47 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -53,9 +53,10 @@ Both are addressed by executing the recipe unchanged inside an image built from
53
53
bodies are identical in either mode, and cloud workflows are unaffected: they run the same recipes natively on their
54
54
own agents. The image is pinned to resemble that environment, not to reproduce it.
55
55
56
-
Image construction has a deliberately stricter compiler contract than native execution: the repository must own
57
-
`rust-toolchain.toml`. The build context admits that file but not `rust-toolchain`, and the build does not inherit the
58
-
host's `RUSTUP_TOOLCHAIN`; rustup therefore selects the image compiler from the repository-owned TOML file.
56
+
Image construction does not require the repository to own a `rust-toolchain.toml`. Where one exists — either
57
+
spelling — the context admits it and rustup selects the image compiler from it, exactly as it would on a host. Where a
58
+
repository pins its compiler by other means and owns neither, the image compiler is the declared root MSRV, which is
59
+
again what a host resolves there. The build does not inherit the host's `RUSTUP_TOOLCHAIN` in either case.
59
60
60
61
## 2. Command surface
61
62
@@ -133,7 +134,7 @@ never read, rewritten or reordered.
133
134
|`anvil-container-base-image`|`ARG BASE_IMAGE`, pinned to a digest. | A second `ARG BASE_IMAGE=…` to build on a different base. |
134
135
|`anvil-container-base`|`FROM`, the version pins for `pwsh`, `just`, `rustup` and `cargo-binstall`, and the `ENV` block. | Anything the first network access needs: a root CA, `http_proxy`, an internal package mirror. |
135
136
|`anvil-container-tools`| System packages and those four tools. | Libraries a catalog tool needs to compile, for tools `binstall` has no prebuilt binary for. |
136
-
|`anvil-container-setup`|`COPY` of the recipe tree and the root manifest, then `just anvil-setup`. | Anything the repository's own checks need at run time. |
137
+
|`anvil-container-setup`|`COPY` of the scoped build context, then `just anvil-setup`. | Anything the repository's own checks need at run time. |
0 commit comments