Add ARC-DinD runner topology with sysroot-stage build-tools image#5697
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces an explicit ARC+DinD runner contract to AWF by adding a runner topology (arc-dind) that stages a “sysroot” into a named Docker volume via a one-shot sysroot-stage service, then mounts that sysroot into the agent’s chroot. It also adds a dedicated build-tools sysroot image (plus release publishing) and updates config types, mapping, schema validation, tests, and docs to support the new config surface.
Changes:
- Add
runner.topology=arc-dindandrunner.sysrootImageto AWF config types/mapping/schema and validation tests. - Extend compose generation to optionally add
sysroot-stage+sysrootvolume and adjust agent mount behavior for sysroot topology. - Add a new
containers/build-toolsimage and publish it from the release workflow (multi-arch + cosign + SBOM), plus update ARC/DinD documentation and guardrail warnings.
Show a summary per file
| File | Description |
|---|---|
| src/types/wrapper-config.ts | Extends WrapperConfig with runner-related options. |
| src/types/runner-options.ts | Introduces runner option types for topology + sysroot image. |
| src/services/sysroot-service.ts | Adds sysroot topology detection, sysroot image resolution, and sysroot-stage service definition. |
| src/services/sysroot-service.test.ts | Unit tests for sysroot service behavior and defaults. |
| src/services/agent-volumes/volume-builder.ts | Threads sysroot topology flag into system mount construction. |
| src/services/agent-volumes/system-mounts.ts | Modifies system mount list behavior when sysroot topology is enabled. |
| src/schema.test.ts | Adds schema validation coverage for the new runner config section. |
| src/config-mapper.ts | Maps file config runner.* into flattened CLI/build-config options. |
| src/config-file.ts | Adds runner section to the config-file TypeScript interface. |
| src/config-file-validation.test.ts | Adds schema-driven validation tests for runner config errors. |
| src/config-file-mapping.test.ts | Adds mapping tests for runner topology fields. |
| src/compose-generator.ts | Adds sysroot-stage service + sysroot volume and agent dependency wiring. |
| src/compose-generator.test.ts | Tests sysroot-stage service inclusion and agent mount/depends_on behavior. |
| src/commands/validators/network-options.ts | Adds ARC-specific warning when RUNNER_TOOL_CACHE is under /opt for arc-dind. |
| src/commands/validators/network-options.test.ts | Adds tests asserting the RUNNER_TOOL_CACHE warning behavior. |
| src/commands/build-config.ts | Plumbs runner topology/sysroot image options into WrapperConfig. |
| src/awf-config-schema.json | Extends the published JSON schema with the runner object. |
| docs/awf-config.schema.json | Mirrors schema changes into docs copy. |
| docs/arc-dind.md | Documents the new runner topology, sysroot staging, and tool cache guidance. |
| containers/build-tools/Dockerfile | Adds build-tools sysroot image definition (Ubuntu 22.04 + build deps/utilities). |
| .github/workflows/release.yml | Builds, pushes, signs, and attests the build-tools image during releases. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 21/21 changed files
- Comments generated: 5
- Review effort level: Low
| const sysrootEnabled = isSysrootTopologyEnabled(config); | ||
| if (sysrootEnabled) { | ||
| agentService.volumes = [`sysroot:/host:ro`, ...(agentService.volumes || [])]; | ||
| agentService.depends_on['sysroot-stage'] = { | ||
| condition: 'service_completed_successfully', | ||
| }; | ||
| } |
There was a problem hiding this comment.
Fixed -- changed sysroot:/host:ro to sysroot:/host:rw since the agent entrypoint writes resolv.conf and hosts into /host/etc during chroot setup.
| const mounts = [ | ||
| '/usr:/host/usr:ro', | ||
| '/bin:/host/bin:ro', | ||
| '/sbin:/host/sbin:ro', | ||
| '/lib:/host/lib:ro', | ||
| '/lib64:/host/lib64:ro', | ||
| '/opt:/host/opt:ro', | ||
| '/sys:/host/sys:ro', | ||
| '/dev:/host/dev:ro', | ||
| ...(useSysroot | ||
| ? [] | ||
| : [ | ||
| '/usr:/host/usr:ro', | ||
| '/bin:/host/bin:ro', | ||
| '/sbin:/host/sbin:ro', | ||
| '/lib:/host/lib:ro', | ||
| '/lib64:/host/lib64:ro', | ||
| '/opt:/host/opt:ro', | ||
| '/sys:/host/sys:ro', | ||
| '/dev:/host/dev:ro', | ||
| ]), | ||
| `${workspaceDir}:/host${workspaceDir}:rw`, | ||
| '/tmp:/host/tmp:rw', | ||
| ]; |
There was a problem hiding this comment.
Good catch -- sysroot mode now keeps /sys and /dev bind mounts (live kernel VFS) while only skipping the userspace dirs (/usr, /bin, /sbin, /lib, /lib64, /opt) that the sysroot volume provides.
| command: [ | ||
| '/bin/bash', | ||
| '-lc', | ||
| "set -euo pipefail; rm -rf /sysroot/.awf-tmp; mkdir -p /sysroot/.awf-tmp; tar -C / --exclude='./sysroot' -cf - . | tar -C /sysroot -xf -", | ||
| ], |
There was a problem hiding this comment.
No change needed here -- the implementation already uses targeted cp -a for specific directories (usr, lib, bin, sbin, etc, and conditionally lib64). It does not archive /proc, /sys, or /dev. The staging command is already selective by design.
| export function resolveSysrootImage(config: WrapperConfig, imageConfig: ImageBuildConfig): string { | ||
| if (config.runnerSysrootImage) { | ||
| return config.runnerSysrootImage; | ||
| } | ||
| return `${imageConfig.registry}/build-tools:${imageConfig.parsedTag.tag}`; | ||
| } |
There was a problem hiding this comment.
Updated the docs to clarify that the default sysroot image is derived dynamically from --image-registry and --image-tag (same as other AWF containers), not hardcoded. The implementation is correct -- it uses the user's configured registry/tag so private registries and pinned versions work out of the box.
| - `runner.topology: "arc-dind"`: enables sysroot staging (`sysroot-stage` init service + `sysroot` volume mounted on agent at `/host:ro`). | ||
| - `runner.sysrootImage`: optional override for the sysroot image used by `runner.topology=arc-dind`. | ||
|
|
||
| ## Build-tools sysroot image | ||
|
|
||
| When `runner.topology` is `arc-dind`, AWF starts a one-shot `sysroot-stage` service that copies | ||
| the filesystem from `ghcr.io/github/gh-aw-firewall/build-tools:latest` into a named `sysroot` | ||
| volume. The agent mounts that volume at `/host:ro`. |
There was a problem hiding this comment.
Fixed -- docs now say /host:rw to match the implementation. The volume is ephemeral per run so read-write access is safe and required for the entrypoint's DNS/hosts setup.
Reconcile duplicate runner topology definitions: - Remove runnerTopology/sysrootImage from PlatformOptions (now in RunnerOptions) - Add "standard" to RunnerOptions.runnerTopology enum for consistency - Remove duplicate runnerTopology property in build-config.ts - Fix test assertion for updated topology enum values Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
✅ Copilot review passed with no inline comments. @copilot Add the |
|
| Metric | Base | PR | Delta |
|---|---|---|---|
| Lines | 98.38% | 98.42% | 📈 +0.04% |
| Statements | 98.31% | 98.35% | 📈 +0.04% |
| Functions | 99.54% | 99.54% | ➡️ +0.00% |
| Branches | 94.45% | 94.44% | 📉 -0.01% |
📁 Per-file Coverage Changes (1 files)
| File | Lines (Before → After) | Statements (Before → After) |
|---|---|---|
src/workdir-setup.ts |
92.7% → 94.5% (+1.82%) | 92.7% → 94.5% (+1.82%) |
Coverage comparison generated by scripts/ci/compare-coverage.ts
- Mount sysroot volume at /host:rw (agent entrypoint writes resolv.conf) - Keep /sys and /dev bind mounts in sysroot mode (live kernel VFS needed) - Clarify docs: default sysroot image derives from --image-registry/--image-tag - Update docs from :ro to :rw to match implementation Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
| Metric | Base | PR | Delta |
|---|---|---|---|
| Lines | 98.38% | 98.42% | 📈 +0.04% |
| Statements | 98.31% | 98.35% | 📈 +0.04% |
| Functions | 99.54% | 99.54% | ➡️ +0.00% |
| Branches | 94.45% | 94.44% | 📉 -0.01% |
📁 Per-file Coverage Changes (1 files)
| File | Lines (Before → After) | Statements (Before → After) |
|---|---|---|
src/workdir-setup.ts |
92.7% → 94.5% (+1.82%) | 92.7% → 94.5% (+1.82%) |
Coverage comparison generated by scripts/ci/compare-coverage.ts
This change adds a stable ARC/DinD contract to AWF via
runner.topologyandrunner.sysrootImage, enabling split-filesystem runners to start agent containers without runtime root package installation. It also introduces a dedicated build-tools sysroot image and wires it into compose generation and release publishing.Config surface:
runner.topology+runner.sysrootImagerunnersection to AWF config types, mapping, and schema validation.runner.topology: "arc-dind"runner.sysrootImageoverride (defaults toghcr.io/github/gh-aw-firewall/build-tools:latest).Compose behavior for ARC/DinD
sysroot-stageone-shot init service.sysrootvolume and mounted it into agent as/host:ro.sysroot-stagecompletion inarc-dindtopology./usr,/bin,/lib*,/opt,/sys,/dev) are not layered over/host, so staged sysroot content remains authoritative.ARC operational guardrail
runner.topology=arc-dindandRUNNER_TOOL_CACHEis under/opt, which is commonly not daemon-visible in DinD layouts.Build-tools sysroot image + release publishing
containers/build-tools/Dockerfile(Ubuntu 22.04 with compilers/linkers, dev libs, and required utilities likecapsh,gosu,gh).build-build-toolsjob to release workflow with multi-arch build, cosign signing, and SBOM attestation.Docs/spec updates
runnersection.{ "runner": { "topology": "arc-dind", "sysrootImage": "ghcr.io/github/gh-aw-firewall/build-tools:latest" } }