system: validate host-initiated native base builds

This commit is contained in:
2026-04-05 16:32:08 +02:00
parent 9e9a0b59fc
commit a3dd5556ae
6 changed files with 522 additions and 26 deletions

View File

@@ -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" <<EOF
workdir=$workdir
inner_metadata=$inner_metadata
phase8_metadata=$phase8_metadata
closure_path=$closure_path
closure_base=$closure_base
vm_id=$vm_id
vdi_id=$vdi_id
guest_ip=$guest_ip
root_size=$root_size
build_jobs=$build_jobs
source_store=$source_store
source_root=$source_root
build_root=$build_root
logdir=$logdir
buildworld_log=$buildworld_log
buildkernel_log=$buildkernel_log
installworld_log=$installworld_log
distribution_log=$distribution_log
installkernel_log=$installkernel_log
world_stage=$world_stage
kernel_stage=$kernel_stage
headers_stage=$headers_stage
bootloader_stage=$bootloader_stage
root_df=$root_df
build_root_size=$build_root_size
world_stage_size=$world_stage_size
kernel_stage_size=$kernel_stage_size
headers_stage_size=$headers_stage_size
bootloader_stage_size=$bootloader_stage_size
sha_kernel=$sha_kernel
sha_loader=$sha_loader
sha_param=$sha_param
buildworld_tail=$buildworld_tail
buildkernel_tail=$buildkernel_tail
installworld_tail=$installworld_tail
distribution_tail=$distribution_tail
installkernel_tail=$installkernel_tail
boot_backend=xcp-ng-xo-cli
init_mode=shepherd-pid1
host_initiated_native_build=ok
EOF
if [ -n "$metadata_target" ]; then
mkdir -p "$(dirname "$metadata_target")"
cp "$metadata_file" "$metadata_target"
fi
printf 'PASS phase20-host-initiated-native-build-xcpng\n'
printf 'Work directory: %s\n' "$workdir"
printf 'Metadata file: %s\n' "$metadata_file"
if [ -n "$metadata_target" ]; then
printf 'Copied metadata to: %s\n' "$metadata_target"
fi
printf '%s\n' '--- metadata ---'
cat "$metadata_file"