Add a fourth build target to gershwin-developer's build.yml — alongside FreeBSD, Arch, and Debian — that verifies Gershwin compiles on NextBSD. The trick we earlier lacked: don't boot the NextBSD image, chroot-build into its rootfs inside a FreeBSD VM. This is the job we scrapped two iterations ago, now actually feasible.
Our first attempt at a NextBSD CI job died on a real wall: vmactions can only boot its own prepared FreeBSD images (no custom-image input), and the NextBSD .img isn't provisioned for SSH/rsync — and it's launchd-based with no rc.d, so we couldn't even reliably get a shell to drive a build. We tried serial-console + FAT-disk plumbing and abandoned it as too fragile.
Researching the NextBSD ISO pipeline flipped the model. NextBSD itself never "runs" its image to build software into it — it chroots into the rootfs from a FreeBSD VM and builds there. We copy that exactly:
vmactions/freebsd-vm (root, with mdconfig/mount/chroot from base), mount the downloaded NextBSD rootfs, and run the normal Gershwin build inside a chroot of it. No booting, no SSH, no launchd, no serial console.build.sh builds its entire Apple userland this way (chroot $WORK/rootfs …). We're not inventing a mechanism.makefs/mkuzip/mkisoimages.sh, no boot-verify. (See the companion gershwin-on-nextbsd ISO plan.)build.ymlA new build-nextbsd-amd64 job, parallel to the existing three. The other jobs use a container (Arch/Debian) or vmactions/freebsd-vm directly with the repo synced in; NextBSD differs only in that the FreeBSD VM is a host for a chroot, and the rootfs is fetched from NextBSD's continuous release rather than provided by vmactions.
| Job | Environment | How sources arrive |
|---|---|---|
build-freebsd-14-amd64 | vmactions/freebsd-vm | rsynced into the VM, build runs in the VM |
build-archlinux / build-debian | container: | checked out into the container |
build-nextbsd-amd64 (new) | vmactions/freebsd-vm → chroot of NextBSD rootfs | rsynced into the VM, then tar'd into the chroot |
build-nextbsd-amd64: name: NextBSD x86_64 runs-on: ubuntu-latest timeout-minutes: 240 steps: - uses: actions/checkout@v4 - uses: jlumbroso/free-disk-space@main # rootfs + obj trees are multi-GB - name: Download latest NextBSD rootfs image env: { GH_TOKEN: "${{ github.token }}" } run: | URL=$(gh api repos/nextbsd-redux/nextbsd/releases/tags/continuous \ --jq '.assets[]|select(.name|test("amd64.*\\.img\\.zip$")).browser_download_url' | head -1) curl -fL -o nextbsd.img.zip "$URL" curl -fL -o nextbsd.img.zip.sha256 "$URL.sha256" sha256sum -c nextbsd.img.zip.sha256 unzip -p nextbsd.img.zip '*.img' > nextbsd.img - name: Build Gershwin in the NextBSD rootfs (FreeBSD VM) uses: vmactions/freebsd-vm@v1 with: release: '15.0' # match the NEXTBSD kernel's FreeBSD base mem: 8192 cpu: 4 usesh: true sync: rsync prepare: pkg install -y bash git rsync run: | sh ./.github/scripts/nextbsd-chroot-build.sh "$PWD/nextbsd.img"
copyback: this is build-verify only — success is a clean make install + a populated /System. (Optionally copy back a build log on failure for debugging.)continue-on-error: true while NextBSD catches up — you noted the compile is expected to fail until NextBSD is updated. Flip it off once green.#!/bin/sh set -eu IMG=$1 WORK=$PWD/work; ROOTFS=$WORK/rootfs mkdir -p "$ROOTFS" # extract the NextBSD rootfs from the .img's freebsd-ufs/ROOTFS partition (p3) md=$(mdconfig -a -t vnode -f "$IMG") mount "/dev/${md}p3" /mnt # confirm index with: gpart show ${md} tar -C /mnt -cf - . | tar -C "$ROOTFS" -xpf - # roomy plain dir; img has only +1.5G headroom umount /mnt; mdconfig -d -u "${md#md}" # build Gershwin inside a chroot of that rootfs (net for pkg + git clone) mount -t devfs devfs "$ROOTFS/dev" cp /etc/resolv.conf "$ROOTFS/private/etc/resolv.conf" mkdir -p "$ROOTFS/build" tar -C "$PWD" --exclude ./work --exclude ./nextbsd.img -cf - . | tar -C "$ROOTFS/build" -xpf - chroot "$ROOTFS" /bin/sh -eu -c ' cd /build sh Library/Scripts/Bootstrap.sh # pkg install nextbsd.txt prerequisites sh Library/Scripts/Checkout.sh # clone the component repos make install # detect_platform() sees /usr/lib/system → NextBSD path ' umount "$ROOTFS/dev" test -d "$ROOTFS/System/Library" || { echo "FAIL: /System not populated"; exit 1; } echo "NextBSD chroot build OK"
Everything above hinges on a single assumption that must be proven on the first run:
configure tests run a compiled probe. If any probe needs a Mach syscall provided by mach.ko (libobjc2 / libs-base are the likely suspects), it will fail or hang inside the chroot even though the real NextBSD kernel would satisfy it..img under qemu with KVM and build inside the running guest (the serial/SSH approach we scrapped). More plumbing, but it runs on the real kernel. The chroot path is strictly nicer if it holds — so spike stage ③ alone first before wiring the full job.| Also confirm on first run | Expected |
|---|---|
UFS root partition index in the .img | p3 (p1 freebsd-boot, p2 efi, p3 ufs) — verify with gpart show |
FreeBSD release matching the NEXTBSD kernel base | freebsd-version in the extracted rootfs |
| VM disk headroom for sources + objs | free-disk-space added; watch during the build |
/usr/lib/system libdispatch — the thing you actually want CI to catch as NextBSD evolves.