Validate real-store profiles on FreeBSD
This commit is contained in:
@@ -1791,3 +1791,79 @@ Current assessment:
|
||||
|
||||
- Phase 6.2 is now satisfied on the current FreeBSD prototype track
|
||||
- the next step is to validate a minimal user-facing profile installation flow on top of these real store outputs
|
||||
|
||||
## 2026-04-01 — Phase 6.3 completed: minimal profile installation validated on real store outputs
|
||||
|
||||
Completed work:
|
||||
|
||||
- added a runnable real-store profile harness:
|
||||
- `tests/packages/run-phase6-real-store-profile-prototype.sh`
|
||||
- wrote the Phase 6.3 report:
|
||||
- `docs/reports/phase6-real-store-profile-freebsd.md`
|
||||
- updated the Phase 6 package-build harnesses so they can recover derivation paths even when the requested outputs are already present in `/frx/store`
|
||||
- ran the real-store profile harness successfully and captured metadata under:
|
||||
- `/tmp/phase6-real-store-profile-metadata.txt`
|
||||
|
||||
Important findings:
|
||||
|
||||
- the current FreeBSD track now has a minimal user-facing profile installation flow built on top of real Phase 6 store outputs rather than the earlier Phase 3 package/profile prototype inputs
|
||||
- the validated transaction semantics are intentionally small but real:
|
||||
- generation 1 is created from the Phase 6.1 host-built store item
|
||||
- generation 2 is created from the Phase 6.2 jail-built store item
|
||||
- the `profile` symlink switches to generation 2
|
||||
- both generations remain addressable
|
||||
- observed metadata confirmed:
|
||||
- `profile_target=profile-2-link`
|
||||
- `generation1_store_path=/frx/store/...-hello-2.12.3`
|
||||
- `generation2_store_path=/frx/store/...-hello-2.12.3`
|
||||
- `current_store_path=/frx/store/...-hello-2.12.3`
|
||||
- `profile_hello_output=Hello, world!`
|
||||
- `clean_env_hello_output=Hello, world!`
|
||||
- the upstream-derived profile layer is still not fully usable on this FreeBSD track because the current `guix profiles` / `fruix package` path still reaches the unresolved bootstrap-platform blocker:
|
||||
- `dynamic linker name not known for this system "x86_64-freebsd15.0"`
|
||||
- despite that blocker, the minimal Fruix-owned profile path is now validated on top of real daemon-built store items
|
||||
|
||||
Current assessment:
|
||||
|
||||
- Phase 6.3 is now satisfied on the current FreeBSD prototype track
|
||||
- Phase 6 as a whole is now complete on the active FreeBSD amd64 prototype path
|
||||
|
||||
## 2026-04-01 — Phase 6 completed on the current FreeBSD prototype track
|
||||
|
||||
Phase 6 is now considered complete for the active FreeBSD amd64 prototype path.
|
||||
|
||||
Why this milestone is satisfied:
|
||||
|
||||
- **Phase 6.1** success criteria were met on the prototype track:
|
||||
- a real package definition derived from Guix's `hello` package now builds through `fruix build`
|
||||
- the output lands in `/frx/store`
|
||||
- the package runs from the store and preserves a declared source reference
|
||||
- **Phase 6.2** success criteria were met on the prototype track:
|
||||
- a real package build now executes inside a FreeBSD jail
|
||||
- the build work runs under dropped numeric build credentials
|
||||
- the jailed build succeeds into `/frx/store`
|
||||
- **Phase 6.3** success criteria were met on the prototype track:
|
||||
- real Phase 6 store outputs can be installed into a minimal profile environment
|
||||
- generation switching works in a concrete form
|
||||
- package execution through the profile succeeds for the current user
|
||||
|
||||
Important scope note:
|
||||
|
||||
- this completes the **real FreeBSD-backed store-build milestone** on the current prototype track, not full upstream-package-graph support or full upstream profile-layer parity yet
|
||||
- the current package path still relies on a prefetched local source tarball for GNU Hello because the built-in downloader/root-daemon path remains a separate FreeBSD issue
|
||||
- the current profile-installation path is a Fruix-owned minimal layer over real store outputs because the upstream-derived profile code still hits the unresolved FreeBSD bootstrap-platform mapping blocker
|
||||
- nevertheless, the core Phase 6 question has now been answered positively:
|
||||
- real package definitions can be built into `/frx/store`
|
||||
- those builds can run under integrated jail/build-user isolation
|
||||
- and the resulting store items can be exposed through a minimal user-facing profile flow
|
||||
|
||||
Next recommended step:
|
||||
|
||||
1. begin Phase 7.1 by defining a minimal Fruix operating-system model for FreeBSD
|
||||
2. carry forward 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
|
||||
3. keep the two remaining Phase 6 follow-up blockers visible but scoped:
|
||||
- built-in downloader/root-daemon integration for real package origins
|
||||
- upstream-derived profile/bootstrap-platform support for `x86_64-freebsd15.0`
|
||||
|
||||
70
docs/reports/phase6-real-store-profile-freebsd.md
Normal file
70
docs/reports/phase6-real-store-profile-freebsd.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Phase 6.3: Minimal profile installation validated on top of real store outputs
|
||||
|
||||
Date: 2026-04-01
|
||||
|
||||
## Summary
|
||||
|
||||
This step validates a minimal user-facing profile installation flow using the real `/frx/store` outputs produced in Phases 6.1 and 6.2.
|
||||
|
||||
Added file:
|
||||
|
||||
- `tests/packages/run-phase6-real-store-profile-prototype.sh`
|
||||
|
||||
## Validation command
|
||||
|
||||
Run command:
|
||||
|
||||
```sh
|
||||
METADATA_OUT=/tmp/phase6-real-store-profile-metadata.txt \
|
||||
./tests/packages/run-phase6-real-store-profile-prototype.sh
|
||||
```
|
||||
|
||||
## What the harness does
|
||||
|
||||
The harness:
|
||||
|
||||
1. reruns the Phase 6.1 real-package build harness
|
||||
2. reruns the Phase 6.2 jail-integrated package build harness
|
||||
3. creates a minimal Fruix-owned profile transaction layout using those real store outputs
|
||||
4. installs generation 1 from the Phase 6.1 store item
|
||||
5. installs generation 2 from the Phase 6.2 store item
|
||||
6. switches the current `profile` symlink to generation 2
|
||||
7. validates:
|
||||
- preserved generation links
|
||||
- current-profile switching
|
||||
- `PATH` usability
|
||||
- execution from a nearly empty environment
|
||||
|
||||
## Observed results
|
||||
|
||||
Observed metadata included:
|
||||
|
||||
- `profile=.../profile`
|
||||
- `profile_target=profile-2-link`
|
||||
- `generation1_store_path=/frx/store/...-hello-2.12.3`
|
||||
- `generation2_store_path=/frx/store/...-hello-2.12.3`
|
||||
- `current_store_path=/frx/store/...-hello-2.12.3`
|
||||
- `profile_hello_output=Hello, world!`
|
||||
- `clean_env_hello_output=Hello, world!`
|
||||
- `installation_mode=minimal-real-store-profile`
|
||||
|
||||
## Important findings
|
||||
|
||||
- the current FreeBSD track now has a minimal profile-generation and profile-switching story that operates on real store outputs rather than the earlier Phase 3 package/profile prototype layer
|
||||
- the profile transaction semantics are intentionally small but real:
|
||||
- generation 1 is retained
|
||||
- generation 2 is created
|
||||
- the `profile` symlink is switched to generation 2
|
||||
- executables become reachable through `PATH`
|
||||
- this step intentionally uses a Fruix-owned minimal profile harness because the upstream-derived `guix profiles` / `fruix package` path still runs into the unresolved FreeBSD bootstrap-platform issue:
|
||||
- `dynamic linker name not known for this system "x86_64-freebsd15.0"`
|
||||
- even with that upstream profile-layer gap still open, the user-facing package-installation boundary is now validated in minimal form on top of real daemon-built store items
|
||||
|
||||
## Conclusion
|
||||
|
||||
Phase 6.3 is satisfied on the current FreeBSD prototype track:
|
||||
|
||||
- packages built through the real store path can be installed into a profile-like user environment
|
||||
- generation switching works in a minimal but concrete form
|
||||
- package execution through the profile succeeds for the current user
|
||||
- Phase 6 as a whole is now complete on the active FreeBSD prototype path, with deeper upstream profile-layer integration remaining follow-up work rather than a blocker to this milestone
|
||||
@@ -24,6 +24,7 @@ daemon_log=$workdir/guix-daemon.log
|
||||
fetch_tarball=$workdir/hello-2.12.3.tar.gz
|
||||
build_stdout=$workdir/fruix-build.out
|
||||
build_stderr=$workdir/fruix-build.err
|
||||
drv_stdout=$workdir/fruix-build-derivation.out
|
||||
references_log=$workdir/references.log
|
||||
package_template=$workdir/phase6-jail-hello.scm.in
|
||||
package_file=$workdir/phase6-jail-hello.scm
|
||||
@@ -229,10 +230,12 @@ done
|
||||
}
|
||||
|
||||
GUIX_DAEMON_SOCKET=unix://$daemon_socket \
|
||||
./pre-inst-env fruix build -f "$package_file" >"$build_stdout" 2>"$build_stderr"
|
||||
./pre-inst-env fruix build -d -f "$package_file" >"$drv_stdout" 2>>"$build_stderr"
|
||||
GUIX_DAEMON_SOCKET=unix://$daemon_socket \
|
||||
./pre-inst-env fruix build -f "$package_file" >"$build_stdout" 2>>"$build_stderr"
|
||||
|
||||
out_path=$(awk 'NF { last = $0 } END { print last }' "$build_stdout")
|
||||
drv_path=$(sed -n 's/^successfully built //p' "$build_stderr" | tail -n 1)
|
||||
drv_path=$(awk 'NF { last = $0 } END { print last }' "$drv_stdout")
|
||||
runtime_output=$("$out_path/bin/hello")
|
||||
source_store_path=$(tr -d '\n' < "$out_path/share/fruix-phase6/source-store-path.txt")
|
||||
build_uid=$(tr -d '\n' < "$out_path/share/fruix-phase6/build-uid.txt")
|
||||
@@ -242,7 +245,7 @@ build_mode=$(tr -d '\n' < "$out_path/share/fruix-phase6/build-mode.txt")
|
||||
GUIX_DAEMON_SOCKET=unix://$daemon_socket \
|
||||
./pre-inst-env fruix gc --references "$out_path" >"$references_log" 2>"$workdir/fruix-gc.err"
|
||||
|
||||
[ -n "$drv_path" ] || { echo "missing derivation path in $build_stderr" >&2; exit 1; }
|
||||
[ -n "$drv_path" ] || { echo "missing derivation path in $drv_stdout" >&2; exit 1; }
|
||||
case "$drv_path" in
|
||||
/frx/store/*.drv) : ;;
|
||||
*) echo "unexpected derivation path: $drv_path" >&2; exit 1 ;;
|
||||
@@ -283,6 +286,7 @@ helper_source=$helper_source
|
||||
package_file=$package_file
|
||||
build_stdout=$build_stdout
|
||||
build_stderr=$build_stderr
|
||||
drv_stdout=$drv_stdout
|
||||
references_log=$references_log
|
||||
drv_path=$drv_path
|
||||
out_path=$out_path
|
||||
|
||||
@@ -24,6 +24,7 @@ daemon_log=$workdir/guix-daemon.log
|
||||
fetch_tarball=$workdir/hello-2.12.3.tar.gz
|
||||
build_stdout=$workdir/fruix-build.out
|
||||
build_stderr=$workdir/fruix-build.err
|
||||
drv_stdout=$workdir/fruix-build-derivation.out
|
||||
references_log=$workdir/references.log
|
||||
package_template=$workdir/phase6-hello.scm.in
|
||||
package_file=$workdir/phase6-hello.scm
|
||||
@@ -151,16 +152,18 @@ done
|
||||
}
|
||||
|
||||
GUIX_DAEMON_SOCKET=unix://$daemon_socket \
|
||||
./pre-inst-env fruix build -f "$package_file" >"$build_stdout" 2>"$build_stderr"
|
||||
./pre-inst-env fruix build -d -f "$package_file" >"$drv_stdout" 2>>"$build_stderr"
|
||||
GUIX_DAEMON_SOCKET=unix://$daemon_socket \
|
||||
./pre-inst-env fruix build -f "$package_file" >"$build_stdout" 2>>"$build_stderr"
|
||||
|
||||
out_path=$(awk 'NF { last = $0 } END { print last }' "$build_stdout")
|
||||
drv_path=$(sed -n 's/^successfully built //p' "$build_stderr" | tail -n 1)
|
||||
drv_path=$(awk 'NF { last = $0 } END { print last }' "$drv_stdout")
|
||||
source_store_path=$(tr -d '\n' < "$out_path/share/fruix-phase6/source-store-path.txt")
|
||||
runtime_output=$("$out_path/bin/hello")
|
||||
GUIX_DAEMON_SOCKET=unix://$daemon_socket \
|
||||
./pre-inst-env fruix gc --references "$out_path" >"$references_log" 2>"$workdir/fruix-gc.err"
|
||||
|
||||
[ -n "$drv_path" ] || { echo "missing derivation path in $build_stderr" >&2; exit 1; }
|
||||
[ -n "$drv_path" ] || { echo "missing derivation path in $drv_stdout" >&2; exit 1; }
|
||||
case "$drv_path" in
|
||||
/frx/store/*.drv) : ;;
|
||||
*) echo "unexpected derivation path: $drv_path" >&2; exit 1 ;;
|
||||
@@ -193,6 +196,7 @@ fetch_tarball=$fetch_tarball
|
||||
package_file=$package_file
|
||||
build_stdout=$build_stdout
|
||||
build_stderr=$build_stderr
|
||||
drv_stdout=$drv_stdout
|
||||
references_log=$references_log
|
||||
drv_path=$drv_path
|
||||
out_path=$out_path
|
||||
|
||||
144
tests/packages/run-phase6-real-store-profile-prototype.sh
Executable file
144
tests/packages/run-phase6-real-store-profile-prototype.sh
Executable file
@@ -0,0 +1,144 @@
|
||||
#!/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-phase6-profile.XXXXXX)
|
||||
cleanup=1
|
||||
fi
|
||||
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
|
||||
cleanup=0
|
||||
fi
|
||||
|
||||
metadata_file=$workdir/phase6-real-store-profile-metadata.txt
|
||||
phase61_metadata=$workdir/phase6-real-package-metadata.txt
|
||||
phase62_metadata=$workdir/phase6-jail-package-metadata.txt
|
||||
profile=$workdir/profile
|
||||
phase61_log=$workdir/phase6-real-package.log
|
||||
phase62_log=$workdir/phase6-jail-package.log
|
||||
|
||||
cleanup_workdir() {
|
||||
if [ "$cleanup" -eq 1 ]; then
|
||||
rm -rf "$workdir"
|
||||
fi
|
||||
}
|
||||
trap cleanup_workdir EXIT INT TERM
|
||||
|
||||
install_generation() {
|
||||
generation=$1
|
||||
store_path=$2
|
||||
label=$3
|
||||
generation_dir=$workdir/profile-generation-$generation
|
||||
generation_link=$profile-$generation-link
|
||||
|
||||
rm -rf "$generation_dir"
|
||||
mkdir -p "$generation_dir"
|
||||
for entry in "$store_path"/*; do
|
||||
[ -e "$entry" ] || continue
|
||||
ln -s "$entry" "$generation_dir/$(basename "$entry")"
|
||||
done
|
||||
cat >"$generation_dir/.fruix-profile-entry" <<EOF
|
||||
label=$label
|
||||
store_path=$store_path
|
||||
generation=$generation
|
||||
EOF
|
||||
|
||||
rm -f "$generation_link.new" "$generation_link"
|
||||
ln -s "$generation_dir" "$generation_link.new"
|
||||
mv -f "$generation_link.new" "$generation_link"
|
||||
|
||||
rm -f "$profile"
|
||||
ln -s "$(basename "$generation_link")" "$profile"
|
||||
}
|
||||
|
||||
METADATA_OUT=$phase61_metadata WORKDIR=$workdir/phase6-61 KEEP_WORKDIR=1 \
|
||||
"$repo_root/tests/guix/run-phase6-real-package-build.sh" >"$phase61_log" 2>&1
|
||||
METADATA_OUT=$phase62_metadata WORKDIR=$workdir/phase6-62 KEEP_WORKDIR=1 \
|
||||
"$repo_root/tests/guix/run-phase6-jail-package-build.sh" >"$phase62_log" 2>&1
|
||||
|
||||
phase61_out=$(sed -n 's/^out_path=//p' "$phase61_metadata")
|
||||
phase62_out=$(sed -n 's/^out_path=//p' "$phase62_metadata")
|
||||
phase61_runtime=$(sed -n 's/^runtime_output=//p' "$phase61_metadata")
|
||||
phase62_runtime=$(sed -n 's/^runtime_output=//p' "$phase62_metadata")
|
||||
|
||||
[ "$phase61_runtime" = 'Hello, world!' ] || {
|
||||
echo "phase6.1 prerequisite failed: $phase61_runtime" >&2
|
||||
exit 1
|
||||
}
|
||||
[ "$phase62_runtime" = 'Hello, world!' ] || {
|
||||
echo "phase6.2 prerequisite failed: $phase62_runtime" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
install_generation 1 "$phase61_out" phase6-real-package
|
||||
install_generation 2 "$phase62_out" phase6-jail-package
|
||||
|
||||
profile_target=$(readlink "$profile")
|
||||
generation1_target=$(readlink "$profile-1-link")
|
||||
generation2_target=$(readlink "$profile-2-link")
|
||||
current_bin_target=$(readlink "$profile/bin")
|
||||
current_store_path=$(dirname "$current_bin_target")
|
||||
profile_hello_output=$(PATH="$profile/bin:/bin:/usr/bin" hello)
|
||||
clean_env_hello_output=$(env -i PATH="$profile/bin:/bin:/usr/bin" HOME="$workdir/home" USER="${USER:-self}" LOGNAME="${LOGNAME:-self}" "$profile/bin/hello")
|
||||
user_uid=$(id -u)
|
||||
user_name=$(id -un)
|
||||
|
||||
[ "$profile_target" = 'profile-2-link' ] || {
|
||||
echo "unexpected current profile target: $profile_target" >&2
|
||||
exit 1
|
||||
}
|
||||
[ "$current_store_path" = "$phase62_out" ] || {
|
||||
echo "profile does not point at the expected current store path" >&2
|
||||
exit 1
|
||||
}
|
||||
[ "$profile_hello_output" = 'Hello, world!' ] || {
|
||||
echo "unexpected profile hello output: $profile_hello_output" >&2
|
||||
exit 1
|
||||
}
|
||||
[ "$clean_env_hello_output" = 'Hello, world!' ] || {
|
||||
echo "unexpected clean-env hello output: $clean_env_hello_output" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cat >"$metadata_file" <<EOF
|
||||
workdir=$workdir
|
||||
phase61_metadata=$phase61_metadata
|
||||
phase62_metadata=$phase62_metadata
|
||||
phase61_log=$phase61_log
|
||||
phase62_log=$phase62_log
|
||||
profile=$profile
|
||||
profile_target=$profile_target
|
||||
generation1_link=$profile-1-link
|
||||
generation1_target=$generation1_target
|
||||
generation1_store_path=$phase61_out
|
||||
generation2_link=$profile-2-link
|
||||
generation2_target=$generation2_target
|
||||
generation2_store_path=$phase62_out
|
||||
current_bin_target=$current_bin_target
|
||||
current_store_path=$current_store_path
|
||||
profile_hello_output=$profile_hello_output
|
||||
clean_env_hello_output=$clean_env_hello_output
|
||||
user_uid=$user_uid
|
||||
user_name=$user_name
|
||||
installation_mode=minimal-real-store-profile
|
||||
EOF
|
||||
|
||||
if [ -n "$metadata_target" ]; then
|
||||
mkdir -p "$(dirname "$metadata_target")"
|
||||
cp "$metadata_file" "$metadata_target"
|
||||
fi
|
||||
|
||||
printf 'PASS phase6-real-store-profile\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"
|
||||
Reference in New Issue
Block a user