Skip to content

feat(cargo-anvil): add Docker-in-WSL container backend - #59

Open
wukchung wants to merge 39 commits into
mainfrom
u/mhavelka/container-backend
Open

feat(cargo-anvil): add Docker-in-WSL container backend#59
wukchung wants to merge 39 commits into
mainfrom
u/mhavelka/container-backend

Conversation

@wukchung

@wukchung wukchung commented Jul 21, 2026

Copy link
Copy Markdown

Problem

cargo-anvil checks 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

  • Add explicit just anvil-container <anvil-recipe> execution through Docker Engine installed directly in Linux or WSL; Docker Desktop is not required.
  • Add opt-in routing of anvil-pr, anvil-scheduled, and anvil-full through the container with ANVIL_RUNNER=container or the repository-owned anvil-runner setting. The generated default remains native.
  • Build content-addressed local linux/amd64 images from the repository toolchain, generated recipes, Containerfile, restricted Docker build context, downstream static image inputs, and a configurable digest-pinned ANVIL_CONTAINER_BASE_IMAGE.
  • Preserve older images for older branches and use repository-scoped Cargo registry and Cargo Git volumes plus repository/image-specific target volumes.
  • Run recipes as the invoking Linux/WSL user and initialize writable Cargo install metadata for non-root prerequisite checks.
  • Use Docker directly from the Bash driver and invoke the default WSL distribution's Docker Engine from the Windows PowerShell driver.
  • Keep the public lifecycle Docker-specific and document the customization contract without API versioning; Podman remains out of scope.
  • Allow downstream catalogs to replace the Containerfile and add optional APRZ classification, build-secret, preparation, runtime, and cleanup customization while reusing the public command surface and lifecycle.
  • For aggregate tiers that require anvil-aprz, run that check in a separate authenticated container and run the remaining checks without the GitHub token. A direct anvil-container anvil-aprz invocation mounts the token only for that requested check.
  • Validate every requested recipe argument and reject invalid runner values instead of silently falling back to native execution.
  • Document accepted concurrent cold-build behavior, nested just limitations, 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, sets ANVIL_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:

  • The default catalog adds the generated container image definition, entrypoint, host drivers, image-identity helpers, recipe, and documentation.
  • A new managed anvil-runner region is added to the repository-root Justfile, defaulting to native execution.
  • anvil-pr, anvil-scheduled, and anvil-full route through _anvil-run. Even in native mode this wrapper launches a shell and a nested just process before invoking the previous dependency chain; this changes command introspection and --dry-run behavior and may not preserve options or variable overrides supplied only to the outer just process.
  • The existing anvil-aprz recipe recognizes container credential and completion markers. Native runs without those variables retain the existing gh auth token fallback and unauthenticated warning behavior.
  • The managed .gitattributes region 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-anvil passed on Windows.
  • Bash driver syntax and fake-Docker scenarios for base-image selection, downstream APRZ classification, repository-scoped caches, token isolation, and all-argument validation passed in WSL.
  • just format, just readme, and just spellcheck passed.
  • After repository artifacts were regenerated, cargo run -p cargo-anvil -- --dry-run reported all 85 generated artifacts unchanged, confirming convergence of the new generated state.
  • Cold Windows-to-WSL just anvil-container anvil-pr-title built and ran the content-addressed Docker image; the Bash/WSL driver reused the same image successfully.
  • Warm just anvil-container anvil-clippy passed.
  • Full authenticated just anvil-container anvil-pr passed, including the 583-test nextest tier, documentation checks, Miri, and 17 diff-scoped mutants.

Out of scope

  • Podman as an additional runtime implementation.
  • Remote image registries or prebuilt-image publication.
  • CI container-job integration.
  • Windows containers.

wukchung and others added 14 commits July 16, 2026 14:17
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
Copilot AI review requested due to automatic review settings July 21, 2026 12:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 via ANVIL_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.lock to 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.

Comment thread crates/cargo-anvil/docs/design/local.md Outdated
Comment thread justfiles/anvil/container/run-in-container.sh Outdated
Comment thread crates/cargo-anvil/templates/justfiles/anvil/container/run-in-container.sh Outdated
Comment thread crates/cargo-anvil/src/anvil/artifacts/container.rs
@codecov-commenter

