feat(cargo-anvil): add Docker-in-WSL container backend - #59
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d4164b11-520f-403b-9082-ef763eece315
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d4164b11-520f-403b-9082-ef763eece315
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d4164b11-520f-403b-9082-ef763eece315
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d4164b11-520f-403b-9082-ef763eece315
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d4164b11-520f-403b-9082-ef763eece315
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d4164b11-520f-403b-9082-ef763eece315
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d4164b11-520f-403b-9082-ef763eece315
Initialize writable Cargo install metadata through the container entrypoint and refresh the generated snapshots and spelling dictionary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f449f21-aeae-45e0-949f-f433d159cdac
Use an existing host GITHUB_TOKEN or authenticated gh session for container entry points that run anvil-aprz. Pass it through a temporary read-only secret and clean it up after the command. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: acc398a0-31e9-4be8-9dde-62dd1f428433
Pause interactive container runs that need anvil-aprz when the host gh session is unauthenticated. Explain the API limit, retry after Enter, and fail immediately in non-interactive runs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: acc398a0-31e9-4be8-9dde-62dd1f428433
Point project-default instructions to the repository-root Justfile, tighten the container documentation, and verify downstream catalogs inherit public artifacts exactly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f449f21-aeae-45e0-949f-f433d159cdac
Regenerate the repository-owned Anvil workflow, hash downstream build hooks, support macOS host defaults, isolate GitHub credentials to anvil-aprz, and strengthen container regressions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f449f21-aeae-45e0-949f-f433d159cdac
There was a problem hiding this comment.
Pull request overview
Adds an opt-in local Podman-based container backend to cargo-anvil, enabling developers to run generated anvil-* recipes inside a pinned Linux image while keeping native execution as the default. This integrates into the existing artifact-generation model (owned files + managed regions) and documents/validates the new workflow.
Changes:
- Route public tier entrypoints (
anvil-pr,anvil-scheduled,anvil-full) through a runner seam that can dispatch to native or container execution viaANVIL_RUNNER. - Introduce a
just anvil-container <recipe>command plus Containerfile + drivers + image-id hashing to build and run a content-addressed local image. - Regenerate and update documentation, templates, snapshots, spelling, and
.anvil.lockto reflect the new container backend and runner region.
Reviewed changes
Copilot reviewed 43 out of 44 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| justfiles/anvil/tiers.just | Routes tier entrypoints through _anvil-run and makes the original tier aggregations private. |
| justfiles/anvil/runner.just | Adds the _anvil-run dispatch seam (native vs container, recursion-guarded). |
| justfiles/anvil/mod.just | Imports container backend and runner into the generated Anvil module. |
| justfiles/anvil/container/run-in-container.sh | Unix/WSL Podman driver: image selection/build + podman run wrapper. |
| justfiles/anvil/container/run-in-container.ps1 | Windows Podman driver with optional build-in-machine behavior and secret mounting. |
| justfiles/anvil/container/README.md | User-facing container prerequisites, usage, controls, and troubleshooting. |
| justfiles/anvil/container/image-id.ps1 | Computes content-addressed image tags from toolchain + generated recipes + container inputs. |
| justfiles/anvil/container/entrypoint.sh | Initializes non-root Cargo metadata and redirects Cargo caches to named volumes. |
| justfiles/anvil/container/Containerfile | Defines the pinned Linux image build that installs the generated Anvil toolset. |
| justfiles/anvil/container/container.just | Adds the public anvil-container recipe entrypoint (OS-dispatched). |
| justfiles/anvil/container/container.ignore | Restricts build context to Anvil-relevant files for image builds. |
| justfiles/anvil/checks/aprz.just | Updates anvil-aprz token acquisition to support container secret mounting + fast-fail in-container. |
| Justfile | Adds the managed anvil_runner region for user-controlled native/container policy. |
| crates/cargo-anvil/tests/snapshots/snapshots__local_only.snap | Regenerates golden output to include container artifacts + runner region + .sh eol rules. |
| crates/cargo-anvil/tests/snapshots/snapshots__github_backend.snap | Regenerates golden output to include container artifacts + runner region + .sh eol rules. |
| crates/cargo-anvil/tests/snapshots/snapshots__ado_backend.snap | Regenerates golden output to include container artifacts + runner region + .sh eol rules. |
| crates/cargo-anvil/tests/extensibility.rs | Adds an extensibility test ensuring downstream catalogs can replace Containerfile and add auth hooks. |
| crates/cargo-anvil/templates/regions/justfile-runner.just | Adds the template for the new anvil-runner managed Justfile region. |
| crates/cargo-anvil/templates/regions/gitattributes | Extends the managed .gitattributes region to pin .sh files to LF. |
| crates/cargo-anvil/templates/justfiles/anvil/tiers.just | Updates the tier template to use _anvil-run + private tier aggregates. |
| crates/cargo-anvil/templates/justfiles/anvil/runner.just | Adds the runner seam template used by generated catalogs. |
| crates/cargo-anvil/templates/justfiles/anvil/mod.just | Imports the container backend and runner in the template module. |
| crates/cargo-anvil/templates/justfiles/anvil/container/run-in-container.sh | Template for the Unix/WSL Podman driver. |
| crates/cargo-anvil/templates/justfiles/anvil/container/run-in-container.ps1 | Template for the Windows Podman driver. |
| crates/cargo-anvil/templates/justfiles/anvil/container/README.md | Template for container documentation emitted into repos. |
| crates/cargo-anvil/templates/justfiles/anvil/container/image-id.ps1 | Template for image content hashing helper emitted into repos. |
| crates/cargo-anvil/templates/justfiles/anvil/container/entrypoint.sh | Template for container entrypoint emitted into repos. |
| crates/cargo-anvil/templates/justfiles/anvil/container/Containerfile | Template for image build definition emitted into repos. |
| crates/cargo-anvil/templates/justfiles/anvil/container/container.just | Template for anvil-container recipe emitted into repos. |
| crates/cargo-anvil/templates/justfiles/anvil/container/container.ignore | Template for build-context ignore rules emitted into repos. |
| crates/cargo-anvil/templates/justfiles/anvil/checks/aprz.just | Mirrors anvil-aprz token behavior changes in templates. |
| crates/cargo-anvil/src/lib.rs | Documents the new container backend in crate-level docs. |
| crates/cargo-anvil/src/anvil/artifacts/region.rs | Adds a new managed region (anvil-runner) for tier execution policy in Justfile. |
| crates/cargo-anvil/src/anvil/artifacts/mod.rs | Registers container artifacts and the new Justfile runner region in the base catalog. |
| crates/cargo-anvil/src/anvil/artifacts/justfile.rs | Adds the runner artifact and extends template tests to assert new imports/tier structure. |
| crates/cargo-anvil/src/anvil/artifacts/container.rs | Introduces the container artifact group (owned files + tests). |
| crates/cargo-anvil/README.md | Propagates container backend docs into the generated crate README. |
| crates/cargo-anvil/docs/design/local.md | Documents the optional container backend in the local-design doc. |
| crates/cargo-anvil/docs/design/extensibility.md | Documents the container runner as an extensibility seam for downstream catalogs. |
| crates/cargo-anvil/docs/design/design.md | Adds containers.md to the design document index. |
| crates/cargo-anvil/docs/design/containers.md | Adds a full design document for the opt-in local container backend. |
| .spelling | Adds container-related words to the spellcheck dictionary. |
| .gitattributes | Updates the managed region to enforce LF for .sh files. |
| .anvil.lock | Updates catalog checksum and file/region checksums to include new artifacts and regions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #59 +/- ##
=======================================
Coverage 100.0% 100.0%
=======================================
Files 50 51 +1
Lines 2652 2740 +88
=======================================
+ Hits 2652 2740 +88
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Use native platform entry recipes and image-ID helpers, improve non-repository diagnostics, keep cross-platform hashes identical, and repair the container design documentation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f449f21-aeae-45e0-949f-f433d159cdac
Remove trailing whitespace from the container documentation fence and regenerate the crate README. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f449f21-aeae-45e0-949f-f433d159cdac
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 45 out of 46 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
justfiles/anvil/container/run-in-container.sh:11
anvil-containervalidates only the first positional argument ($1), sojust anvil-container anvil-pr some-other-recipewill bypass the stated “expected an anvil-* recipe” guard for later args and can run non-anvil recipes inside the container. Validate every provided recipe name before proceeding.
justfiles/anvil/container/run-in-container.ps1:68anvil-containeronly validates$Recipe[0]. If multiple recipes are passed, later args can be non-anvil-*despite the error message claiming an anvil-only contract. Validate every$Recipeentry (or disallow multiple arguments) to keep behavior consistent.
Mark the image-ID filesystem/subprocess regression tests with the repository-standard Miri isolation exclusion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2f449f21-aeae-45e0-949f-f433d159cdac
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 45 out of 46 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
justfiles/anvil/container/run-in-container.sh:11
- The argument validation only checks
$1. If multiple recipe names are passed (supported by the*recipevariadic param), later invalid arguments will be forwarded tojustin the container and fail with a less actionable error. Validate every argument up front and fail with the same clear message.
crates/cargo-anvil/templates/justfiles/anvil/container/run-in-container.sh:11 - The argument validation only checks
$1. If multiple recipe names are passed, later invalid arguments will be forwarded tojustin the container and fail with a less actionable error. Validate every argument up front in the template so generated drivers get the same behavior.
if (($# > 0)) && [[ ! "$1" =~ ^_?anvil-[A-Za-z0-9-]+$ ]]; then
echo "anvil-container: expected an anvil-* recipe, got '$1'." >&2
exit 2
fi
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 47 out of 48 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
justfiles/anvil/checks/aprz.just:27
ANVIL_APRZ_ALREADY_RAN=1currently short-circuitsanvil-aprzeven in native runs. Since this marker is intended to mean “already ran inside an authenticated container”, it should only be honored whenANVIL_IN_CONTAINERis also set; otherwise users/CI could accidentally (or intentionally) skip the advisory check by exporting this env var.
crates/cargo-anvil/templates/justfiles/anvil/checks/aprz.just:27- The
ANVIL_APRZ_ALREADY_RANmarker is meant for container orchestration, but the template currently skipsanvil-aprzeven outside containers. Gate this fast-path onANVIL_IN_CONTAINERso native runs can’t accidentally bypass the check via an environment variable.
if ($env:ANVIL_APRZ_ALREADY_RAN -eq '1') {
Write-Host 'anvil-aprz: already completed in an isolated authenticated container'
exit 0
}
Keep configured container routing while preserving native stdout, stderr, exit status, and custom Justfile resolution. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3ba336df-571d-4420-84a8-d54ac7e1f5ff
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 48 out of 49 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
justfiles/anvil/container/run-in-container.sh:226
container_uid/container_gidare taken fromidwithout validation and then interpolated into a rootsh -c "chown ..."command. Ifidever returns an unexpected value (or is shadowed), this can break the chown step or allow shell injection in the root container. Validate the values are numeric and invokechownwithoutsh -cto avoid interpolation.
crates/cargo-anvil/templates/justfiles/anvil/container/run-in-container.sh:226container_uid/container_gidare used without validation and are interpolated into a rootsh -c "chown ..."string. Validating the IDs are numeric and invokingchowndirectly (no shell) avoids brittle parsing and eliminates a potential injection vector ifidoutput is unexpected.
container_uid="$(id -u)"
container_gid="$(id -g)"
registry_volume="anvil-cargo-registry"
Preserve image identity, verify APRZ auth routing, strengthen secret lifecycle tests, and clarify the container removal contract. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3dc983de-0d48-406f-a12d-3bc193ee1164
Use a deterministic failing tier, skip environments without just, and remove unnecessary formatting. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3dc983de-0d48-406f-a12d-3bc193ee1164
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 48 out of 49 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
crates/cargo-anvil/tests/tier_routing.rs:84
just_available()returns true whenever thejustprocess can be spawned, even if it exits non-zero. That can cause these tests to run (and then fail later) in environments wherejustis present but not usable. Check the exit status so the tests reliably skip whenjust --versionfails.
Review notesReviewed the Docker-in-WSL container backend for 1. [security] Shared, unscoped Cargo registry/git cache volumes across repos
Suggested fix: scope both volumes per 2. [correctness] Race between image-existence check and
|
Add configurable pinned base images, repository-scoped caches, extensible APRZ classification, strict command validation, and complete public design rationale. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3dc983de-0d48-406f-a12d-3bc193ee1164
|
🤖 @psandana Thanks for the thorough review. Addressed in
The same change also adds a digest-pinned configurable base image included in image identity, enabling a lower-glibc Debian baseline without replacing the public Containerfile. AI response generated by Road to Gas Town |
Prove Cargo caches differ across repositories while remaining stable across image IDs within one repository. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3dc983de-0d48-406f-a12d-3bc193ee1164
Problem
cargo-anvilchecks run natively on the developer host, which makes Linux-only validation difficult from Windows and requires the full pinned Rust/tool catalog to be installed locally. Downstream catalogs also need a supported way to customize base images and authenticated dependency preparation without forking the public drivers.Change
just anvil-container <anvil-recipe>execution through Docker Engine installed directly in Linux or WSL; Docker Desktop is not required.anvil-pr,anvil-scheduled, andanvil-fullthrough the container withANVIL_RUNNER=containeror the repository-ownedanvil-runnersetting. The generated default remainsnative.linux/amd64images from the repository toolchain, generated recipes, Containerfile, restricted Docker build context, downstream static image inputs, and a configurable digest-pinnedANVIL_CONTAINER_BASE_IMAGE.anvil-aprz, run that check in a separate authenticated container and run the remaining checks without the GitHub token. A directanvil-container anvil-aprzinvocation mounts the token only for that requested check.justlimitations, the host-code trust boundary, and alternatives considered.Compatibility and generated repository changes
Container execution is opt-in: Docker is not started unless the user invokes
anvil-container, setsANVIL_RUNNER=container, or changes the repository runner setting. Existing repositories that do not upgrade and regenerate are unaffected.Regenerating with this version is not a no-op for existing repositories:
anvil-runnerregion is added to the repository-rootJustfile, defaulting to native execution.anvil-pr,anvil-scheduled, andanvil-fullroute through_anvil-run. Even in native mode this wrapper launches a shell and a nestedjustprocess before invoking the previous dependency chain; this changes command introspection and--dry-runbehavior and may not preserve options or variable overrides supplied only to the outerjustprocess.anvil-aprzrecipe recognizes container credential and completion markers. Native runs without those variables retain the existinggh auth tokenfallback and unauthenticated warning behavior..gitattributesregion adds*.sh text eol=lf, applying LF normalization to shell scripts throughout the repository.Generated GitHub and Azure DevOps workflows continue to invoke the existing per-group recipes directly and do not opt into container execution.
Related work
Validation
cargo test -p cargo-anvilpassed on Windows.just format,just readme, andjust spellcheckpassed.cargo run -p cargo-anvil -- --dry-runreported all 85 generated artifacts unchanged, confirming convergence of the new generated state.just anvil-container anvil-pr-titlebuilt and ran the content-addressed Docker image; the Bash/WSL driver reused the same image successfully.just anvil-container anvil-clippypassed.just anvil-container anvil-prpassed, including the 583-test nextest tier, documentation checks, Miri, and 17 diff-scoped mutants.Out of scope