From c3a2d45a02ac217d39b67ac38004635725724b69 Mon Sep 17 00:00:00 2001 From: Steffen Beyer Date: Wed, 1 Apr 2026 14:10:53 +0200 Subject: [PATCH] Validate FreeBSD daemon store RPC --- docs/PROGRESS.md | 77 ++++++++ docs/reports/phase5-daemon-rpc-freebsd.md | 71 +++++++ tests/guix/run-phase5-daemon-rpc.sh | 215 ++++++++++++++++++++++ 3 files changed, 363 insertions(+) create mode 100644 docs/reports/phase5-daemon-rpc-freebsd.md create mode 100755 tests/guix/run-phase5-daemon-rpc.sh diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 31833db..b8beea7 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -1646,3 +1646,80 @@ Current assessment: - Phase 5.2 is now satisfied on the current FreeBSD prototype track - the next step is no longer “can we emit a derivation at all?” but “can the same daemon/store path accept and execute a derivation-backed build request successfully?” + +## 2026-04-01 — Phase 5.3 completed: minimal daemon/store RPC integration validated on FreeBSD + +Completed work: + +- added a runnable daemon/store RPC validation harness: + - `tests/guix/run-phase5-daemon-rpc.sh` +- wrote the Phase 5.3 report: + - `docs/reports/phase5-daemon-rpc-freebsd.md` +- ran the daemon/store RPC harness successfully and captured metadata under: + - `/tmp/phase5-daemon-rpc-metadata.txt` + +Important findings: + +- the patched checkout can now contact a real FreeBSD-aware daemon over a Unix socket and submit a derivation-backed build request successfully +- the resulting build path is a real `/frx/store` output rather than a simulated prototype artifact +- the successful metadata path now includes the full minimal chain needed for later system work: + - checkout command path + - daemon RPC + - derivation submission + - build execution + - store output materialization +- observed metadata confirmed: + - `drv_path=/frx/store/...-phase5-freebsd-daemon-build-0.drv` + - `out_path=/frx/store/...-phase5-freebsd-daemon-build-0` + - `payload=phase5-daemon-build-source` + - `source_path=/frx/store/...-phase5-source.txt` +- this step was exercised through the Fruix-facing checkout boundary: + - `./pre-inst-env fruix repl -- ...` + which means the user-facing transition is now connected to actual daemon/store activity, not just to help text or version banners + +Current assessment: + +- Phase 5.3 is now satisfied on the current FreeBSD prototype track +- the project now has a real but narrow end-to-end host-side execution path on FreeBSD: + - runnable checkout + - Fruix front-end boundary + - real derivation emission + - daemon/store RPC + - successful derivation-backed build into `/frx/store` + +## 2026-04-01 — Phase 5 completed on the current FreeBSD prototype track + +Phase 5 is now considered complete for the active FreeBSD amd64 prototype path. + +Why this milestone is satisfied: + +- **Phase 5.1** success criteria were met on the prototype track: + - the checkout runtime blocker around `leave-on-EPIPE` was root-caused and fixed in the patch queue + - the uninstalled checkout command path now runs on FreeBSD + - a first user-facing `fruix` command boundary was established +- **Phase 5.2** success criteria were met on the prototype track: + - a real package object was lowered through `package->bag` and `bag->derivation` + - a real derivation was emitted targeting `/frx/store` +- **Phase 5.3** success criteria were met on the prototype track: + - a real checkout command path contacted a FreeBSD-aware daemon/store path + - that path accepted and executed a derivation-backed build request + - the resulting output was materialized successfully in `/frx/store` + +Important scope note: + +- this completes the **real checkout and host runtime unblocking milestone** on the current prototype track, not full upstream-package-graph support for arbitrary native FreeBSD package builds yet +- the successful derivation/build path currently uses a deliberately minimal custom package/build-system path to isolate real daemon/store viability from still-unresolved upstream bootstrap and package-graph assumptions for native FreeBSD +- nevertheless, the core Phase 5 question has now been answered positively: + - the checkout runs + - real derivations can be emitted + - the daemon can be built far enough to serve store RPC + - and derivation-backed builds can succeed into `/frx/store` + +Next recommended step: + +1. begin Phase 6.1 by moving from the minimal custom derivation-backed package path to at least one real FreeBSD store-backed package build driven by Fruix/Guix mechanisms +2. then integrate the already validated jail/build-user model more directly into the live daemon build path +3. continue preserving the selective Fruix naming policy: + - Fruix at the product boundary + - `/frx` as the canonical store root + - stable upstream-derived internal names unless there is strong architectural value in renaming them diff --git a/docs/reports/phase5-daemon-rpc-freebsd.md b/docs/reports/phase5-daemon-rpc-freebsd.md new file mode 100644 index 0000000..1ea4775 --- /dev/null +++ b/docs/reports/phase5-daemon-rpc-freebsd.md @@ -0,0 +1,71 @@ +# Phase 5.3: Minimal daemon/store RPC integration validated on FreeBSD + +Date: 2026-04-01 + +## Summary + +This step validates that the FreeBSD-aware checkout can do more than emit a derivation: it can contact a real daemon/store path and submit an actual derivation-backed build request that succeeds into `/frx/store`. + +Added file: + +- `tests/guix/run-phase5-daemon-rpc.sh` + +## Validation command + +Run command: + +```sh +METADATA_OUT=/tmp/phase5-daemon-rpc-metadata.txt \ +./tests/guix/run-phase5-daemon-rpc.sh +``` + +## What the harness does + +The harness: + +1. reuses the patched checkout/runtime setup from Phase 5.1 +2. starts the patched `guix-daemon` on a temporary Unix socket +3. invokes the checkout through the user-facing boundary: + - `./pre-inst-env fruix repl -- ...` +4. defines a minimal custom package with the same low-level FreeBSD build system style used in Phase 5.2 +5. lowers that package to a derivation +6. submits the resulting derivation to the daemon through `build-derivations` +7. validates the built output content in `/frx/store` + +## Observed results + +Observed metadata included: + +- `drv_path=/frx/store/...-phase5-freebsd-daemon-build-0.drv` +- `out_path=/frx/store/...-phase5-freebsd-daemon-build-0` +- `payload=phase5-daemon-build-source` +- `source_path=/frx/store/...-phase5-source.txt` +- `frontend_invocation=./pre-inst-env fruix repl -- ...` + +This demonstrates that: + +- the checkout can contact a real daemon over a Unix socket on FreeBSD +- the daemon accepts a derivation-backed build request +- the build succeeds into `/frx/store` +- the resulting output is a real store item +- the source was itself materialized as a store path and referenced by the built output path + +## Important findings + +- the FreeBSD daemon path is now operational enough to support a narrow but real end-to-end flow: + - checkout command path + - daemon RPC + - derivation submission + - build execution + - store output materialization +- this validation intentionally used a minimal custom derivation-backed package rather than a full upstream GNU package graph, so the result isolates actual daemon/store viability from unrelated unresolved bootstrap-package assumptions +- the command path was exercised through the Fruix-facing frontend boundary (`fruix repl`), which is important because Phase 5 is not just about making the upstream-derived checkout work internally, but about beginning the transition to Fruix as the operator-facing identity + +## Conclusion + +Phase 5.3 is satisfied on the current FreeBSD prototype track: + +- a real checkout command path can contact a FreeBSD-aware daemon/store path +- that path accepts and executes a derivation-backed build request +- the build succeeds into `/frx/store` +- the Fruix-facing checkout frontend is now being exercised against the real daemon/store path rather than only against no-op or help-text commands diff --git a/tests/guix/run-phase5-daemon-rpc.sh b/tests/guix/run-phase5-daemon-rpc.sh new file mode 100755 index 0000000..3f1f717 --- /dev/null +++ b/tests/guix/run-phase5-daemon-rpc.sh @@ -0,0 +1,215 @@ +#!/bin/sh +set -eu + +repo_root=$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd) +metadata_target=${METADATA_OUT:-} + +cleanup=0 +if [ -n "${WORKDIR:-}" ]; then + workdir=$WORKDIR + mkdir -p "$workdir" +else + workdir=$(mktemp -d /tmp/fruix-phase5-daemon.XXXXXX) + cleanup=1 +fi +if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then + cleanup=0 +fi + +setup_metadata=$workdir/setup-metadata.txt +setup_env=$workdir/setup-env.sh +metadata_file=$workdir/phase5-daemon-rpc-metadata.txt +daemon_socket=$workdir/guix-daemon.sock +daemon_log=$workdir/guix-daemon.log +scheme_log=$workdir/phase5-build.log +scheme_template=$workdir/phase5-build.scm.in +scheme_file=$workdir/phase5-build.scm +source_file=$workdir/phase5-source.txt + +daemon_pid= +cleanup_workdir() { + if [ -n "$daemon_pid" ]; then + sudo kill "$daemon_pid" >/dev/null 2>&1 || true + wait "$daemon_pid" 2>/dev/null || true + fi + rm -f "$daemon_socket" + if [ "$cleanup" -eq 1 ]; then + rm -rf "$workdir" + fi +} +trap cleanup_workdir EXIT INT TERM + +WORKDIR=$workdir/setup KEEP_WORKDIR=1 \ + METADATA_OUT=$setup_metadata ENV_OUT=$setup_env \ + "$repo_root/tests/guix/setup-phase5-checkout.sh" >"$workdir/setup.log" 2>&1 + +# shellcheck disable=SC1090 +. "$setup_env" + +cd "$PHASE5_BUILDDIR" +export LD_LIBRARY_PATH GUILE_LOAD_PATH GUILE_LOAD_COMPILED_PATH GUILE_EXTENSIONS_PATH +export GUILE_AUTO_COMPILE=0 +printf 'phase5-daemon-build-source\n' > "$source_file" + +cat >"$scheme_template" <<'EOF' +(use-modules (guix store) + (guix monads) + (guix gexp) + (guix packages) + (guix build-system) + (guix derivations) + (ice-9 match) + (srfi srfi-1) + (rnrs io ports)) + +(define source-file "__SOURCE_FILE__") +(call-with-output-file source-file + (lambda (port) + (display "phase5-daemon-build-source\n" port))) + +(define source-item + (local-file source-file "__SOURCE_NAME__")) + +(define* (phase5-lower name #:key source inputs native-inputs outputs system target #:allow-other-keys) + (bag + (name name) + (system system) + (target target) + (host-inputs `(,@(if source `(("source" ,source)) '()) ,@inputs)) + (build-inputs native-inputs) + (outputs outputs) + (build phase5-build) + (arguments `(#:source ,source)))) + +(define* (phase5-build name inputs #:key source (outputs '("out")) (system (%current-system)) #:allow-other-keys) + (mlet* %store-monad ((shell (interned-file "/bin/sh" "sh" #:recursive? #t)) + (source* (lower-object source system)) + (builder (text-file "phase5-builder.sh" + "#!/bin/sh\nset -eu\n/bin/mkdir -p \"$out\"\n/bin/cp \"$1\" \"$out/payload.txt\"\nprintf '%s\\n' \"$1\" > \"$out/source-path.txt\"\n" + (list (if (derivation? source*) + (derivation->output-path source*) + source*))))) + (lambda (store) + (values (derivation store name shell + (list "-e" builder + (if (derivation? source*) + (derivation->output-path source*) + source*)) + #:env-vars '(("HOME" . "/homeless")) + #:inputs `(,@(if (derivation? source*) + `((,source*)) + '()) + (,shell) (,builder)) + #:sources `(,shell ,builder) + #:system system + #:outputs outputs) + store)))) + +(define phase5-build-system + (build-system + (name 'phase5-freebsd) + (description "Phase 5 FreeBSD daemon test build system") + (lower phase5-lower))) + +(define phase5-package + (package + (name "phase5-freebsd-daemon-build") + (version "0") + (source source-item) + (build-system phase5-build-system) + (synopsis "Phase 5 daemon build test package") + (description "Minimal package used to validate daemon-backed builds on FreeBSD.") + (home-page "https://example.invalid/fruix") + (license #f))) + +(let* ((store (open-connection)) + (drv (run-with-store store + (mlet* %store-monad ((bag -> (package->bag phase5-package (%current-system) #f)) + (drv (bag->derivation bag phase5-package))) + (return drv))))) + (build-derivations store (list drv)) + (let* ((out (derivation->output-path drv)) + (payload (call-with-input-file (string-append out "/payload.txt") get-string-all)) + (source-path (call-with-input-file (string-append out "/source-path.txt") get-string-all))) + (format #t "drv-path=~a~%" (derivation-file-name drv)) + (format #t "out-path=~a~%" out) + (format #t "payload=~a" payload) + (format #t "source-path=~a" source-path))) +EOF +sed \ + -e "s|__SOURCE_FILE__|$source_file|g" \ + -e "s|__SOURCE_NAME__|$(basename "$source_file")|g" \ + "$scheme_template" > "$scheme_file" + +sudo env GUIX_DAEMON_SOCKET=unix://$daemon_socket \ + "$PHASE5_GUIX_DAEMON" --disable-chroot --listen "$daemon_socket" --no-substitutes \ + >"$daemon_log" 2>&1 & +daemon_pid=$! +for _ in 1 2 3 4 5 6 7 8 9 10; do + [ -S "$daemon_socket" ] && break + sleep 1 +done +[ -S "$daemon_socket" ] || { + echo "daemon socket was not created: $daemon_socket" >&2 + exit 1 +} + +GUIX_DAEMON_SOCKET=unix://$daemon_socket \ +./pre-inst-env fruix repl -- "$scheme_file" >"$scheme_log" 2>&1 + +drv_path=$(sed -n 's/^drv-path=//p' "$scheme_log") +out_path=$(sed -n 's/^out-path=//p' "$scheme_log") +payload=$(sed -n 's/^payload=//p' "$scheme_log") +source_path=$(sed -n 's/^source-path=//p' "$scheme_log") + +[ -n "$drv_path" ] || { echo "missing drv-path in $scheme_log" >&2; exit 1; } +[ -n "$out_path" ] || { echo "missing out-path in $scheme_log" >&2; exit 1; } +[ -n "$payload" ] || { echo "missing payload in $scheme_log" >&2; exit 1; } +[ -n "$source_path" ] || { echo "missing source-path in $scheme_log" >&2; exit 1; } +case "$drv_path" in + /frx/store/*.drv) : ;; + *) echo "unexpected derivation path: $drv_path" >&2; exit 1 ;; +esac +case "$out_path" in + /frx/store/*) : ;; + *) echo "unexpected output path: $out_path" >&2; exit 1 ;; +esac +[ "$payload" = 'phase5-daemon-build-source' ] || { + echo "unexpected payload content: $payload" >&2 + exit 1 +} +case "$source_path" in + /frx/store/*) : ;; + *) echo "unexpected source store path: $source_path" >&2; exit 1 ;; +esac + +cat >"$metadata_file" <