codecov-commenter commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (beeaf82) to head (e4f4af6).

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     
Flag Coverage Δ
linux 100.0% <100.0%> (ø)
linux-arm 100.0% <100.0%> (ø)
scheduled ?
windows 100.0% <100.0%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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
Copilot AI review requested due to automatic review settings July 21, 2026 12:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 45 out of 46 changed files in this pull request and generated 2 comments.

Comment thread justfiles/anvil/container/run-in-container.sh Outdated
Comment thread justfiles/anvil/container/run-in-container.ps1 Outdated
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
Copilot AI review requested due to automatic review settings July 21, 2026 12:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-container validates only the first positional argument ($1), so just anvil-container anvil-pr some-other-recipe will 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:68
  • anvil-container only 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 $Recipe entry (or disallow multiple arguments) to keep behavior consistent.

Comment thread crates/cargo-anvil/templates/justfiles/anvil/container/run-in-container.sh Outdated
Comment thread crates/cargo-anvil/templates/justfiles/anvil/container/run-in-container.ps1 Outdated
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
Copilot AI review requested due to automatic review settings July 21, 2026 13:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 *recipe variadic param), later invalid arguments will be forwarded to just in 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 to just in 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

Comment thread justfiles/anvil/container/run-in-container.sh

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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=1 currently short-circuits anvil-aprz even in native runs. Since this marker is intended to mean “already ran inside an authenticated container”, it should only be honored when ANVIL_IN_CONTAINER is 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_RAN marker is meant for container orchestration, but the template currently skips anvil-aprz even outside containers. Gate this fast-path on ANVIL_IN_CONTAINER so 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
Copilot AI review requested due to automatic review settings July 28, 2026 10:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_gid are taken from id without validation and then interpolated into a root sh -c "chown ..." command. If id ever 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 invoke chown without sh -c to avoid interpolation.
    crates/cargo-anvil/templates/justfiles/anvil/container/run-in-container.sh:226
  • container_uid/container_gid are used without validation and are interpolated into a root sh -c "chown ..." string. Validating the IDs are numeric and invoking chown directly (no shell) avoids brittle parsing and eliminates a potential injection vector if id output 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
Copilot AI review requested due to automatic review settings July 28, 2026 11:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 48 out of 49 changed files in this pull request and generated no new comments.

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
Copilot AI review requested due to automatic review settings July 28, 2026 11:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 the just process can be spawned, even if it exits non-zero. That can cause these tests to run (and then fail later) in environments where just is present but not usable. Check the exit status so the tests reliably skip when just --version fails.

Comment thread justfiles/anvil/container/Containerfile
Comment thread crates/cargo-anvil/tests/container_customization_bash.rs
@psandana

Copy link
Copy Markdown
Contributor

Review notes

Reviewed the Docker-in-WSL container backend for cargo-anvil. Summary of findings, ranked by severity/confidence:

1. [security] Shared, unscoped Cargo registry/git cache volumes across repos

crates/cargo-anvil/templates/justfiles/anvil/container/run-in-container.sh (~line 240) and run-in-container.ps1 (~line 221)

registry_volume (anvil-cargo-registry) and git_volume (anvil-cargo-git) use fixed, unscoped names shared by every repository/branch/PR run through this driver on the same host, unlike target_volume, which is correctly scoped per-repo + image-hash. Since containers run with the invoking user's uid/gid and get read-write access, a build for one (possibly untrusted, e.g. PR) repo can write tampered content (extracted crate sources, git checkouts) into these shared volumes, which a later, more-trusted repo's build then picks up instead of pristine content — classic cross-repo build-cache poisoning.

Suggested fix: scope both volumes per repo_id, the same way target_volume already is (e.g. anvil-cargo-registry-${repo_id} / anvil-cargo-git-${repo_id}), or explicitly document/accept the shared-cache trust model.

2. [correctness] Race between image-existence check and docker build

run-in-container.sh (~line 217) and run-in-container.ps1

