feat(containers): agent-tools OCI images + registry CI pipeline #210
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab!210
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/203"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
containers/agent-tools/: versions.env — the new CLI version catalog (claude-code 2.1.198, codex 0.133.0, both artifact-sha256-pinned; mirrors the compat-record pins, which stay the authority), Containerfile.claude, Containerfile.codex, build.sh, smoke-test.sh; plus a root.containerignore..forgejo/workflows/agent-tools.yml: PR leg = local podman build + injection smoke test (no push, no secrets); release leg (push:main path-gated + workflow_dispatch) = re-smoke, push digest-pinned tags to git.cloonar.com, emit@sha256:refs to the job summary. Fails loud with provisioning instructions whenFORGE_REGISTRY_TOKENis absent.docs/ops.md§ Agent-tools images (contents + tagging scheme), ADR-0051, CONTEXT.md glossary entry Agent-tools image.Mechanism — why the CLIs run on glibc AND musl bases without the base libc
linux-x64-muslnative build's only external dependency is the musl loader. We bundle Alpine's loader in the image and rewrite the binary's PT_INTERP to/opt/lab/lib/ld-musl-x86_64.so.1(patchelf, NOT a loader wrapper — claude re-execsprocess.execPathas its embedded ripgrep, which a wrapper would break). Verified empirically on the exact pinned 2.1.198 binary: the patched binary answers--versionand serves ripgrep 14.1.1 underenv -i. This makes /opt/lab a hard mount destination (ADR-0051).CGO_ENABLED=0, statically linked (verified locally).Verification
No podman exists in the agent sandbox (as the triage brief anticipated), so this PR's agent-tools smoke job is the end-to-end proof: it podman-builds both images and injects them into stock
debian:stable-slimandalpine, runningclaude --version/codex --version/labctl --help. Verified locally: full Go suite + golangci-lint green (test-env quirk: suite needs/binon PATH + short TMPDIR in this sandbox — pre-existing, unrelated to this diff); workflow YAML parses; labctl static build replicated with the exact Containerfile command inside a.containerignore-simulated context; both artifact URLs live and sha256s match Anthropic's per-version manifest / the GitHub release digest. If the CI runner turns out unable to run podman, the smoke job'spodman infodiagnostics are the evidence — flag back to the issue rather than merging unverified.Operator prerequisites (release leg only)
FORGE_REGISTRY_TOKEN(Forgejo access token,write:packageon git.cloonar.com) as a repo CI secret; optionalFORGE_REGISTRY_USERwhen the token isn't owned by the repository-owner account. PRs never see either.Out of scope per the brief: the container runner (#205), dev-image selection (#207), a gemini image (seam left in versions.env until #126 lands).
Closes #203
[autoland] verdict: reject
Automated lander verdict: FAIL — CI check aggregate is
failure, so the PR is not landable.Verification signal relied on:
labctl pr checks 210.Failing row (blocker):
agent-tools / smoke (pull_request)— failure, "Failing after 48s"(run 234 / job 0: /Cloonar/coding-lab/actions/runs/234/jobs/0)
Context of the other rows:
agent-tools / publish (pull_request)— skipped (expected; the publish leg ispush/workflow_dispatch-only).ci / native (pull_request)— stillpendingat verdict time; does not affect the outcome since a red row already dominates (precedence failure > pending > success).Why this blocks: the
agent-tools / smokejob is this PR's own designated end-to-end proof (per the PR body: "this PR's agent-tools smoke job is the end-to-end proof"). With it red, the build+injection path is unverified and the PR cannot land. The fix belongs to the author, not the lander — per the validation core a red check is a blocker and this is not a trivial (formatting/lint/conflict) fix.I could not read the job logs to pinpoint the exact failing step — the lander has no Forgejo API/log credential (git.cloonar.com returns 307→login; the run token is not a forge token). Please inspect the job at the URL above. The likely first suspects, given a ~48s failure:
Ensure podmanstep — this is the first workflow to require podman on these runners; if the runner cannot initialize podman (rootless/subuid-subgid, /dev/fuse, cgroups),podman infofails here. The PR body anticipates exactly this: "If the CI runner turns out unable to run podman, the smoke job'spodman infodiagnostics are the evidence — flag back to the issue rather than merging unverified." If that is the cause, it is a runner-provisioning matter (docs/ops.md § "CI runner prerequisites" + podman) rather than a code defect, and needs a human/infra decision — surface it on #203.containers/agent-tools/build.sh claude(base-image pull or CLI-artifact fetch).Read the
Ensure podmanandclaude: build + injection smoke teststep output to distinguish these, fix, and hand back withlabctl pr rerequest.Conventions were otherwise satisfied: Conventional Commits title,
Closes #203present, head branchafk/203matches, and the diff is purely additive (589 insertions, no edits to existing logic). The sole blocker is the red check.Run 240 got the claude image fully built and tagged (the disk fix held) and then died starting the first smoke container: Ubuntu's podman defaults to runc, which refuses --cgroups=disabled ("requested OCI runtime runc is not compatible with NoCgroups"). Install crun alongside podman and pin runtime = crun in the CI containers.conf; crun supports cgroup-less containers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>Run 244 proved the whole build path green and moved the failure into the smoke run itself: the runner's job container carries locked masked-path overmounts on /proc, so the kernel's fully-visible rule denies crun's fresh procfs mount ("mount `proc` to `proc`: Operation not permitted"). smoke-test.sh gains an SMOKE_EXTRA_RUN_ARGS seam (default empty — real hosts stay pristine); CI sets it to --pid=host plus a recursive bind of the job's /proc, which sidesteps the fresh-procfs mount while leaving the mechanism under test (image mount, PATH, loader, exec) untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>Two findings from run 250 (whose claude image built green end-to-end): - crun refuses --cgroups=disabled combined with --pid=host ("containers not creating Cgroups must create a private PID namespace"). Drop --pid=host: the /proc rbind alone is what dodges the denied fresh procfs mount, and the rbound /proc merely shows the job's pidns view, which the version/help probes never read. - actions/cache only saves on job SUCCESS, so run 250's good claude fetch was discarded when the smoke step failed. Split into cache/restore + cache/save with if: always() and a run-id-suffixed key (entries are immutable), so every fetched artifact is banked no matter how the job ends; sha256 pinning in build.sh makes restored files trustless either way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>[autoland] verdict: fix-done
[autoland] verdict: pass