diff --git a/docs/GUIX_DIFFERENCES.md b/docs/GUIX_DIFFERENCES.md index d2bb7b8..8118dd8 100644 --- a/docs/GUIX_DIFFERENCES.md +++ b/docs/GUIX_DIFFERENCES.md @@ -251,11 +251,14 @@ On those systems, Fruix exposes: - `/run/current-system/development-profile` - `/run/current-development` - `/usr/local/bin/fruix-development-environment` +- `/usr/include -> /run/current-system/development-profile/usr/include` +- `/usr/share/mk -> /run/current-system/development-profile/usr/share/mk` The intent is: - keep the main runtime profile lean - expose headers, `usr/share/mk`, and selected toolchain commands explicitly +- satisfy native FreeBSD buildworld/buildkernel expectations for canonical system paths when development support is enabled - avoid treating a development-heavy system image as the default runtime shape Compared with Guix, this is conceptually similar to keeping development-oriented state separate from the main runtime identity, but Fruix currently expresses it as a system-attached development overlay rather than through Guix's broader profile/tooling model. diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 44c6c9b..4dcc661 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -38,6 +38,10 @@ Fruix currently has: - `/run/current-system/development-profile` - `/run/current-development` - `/usr/local/bin/fruix-development-environment` +- a validated host-initiated native base-build path inside a Fruix-managed guest via: + - real XCP-ng boot of a development-enabled Fruix system + - in-guest `buildworld` / `buildkernel` + - staged `installworld` / `distribution` / `installkernel` Validated boot modes still are: @@ -50,37 +54,40 @@ The validated Phase 18 installation work currently uses: ## Latest completed achievement -### 2026-04-05 — Phase 20.1 completed +### 2026-04-05 — Phase 20.2 completed -Fruix now has a validated real-VM path where a booted Fruix-managed FreeBSD system exposes a separate development environment for native base work without collapsing the runtime/development split. +Fruix now has a validated intermediate path where the host still orchestrates the workflow, but real FreeBSD native base-build work runs inside a booted Fruix-managed FreeBSD guest. Highlights: -- operating-system declarations now support: - - `#:development-packages` -- system closures can now carry a separate development profile at: - - `/run/current-system/development-profile` - - `/run/current-development` -- opt-in systems now ship an in-guest helper at: - - `/usr/local/bin/fruix-development-environment` -- the validated Phase 20.1 guest path exposes: - - native headers - - `usr/share/mk` for `bsd.*.mk` - - Clang toolchain commands such as `cc`, `c++`, `ar`, `ranlib`, and `nm` -- the validated guest workflow now supports: - - `eval "$(/usr/local/bin/fruix-development-environment)"` - - direct compilation with the Fruix-provided toolchain - - a simple `bsd.prog.mk` build on the running Fruix guest +- development-enabled systems now expose canonical native-build compatibility links at: + - `/usr/include -> /run/current-system/development-profile/usr/include` + - `/usr/share/mk -> /run/current-system/development-profile/usr/share/mk` +- media builder versions were bumped so booted images and future installed targets pick up that rootfs layout change +- the validated guest build path now runs real FreeBSD native build steps inside the Fruix-managed guest: + - `buildworld` + - `buildkernel` + - `installworld` + - `distribution` + - `installkernel` +- staged install steps use: + - `DB_FROM_SRC=yes` + - so the staged install is driven by the declared source tree's account database rather than by the guest's minimal local `/etc` state +- the validated result now includes staged native artifact outputs for: + - kernel + - bootloader slice + - headers / `usr/share/mk` Validation: -- `PASS phase20-development-environment-xcpng` +- `PASS phase20-host-initiated-native-build-xcpng` Reports: - `docs/system-deployment-workflow.md` - `docs/GUIX_DIFFERENCES.md` - `docs/reports/phase20-development-environment-freebsd.md` +- `docs/reports/phase20-host-initiated-native-builds-freebsd.md` ## Recent major milestones @@ -106,6 +113,6 @@ Reports: Per `docs/PLAN_4.md`, the next planned step is: -- **Phase 20.2** — run host-initiated native base builds inside a Fruix-managed environment +- **Phase 20.3** — reassess and potentially prototype guest self-hosted base builds -Phase 20.1 is now complete: Fruix validates a separate in-system development environment for native FreeBSD base work on the approved real XCP-ng path. +Phase 20.2 is now complete: Fruix validates host-initiated native FreeBSD base builds running inside the approved real XCP-ng Fruix guest path. diff --git a/docs/reports/phase20-host-initiated-native-builds-freebsd.md b/docs/reports/phase20-host-initiated-native-builds-freebsd.md new file mode 100644 index 0000000..150648f --- /dev/null +++ b/docs/reports/phase20-host-initiated-native-builds-freebsd.md @@ -0,0 +1,169 @@ +# Phase 20.2: host-initiated native base builds inside a Fruix-managed environment + +Date: 2026-04-05 + +## Goal + +Validate the next step after Phase 20.1: + +- the host still orchestrates the outer loop +- the actual FreeBSD native base build work runs inside a booted Fruix-managed system + +This is the intermediate path between: + +- purely host-side native base builds +- any future claim of guest self-hosting + +The target here was not a new self-hosted package manager story. + +It was narrower: + +- boot a Fruix-managed FreeBSD system on the approved real XCP-ng path +- expose the development environment required for native base work +- run real `buildworld` / `buildkernel` / staged install steps inside that Fruix guest +- confirm that the resulting staged artifacts are the expected FreeBSD base slices + +## Implementation + +### Canonical development-path compatibility links + +Phase 20.1 proved that Fruix could keep development content separate in: + +- `/run/current-system/development-profile` +- `/run/current-development` + +Phase 20.2 exposed an additional practical requirement: + +- FreeBSD native base builds still expect canonical system paths such as: + - `/usr/include` + - `/usr/share/mk` + +For development-enabled systems, `populate-rootfs-from-closure` now also exposes: + +- `/usr/include -> /run/current-system/development-profile/usr/include` +- `/usr/share/mk -> /run/current-system/development-profile/usr/share/mk` + +This keeps the development profile separate while still satisfying the buildworld/buildkernel assumptions of the native FreeBSD build system. + +### Media builder invalidation + +Because this changed the visible rootfs layout of booted systems, the media builder versions were bumped in `modules/fruix/system/freebsd/media.scm`: + +- `image-builder-version` +- `install-builder-version` +- `installer-image-builder-version` +- `installer-iso-builder-version` + +That ensured booted images and future installed targets actually pick up the new compatibility links. + +### New validation harness + +Added: + +- `tests/system/run-phase20-host-initiated-native-build-xcpng.sh` + +This harness reuses the validated Phase 20.1 XCP-ng path first, then performs the 20.2-native-build step over SSH from the host. + +The guest build flow is: + +1. boot the development-enabled Fruix guest on XCP-ng +2. recover the materialized source store from `/run/current-system/metadata/store-layout.scm` +3. run real FreeBSD native build commands inside the guest: + - `make -j8 buildworld` + - `make -j8 buildkernel` + - `make DESTDIR=... installworld` + - `make DESTDIR=... distribution` + - `make DESTDIR=... installkernel` +4. stage narrower artifact slices from the staged output: + - headers slice + - bootloader slice + - kernel stage + +### Why `DB_FROM_SRC=yes` is used for staged install steps + +The development-enabled Fruix guest is intentionally lean and does not carry the full ambient host account database. + +`installworld` on modern FreeBSD checks for required users/groups unless `DB_FROM_SRC` is defined. For staged installs into `DESTDIR`, the appropriate controlled input is the source tree's own account database under `etc/`, not the minimal running guest's `/etc/master.passwd`. + +So the validated Phase 20.2 staged install path uses: + +- `DB_FROM_SRC=yes` + +for: + +- `installworld` +- `distribution` +- `installkernel` + +That keeps the staged install driven by the declared source input rather than by accidental guest-local account state. + +## Validation + +Passing run: + +- `PASS phase20-host-initiated-native-build-xcpng` +- workdir: `/tmp/fruix-phase20-host-initiated-native-build-xcpng` + +Validated on the approved real XCP-ng path: + +- VM `90490f2e-e8fc-4b7a-388e-5c26f0157289` +- VDI `0f1f90d3-48ca-4fa2-91d8-fc6339b95743` + +Representative result: + +```text +build_jobs=8 +source_store=/frx/store/12d7704362e95afc2697db63f168b878e082b372-freebsd-source-default +source_root=/frx/store/12d7704362e95afc2697db63f168b878e082b372-freebsd-source-default/tree +build_root=/var/tmp/fruix-phase20-native-build +world_stage=/var/tmp/fruix-phase20-native-build/stage-world +kernel_stage=/var/tmp/fruix-phase20-native-build/stage-kernel +headers_stage=/var/tmp/fruix-phase20-native-build/artifact-headers +bootloader_stage=/var/tmp/fruix-phase20-native-build/artifact-bootloader +build_root_size=7.6G +world_stage_size=672M +kernel_stage_size=739M +headers_stage_size=32M +bootloader_stage_size=1.3M +sha_kernel=16950f116a52134b98e2f8e0dacc556e18fe254e4a0ac2c1741422dde281a341 +sha_loader=ea417846167ece270ada611624dca622ca38bd30125b9a125cd8ebb8b3600313 +sha_param=9eb140ca7d9666f3d484a4174c9acd94b45427db6292b4e17de19af2c6aa5219 +host_initiated_native_build=ok +``` + +The harness verified all of the following: + +- the guest still boots and passes the Phase 20.1 development-environment checks first +- development-enabled systems expose canonical native-build compatibility links at: + - `/usr/include` + - `/usr/share/mk` +- the guest can recover the declared materialized FreeBSD source store from system metadata +- real FreeBSD `buildworld` succeeds inside the booted Fruix guest +- real FreeBSD `buildkernel` succeeds inside the booted Fruix guest +- staged `installworld`, `distribution`, and `installkernel` also succeed inside the guest +- the staged outputs contain the expected artifact shapes: + - `boot/kernel/kernel` + - `usr/include/sys/param.h` + - `usr/share/mk/bsd.prog.mk` + - `boot/loader.efi` + - `boot/defaults/loader.conf` + - `boot/lua/loader.lua` + +## Result + +Phase 20.2 is complete. + +Fruix now validates a real host-orchestrated path where: + +- the host boots and reaches a Fruix-managed development-enabled guest +- the guest uses its own Fruix-exposed development paths and declared source store +- the native FreeBSD base build work runs inside that Fruix-managed environment +- the host remains the outer orchestrator and result collector + +This materially narrows the gap to any future self-hosting experiment while still avoiding the complexity jump to a full guest-driven package/deployment loop. + +## Next step + +Per `docs/PLAN_4.md`, the next planned step is: + +- **Phase 20.3** — reassess and potentially prototype guest self-hosted base builds diff --git a/docs/system-deployment-workflow.md b/docs/system-deployment-workflow.md index 5bf3ca9..f332cc8 100644 --- a/docs/system-deployment-workflow.md +++ b/docs/system-deployment-workflow.md @@ -214,15 +214,51 @@ Intended use: eval "$(/usr/local/bin/fruix-development-environment)" ``` -That helper exports a development-oriented environment while keeping the main runtime profile separate. The validated Phase 20.1 path currently uses this to expose at least: +That helper exports a development-oriented environment while keeping the main runtime profile separate. The validated Phase 20 path currently uses this to expose at least: - native headers under `usr/include` - FreeBSD `share/mk` files for `bsd.*.mk` - Clang toolchain commands such as `cc`, `c++`, `ar`, `ranlib`, and `nm` - `MAKEFLAGS` pointing at the development profile's `usr/share/mk` +For native base-build compatibility, development-enabled systems also now expose canonical links at: + +- `/usr/include -> /run/current-system/development-profile/usr/include` +- `/usr/share/mk -> /run/current-system/development-profile/usr/share/mk` + This is the current Fruix-native way to make a running system suitable for controlled native base-development work without merging development content back into the main runtime profile. +### Host-initiated native base builds inside a Fruix-managed guest + +The currently validated intermediate path toward self-hosting is still host-orchestrated. + +The host: + +1. boots a development-enabled Fruix guest +2. connects over SSH +3. recovers the materialized FreeBSD source store from system metadata +4. runs native FreeBSD build commands inside the guest +5. collects and records the staged outputs + +The validated build sequence inside the guest is: + +- `make -jN buildworld` +- `make -jN buildkernel` +- `make DESTDIR=... installworld` +- `make DESTDIR=... distribution` +- `make DESTDIR=... installkernel` + +For staged install steps, the validated path uses: + +- `DB_FROM_SRC=yes` + +so the staged install is driven by the declared source tree's account database rather than by accidental guest-local `/etc/master.passwd` contents. + +This is the current Phase 20.2 answer to “where should native base builds run?” + +- **inside** a Fruix-managed FreeBSD environment +- but still with the **host** driving the outer orchestration loop + ## Deployment patterns ### 1. Build-first workflow diff --git a/modules/fruix/system/freebsd/media.scm b/modules/fruix/system/freebsd/media.scm index 271a06a..cf27881 100644 --- a/modules/fruix/system/freebsd/media.scm +++ b/modules/fruix/system/freebsd/media.scm @@ -376,7 +376,13 @@ (string-append rootfs "/usr/local/bin/fruix")) (when (file-exists? (string-append closure-path "/development-profile")) (symlink-force "/run/current-system/development-profile" - (string-append rootfs "/run/current-development"))) + (string-append rootfs "/run/current-development")) + (when (file-exists? (string-append closure-path "/development-profile/usr/include")) + (symlink-force "/run/current-system/development-profile/usr/include" + (string-append rootfs "/usr/include"))) + (when (file-exists? (string-append closure-path "/development-profile/usr/share/mk")) + (symlink-force "/run/current-system/development-profile/usr/share/mk" + (string-append rootfs "/usr/share/mk")))) (when (file-exists? (string-append closure-path "/usr/local/bin/fruix-development-environment")) (symlink-force "/run/current-system/usr/local/bin/fruix-development-environment" (string-append rootfs "/usr/local/bin/fruix-development-environment"))) @@ -598,10 +604,10 @@ (installer-root-partition-label . ,installer-root-partition-label) (target-install . ,target-install-spec)))) -(define image-builder-version "2") -(define install-builder-version "1") -(define installer-image-builder-version "1") -(define installer-iso-builder-version "2") +(define image-builder-version "3") +(define install-builder-version "2") +(define installer-image-builder-version "2") +(define installer-iso-builder-version "3") (define (operating-system-install-metadata-object install-spec closure-path store-items) `((install-version . ,install-builder-version) diff --git a/tests/system/run-phase20-host-initiated-native-build-xcpng.sh b/tests/system/run-phase20-host-initiated-native-build-xcpng.sh new file mode 100755 index 0000000..2a5611e --- /dev/null +++ b/tests/system/run-phase20-host-initiated-native-build-xcpng.sh @@ -0,0 +1,275 @@ +#!/bin/sh +set -eu + +repo_root=${PROJECT_ROOT:-$(pwd)} +os_template=${OS_TEMPLATE:-$repo_root/tests/system/phase20-development-operating-system.scm.in} +system_name=${SYSTEM_NAME:-phase20-operating-system} +root_size=${ROOT_SIZE:-20g} +metadata_target=${METADATA_OUT:-} +root_authorized_key_file=${ROOT_AUTHORIZED_KEY_FILE:-$HOME/.ssh/id_ed25519.pub} +root_ssh_private_key_file=${ROOT_SSH_PRIVATE_KEY_FILE:-$HOME/.ssh/id_ed25519} +cleanup=0 + +if [ -n "${WORKDIR:-}" ]; then + workdir=$WORKDIR + mkdir -p "$workdir" +else + workdir=$(mktemp -d /tmp/fruix-phase20-native-build-xcpng.XXXXXX) + cleanup=1 +fi +if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then + cleanup=0 +fi + +inner_metadata=$workdir/phase20-native-build-inner-metadata.txt +metadata_file=$workdir/phase20-host-initiated-native-build-xcpng-metadata.txt + +action_cleanup() { + if [ "$cleanup" -eq 1 ]; then + rm -rf "$workdir" + fi +} +trap action_cleanup EXIT INT TERM + +KEEP_WORKDIR=1 WORKDIR="$workdir/inner" METADATA_OUT="$inner_metadata" \ + ROOT_AUTHORIZED_KEY_FILE="$root_authorized_key_file" \ + ROOT_SSH_PRIVATE_KEY_FILE="$root_ssh_private_key_file" \ + OS_TEMPLATE="$os_template" SYSTEM_NAME="$system_name" ROOT_SIZE="$root_size" \ + "$repo_root/tests/system/run-phase20-development-environment-xcpng.sh" + +phase8_metadata=$(sed -n 's/^phase8_metadata=//p' "$inner_metadata") +closure_path=$(sed -n 's/^closure_path=//p' "$inner_metadata") +closure_base=$(sed -n 's/^closure_base=//p' "$inner_metadata") +guest_ip=$(sed -n 's/^guest_ip=//p' "$inner_metadata") +vm_id=$(sed -n 's/^vm_id=//p' "$inner_metadata") +vdi_id=$(sed -n 's/^vdi_id=//p' "$inner_metadata") +shepherd_pid=$(sed -n 's/^shepherd_pid=//p' "$inner_metadata") +sshd_status=$(sed -n 's/^sshd_status=//p' "$inner_metadata") +compat_prefix_shims=$(sed -n 's/^compat_prefix_shims=//p' "$inner_metadata") +guile_module_smoke=$(sed -n 's/^guile_module_smoke=//p' "$inner_metadata") + +[ "$shepherd_pid" = 1 ] || { echo "shepherd was not PID 1" >&2; exit 1; } +[ "$sshd_status" = running ] || { echo "sshd is not running" >&2; exit 1; } +[ "$compat_prefix_shims" = absent ] || { echo "compatibility prefix shims reappeared" >&2; exit 1; } +[ "$guile_module_smoke" = ok ] || { echo "guest Guile module smoke failed" >&2; exit 1; } + +ssh_guest() { + ssh -i "$root_ssh_private_key_file" \ + -o BatchMode=yes \ + -o StrictHostKeyChecking=no \ + -o UserKnownHostsFile=/dev/null \ + -o ConnectTimeout=5 \ + root@"$guest_ip" "$@" +} + +guest_build_jobs=${GUEST_BUILD_JOBS:-$(ssh_guest 'sysctl -n hw.ncpu')} +case "$guest_build_jobs" in + ''|*[!0-9]*) + echo "invalid guest build job count: $guest_build_jobs" >&2 + exit 1 + ;; +esac + +native_build_metadata=$(ssh_guest env BUILD_JOBS="$guest_build_jobs" sh -s <<'EOF' +set -eu +[ -L /run/current-development ] +[ -L /usr/include ] +[ "$(readlink /usr/include)" = "/run/current-system/development-profile/usr/include" ] +[ -L /usr/share/mk ] +[ "$(readlink /usr/share/mk)" = "/run/current-system/development-profile/usr/share/mk" ] +closure=$(readlink /run/current-system) +source_store=$(sed -n 's/.*"\(\/frx\/store\/[^\"]*-freebsd-source-[^\"]*\)".*/\1/p' "$closure/metadata/store-layout.scm" | head -n 1) +source_root="$source_store/tree" +build_root=/var/tmp/fruix-phase20-native-build +logdir=$build_root/logs +world_stage=$build_root/stage-world +kernel_stage=$build_root/stage-kernel +headers_stage=$build_root/artifact-headers +bootloader_stage=$build_root/artifact-bootloader +rm -rf "$build_root" +mkdir -p "$logdir" +export MAKEOBJDIRPREFIX="$build_root/obj" +common='TARGET=amd64 TARGET_ARCH=amd64 KERNCONF=GENERIC __MAKE_CONF=/dev/null SRCCONF=/dev/null SRC_ENV_CONF=/dev/null MK_DEBUG_FILES=no MK_TESTS=no DB_FROM_SRC=yes' +make -j"$BUILD_JOBS" -C "$source_root" TARGET=amd64 TARGET_ARCH=amd64 KERNCONF=GENERIC __MAKE_CONF=/dev/null SRCCONF=/dev/null SRC_ENV_CONF=/dev/null MK_DEBUG_FILES=no MK_TESTS=no buildworld > "$logdir/buildworld.log" 2>&1 +make -j"$BUILD_JOBS" -C "$source_root" TARGET=amd64 TARGET_ARCH=amd64 KERNCONF=GENERIC __MAKE_CONF=/dev/null SRCCONF=/dev/null SRC_ENV_CONF=/dev/null MK_DEBUG_FILES=no MK_TESTS=no buildkernel > "$logdir/buildkernel.log" 2>&1 +make -C "$source_root" $common DESTDIR="$world_stage" installworld > "$logdir/installworld.log" 2>&1 +make -C "$source_root" $common DESTDIR="$world_stage" distribution > "$logdir/distribution.log" 2>&1 +make -C "$source_root" $common DESTDIR="$kernel_stage" installkernel > "$logdir/installkernel.log" 2>&1 +mkdir -p "$headers_stage/usr" "$bootloader_stage/boot" +cp -a "$world_stage/usr/include" "$headers_stage/usr/include" +mkdir -p "$headers_stage/usr/share" +cp -a "$world_stage/usr/share/mk" "$headers_stage/usr/share/mk" +cp -a "$world_stage/boot/loader" "$bootloader_stage/boot/loader" +cp -a "$world_stage/boot/loader.efi" "$bootloader_stage/boot/loader.efi" +cp -a "$world_stage/boot/device.hints" "$bootloader_stage/boot/device.hints" +cp -a "$world_stage/boot/defaults" "$bootloader_stage/boot/defaults" +cp -a "$world_stage/boot/lua" "$bootloader_stage/boot/lua" +[ -f "$kernel_stage/boot/kernel/kernel" ] +[ -f "$headers_stage/usr/include/sys/param.h" ] +[ -f "$headers_stage/usr/share/mk/bsd.prog.mk" ] +[ -f "$bootloader_stage/boot/loader.efi" ] +[ -f "$bootloader_stage/boot/defaults/loader.conf" ] +[ -f "$bootloader_stage/boot/lua/loader.lua" ] +sha_kernel=$(sha256 -q "$kernel_stage/boot/kernel/kernel") +sha_loader=$(sha256 -q "$bootloader_stage/boot/loader.efi") +sha_param=$(sha256 -q "$headers_stage/usr/include/sys/param.h") +buildworld_tail=$(tail -n 20 "$logdir/buildworld.log" | tr '\n' ' ') +buildkernel_tail=$(tail -n 20 "$logdir/buildkernel.log" | tr '\n' ' ') +installworld_tail=$(tail -n 20 "$logdir/installworld.log" | tr '\n' ' ') +distribution_tail=$(tail -n 20 "$logdir/distribution.log" | tr '\n' ' ') +installkernel_tail=$(tail -n 20 "$logdir/installkernel.log" | tr '\n' ' ') +root_df=$(df -h / | tail -n 1 | tr -s ' ' | tr '\t' ' ') +build_root_size=$(du -sh "$build_root" | awk '{print $1}') +world_stage_size=$(du -sh "$world_stage" | awk '{print $1}') +kernel_stage_size=$(du -sh "$kernel_stage" | awk '{print $1}') +headers_stage_size=$(du -sh "$headers_stage" | awk '{print $1}') +bootloader_stage_size=$(du -sh "$bootloader_stage" | awk '{print $1}') +printf 'build_jobs=%s\n' "$BUILD_JOBS" +printf 'source_store=%s\n' "$source_store" +printf 'source_root=%s\n' "$source_root" +printf 'build_root=%s\n' "$build_root" +printf 'logdir=%s\n' "$logdir" +printf 'buildworld_log=%s\n' "$logdir/buildworld.log" +printf 'buildkernel_log=%s\n' "$logdir/buildkernel.log" +printf 'installworld_log=%s\n' "$logdir/installworld.log" +printf 'distribution_log=%s\n' "$logdir/distribution.log" +printf 'installkernel_log=%s\n' "$logdir/installkernel.log" +printf 'world_stage=%s\n' "$world_stage" +printf 'kernel_stage=%s\n' "$kernel_stage" +printf 'headers_stage=%s\n' "$headers_stage" +printf 'bootloader_stage=%s\n' "$bootloader_stage" +printf 'root_df=%s\n' "$root_df" +printf 'build_root_size=%s\n' "$build_root_size" +printf 'world_stage_size=%s\n' "$world_stage_size" +printf 'kernel_stage_size=%s\n' "$kernel_stage_size" +printf 'headers_stage_size=%s\n' "$headers_stage_size" +printf 'bootloader_stage_size=%s\n' "$bootloader_stage_size" +printf 'sha_kernel=%s\n' "$sha_kernel" +printf 'sha_loader=%s\n' "$sha_loader" +printf 'sha_param=%s\n' "$sha_param" +printf 'buildworld_tail=%s\n' "$buildworld_tail" +printf 'buildkernel_tail=%s\n' "$buildkernel_tail" +printf 'installworld_tail=%s\n' "$installworld_tail" +printf 'distribution_tail=%s\n' "$distribution_tail" +printf 'installkernel_tail=%s\n' "$installkernel_tail" +EOF +) + +build_jobs=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^build_jobs=//p') +source_store=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^source_store=//p') +source_root=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^source_root=//p') +build_root=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^build_root=//p') +logdir=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^logdir=//p') +buildworld_log=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^buildworld_log=//p') +buildkernel_log=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^buildkernel_log=//p') +installworld_log=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^installworld_log=//p') +distribution_log=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^distribution_log=//p') +installkernel_log=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^installkernel_log=//p') +world_stage=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^world_stage=//p') +kernel_stage=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^kernel_stage=//p') +headers_stage=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^headers_stage=//p') +bootloader_stage=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^bootloader_stage=//p') +root_df=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^root_df=//p') +build_root_size=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^build_root_size=//p') +world_stage_size=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^world_stage_size=//p') +kernel_stage_size=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^kernel_stage_size=//p') +headers_stage_size=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^headers_stage_size=//p') +bootloader_stage_size=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^bootloader_stage_size=//p') +sha_kernel=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^sha_kernel=//p') +sha_loader=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^sha_loader=//p') +sha_param=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^sha_param=//p') +buildworld_tail=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^buildworld_tail=//p') +buildkernel_tail=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^buildkernel_tail=//p') +installworld_tail=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^installworld_tail=//p') +distribution_tail=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^distribution_tail=//p') +installkernel_tail=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^installkernel_tail=//p') + +case "$source_store" in + /frx/store/*-freebsd-source-*) : ;; + *) echo "unexpected source store path: $source_store" >&2; exit 1 ;; +esac +case "$source_root" in + /frx/store/*-freebsd-source-*/*) : ;; + *) echo "unexpected source root path: $source_root" >&2; exit 1 ;; +esac +case "$build_root" in + /var/tmp/fruix-phase20-native-build) : ;; + *) echo "unexpected build root: $build_root" >&2; exit 1 ;; +esac +printf '%s\n' "$sha_kernel" | grep -E '^[0-9a-f]{64}$' >/dev/null || { + echo "invalid kernel sha256: $sha_kernel" >&2 + exit 1 +} +printf '%s\n' "$sha_loader" | grep -E '^[0-9a-f]{64}$' >/dev/null || { + echo "invalid loader sha256: $sha_loader" >&2 + exit 1 +} +printf '%s\n' "$sha_param" | grep -E '^[0-9a-f]{64}$' >/dev/null || { + echo "invalid param.h sha256: $sha_param" >&2 + exit 1 +} +case "$buildworld_tail" in + *'World build completed on'*) : ;; + *) echo "buildworld log does not show completion" >&2; exit 1 ;; +esac +case "$buildkernel_tail" in + *'Kernel(s) GENERIC built in'*) : ;; + *) echo "buildkernel log does not show successful kernel build" >&2; exit 1 ;; +esac + +cat >"$metadata_file" <