Skip to content

fix(ci): build the guest init image on a Linux runner - #59

Merged
philipparndt merged 1 commit into
mainfrom
fix/release-initfs-on-linux
Aug 25, 2026
Merged

philipparndt merged 1 commit into
mainfrom
fix/release-initfs-on-linux

Conversation

@philipparndt

Copy link
Copy Markdown
Owner

Unblocks the v0.26.0 release, which failed in 17s:

make -C .forks/containerization/vminitd cross-prep
make: *** No rule to make target `cross-prep'.  Stop.

Why it broke

The fork rebase in #58 picked up apple/containerization#810 ("Build vminitd and initfs inside the dev container"), which removed the host cross-compile path this release depended on:

  • make -C vminitd cross-prep (swiftly + Static Linux SDK on the macOS host) is gone.
  • cctl rootfs create no longer builds the ext4 from --vminitd/--vmexec; it only packages a prebuilt rootfs tar.
  • make init on macOS now runs the build inside a Linux dev container launched by apple/container.

A GitHub macOS runner has no nested virtualization, so it cannot launch that container. This is the same constraint that already forces us to ship a prebuilt vmlinux-16k rather than building the kernel in CI.

The fix

Upstream split init-image out of init precisely so CI can build the initfs on Linux and create the image from the tar afterwards. This PR takes that split, and goes one step further by creating the OCI image on the Linux side too — so the macOS job just consumes a finished init.tar.

New job layout:

job runner does
payload ubuntu-latest computes the payload cache key, probes for the runtime-* prerelease, gates the two jobs below
initfs ubuntu-24.04-arm, container: swift:6.3-noble vminitd + vmexec + initfs + vminit:latest, uploaded as an init.tar artifact
goreleaser macos-26 container app, staging tree, payload assembly, bundle, release

Notes on the choices:

  • The container image mirrors images/linux-dev/Dockerfile in the fork — same Swift release, same apt packages. The Static Linux SDK is installed via the linux-sdk target rather than baked in.
  • build-initfs.sh falls back to mke2fs -d when no loop device is available, which is exactly the unprivileged-Actions-container case, so no privileged runner is needed.
  • ubuntu-24.04-arm is free for public repos.
  • The payload probe moved into its own cheap job so a cache hit skips both expensive jobs rather than just the steps inside one. Both fork refs are pinned full SHAs, so the key is computed by string slicing — no fork checkout needed.
  • goreleaser uses if: always() && ... because initfs is skipped on a cache hit and a skipped dependency would otherwise block it.

Local builds

make build now needs a working container on PATH, for the same upstream reason — it used to cross-compile on the host. Noted on the runtime target. The cross-prep invocation is removed.

Verification

Workflow YAML parses and the three jobs wire up as intended; make -n runtime is clean; swift:6.3-noble confirmed on Docker Hub. The Linux initfs build itself is unverified until this runs — it is the thing being changed.

After merge, v0.26.0 needs to be retagged onto the new main (the tag currently points at 0832a71, which predates this fix; no release object was created, so nothing is half-published).

🤖 Generated with Claude Code

The v0.26.0 release failed in 17s at `make -C .forks/containerization/vminitd
cross-prep` — "No rule to make target". apple/containerization#810 removed the
host cross-compile path: vminitd and the initfs are now built inside a Linux
dev container that containerization launches via apple/container, and cctl's
`rootfs create` no longer builds the ext4 from binaries, only packages a
prebuilt tar. A GitHub macOS runner has no nested virtualization, so it cannot
launch that container.

Upstream split `init-image` out of `init` for exactly this case. Build the
initfs on ubuntu-24.04-arm (free for public repos) in a swift:6.3-noble
container mirroring images/linux-dev/Dockerfile, create the OCI image there
too, and hand the macOS job a finished init.tar as an artifact.
build-initfs.sh falls back to `mke2fs -d` when no loop device is available,
which is the case in an unprivileged Actions container.

To keep the ~30 minute Swift build skippable, the payload-cache probe moves
into its own cheap job whose output gates both expensive jobs. Both fork refs
are pinned full SHAs, so the key no longer needs the forks checked out.

`make build` locally now requires a working `container` on PATH for the same
upstream reason; noted on the runtime target.

Co-Authored-By: Claude Opus 5 <[email protected]>
@philipparndt
philipparndt merged commit 19e01eb into main Aug 25, 2026
1 check passed
@philipparndt
philipparndt deleted the fix/release-initfs-on-linux branch August 25, 2026 10:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant