system: validate development environment overlay
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Fruix differences for Guix sysadmins
|
||||
|
||||
Date: 2026-04-04
|
||||
Date: 2026-04-05
|
||||
|
||||
This document is aimed at operators who already know Guix well and want a quick map of where Fruix behaves similarly and where it intentionally differs.
|
||||
|
||||
@@ -242,6 +242,24 @@ For Guix-familiar operators, the practical takeaway is:
|
||||
- do **not** assume Fruix store prefixes are byte-for-byte comparable to Guix/Nix ones
|
||||
- expect Fruix to prefer a simpler, centralized naming policy unless exact Guix/Nix behavior becomes necessary later
|
||||
|
||||
## 8. Fruix can expose a separate in-system development profile overlay
|
||||
|
||||
For the validated Phase 20.1 path, Fruix can now expose development tooling separately from the main runtime profile.
|
||||
|
||||
On those systems, Fruix exposes:
|
||||
|
||||
- `/run/current-system/development-profile`
|
||||
- `/run/current-development`
|
||||
- `/usr/local/bin/fruix-development-environment`
|
||||
|
||||
The intent is:
|
||||
|
||||
- keep the main runtime profile lean
|
||||
- expose headers, `usr/share/mk`, and selected toolchain commands explicitly
|
||||
- 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.
|
||||
|
||||
## Where Fruix is intentionally trying to improve on Guix's representation
|
||||
|
||||
Fruix is not trying to improve on Guix's core semantics. Guix already got those right.
|
||||
|
||||
@@ -34,6 +34,10 @@ Fruix currently has:
|
||||
- `fruix system status`
|
||||
- `fruix system switch`
|
||||
- `fruix system rollback`
|
||||
- a validated separate in-system development environment overlay via:
|
||||
- `/run/current-system/development-profile`
|
||||
- `/run/current-development`
|
||||
- `/usr/local/bin/fruix-development-environment`
|
||||
|
||||
Validated boot modes still are:
|
||||
|
||||
@@ -46,44 +50,37 @@ The validated Phase 18 installation work currently uses:
|
||||
|
||||
## Latest completed achievement
|
||||
|
||||
### 2026-04-04 — Phase 19.3 completed
|
||||
### 2026-04-05 — Phase 20.1 completed
|
||||
|
||||
Fruix now has a validated installed-system operator workflow for switching to a staged candidate generation and rolling back to the recorded previous generation.
|
||||
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.
|
||||
|
||||
Highlights:
|
||||
|
||||
- installed systems now ship an in-guest Fruix deployment helper at:
|
||||
- `/usr/local/bin/fruix`
|
||||
- validated in-guest command surface:
|
||||
- `fruix system status`
|
||||
- `fruix system switch /frx/store/...-fruix-system-...`
|
||||
- `fruix system rollback`
|
||||
- switching now records explicit rollback state under:
|
||||
- `/var/lib/fruix/system/rollback`
|
||||
- `/var/lib/fruix/system/rollback-generation`
|
||||
- switching now records explicit rollback GC roots under:
|
||||
- `/frx/var/fruix/gcroots/rollback-system`
|
||||
- the validated installed-system workflow now supports:
|
||||
- stage candidate closure in `/frx/store`
|
||||
- switch to generation 2
|
||||
- reboot into the candidate
|
||||
- rollback to generation 1
|
||||
- reboot into the restored current system
|
||||
- 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
|
||||
|
||||
Validation:
|
||||
|
||||
- `PASS phase19-installed-system-rollback-qemu`
|
||||
- regression re-checks:
|
||||
- `PASS phase19-generation-layout-qemu`
|
||||
- `PASS phase18-installer-iso`
|
||||
- `PASS phase20-development-environment-xcpng`
|
||||
|
||||
Reports:
|
||||
|
||||
- `docs/system-deployment-workflow.md`
|
||||
- `docs/GUIX_DIFFERENCES.md`
|
||||
- `docs/reports/phase19-deployment-workflow-freebsd.md`
|
||||
- `docs/reports/phase19-generation-layout-freebsd.md`
|
||||
- `docs/reports/phase19-installed-system-rollback-freebsd.md`
|
||||
- `docs/reports/phase20-development-environment-freebsd.md`
|
||||
|
||||
## Recent major milestones
|
||||
|
||||
@@ -109,6 +106,6 @@ Reports:
|
||||
|
||||
Per `docs/PLAN_4.md`, the next planned step is:
|
||||
|
||||
- **Phase 20.1** — validate a Fruix-managed development environment for native FreeBSD base work
|
||||
- **Phase 20.2** — run host-initiated native base builds inside a Fruix-managed environment
|
||||
|
||||
Phase 19.3 is now complete: Fruix validates installed-system generation switching and rollback through the intended operator-facing workflow.
|
||||
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.
|
||||
|
||||
160
docs/reports/phase20-development-environment-freebsd.md
Normal file
160
docs/reports/phase20-development-environment-freebsd.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# Phase 20.1: Fruix-managed development environment for native FreeBSD base work
|
||||
|
||||
Date: 2026-04-05
|
||||
|
||||
## Goal
|
||||
|
||||
Validate that a booted Fruix-managed FreeBSD system can expose a usable development environment for deeper native base work without collapsing the runtime/development boundary back into one broad profile.
|
||||
|
||||
This step explicitly builds on the Phase 14 split between:
|
||||
|
||||
- native runtime/boot artifacts for the running system
|
||||
- separate development-facing artifacts such as headers and toolchain pieces
|
||||
|
||||
The goal is **not** full self-hosting yet.
|
||||
|
||||
It is to prove that a running Fruix system can expose the tools and paths needed for native FreeBSD build work in a controlled way.
|
||||
|
||||
## Implementation
|
||||
|
||||
### New operating-system field
|
||||
|
||||
`modules/fruix/system/freebsd/model.scm` now supports:
|
||||
|
||||
- `#:development-packages`
|
||||
- `operating-system-development-packages`
|
||||
|
||||
The default remains empty, so existing systems do not change unless they opt in.
|
||||
|
||||
### Separate development profile inside the system closure
|
||||
|
||||
`modules/fruix/system/freebsd/media.scm` now materializes an additional profile tree when `development-packages` is non-empty:
|
||||
|
||||
- `/frx/store/...-fruix-system-.../development-profile`
|
||||
|
||||
The main runtime tree remains:
|
||||
|
||||
- `/frx/store/...-fruix-system-.../profile`
|
||||
|
||||
This preserves the runtime/development split:
|
||||
|
||||
- runtime stays under `profile`
|
||||
- development tooling stays under `development-profile`
|
||||
|
||||
The development stores are also now part of the closure references and recorded in `metadata/store-layout.scm`.
|
||||
|
||||
### In-guest development environment helper
|
||||
|
||||
Opt-in systems with development packages now ship:
|
||||
|
||||
- `/usr/local/bin/fruix-development-environment`
|
||||
|
||||
and expose a stable runtime link:
|
||||
|
||||
- `/run/current-development -> /run/current-system/development-profile`
|
||||
|
||||
The helper emits shell exports for the active development profile, including at least:
|
||||
|
||||
- `FRUIX_DEVELOPMENT_PROFILE`
|
||||
- `FRUIX_DEVELOPMENT_INCLUDE`
|
||||
- `FRUIX_DEVELOPMENT_SHARE_MK`
|
||||
- `FRUIX_CC`
|
||||
- `FRUIX_CXX`
|
||||
- `FRUIX_AR`
|
||||
- `FRUIX_RANLIB`
|
||||
- `FRUIX_NM`
|
||||
- `FRUIX_BMAKE`
|
||||
- `CPPFLAGS`
|
||||
- `MAKEFLAGS`
|
||||
- `PATH`
|
||||
|
||||
Intended use:
|
||||
|
||||
```sh
|
||||
eval "$(/usr/local/bin/fruix-development-environment)"
|
||||
```
|
||||
|
||||
### Chosen development overlay for this phase
|
||||
|
||||
For the validated Phase 20.1 guest path, the development overlay is intentionally narrow:
|
||||
|
||||
- `freebsd-native-headers`
|
||||
- `freebsd-clang-toolchain`
|
||||
|
||||
This was chosen deliberately.
|
||||
|
||||
The earlier standalone Phase 14 development-profile package set was designed for broad profile composition, but for a running Fruix system it unnecessarily duplicated runtime pieces already present in the system profile.
|
||||
|
||||
For native base work, the important additions here are:
|
||||
|
||||
- `usr/include`
|
||||
- `usr/share/mk`
|
||||
- Clang/binutils-style frontend tools
|
||||
|
||||
while the running system already supplies:
|
||||
|
||||
- base runtime
|
||||
- `/usr/bin/make`
|
||||
- system libraries and boot/runtime state
|
||||
|
||||
That keeps the Phase 20.1 environment focused on native base development rather than reintroducing a broad mixed profile.
|
||||
|
||||
## New files
|
||||
|
||||
Added:
|
||||
|
||||
- `tests/system/phase20-development-operating-system.scm.in`
|
||||
- `tests/system/run-phase20-development-environment-xcpng.sh`
|
||||
|
||||
## Validation
|
||||
|
||||
Passing run:
|
||||
|
||||
- `PASS phase20-development-environment-xcpng`
|
||||
- workdir: `/tmp/fruix-phase20-development-xcpng`
|
||||
|
||||
Validated on the approved real XCP-ng path:
|
||||
|
||||
- VM `90490f2e-e8fc-4b7a-388e-5c26f0157289`
|
||||
- VDI `0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
|
||||
|
||||
Representative result:
|
||||
|
||||
```text
|
||||
closure_path=/frx/store/c0ad43d7ef72323d4270a4f1e96ca1f5cc99566c-fruix-system-fruix-freebsd
|
||||
development_profile_path=/frx/store/c0ad43d7ef72323d4270a4f1e96ca1f5cc99566c-fruix-system-fruix-freebsd/development-profile
|
||||
development_profile_guest=/run/current-system/development-profile
|
||||
cc_version=FreeBSD clang version 19.1.7 (https://github.com/llvm/llvm-project.git llvmorg-19.1.7-0-gcd708029e0b2)
|
||||
hello_direct=hello-from-direct-dev-env
|
||||
hello_make=hello-from-make-dev-env
|
||||
development_environment=ok
|
||||
```
|
||||
|
||||
The harness verified all of the following on the real booted Fruix guest:
|
||||
|
||||
- runtime boot/regression still succeeds on XCP-ng
|
||||
- the closure contains a separate `development-profile`
|
||||
- runtime `profile` does **not** regain headers or `usr/share/mk`
|
||||
- `/usr/local/bin/fruix-development-environment` exists and emits the expected exports
|
||||
- `/run/current-development` points at `/run/current-system/development-profile`
|
||||
- direct compilation works with the exported Clang toolchain and headers
|
||||
- a simple native FreeBSD `make` build using `.include <bsd.prog.mk>` also succeeds
|
||||
|
||||
## Result
|
||||
|
||||
Phase 20.1 is complete.
|
||||
|
||||
Fruix now validates a real booted FreeBSD system path where:
|
||||
|
||||
- the running system remains lean and runtime-focused
|
||||
- native development artifacts are exposed separately and explicitly
|
||||
- the guest can compile code directly with the Fruix-provided toolchain
|
||||
- the guest can also drive a simple `bsd.prog.mk` build using the exported development environment
|
||||
|
||||
This is enough to say that Fruix can host a controlled native FreeBSD base-development environment, without yet claiming full self-hosting.
|
||||
|
||||
## Next step
|
||||
|
||||
Per `docs/PLAN_4.md`, the next planned step is:
|
||||
|
||||
- **Phase 20.2** — run host-initiated native base builds inside a Fruix-managed environment
|
||||
@@ -1,6 +1,6 @@
|
||||
# Fruix system deployment workflow
|
||||
|
||||
Date: 2026-04-04
|
||||
Date: 2026-04-05
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -197,6 +197,32 @@ Important current limitation:
|
||||
- `fruix system switch` does **not** yet fetch or copy the candidate closure onto the target for you
|
||||
- it assumes the selected closure is already present in the installed system's `/frx/store`
|
||||
|
||||
### In-guest development environment
|
||||
|
||||
Opt-in systems can also expose a separate development overlay under:
|
||||
|
||||
- `/run/current-system/development-profile`
|
||||
- `/run/current-development`
|
||||
|
||||
Those systems now ship a helper at:
|
||||
|
||||
- `/usr/local/bin/fruix-development-environment`
|
||||
|
||||
Intended use:
|
||||
|
||||
```sh
|
||||
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:
|
||||
|
||||
- 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`
|
||||
|
||||
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.
|
||||
|
||||
## Deployment patterns
|
||||
|
||||
### 1. Build-first workflow
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
operating-system-kernel
|
||||
operating-system-bootloader
|
||||
operating-system-base-packages
|
||||
operating-system-development-packages
|
||||
operating-system-users
|
||||
operating-system-groups
|
||||
operating-system-file-systems
|
||||
|
||||
@@ -79,11 +79,16 @@
|
||||
(kernel-package (operating-system-kernel os))
|
||||
(bootloader-package (operating-system-bootloader os))
|
||||
(base-packages (operating-system-base-packages os))
|
||||
(development-packages (operating-system-development-packages os))
|
||||
(kernel-store (materialize-freebsd-package kernel-package store-dir cache source-cache))
|
||||
(bootloader-store (materialize-freebsd-package bootloader-package store-dir cache source-cache))
|
||||
(base-package-stores (map (lambda (package)
|
||||
(materialize-freebsd-package package store-dir cache source-cache))
|
||||
base-packages))
|
||||
(development-package-stores
|
||||
(map (lambda (package)
|
||||
(materialize-freebsd-package package store-dir cache source-cache))
|
||||
development-packages))
|
||||
(base-package-pairs (map cons base-packages base-package-stores))
|
||||
(store-classification
|
||||
(append (list (cons kernel-package kernel-store)
|
||||
@@ -148,6 +153,8 @@
|
||||
(host-base-stores . ,host-base-stores)
|
||||
(native-base-store-count . ,(length native-base-stores))
|
||||
(native-base-stores . ,native-base-stores)
|
||||
(development-package-store-count . ,(length development-package-stores))
|
||||
(development-package-stores . ,development-package-stores)
|
||||
(fruix-runtime-store-count . ,(length fruix-runtime-stores))
|
||||
(fruix-runtime-stores . ,fruix-runtime-stores)
|
||||
(host-base-replacement-order . ,%freebsd-host-staged-replacement-order)
|
||||
@@ -161,7 +168,12 @@
|
||||
. ,(render-activation-rc-script))
|
||||
("usr/local/etc/rc.d/fruix-shepherd"
|
||||
. ,(render-rc-script shepherd-store guile-store guile-extra-store)))))
|
||||
(references (delete-duplicates (append materialized-source-stores host-base-stores native-base-stores fruix-runtime-stores)))
|
||||
(references (delete-duplicates
|
||||
(append materialized-source-stores
|
||||
host-base-stores
|
||||
native-base-stores
|
||||
development-package-stores
|
||||
fruix-runtime-stores)))
|
||||
(manifest (string-append
|
||||
"closure-spec=\n"
|
||||
(object->string (operating-system-closure-spec os))
|
||||
@@ -175,7 +187,9 @@
|
||||
(display-name (string-append "fruix-system-"
|
||||
(operating-system-host-name os)))
|
||||
(closure-path (make-store-path store-dir display-name manifest
|
||||
#:kind 'operating-system)))
|
||||
#:kind 'operating-system))
|
||||
(development-profile-path (and (not (null? development-package-stores))
|
||||
(string-append closure-path "/development-profile"))))
|
||||
(unless (file-exists? closure-path)
|
||||
(mkdir-p closure-path)
|
||||
(mkdir-p (string-append closure-path "/boot/kernel"))
|
||||
@@ -193,6 +207,11 @@
|
||||
(for-each (lambda (output)
|
||||
(merge-output-into-tree output (string-append closure-path "/profile")))
|
||||
base-package-stores)
|
||||
(when development-profile-path
|
||||
(mkdir-p development-profile-path)
|
||||
(for-each (lambda (output)
|
||||
(merge-output-into-tree output development-profile-path))
|
||||
development-package-stores))
|
||||
(for-each
|
||||
(lambda (entry)
|
||||
(write-file (string-append closure-path "/" (car entry)) (cdr entry)))
|
||||
@@ -204,6 +223,8 @@
|
||||
(chmod (string-append closure-path "/usr/local/etc/rc.d/fruix-shepherd") #o555)
|
||||
(when (file-exists? (string-append closure-path "/usr/local/bin/fruix"))
|
||||
(chmod (string-append closure-path "/usr/local/bin/fruix") #o555))
|
||||
(when (file-exists? (string-append closure-path "/usr/local/bin/fruix-development-environment"))
|
||||
(chmod (string-append closure-path "/usr/local/bin/fruix-development-environment") #o555))
|
||||
(when (file-exists? (string-append closure-path "/boot/fruix-pid1"))
|
||||
(chmod (string-append closure-path "/boot/fruix-pid1") #o555))
|
||||
(write-file (string-append closure-path "/parameters.scm")
|
||||
@@ -218,6 +239,8 @@
|
||||
(guile-extra-store . ,guile-extra-store)
|
||||
(shepherd-store . ,shepherd-store)
|
||||
(base-package-stores . ,base-package-stores)
|
||||
(development-package-stores . ,development-package-stores)
|
||||
(development-profile-path . ,development-profile-path)
|
||||
(host-base-stores . ,host-base-stores)
|
||||
(native-base-stores . ,native-base-stores)
|
||||
(fruix-runtime-stores . ,fruix-runtime-stores)
|
||||
@@ -351,6 +374,12 @@
|
||||
'("kernel" "loader" "loader.efi" "device.hints" "defaults" "lua" "loader.conf"))
|
||||
(symlink-force "/run/current-system/usr/local/bin/fruix"
|
||||
(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")))
|
||||
(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")))
|
||||
(symlink-force "/run/current-system/usr/local/etc/rc.d/fruix-activate"
|
||||
(string-append rootfs "/usr/local/etc/rc.d/fruix-activate"))
|
||||
(symlink-force "/run/current-system/usr/local/etc/rc.d/fruix-shepherd"
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
operating-system-kernel
|
||||
operating-system-bootloader
|
||||
operating-system-base-packages
|
||||
operating-system-development-packages
|
||||
operating-system-users
|
||||
operating-system-groups
|
||||
operating-system-file-systems
|
||||
@@ -95,8 +96,8 @@
|
||||
(make-file-system device mount-point type options needed-for-boot?))
|
||||
|
||||
(define-record-type <operating-system>
|
||||
(make-operating-system host-name freebsd-base kernel bootloader base-packages users groups
|
||||
file-systems services loader-entries rc-conf-entries
|
||||
(make-operating-system host-name freebsd-base kernel bootloader base-packages development-packages
|
||||
users groups file-systems services loader-entries rc-conf-entries
|
||||
init-mode ready-marker root-authorized-keys)
|
||||
operating-system?
|
||||
(host-name operating-system-host-name)
|
||||
@@ -104,6 +105,7 @@
|
||||
(kernel operating-system-kernel)
|
||||
(bootloader operating-system-bootloader)
|
||||
(base-packages operating-system-base-packages)
|
||||
(development-packages operating-system-development-packages)
|
||||
(users operating-system-users)
|
||||
(groups operating-system-groups)
|
||||
(file-systems operating-system-file-systems)
|
||||
@@ -120,6 +122,7 @@
|
||||
(kernel freebsd-kernel)
|
||||
(bootloader freebsd-bootloader)
|
||||
(base-packages %freebsd-system-packages)
|
||||
(development-packages '())
|
||||
(users (list (user-account #:name "root"
|
||||
#:uid 0
|
||||
#:group "wheel"
|
||||
@@ -161,8 +164,8 @@
|
||||
(init-mode 'freebsd-init+rc.d-shepherd)
|
||||
(ready-marker "/var/lib/fruix/ready")
|
||||
(root-authorized-keys '()))
|
||||
(make-operating-system host-name freebsd-base kernel bootloader base-packages users groups
|
||||
file-systems services loader-entries rc-conf-entries
|
||||
(make-operating-system host-name freebsd-base kernel bootloader base-packages development-packages
|
||||
users groups file-systems services loader-entries rc-conf-entries
|
||||
init-mode ready-marker root-authorized-keys))
|
||||
|
||||
(define default-minimal-operating-system (operating-system))
|
||||
@@ -231,6 +234,8 @@
|
||||
(define (validate-operating-system os)
|
||||
(let* ((host-name (operating-system-host-name os))
|
||||
(base (operating-system-freebsd-base os))
|
||||
(base-packages (operating-system-base-packages os))
|
||||
(development-packages (operating-system-development-packages os))
|
||||
(users (operating-system-users os))
|
||||
(groups (operating-system-groups os))
|
||||
(file-systems (operating-system-file-systems os))
|
||||
@@ -242,6 +247,10 @@
|
||||
(error "operating-system host-name must not be empty"))
|
||||
(unless (freebsd-base? base)
|
||||
(error "operating-system freebsd-base must be a <freebsd-base> record"))
|
||||
(unless (every freebsd-package? base-packages)
|
||||
(error "operating-system base-packages must be a list of <freebsd-package> records"))
|
||||
(unless (every freebsd-package? development-packages)
|
||||
(error "operating-system development-packages must be a list of <freebsd-package> records"))
|
||||
(validate-freebsd-source (freebsd-base-source base))
|
||||
(let ((dups (duplicate-elements user-names)))
|
||||
(unless (null? dups)
|
||||
@@ -297,6 +306,9 @@
|
||||
"activate"
|
||||
"shepherd/init.scm"
|
||||
"usr/local/bin/fruix")
|
||||
(if (null? (operating-system-development-packages os))
|
||||
'()
|
||||
'("usr/local/bin/fruix-development-environment"))
|
||||
(if (pid1-init-mode? os)
|
||||
'("boot/fruix-pid1")
|
||||
'())
|
||||
@@ -316,6 +328,8 @@
|
||||
(bootloader-package . ,(freebsd-package-name (operating-system-bootloader os)))
|
||||
(base-package-count . ,(length (operating-system-base-packages os)))
|
||||
(base-packages . ,(package-names (operating-system-base-packages os)))
|
||||
(development-package-count . ,(length (operating-system-development-packages os)))
|
||||
(development-packages . ,(package-names (operating-system-development-packages os)))
|
||||
(user-count . ,(length (operating-system-users os)))
|
||||
(users . ,(map user-account-name (operating-system-users os)))
|
||||
(group-count . ,(length (operating-system-groups os)))
|
||||
|
||||
@@ -751,6 +751,40 @@
|
||||
" exit 1\n"
|
||||
" ;;\n"
|
||||
"esac\n"))
|
||||
(define (render-development-environment-script os)
|
||||
(string-append
|
||||
"#!/bin/sh\n"
|
||||
"set -eu\n"
|
||||
"profile=/run/current-system/development-profile\n"
|
||||
"[ -d \"$profile\" ] || {\n"
|
||||
" echo \"fruix-development-environment: development profile is not available\" >&2\n"
|
||||
" exit 1\n"
|
||||
"}\n"
|
||||
"cat <<EOF\n"
|
||||
"export FRUIX_DEVELOPMENT_PROFILE=\"$profile\"\n"
|
||||
"export FRUIX_DEVELOPMENT_INCLUDE=\"$profile/usr/include\"\n"
|
||||
"export FRUIX_DEVELOPMENT_LIB=\"$profile/lib\"\n"
|
||||
"export FRUIX_DEVELOPMENT_SHARE_MK=\"$profile/usr/share/mk\"\n"
|
||||
"export FRUIX_DEVELOPMENT_BIN=\"$profile/bin\"\n"
|
||||
"export FRUIX_DEVELOPMENT_USR_BIN=\"$profile/usr/bin\"\n"
|
||||
"export FRUIX_CC=\"$profile/bin/cc\"\n"
|
||||
"export FRUIX_CXX=\"$profile/bin/c++\"\n"
|
||||
"export FRUIX_AR=\"$profile/bin/ar\"\n"
|
||||
"export FRUIX_RANLIB=\"$profile/bin/ranlib\"\n"
|
||||
"export FRUIX_NM=\"$profile/bin/nm\"\n"
|
||||
"export FRUIX_BMAKE=\"/usr/bin/make\"\n"
|
||||
"export CC=\"$profile/bin/cc\"\n"
|
||||
"export CXX=\"$profile/bin/c++\"\n"
|
||||
"export AR=\"$profile/bin/ar\"\n"
|
||||
"export RANLIB=\"$profile/bin/ranlib\"\n"
|
||||
"export NM=\"$profile/bin/nm\"\n"
|
||||
"export CPPFLAGS=\"-I$profile/usr/include\"\n"
|
||||
"export CFLAGS=\"-I$profile/usr/include\"\n"
|
||||
"export CXXFLAGS=\"-I$profile/usr/include\"\n"
|
||||
"export LDFLAGS=\"-L$profile/lib\"\n"
|
||||
"export MAKEFLAGS=\"-m $profile/usr/share/mk\"\n"
|
||||
"export PATH=\"/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$profile/bin:$profile/sbin:$profile/usr/bin:$profile/usr/sbin\"\n"
|
||||
"EOF\n"))
|
||||
|
||||
|
||||
(define* (operating-system-generated-files os #:key guile-store guile-extra-store shepherd-store)
|
||||
@@ -772,6 +806,10 @@
|
||||
#:shepherd-store shepherd-store))
|
||||
("shepherd/init.scm" . ,(render-shepherd-config os))
|
||||
("usr/local/bin/fruix" . ,(render-installed-system-fruix os)))
|
||||
(if (null? (operating-system-development-packages os))
|
||||
'()
|
||||
`(("usr/local/bin/fruix-development-environment"
|
||||
. ,(render-development-environment-script os))))
|
||||
(if (pid1-init-mode? os)
|
||||
`(("boot/fruix-pid1" . ,(render-pid1-script os shepherd-store guile-store guile-extra-store)))
|
||||
'())
|
||||
|
||||
73
tests/system/phase20-development-operating-system.scm.in
Normal file
73
tests/system/phase20-development-operating-system.scm.in
Normal file
@@ -0,0 +1,73 @@
|
||||
(use-modules (fruix system freebsd)
|
||||
(fruix packages freebsd))
|
||||
|
||||
(define phase20-operating-system
|
||||
(operating-system
|
||||
#:host-name "fruix-freebsd"
|
||||
#:kernel freebsd-native-kernel
|
||||
#:bootloader freebsd-native-bootloader
|
||||
#:base-packages %freebsd-native-system-packages
|
||||
#:development-packages (list freebsd-native-headers
|
||||
freebsd-clang-toolchain)
|
||||
#:groups (list (user-group #:name "wheel" #:gid 0 #:system? #t)
|
||||
(user-group #:name "sshd" #:gid 22 #:system? #t)
|
||||
(user-group #:name "_dhcp" #:gid 65 #:system? #t)
|
||||
(user-group #:name "operator" #:gid 1000 #:system? #f))
|
||||
#:users (list (user-account #:name "root"
|
||||
#:uid 0
|
||||
#:group "wheel"
|
||||
#:comment "Charlie &"
|
||||
#:home "/root"
|
||||
#:shell "/bin/sh"
|
||||
#:system? #t)
|
||||
(user-account #:name "sshd"
|
||||
#:uid 22
|
||||
#:group "sshd"
|
||||
#:comment "Secure Shell Daemon"
|
||||
#:home "/var/empty"
|
||||
#:shell "/usr/sbin/nologin"
|
||||
#:system? #t)
|
||||
(user-account #:name "_dhcp"
|
||||
#:uid 65
|
||||
#:group "_dhcp"
|
||||
#:comment "dhcp programs"
|
||||
#:home "/var/empty"
|
||||
#:shell "/usr/sbin/nologin"
|
||||
#:system? #t)
|
||||
(user-account #:name "operator"
|
||||
#:uid 1000
|
||||
#:group "operator"
|
||||
#:supplementary-groups '("wheel")
|
||||
#:comment "Fruix Operator"
|
||||
#:home "/home/operator"
|
||||
#:shell "/bin/sh"
|
||||
#:system? #f))
|
||||
#:file-systems (list (file-system #:device "/dev/gpt/fruix-root"
|
||||
#:mount-point "/"
|
||||
#:type "ufs"
|
||||
#:options "rw"
|
||||
#:needed-for-boot? #t)
|
||||
(file-system #:device "devfs"
|
||||
#:mount-point "/dev"
|
||||
#:type "devfs"
|
||||
#:options "rw"
|
||||
#:needed-for-boot? #t)
|
||||
(file-system #:device "tmpfs"
|
||||
#:mount-point "/tmp"
|
||||
#:type "tmpfs"
|
||||
#:options "rw,size=64m"))
|
||||
#:services '(shepherd ready-marker sshd)
|
||||
#:loader-entries '(("autoboot_delay" . "1")
|
||||
("boot_multicons" . "YES")
|
||||
("boot_serial" . "YES")
|
||||
("console" . "comconsole,vidconsole"))
|
||||
#:rc-conf-entries '(("clear_tmp_enable" . "NO")
|
||||
("hostid_enable" . "NO")
|
||||
("sendmail_enable" . "NONE")
|
||||
("sshd_enable" . "YES")
|
||||
("ifconfig_xn0" . "SYNCDHCP")
|
||||
("ifconfig_em0" . "SYNCDHCP")
|
||||
("ifconfig_vtnet0" . "SYNCDHCP"))
|
||||
#:init-mode 'shepherd-pid1
|
||||
#:ready-marker "/var/lib/fruix/ready"
|
||||
#:root-authorized-keys '("__ROOT_AUTHORIZED_KEY__")))
|
||||
220
tests/system/run-phase20-development-environment-xcpng.sh
Executable file
220
tests/system/run-phase20-development-environment-xcpng.sh
Executable file
@@ -0,0 +1,220 @@
|
||||
#!/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:-8g}
|
||||
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-development-xcpng.XXXXXX)
|
||||
cleanup=1
|
||||
fi
|
||||
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
|
||||
cleanup=0
|
||||
fi
|
||||
|
||||
inner_metadata=$workdir/phase20-development-inner-metadata.txt
|
||||
metadata_file=$workdir/phase20-development-environment-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-phase11-shepherd-pid1-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")
|
||||
activate_log=$(sed -n 's/^activate_log=//p' "$inner_metadata")
|
||||
|
||||
development_profile_path=$closure_path/development-profile
|
||||
runtime_profile_path=$closure_path/profile
|
||||
development_env_script=$closure_path/usr/local/bin/fruix-development-environment
|
||||
|
||||
[ "$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; }
|
||||
case "$activate_log" in
|
||||
*fruix-activate:done*) : ;;
|
||||
*) echo "activation log does not show success" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
for path in \
|
||||
"$development_profile_path/bin/cc" \
|
||||
"$development_profile_path/bin/c++" \
|
||||
"$development_profile_path/bin/ar" \
|
||||
"$development_profile_path/usr/include/sys/param.h" \
|
||||
"$development_profile_path/usr/share/mk/bsd.prog.mk" \
|
||||
"$development_env_script"
|
||||
do
|
||||
[ -e "$path" ] || {
|
||||
echo "required development environment path missing: $path" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
[ ! -e "$runtime_profile_path/include" ] || { echo "runtime profile unexpectedly contains headers" >&2; exit 1; }
|
||||
[ ! -e "$runtime_profile_path/usr/share/mk" ] || { echo "runtime profile unexpectedly contains /usr/share/mk" >&2; exit 1; }
|
||||
[ ! -e "$runtime_profile_path/bin/cc" ] || { echo "runtime profile unexpectedly contains cc" >&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_dev_metadata=$(ssh -i "$root_ssh_private_key_file" \
|
||||
-o BatchMode=yes \
|
||||
-o StrictHostKeyChecking=no \
|
||||
-o UserKnownHostsFile=/dev/null \
|
||||
-o ConnectTimeout=5 \
|
||||
root@"$guest_ip" 'sh -s' <<'EOF'
|
||||
set -eu
|
||||
[ -x /usr/local/bin/fruix-development-environment ]
|
||||
[ -L /run/current-development ]
|
||||
[ "$(readlink /run/current-development)" = "/run/current-system/development-profile" ]
|
||||
exports=$(/usr/local/bin/fruix-development-environment)
|
||||
printf '%s\n' "$exports" | grep '^export FRUIX_DEVELOPMENT_PROFILE="/run/current-system/development-profile"$' >/dev/null
|
||||
printf '%s\n' "$exports" | grep '^export MAKEFLAGS="-m /run/current-system/development-profile/usr/share/mk"$' >/dev/null
|
||||
eval "$exports"
|
||||
[ -d "$FRUIX_DEVELOPMENT_PROFILE" ]
|
||||
[ -x "$FRUIX_CC" ]
|
||||
[ -x "$FRUIX_CXX" ]
|
||||
[ -x "$FRUIX_AR" ]
|
||||
[ -f "$FRUIX_DEVELOPMENT_INCLUDE/sys/param.h" ]
|
||||
[ -f "$FRUIX_DEVELOPMENT_SHARE_MK/bsd.prog.mk" ]
|
||||
cc_version=$($FRUIX_CC --version | awk 'NR==1 { print; exit }')
|
||||
tmp=/tmp/fruix-phase20-development-env
|
||||
rm -rf "$tmp"
|
||||
mkdir -p "$tmp/direct" "$tmp/mk"
|
||||
cat > "$tmp/direct/hello.c" <<'EOS'
|
||||
#include <stdio.h>
|
||||
int main(void){puts("hello-from-direct-dev-env");return 0;}
|
||||
EOS
|
||||
$FRUIX_CC $CPPFLAGS $LDFLAGS "$tmp/direct/hello.c" -o "$tmp/direct/hello"
|
||||
hello_direct=$($tmp/direct/hello)
|
||||
cat > "$tmp/mk/hello.c" <<'EOS'
|
||||
#include <stdio.h>
|
||||
int main(void){puts("hello-from-make-dev-env");return 0;}
|
||||
EOS
|
||||
cat > "$tmp/mk/Makefile" <<'EOS'
|
||||
PROG=hello
|
||||
SRCS=hello.c
|
||||
NO_MAN=yes
|
||||
.include <bsd.prog.mk>
|
||||
EOS
|
||||
cat > "$tmp/mk/hello.1" <<'EOS'
|
||||
.Dd April 5, 2026
|
||||
.Dt HELLO 1
|
||||
.Sh NAME
|
||||
.Nm hello
|
||||
.Nd phase20 development environment smoke binary
|
||||
EOS
|
||||
cd "$tmp/mk"
|
||||
make clean >/dev/null 2>&1 || true
|
||||
make > "$tmp/mk/make.log" 2>&1
|
||||
hello_make=$(./hello)
|
||||
make_log_tail=$(tail -n 20 "$tmp/mk/make.log" | tr '\n' ' ')
|
||||
exports_flat=$(printf '%s' "$exports" | tr '\n' ' ')
|
||||
printf 'development_profile=%s\n' "$FRUIX_DEVELOPMENT_PROFILE"
|
||||
printf 'cc_version=%s\n' "$cc_version"
|
||||
printf 'hello_direct=%s\n' "$hello_direct"
|
||||
printf 'hello_make=%s\n' "$hello_make"
|
||||
printf 'exports=%s\n' "$exports_flat"
|
||||
printf 'make_log_tail=%s\n' "$make_log_tail"
|
||||
EOF
|
||||
)
|
||||
|
||||
development_profile=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^development_profile=//p')
|
||||
cc_version=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^cc_version=//p')
|
||||
hello_direct=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^hello_direct=//p')
|
||||
hello_make=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^hello_make=//p')
|
||||
development_exports=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^exports=//p')
|
||||
make_log_tail=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^make_log_tail=//p')
|
||||
|
||||
[ "$development_profile" = "/run/current-system/development-profile" ] || {
|
||||
echo "unexpected guest development profile path: $development_profile" >&2
|
||||
exit 1
|
||||
}
|
||||
case "$cc_version" in
|
||||
*"FreeBSD clang version"*) : ;;
|
||||
*) echo "unexpected cc version output: $cc_version" >&2; exit 1 ;;
|
||||
esac
|
||||
[ "$hello_direct" = hello-from-direct-dev-env ] || {
|
||||
echo "unexpected direct compile output: $hello_direct" >&2
|
||||
exit 1
|
||||
}
|
||||
[ "$hello_make" = hello-from-make-dev-env ] || {
|
||||
echo "unexpected make-based compile output: $hello_make" >&2
|
||||
exit 1
|
||||
}
|
||||
case "$development_exports" in
|
||||
*'export FRUIX_CC="/run/current-system/development-profile/bin/cc"'*) : ;;
|
||||
*) echo "development environment exports do not include FRUIX_CC" >&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
|
||||
development_profile_path=$development_profile_path
|
||||
development_env_script=$development_env_script
|
||||
shepherd_pid=$shepherd_pid
|
||||
sshd_status=$sshd_status
|
||||
compat_prefix_shims=$compat_prefix_shims
|
||||
guile_module_smoke=$guile_module_smoke
|
||||
development_profile_guest=$development_profile
|
||||
cc_version=$cc_version
|
||||
hello_direct=$hello_direct
|
||||
hello_make=$hello_make
|
||||
development_exports=$development_exports
|
||||
make_log_tail=$make_log_tail
|
||||
boot_backend=xcp-ng-xo-cli
|
||||
init_mode=shepherd-pid1
|
||||
development_environment=ok
|
||||
EOF
|
||||
|
||||
if [ -n "$metadata_target" ]; then
|
||||
mkdir -p "$(dirname "$metadata_target")"
|
||||
cp "$metadata_file" "$metadata_target"
|
||||
fi
|
||||
|
||||
printf 'PASS phase20-development-environment-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"
|
||||
Reference in New Issue
Block a user