docker image inspect is checked once; if missing, docker build --tag runs unconditionally. Two concurrent invocations for the same content hash both observe "missing" and both build, wasting significant time and racing on which build's output ends up under the shared tag (relevant since customization can vary build args). Consider a per-image-id lock (e.g. flock) before entering the build branch.

3. [design] Native execution now always pays the nested shell+just wrapper cost

crates/cargo-anvil/templates/justfiles/anvil/runner.just

anvil-pr/anvil-scheduled/anvil-full now depend on _anvil-run, which always spawns a nested pwsh/bash + second just process — even when ANVIL_RUNNER is (and stays) native. Per the design doc this changes --dry-run, command introspection, and variable-override semantics for the outer invocation, for all users, including the vast majority who never opt into containers. Consider gating the wrapper on whether the container backend is actually enabled/generated for the repo, so native-only repos keep the original direct dependency chain and unaltered just semantics.

4. [design] GitHub-token requirement is a hardcoded recipe-name allowlist in a non-replaceable driver

run-in-container.sh / run-in-container.ps1

Test-AnvilRecipeNeedsGitHubToken (and its Bash equivalent) hardcodes a fixed list of recipe/tier names that require GitHub auth, but the extensibility docs say the drivers are not part of what a downstream catalog may replace (only Containerfile/customize.* are). Catalogs with renamed tiers or new privileged checks needing auth have no supported way to add themselves to this list. Consider deriving the auth requirement from catalog-supplied metadata or dependency-graph traversal (similar to the existing reaches_aprz test helper) instead of a hand-maintained literal list.

5. [correctness] Only the first requested recipe is validated against the anvil-* pattern

run-in-container.sh (~line 8) and run-in-container.ps1 (~line 83)

The format guard ([[ ! "$1" =~ ^_?anvil-[A-Za-z0-9-]+$ ]] / $Recipe[0] -notmatch ...) only checks the first recipe argument, even though multi-recipe invocations are an explicitly supported and tested scenario (e.g. anvil-clippy anvil-aprz). A malformed 2nd+ recipe name silently bypasses the intended fail-fast check and is only caught later inside the container by just's own error handling.

Suggested fix: iterate the check over all of "$@" / $Recipe, not just the first element.

6. [doc-consistency] Prerequisite bullet contradicts ARM64 support described a few lines later

crates/cargo-anvil/templates/justfiles/anvil/container/README.md (~line 29)

Line 29 lists as a hard prerequisite "An x86-64 Linux or WSL environment capable of running linux/amd64 images," but line 41 of the same file says ARM64 hosts work via Docker emulation (just slower) — consistent with docs/design/containers.md and the actual driver code, which only warns on non-x86_64 hosts rather than refusing to run.

Suggested fix: "A Linux or WSL environment capable of running linux/amd64 images (natively on x86-64, or via emulation on ARM64, which is substantially slower)."

7. [design] "Runtime-neutral lifecycle" abstraction has one implementation and no second consumer

crates/cargo-anvil/docs/design/containers.md

The container lifecycle and customization API are framed as runtime-neutral and versioned (API version 1), with Podman explicitly out of scope, yet Docker is the only implementation validating the abstraction. Locking in a compatibility contract this early risks having to break it anyway once a second runtime with different semantics (e.g. no BuildKit --secret, different volume model) is actually attempted. Consider shipping this as an explicitly unversioned/experimental Docker-only backend until a second runtime or downstream catalog exercises the abstraction.

8. [design] customize.sh/customize.ps1 are a large, unsandboxed code-execution extension point

crates/cargo-anvil/docs/design/containers.md (~line 300)

These scripts execute arbitrary code on the host with the developer's permissions as part of routine just anvil-container invocations (checking out a branch that adds/changes one silently executes it on next run). This is documented as a trust boundary, but it's a much larger surface than "replace the Containerfile." Consider a narrower extension point (e.g. a build-arg/env allowlist without arbitrary script sourcing), or an explicit one-time trust/allowlist model per customize-file hash (similar to direnv's .envrc trust flow) rather than relying solely on documentation.

9. [design] Four overlapping toggles for one runner setting, with silent-fallback exact-string matching

crates/cargo-anvil/templates/regions/justfile-runner.just

Container execution is controlled by a committed repo default, an ANVIL_RUNNER env var, a just anvil_runner=container CLI override, and an internal ANVIL_IN_CONTAINER recursion guard, with precedence only documented in prose. Exact-string comparison also means a typo like anvil_runner=Container silently falls back to native rather than erroring. Consider collapsing to two knobs (repo default + env var override) and/or validating the value.

10. [design] No documented rationale for simpler alternatives

General (design doc)

The design doc doesn't record why lighter-weight alternatives (e.g. VS Code Dev Containers, or a plain docker run -v wrapper without the custom image-ID/build/prepare/cleanup lifecycle and customization API) were considered and rejected in favor of the bespoke pipeline built here. Worth noting for future maintainers so the cost of the custom machinery isn't re-litigated from scratch later.

11. [doc-consistency] Curly vs. straight apostrophe between generated README and source rustdoc

crates/cargo-anvil/README.md (lines 166, 250)

Minor: the generated README renders a typographic apostrophe ("repository's") where the source doc comment in src/lib.rs uses a straight one. Likely just cargo-doc2readme's smart-punctuation pass — flagging only in case byte-for-byte parity with source is expected; no action needed otherwise.


Notably well-handled from a security standpoint: the GitHub token is never passed via --env/build-args (avoiding leakage into docker inspect output or image layers), is bind-mounted read-only only into the isolated anvil-aprz container, and is cleaned up via trap on all exit paths; the Containerfile pins its base image by digest and verifies downloaded tool archives against pinned SHA-256 hashes; .dockerignore is deny-by-default; and recipe-name arguments are regex-validated and never reach eval.

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
Copilot AI review requested due to automatic review settings July 29, 2026 08:28
@wukchung

Copy link
Copy Markdown
Author

🤖 @psandana Thanks for the thorough review. Addressed in 8fbd4bb:

  1. Shared Cargo caches: registry and Git volumes are now repository-scoped while remaining reusable across that repository's branches and image IDs.
  2. Concurrent image builds: accepted as redundant local work and documented explicitly; BuildKit secrets must provide equivalent authenticated access rather than select different content.
  3. Nested just: retained because runtime runner selection requires it; the --dry-run, introspection, global-option, and CLI-variable limitations are now prominent in user and design documentation.
  4. Authentication classification: downstream customization can now elevate ANVIL_CONTAINER_NEEDS_GITHUB_TOKEN / $AnvilContainerNeedsGitHubToken for additional aggregate recipes.
  5. Recipe validation: every positional argument is validated as an anvil-* or _anvil-* recipe; recipe parameters are explicitly unsupported by this command surface.
  6. ARM64 prerequisite: corrected to distinguish native x86-64 execution from ARM64 emulation.
  7. API maturity: removed customization API versioning and runtime-neutral framing; the contract is documented as Docker-specific.
  8. Host customization trust: retained the extension point, with prominent warnings that it executes trusted host code before container isolation. This has the same trust root as other checked-in host scripts, so no separate hash-approval mechanism was added.
  9. Runner controls: retained the distinct repository default, environment/CLI override, and internal recursion guard, but invalid or incorrectly cased runner values now fail instead of silently selecting native execution.
  10. Alternatives: added an alternatives-considered section covering native setup, Dev Containers, a plain docker run wrapper, prebuilt images, and fixed deployment distributions.
  11. Typography: no functional change; the crate README remains generated from rustdoc and smart punctuation is presentation-only.

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 48 out of 49 changed files in this pull request and generated no new comments.

Comment thread crates/cargo-anvil/tests/container_customization_bash.rs
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
Copilot AI review requested due to automatic review settings July 29, 2026 08:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 48 out of 49 changed files in this pull request and generated 2 comments.

Comment thread justfiles/anvil/container/run-in-container.sh
Comment thread justfiles/anvil/container/run-in-container.ps1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

road-to-gas-town Touched by a Road to Gas Town skill

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants