Compare commits

...

5 Commits

25 changed files with 3332 additions and 275 deletions

View File

@@ -1,6 +1,6 @@
# Fruix differences for Guix sysadmins
Date: 2026-04-05
Date: 2026-04-06
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.
@@ -171,8 +171,10 @@ So compared with Guix-on-Linux intuition, Fruix operators should be more explici
This remains the biggest operational gap, but it is no longer a complete gap.
Installed Fruix systems now provide a small in-guest helper:
Installed Fruix systems now provide a larger in-guest helper surface:
- `fruix system build`
- `fruix system reconfigure`
- `fruix system status`
- `fruix system switch /frx/store/...-fruix-system-...`
- `fruix system rollback`
@@ -183,11 +185,14 @@ What this gives you today:
- explicit rollback-generation tracking
- in-place switching between already-staged closures on the installed target
- rollback without reinstalling the whole system image again
- a validated in-node build path that can read the embedded current declaration inputs
- a validated in-node `reconfigure` path that can build a candidate closure locally and stage it as the next generation
What it still does **not** give you yet compared with Guix:
- a mature `reconfigure`-style workflow that builds and stages the new closure from inside the target system
- a mature end-to-end `upgrade` story for advancing declared inputs automatically
- automatic closure transfer/fetch as part of `switch`
- a higher-level `deploy` workflow across multiple machines or targets
- the broader generation-management UX Guix operators expect
So if you come from Guix, assume that Fruix now has:
@@ -195,7 +200,8 @@ So if you come from Guix, assume that Fruix now has:
- strong closure/store semantics
- explicit install artifacts
- explicit generation metadata roots
- a real but still modest installed-system switch/rollback UX
- a real installed-system build/reconfigure/switch/rollback surface
- but not yet the fuller long-term node/deployment UX that Guix users may expect
## 7. Fruix keeps Guix-like store semantics, but not Guix/Nix hash-prefix machinery exactly
@@ -242,37 +248,45 @@ 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
## 8. Fruix can expose separate in-system development and build overlays
For the validated Phase 20.1 path, Fruix can now expose development tooling separately from the main runtime profile.
For the validated Phase 20 path, Fruix now distinguishes three layers instead of two:
- runtime profile
- development profile
- build profile
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`
- development:
- `/run/current-system/development-profile`
- `/run/current-development`
- `/usr/local/bin/fruix-development-environment`
- build:
- `/run/current-system/build-profile`
- `/run/current-build`
- `/usr/local/bin/fruix-build-environment`
- canonical base-build compatibility links:
- `/usr/include -> /run/current-system/build-profile/usr/include`
- `/usr/share/mk -> /run/current-system/build-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
- keep the development helper interactive and convenient
- keep the build helper narrower, more sanitized, and closer to the actual `buildworld` / `buildkernel` contract
- avoid treating a development-heavy or build-heavy 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.
Compared with Guix, this is conceptually similar to keeping development-oriented and build-oriented state separate from the main runtime identity, but Fruix currently expresses it as system-attached overlays rather than through Guix's broader profile/tooling model.
Fruix now also has a narrow guest self-hosted native-build prototype helper at:
Fruix still has the guest self-hosted native-build prototype helper at:
- `/usr/local/bin/fruix-self-hosted-native-build`
That helper does **not** just reuse the whole exported development shell wholesale. The validated prototype had to sanitize development-oriented variables such as `MAKEFLAGS`, `CPPFLAGS`, `CFLAGS`, `CXXFLAGS`, and `LDFLAGS` before `buildworld`, because those are convenient for smaller development tasks but can poison the FreeBSD world/kernel bootstrap path.
The practical Fruix takeaway is:
But that helper now explicitly uses the build helper contract instead of trying to reuse the full interactive development shell. The practical Fruix takeaway is:
- the development overlay makes native base work possible inside the system
- but real guest self-hosted base builds still need their own stricter build contract
- the build overlay gives Fruix a stricter, more reproducible contract for real base builds
Fruix now also makes an explicit distinction between:
@@ -294,6 +308,32 @@ Compared with Guix, this is a more explicit split between:
- a mutable result/staging area for native build execution
- and the immutable store identities that Fruix treats as the real promoted result
Fruix also now models native-build placement more explicitly as an executor choice.
Current executor kinds are:
- `host`
- `ssh-guest`
- `self-hosted`
So instead of treating:
- host-driven builds
- host-initiated guest builds
- guest self-hosted builds
as three unrelated architectural forks, Fruix is moving toward one native-build result model with different executor policies attached to it.
Fruix also now lets system declarations consume a promoted native-build result bundle directly.
That means Fruix can now distinguish more explicitly between:
- a mutable build/result root used during execution
- an immutable promoted native-build identity in `/frx/store`
- and a later system declaration that points at that promoted identity as an input
Compared with Guix, this is still Guix-like in the important sense that the deployed system is identified by immutable store objects, but Fruix is making the FreeBSD native-base result set itself a more explicit first-class declaration input than it was before.
## 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.

View File

@@ -38,6 +38,12 @@ Fruix currently has:
- `/run/current-system/development-profile`
- `/run/current-development`
- `/usr/local/bin/fruix-development-environment`
- a validated separate in-system build environment overlay via:
- `/run/current-system/build-profile`
- `/run/current-build`
- `/usr/local/bin/fruix-build-environment`
- `/usr/include -> /run/current-system/build-profile/usr/include`
- `/usr/share/mk -> /run/current-system/build-profile/usr/share/mk`
- 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`
@@ -53,6 +59,22 @@ Fruix currently has:
- `/frx/store/...-fruix-native-headers-...`
- `/frx/store/...-fruix-native-bootloader-...`
- `/frx/store/...-fruix-native-build-result-...`
- an explicit executor model for native base builds with current executor kinds:
- `host`
- `ssh-guest`
- `self-hosted`
- end-to-end validated staged-result-plus-promotion paths for executor policies:
- `ssh-guest`
- `self-hosted`
- system declarations that can now consume a promoted native-build result bundle directly via:
- `promoted-native-build-result`
- `operating-system-from-promoted-native-build-result`
- a real XCP-ng boot validation of a system materialized from a promoted native-build result identity
- installed systems that now carry their own canonical declaration inputs and bundled Fruix node CLI sources
- a real XCP-ng validation of in-node:
- `fruix system build`
- `fruix system reconfigure`
- `fruix system rollback`
Validated boot modes still are:
@@ -65,48 +87,98 @@ The validated Phase 18 installation work currently uses:
## Latest completed achievement
### 2026-04-05Native base builds promoted into first-class Fruix store objects
### 2026-04-06Fruix now separates interactive development from strict native base-build environment
Fruix now has a validated end-to-end path that treats guest native base-build results as **staged mutable results first**, and then promotes them into **immutable Fruix store identities**.
Fruix now has a more explicit three-layer model for build-capable FreeBSD systems:
- runtime profile
- development profile
- build profile
Highlights:
- guest self-hosted runs still record staged results under:
- `/var/lib/fruix/native-builds/<run-id>`
- `/var/lib/fruix/native-builds/latest`
- those result roots now carry promotion metadata describing:
- executor / executor-version
- closure path
- source store provenance
- build policy
- artifact entries for:
- `world`
- `kernel`
- `headers`
- `bootloader`
- the host can now run:
- `fruix native-build promote RESULT_ROOT`
- promotion creates immutable `/frx/store` objects for:
- `world`
- `kernel`
- `headers`
- `bootloader`
- promotion also creates a result-bundle store object that references those artifact stores
- the validated promotion metadata now makes Fruix-native native-build identity explicit instead of leaving results only as ad hoc files under `/var/lib/fruix/native-builds/...`
- `<operating-system>` now supports separate `build-packages`
- system closures can now materialize both:
- `development-profile`
- `build-profile`
- build-capable systems now expose:
- `/run/current-system/build-profile`
- `/run/current-build`
- `/usr/local/bin/fruix-build-environment`
- canonical compatibility links for native base builds now come from the build profile:
- `/usr/include -> /run/current-system/build-profile/usr/include`
- `/usr/share/mk -> /run/current-system/build-profile/usr/share/mk`
- the new build helper intentionally clears development-shell variables such as:
- `MAKEFLAGS`
- `CPPFLAGS`
- `CFLAGS`
- `CXXFLAGS`
- `LDFLAGS`
- the self-hosted native-build helper now uses this stricter build-helper contract instead of manually reconstructing that sanitization ad hoc
- promotion metadata for native-build results now records `build-profile` explicitly
Validation:
- `PASS phase20-development-environment-xcpng`
- `PASS phase20-self-hosted-native-build-xcpng`
- `PASS phase20-native-build-store-promotion-xcpng`
- `PASS phase20-host-initiated-native-build-xcpng`
- `PASS phase20-host-initiated-native-build-store-promotion-xcpng`
Representative validated metadata included:
- `build_profile_guest=/run/current-system/build-profile`
- `build_profile=/run/current-system/build-profile`
- `helper_version=5`
- `executor_version=5`
Report:
- `docs/reports/postphase20-build-profile-separation-freebsd.md`
- `docs/system-deployment-workflow.md`
- `docs/GUIX_DIFFERENCES.md`
### 2026-04-06 — Installed systems can now build and reconfigure themselves from local declaration state
Fruix-installed systems are now meaningfully closer to real Fruix nodes.
Highlights:
- system closures now carry canonical declaration metadata in:
- `metadata/system-declaration.scm`
- `metadata/system-declaration-info.scm`
- `metadata/system-declaration-system`
- system closures now also carry bundled Fruix node CLI sources in:
- `share/fruix/node/scripts/fruix.scm`
- `share/fruix/node/modules/...`
- `share/fruix/node/guix/guix/build/utils.scm`
- the installed helper at `/usr/local/bin/fruix` now supports:
- `fruix system build`
- `fruix system reconfigure`
- `fruix system status`
- `fruix system switch`
- `fruix system rollback`
- no-argument in-node `build` and `reconfigure` now use the node's own embedded declaration inputs
- in-node Fruix builds now reuse the installed Guile/Shepherd runtime stores already referenced by the system instead of assuming host-only `/tmp/...` build prefixes
- the real XCP-ng validation proved the full installed-node flow:
- boot current system
- build from local declaration state
- build a candidate declaration on-node
- `reconfigure` into that candidate generation
- reboot into the candidate generation
- `rollback`
- reboot back into the original generation
Validation:
- `PASS postphase20-installed-node-build-reconfigure-xcpng`
Reports:
- `docs/reports/postphase20-promoted-native-base-declarations-freebsd.md`
- `docs/reports/postphase20-installed-node-management-freebsd.md`
- `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`
- `docs/reports/phase20-self-hosted-native-builds-freebsd.md`
- `docs/reports/phase20-native-build-store-promotion-freebsd.md`
## Recent major milestones
@@ -134,12 +206,15 @@ Reports:
The next practical follow-up is now clearer:
- unify host-initiated and self-hosted native-build execution behind a shared Fruix executor/result model
- make the same first-class promotion story available regardless of whether the outer loop is host-driven or guest-driven
- decide how much of result import/promotion should remain host-side versus become a more integrated Fruix deployment action
- grow the installed-node command surface from validated `build`/`reconfigure`/`rollback` toward:
- `upgrade`
- `build-base`
- `deploy`
- decide how executor policy, native-base promotion, and installed-node reconfiguration should compose in one operator-facing workflow
- determine how much of native-build request/promotion should remain explicit versus being absorbed into higher-level Fruix node actions
The immediate architectural direction is no longer just “can guest self-hosting work?”
It is now:
- how should Fruix represent native base builds as real first-class objects and workflows across different executors?
- how should Fruix expose real managed-node behavior across declaration inputs, native-base results, generation switching, and deployment actions?

View File

@@ -0,0 +1,202 @@
# Post-Phase 20: native build executor model
Date: 2026-04-06
## Goal
Turn the now-proven native base-build placement options into one Fruix abstraction instead of treating them as unrelated paths.
The desired model is:
- same declared source identity
- same expected artifact kinds
- same staged-result shape
- same promotion/provenance shape
- different executor policy
So the question becomes:
- where should the build run?
and the answer is expressed as executor policy rather than as a separate architecture each time.
## Executor model
Fruix now has an explicit native-build executor model with current executor kinds:
- `host`
- `ssh-guest`
- `self-hosted`
and intended future extension points:
- `jail`
- `remote-builder`
The new executor model is implemented in:
- `modules/fruix/system/freebsd/executor.scm`
It defines a structured executor object that records at least:
- `kind`
- `name`
- `version`
- `properties`
## What changed
### 1. Structured executor metadata
Native-build result metadata is no longer limited to a flat string such as:
- `guest-self-hosted`
Instead, result/promotion objects now carry a structured executor description.
Representative executor objects now look like:
```scheme
((kind . self-hosted)
(name . "guest-self-hosted")
(version . "4")
(properties . (...)))
```
or:
```scheme
((kind . ssh-guest)
(name . "ssh-guest")
(version . "1")
(properties . (...)))
```
Promoted store metadata also now records compatibility fields derived from that executor object:
- `executor-kind`
- `executor-name`
- `executor-version`
### 2. Shared staged-result shape across executors
Both validated executor paths now converge on the same mutable staging layout under:
- `/var/lib/fruix/native-builds/<run-id>`
with promoted artifacts staged under:
- `artifacts/world`
- `artifacts/kernel`
- `artifacts/headers`
- `artifacts/bootloader`
and promotion metadata recorded in:
- `promotion.scm`
The heavy build work remains executor-specific, but the result shape is now shared.
### 3. Shared immutable promotion path
Both validated executor paths now promote through the same command:
```sh
fruix native-build promote RESULT_ROOT
```
That promotion creates immutable store identities for:
- `world`
- `kernel`
- `headers`
- `bootloader`
- result bundle
under `/frx/store/...`.
## Validated executor policies
### `self-hosted`
The self-hosted guest helper now emits the structured executor metadata directly from:
- `/usr/local/bin/fruix-self-hosted-native-build`
Validated self-hosted promotion flow:
- `PASS phase20-self-hosted-native-build-xcpng`
- `PASS phase20-native-build-store-promotion-xcpng`
Representative promoted result:
```text
result_store=/frx/store/0423193b9bd5e652bdb9d94d077e40dfcc3e9e78-fruix-native-build-result-15.0-STABLE-guest-self-hosted
world_store=/frx/store/5f67e95058186147206ff6f5da2243a09212e358-fruix-native-world-15.0-STABLE-guest-self-hosted
kernel_store=/frx/store/3a32797cc187a90e8273f205eababae6246568d9-fruix-native-kernel-15.0-STABLE-guest-self-hosted
headers_store=/frx/store/1d54d9814461f003e91add8cd37e94ac5f3d04ce-fruix-native-headers-15.0-STABLE-guest-self-hosted
bootloader_store=/frx/store/49f4885f0f05a324ac826f2618d4c7a923ca30d2-fruix-native-bootloader-15.0-STABLE-guest-self-hosted
```
### `ssh-guest`
The host-initiated guest path now also stages a shared native-build result root under:
- `/var/lib/fruix/native-builds/<run-id>`
instead of stopping at temporary build directories alone.
Its promotion metadata records executor policy as:
- `kind = ssh-guest`
- `name = ssh-guest`
- `version = 1`
with executor properties including:
- `transport = ssh`
- `orchestrator = host`
- guest addressing / VM identity metadata
Validated host-initiated promotion flow:
- `PASS phase20-host-initiated-native-build-xcpng`
- `PASS phase20-host-initiated-native-build-store-promotion-xcpng`
Representative promoted result:
```text
result_store=/frx/store/ffe44f5d1ba576e1f811ad3fe3a526a242b5c4a5-fruix-native-build-result-15.0-STABLE-ssh-guest
world_store=/frx/store/89c7a71c3df148a1f99b13d57fd6be88243eb2cb-fruix-native-world-15.0-STABLE-ssh-guest
kernel_store=/frx/store/93bac81122022b40438d356146a6854b4ee48513-fruix-native-kernel-15.0-STABLE-ssh-guest
headers_store=/frx/store/dd7f39f526bca4849caf1eaf96ae25d29b43493c-fruix-native-headers-15.0-STABLE-ssh-guest
bootloader_store=/frx/store/78b1c6b0b5c0c2c1549f5f42f3d64b6d9293669b-fruix-native-bootloader-15.0-STABLE-ssh-guest
```
## Important result
The same declared source and artifact contract now works across two different executor policies:
- `ssh-guest`
- `self-hosted`
while preserving the same Fruix-native staging/promotion split:
- mutable result roots under `/var/lib/fruix/native-builds/...`
- immutable promoted identities under `/frx/store/...`
That is the core architectural win of the executor model.
## What is not yet fully unified
The `host` executor kind now exists in the model, but the fully shared staged-result-plus-promotion workflow is not yet wired through the existing host-local native build path.
So the executor model is introduced and real-VM validated across two policies, but not yet uniformly productized across every native-build entry point.
## Result
Fruix now treats native-build placement as executor policy rather than as an architectural fork.
That means the next step is no longer to invent another build path from scratch.
It is to keep extending the same executor/result/promotion model so additional execution policies can plug into the same Fruix-native object story.

View File

@@ -0,0 +1,223 @@
# Post-Phase 20: separate development from native base-build environment
Date: 2026-04-06
## Goal
Tighten the distinction that Phase 20.3 exposed in practice:
- an interactive development shell is not the same thing as a reliable native base-build environment
Fruix now models three layers instead of two:
- runtime profile
- development profile
- build profile
## Why this change was needed
The earlier self-hosted native-build prototype proved that simply reusing the exported development environment for `buildworld` / `buildkernel` was too loose.
Development-oriented exports like:
- `MAKEFLAGS`
- `CPPFLAGS`
- `CFLAGS`
- `CXXFLAGS`
- `LDFLAGS`
are useful for interactive compilation work, but they are the wrong contract for a real FreeBSD world/kernel bootstrap.
Phase 20.3 previously worked around that by manually sanitizing the shell before running the build.
This change makes that separation explicit in the product model instead of keeping it as an ad hoc helper detail.
## Implementation
### New operating-system layer
`modules/fruix/system/freebsd/model.scm` now supports:
- `#:build-packages`
- `operating-system-build-packages`
So a build-capable system can now carry both:
- `development-packages`
- `build-packages`
separately.
### New build profile inside the system closure
When `build-packages` is non-empty, Fruix now materializes:
- `/frx/store/...-fruix-system-.../build-profile`
alongside the existing:
- `/frx/store/...-fruix-system-.../profile`
- `/frx/store/...-fruix-system-.../development-profile`
Store-layout metadata now records both development-package stores and build-package stores explicitly.
### New in-guest build helper
Build-capable systems now ship:
- `/usr/local/bin/fruix-build-environment`
and expose a stable runtime link:
- `/run/current-build -> /run/current-system/build-profile`
That helper intentionally emits a stricter environment contract than the interactive development helper.
It clears development-shell variables such as:
- `MAKEOBJDIRPREFIX`
- `MAKEFLAGS`
- `CC`
- `CXX`
- `AR`
- `RANLIB`
- `NM`
- `CPPFLAGS`
- `CFLAGS`
- `CXXFLAGS`
- `LDFLAGS`
and also clears development-helper exports such as:
- `FRUIX_DEVELOPMENT_PROFILE`
- `FRUIX_CC`
- `FRUIX_CXX`
- `FRUIX_AR`
- `FRUIX_RANLIB`
- `FRUIX_NM`
- `FRUIX_BMAKE`
Then it exports build-specific values including at least:
- `FRUIX_BUILD_PROFILE`
- `FRUIX_BUILD_INCLUDE`
- `FRUIX_BUILD_SHARE_MK`
- `FRUIX_BUILD_BIN`
- `FRUIX_BUILD_USR_BIN`
- `FRUIX_BUILD_CC`
- `FRUIX_BUILD_CXX`
- `FRUIX_BUILD_AR`
- `FRUIX_BUILD_RANLIB`
- `FRUIX_BUILD_NM`
- `FRUIX_BMAKE`
- `PATH`
Intended use:
```sh
eval "$(/usr/local/bin/fruix-build-environment)"
```
### Canonical build compatibility links now come from the build profile
For native base-build compatibility, build-capable systems now expose:
- `/usr/include -> /run/current-system/build-profile/usr/include`
- `/usr/share/mk -> /run/current-system/build-profile/usr/share/mk`
This means the running system can still keep development and build content separate while offering the canonical paths that FreeBSD native build machinery expects.
### Self-hosted native-build helper now uses the build helper contract
`/usr/local/bin/fruix-self-hosted-native-build` now:
- requires `build-profile`
- requires `/usr/local/bin/fruix-build-environment`
- evaluates the build helper contract before `buildworld`
- verifies the canonical compatibility links point at `build-profile`
This replaces the earlier approach where the helper had to reconstruct sanitization manually around a development-oriented environment.
### Promotion metadata now records build profile provenance
Promotion metadata emitted for self-hosted native-build results now records:
- `build-profile`
Promoted artifact/result metadata also now preserves that field.
## Validation
Validated on the approved real XCP-ng path:
- VM `90490f2e-e8fc-4b7a-388e-5c26f0157289`
- VDI `0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
Passing runs:
- `PASS phase20-development-environment-xcpng`
- `PASS phase20-self-hosted-native-build-xcpng`
- `PASS phase20-native-build-store-promotion-xcpng`
- `PASS phase20-host-initiated-native-build-xcpng`
- `PASS phase20-host-initiated-native-build-store-promotion-xcpng`
## Representative validated results
Development/build environment validation:
```text
development_profile_guest=/run/current-system/development-profile
build_profile_guest=/run/current-system/build-profile
development_env_script=/frx/store/...-fruix-system-.../usr/local/bin/fruix-development-environment
build_env_script=/frx/store/...-fruix-system-.../usr/local/bin/fruix-build-environment
development_environment=ok
```
Self-hosted native-build validation:
```text
helper_version=5
executor_kind=self-hosted
executor_name=guest-self-hosted
executor_version=5
build_profile=/run/current-system/build-profile
source_store=/frx/store/12d7704362e95afc2697db63f168b878e082b372-freebsd-source-default
self_hosted_native_build=ok
```
Promotion validation:
```text
executor_kind=self-hosted
executor_name=guest-self-hosted
executor_version=5
result_store=/frx/store/3ce6aefd564bc51f2465dcbb5c261355be4c7076-fruix-native-build-result-15.0-STABLE-guest-self-hosted
native_build_store_promotion=ok
```
Host-initiated path revalidation with the new build-profile split also succeeded:
```text
executor_kind=ssh-guest
executor_name=ssh-guest
executor_version=1
result_store=/frx/store/93a4837d2588acfde2262010b296df9c7b7b367a-fruix-native-build-result-15.0-STABLE-ssh-guest
host_initiated_native_build_store_promotion=ok
```
## Result
Fruix now expresses an important product distinction more honestly:
- development is for interactive work
- build is for a stricter native base-build contract
That reduces special-case cleanup inside the self-hosted helper and gives Fruix a clearer path for future operator-facing commands such as:
- `fruix system build-base`
- `fruix system upgrade`
because the system model now has an explicit place to say:
- what is available for interactive development
- what is available for real native base builds

View File

@@ -0,0 +1,178 @@
# Post-Phase 20: installed systems as real Fruix nodes
Date: 2026-04-06
## Goal
Make a Fruix-installed machine feel like a managed Fruix node instead of only a deployed image with switch/rollback helpers.
The immediate target was not yet the full long-term command surface of:
- `fruix system upgrade`
- `fruix system build-base`
- `fruix system deploy`
but it was enough to cross an important product threshold:
- installed nodes can now remember their own declaration inputs
- installed nodes can build from those inputs locally
- installed nodes can reconfigure themselves into a newly built generation
- installed nodes can still roll back cleanly
## What changed
### Canonical declaration state now ships inside the system closure
Fruix system closures now carry explicit declaration metadata:
- `metadata/system-declaration.scm`
- `metadata/system-declaration-info.scm`
- `metadata/system-declaration-system`
That gives an installed system a canonical local answer to:
- what declaration source produced me?
- what top-level system variable should be used?
This declaration metadata is also recorded through the installed generation layout metadata.
### Bundled Fruix node CLI sources now ship inside the closure
Installed system closures now also carry a self-contained Fruix node CLI source bundle under:
- `share/fruix/node/scripts/fruix.scm`
- `share/fruix/node/modules/...`
- `share/fruix/node/guix/guix/build/utils.scm`
This gives the installed node enough local Fruix/Guix Scheme source to run Fruix system actions from the node itself.
### Installed `fruix` helper gained local build/reconfigure support
The installed helper at:
- `/usr/local/bin/fruix`
now supports:
- `fruix system build`
- `fruix system reconfigure`
- `fruix system status`
- `fruix system switch`
- `fruix system rollback`
Current behavior:
- `fruix system build` with no extra arguments uses:
- `/run/current-system/metadata/system-declaration.scm`
- `/run/current-system/metadata/system-declaration-system`
- `fruix system reconfigure` with no extra arguments builds from that same embedded declaration and then stages a switch to the resulting closure
- both commands can also take an explicit declaration file plus `--system NAME`, using the same general CLI shape as the host-side Fruix frontend
### In-node builds now reuse the installed Fruix runtime stores
A crucial implementation fix was needed here.
An installed node should not try to reconstruct the Guile/Shepherd runtime prefixes from host-side `/tmp/...` build roots or host `/usr/local/lib/...` assumptions.
Instead, in-node Fruix builds now explicitly reuse the installed runtime stores already referenced by the current system closure.
That allows the in-node build path to work on the real installed system rather than depending on build-host-only paths.
## Validation harness
Added:
- `tests/system/postphase20-installed-node-operating-system.scm.in`
- `tests/system/run-postphase20-installed-node-build-reconfigure-xcpng.sh`
This harness validates the new installed-node workflow on the approved real XCP-ng path.
## Validation performed
Approved real XCP-ng path:
- VM `90490f2e-e8fc-4b7a-388e-5c26f0157289`
- VDI `0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
Promoted native-base result consumed by the installed node declaration:
- `/frx/store/ffe44f5d1ba576e1f811ad3fe3a526a242b5c4a5-fruix-native-build-result-15.0-STABLE-ssh-guest`
Validated flow:
1. boot a Fruix-installed node built from the promoted native-base result
2. confirm the installed node exposes:
- embedded declaration metadata
- bundled node CLI sources
3. run in-guest:
- `fruix system build`
using the node's own embedded declaration inputs
4. copy a candidate declaration to the guest
5. run in-guest:
- `fruix system build /root/candidate.scm --system postphase20-installed-node-operating-system`
6. run in-guest:
- `fruix system reconfigure /root/candidate.scm --system postphase20-installed-node-operating-system`
7. reboot and verify the candidate generation boots
8. run in-guest:
- `fruix system rollback`
9. reboot and verify the original generation boots again
Passing run:
- `PASS postphase20-installed-node-build-reconfigure-xcpng`
Representative metadata:
```text
closure_path=/frx/store/cd4e52d8bff348953939401c8623d4189d7c9432-fruix-system-fruix-node-current
current_built_closure=/frx/store/18ee10925a15b48c676463a3359c45ff766e16a0-fruix-system-fruix-node-current
candidate_closure=/frx/store/46fc9631faf556c30a1a5f39f718d5d38a3f6ba8-fruix-system-fruix-node-canary
reconfigure_closure=/frx/store/46fc9631faf556c30a1a5f39f718d5d38a3f6ba8-fruix-system-fruix-node-canary
reconfigure_current_generation=2
reconfigure_current_closure=/frx/store/46fc9631faf556c30a1a5f39f718d5d38a3f6ba8-fruix-system-fruix-node-canary
reconfigure_rollback_generation=1
reconfigure_rollback_closure=/frx/store/cd4e52d8bff348953939401c8623d4189d7c9432-fruix-system-fruix-node-current
candidate_hostname=fruix-node-canary
rollback_current_generation=1
rollback_current_closure=/frx/store/cd4e52d8bff348953939401c8623d4189d7c9432-fruix-system-fruix-node-current
rollback_rollback_generation=2
rollback_rollback_closure=/frx/store/46fc9631faf556c30a1a5f39f718d5d38a3f6ba8-fruix-system-fruix-node-canary
rollback_hostname=fruix-node-current
installed_node_build_reconfigure=ok
```
## Important observation
The no-argument in-node build of the current declaration did not necessarily reproduce the exact original current closure path.
That is expected with the current model because closure metadata still records builder-local provenance such as the current build host context.
So the significant validated fact is not strict bit-for-bit closure reuse.
It is that the installed node can now:
- read its own declaration inputs locally
- build a valid local candidate closure from them
- build a different candidate declaration locally
- reconfigure itself into that candidate generation
- boot the candidate generation
- roll back and boot the prior generation again
## Result
Fruix-installed machines are now meaningfully closer to real Fruix nodes.
They no longer only support:
- `status`
- `switch`
- `rollback`
They now also support a validated local node workflow for:
- reading embedded declaration inputs
- building a local candidate closure
- reconfiguring into that locally built candidate
- rolling back through the same installed generation model
This is the first concrete step toward a fuller operator-facing Fruix node command surface.

View File

@@ -0,0 +1,165 @@
# Post-Phase 20: promoted native base result sets as declaration inputs
Date: 2026-04-06
## Goal
Move Fruix from:
- “the guest built a kernel”
to:
- “Fruix materialized a native-base result set with identity X”
and then prove that a normal Fruix system declaration can consume that promoted result set directly.
Concretely, the desired product behavior was:
1. build runs somewhere
2. result is recorded in a staging/result root
3. Fruix validates the result shape and metadata
4. Fruix promotes it into store objects
5. system declarations can refer to those promoted artifacts
Before this step, Fruix already had validated steps 1 through 4.
This step implemented and validated step 5.
## What changed
### First-class promoted result reference in system declarations
Fruix now has a first-class promoted native-build result reference object.
Current declaration-level entry points are:
- `promoted-native-build-result`
- `operating-system-from-promoted-native-build-result`
A declaration can now point at a promoted native-build result bundle in `/frx/store`, for example:
```scheme
(define promoted
(promoted-native-build-result
#:store-path "/frx/store/...-fruix-native-build-result-..."))
(define os
(operating-system-from-promoted-native-build-result
promoted
...))
```
### Promoted result bundles now drive system composition
From a promoted result bundle, Fruix now derives:
- the effective FreeBSD base metadata used by the system declaration
- kernel package identity from the promoted kernel artifact store
- bootloader package identity from the promoted bootloader artifact store
- base-package identity from the promoted world artifact store
- optional development-package identity from the promoted headers artifact store
This means a system declaration can now be based on a promoted native-build result set rather than only on source-driven base-package reconstruction.
### Closure metadata now records promoted-result provenance explicitly
When a system declaration uses a promoted native-build result set, Fruix now records that explicitly in the resulting closure metadata.
New closure metadata includes:
- `metadata/promoted-native-build-result.scm`
- `metadata/store-layout.scm` entries for:
- promoted result summary
- promoted artifact stores
The system closure also retains the promoted result bundle itself as an explicit reference in:
- `/frx/store/...-fruix-system-.../.references`
So the resulting deployed closure does not merely happen to contain files copied from a promoted artifact; it explicitly records which promoted result set it came from.
## Implementation notes
The key plumbing additions were:
- a promoted native-build result record in:
- `modules/fruix/system/freebsd/model.scm`
- promoted-result readers/helpers in:
- `modules/fruix/system/freebsd/build.scm`
- direct system-construction helper:
- `operating-system-from-promoted-native-build-result`
- support for consuming promoted artifact stores directly when materializing packages
- closure/store-layout metadata wiring in:
- `modules/fruix/system/freebsd/media.scm`
The important product-level effect is that promoted native-build results are no longer only post-build artifacts. They are now declaration inputs.
## Validation harness
Added:
- `tests/system/phase20-promoted-native-base-operating-system.scm.in`
- `tests/system/run-phase20-promoted-native-base-declaration-xcpng.sh`
The harness supports two modes:
- use an explicit `RESULT_STORE=/frx/store/...-fruix-native-build-result-...`
- or, if no result store is supplied, first run the validated host-initiated promotion path and consume that result
## Validation performed
Validated on the approved real XCP-ng path using the promoted `ssh-guest` result bundle:
- result bundle:
- `/frx/store/ffe44f5d1ba576e1f811ad3fe3a526a242b5c4a5-fruix-native-build-result-15.0-STABLE-ssh-guest`
- VM:
- `90490f2e-e8fc-4b7a-388e-5c26f0157289`
- VDI:
- `0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
Passing run:
- `PASS phase20-promoted-native-base-declaration-xcpng`
Representative metadata:
```text
closure_path=/frx/store/ac1e6dcfe67d3cde49d4fd5da97740f6244276b4-fruix-system-fruix-freebsd
result_store=/frx/store/ffe44f5d1ba576e1f811ad3fe3a526a242b5c4a5-fruix-native-build-result-15.0-STABLE-ssh-guest
executor_kind=ssh-guest
executor_name=ssh-guest
executor_version=1
world_store=/frx/store/89c7a71c3df148a1f99b13d57fd6be88243eb2cb-fruix-native-world-15.0-STABLE-ssh-guest
kernel_store=/frx/store/93bac81122022b40438d356146a6854b4ee48513-fruix-native-kernel-15.0-STABLE-ssh-guest
headers_store=/frx/store/dd7f39f526bca4849caf1eaf96ae25d29b43493c-fruix-native-headers-15.0-STABLE-ssh-guest
bootloader_store=/frx/store/78b1c6b0b5c0c2c1549f5f42f3d64b6d9293669b-fruix-native-bootloader-15.0-STABLE-ssh-guest
kernel_link=/frx/store/93bac81122022b40438d356146a6854b4ee48513-fruix-native-kernel-15.0-STABLE-ssh-guest/boot/kernel/kernel
bootloader_link=/frx/store/78b1c6b0b5c0c2c1549f5f42f3d64b6d9293669b-fruix-native-bootloader-15.0-STABLE-ssh-guest/boot/loader.efi
guest_kernel_link=/frx/store/93bac81122022b40438d356146a6854b4ee48513-fruix-native-kernel-15.0-STABLE-ssh-guest/boot/kernel/kernel
guest_bootloader_link=/frx/store/78b1c6b0b5c0c2c1549f5f42f3d64b6d9293669b-fruix-native-bootloader-15.0-STABLE-ssh-guest/boot/loader.efi
guest_uname=FreeBSD 15.0-STABLE
promoted_native_base_declaration=ok
```
## Validated facts
The real-VM validation proved that Fruix can now:
- consume a promoted native-build result bundle directly from a declaration
- materialize a normal system closure from that promoted identity
- retain explicit promoted-result provenance in closure metadata
- boot the resulting system successfully on the approved XCP-ng path
- keep the closure's kernel and bootloader linked directly to the promoted artifact stores
- preserve the promoted result-bundle store itself as an explicit closure reference
So the operator-facing story is now materially better:
- not only “a build happened somewhere”
- but “this system declaration is based on promoted native-base result X”
## Result
Promoted native-build result sets are now first-class Fruix declaration inputs.
That is the point where the native FreeBSD base path stops being only a build/promotion harness and starts acting more like real product behavior.

View File

@@ -1,6 +1,6 @@
# Fruix system deployment workflow
Date: 2026-04-05
Date: 2026-04-06
## Purpose
@@ -178,12 +178,32 @@ Installed Fruix systems now also ship a small in-guest deployment helper at:
Current validated in-guest commands are:
```sh
fruix system build
fruix system reconfigure
fruix system status
fruix system switch /frx/store/...-fruix-system-...
fruix system rollback
```
Current intended usage:
Installed systems now carry canonical declaration state in:
- `/run/current-system/metadata/system-declaration.scm`
- `/run/current-system/metadata/system-declaration-info.scm`
- `/run/current-system/metadata/system-declaration-system`
So the in-guest helper can now build from the node's own embedded declaration inputs.
Current validated build/reconfigure behavior is:
- `fruix system build`
- with no extra arguments, builds from the embedded current declaration
- `fruix system reconfigure`
- with no extra arguments, builds from the embedded current declaration and stages a switch to the resulting closure
- both commands can also take an explicit declaration file plus `--system NAME`
Current intended usage now has two validated patterns.
### Pattern A: build elsewhere, then switch/rollback locally
1. build a candidate closure on the operator side with `./bin/fruix system build`
2. ensure that candidate closure is present on the installed target's `/frx/store`
@@ -192,21 +212,56 @@ Current intended usage:
5. if needed, run `fruix system rollback`
6. reboot back into the recorded rollback generation
Important current limitation:
Important current limitation of this lower-level pattern:
- `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
### Pattern B: build and reconfigure from the node itself
Opt-in systems can also expose a separate development overlay under:
1. inspect or edit the node declaration inputs
- embedded current declaration, or
- an explicit replacement declaration file
2. run:
- `/run/current-system/development-profile`
- `/run/current-development`
```sh
fruix system build
```
Those systems now ship a helper at:
or:
- `/usr/local/bin/fruix-development-environment`
```sh
fruix system build /path/to/candidate.scm --system my-operating-system
```
3. stage a local generation update with:
```sh
fruix system reconfigure
```
or:
```sh
fruix system reconfigure /path/to/candidate.scm --system my-operating-system
```
4. reboot into the staged generation
5. if needed, run `fruix system rollback`
6. reboot back into the recorded prior generation
### In-guest development and build environments
Opt-in systems can now expose two separate overlays above the main runtime profile:
- development:
- `/run/current-system/development-profile`
- `/run/current-development`
- `/usr/local/bin/fruix-development-environment`
- build:
- `/run/current-system/build-profile`
- `/run/current-build`
- `/usr/local/bin/fruix-build-environment`
Intended use:
@@ -214,19 +269,53 @@ 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 path currently uses this to expose at least:
for interactive development work, and:
```sh
eval "$(/usr/local/bin/fruix-build-environment)"
```
for a narrower native base-build contract.
The current split is:
- runtime profile
- development profile
- build profile
The development helper remains intentionally interactive and currently exposes 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:
The build helper is intentionally more sanitized and less interactive. It clears development-shell variables such as:
- `/usr/include -> /run/current-system/development-profile/usr/include`
- `/usr/share/mk -> /run/current-system/development-profile/usr/share/mk`
- `MAKEFLAGS`
- `CPPFLAGS`
- `CFLAGS`
- `CXXFLAGS`
- `LDFLAGS`
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.
and then exposes build-oriented paths such as:
- `FRUIX_BUILD_PROFILE`
- `FRUIX_BUILD_INCLUDE`
- `FRUIX_BUILD_SHARE_MK`
- `FRUIX_BUILD_CC`
- `FRUIX_BUILD_CXX`
- `FRUIX_BUILD_AR`
- `FRUIX_BUILD_RANLIB`
- `FRUIX_BUILD_NM`
- `FRUIX_BMAKE`
For native base-build compatibility, build-enabled systems now expose canonical links at:
- `/usr/include -> /run/current-system/build-profile/usr/include`
- `/usr/share/mk -> /run/current-system/build-profile/usr/share/mk`
So Fruix now separates interactive development support from the stricter environment used for `buildworld` / `buildkernel` style work, instead of treating them as one overlay.
### Host-initiated native base builds inside a Fruix-managed guest
@@ -274,7 +363,7 @@ FRUIX_SELF_HOSTED_NATIVE_BUILD_JOBS=8 \
That helper:
1. verifies the development overlay and canonical compatibility links
1. evaluates the build helper and verifies the build overlay plus canonical compatibility links
2. recovers the materialized FreeBSD source store from:
- `/run/current-system/metadata/store-layout.scm`
3. runs the native FreeBSD build/install phases inside the guest
@@ -291,8 +380,9 @@ That helper:
Important current detail:
- the self-hosted helper intentionally **sanitizes** development-shell exports such as `MAKEFLAGS`, `CPPFLAGS`, `CFLAGS`, `CXXFLAGS`, and `LDFLAGS` before `buildworld`
- directly reusing the full development-shell environment polluted FreeBSD's bootstrap path and was not reliable enough for real world/kernel builds
- the self-hosted helper now uses the separate `fruix-build-environment` contract instead of reusing the interactive development helper wholesale
- that build helper intentionally clears development-shell exports such as `MAKEFLAGS`, `CPPFLAGS`, `CFLAGS`, `CXXFLAGS`, and `LDFLAGS` before `buildworld`
- this keeps the base-build path closer to the exact contract needed for real world/kernel bootstrap work
So the validated Phase 20.3 answer is:
@@ -337,7 +427,7 @@ Current metadata split:
The promoted store objects record explicit Fruix-native metadata including at least:
- executor / executor-version
- executor kind / name / version
- run-id / guest-host-name
- closure path
- source store provenance
@@ -353,6 +443,87 @@ This is the current Fruix-native answer to the question:
- where should immutable native-build identity live?
- `/frx/store/...`
### Using promoted native-build results in system declarations
Fruix system declarations can now refer directly to a promoted native-build result bundle.
Current declaration-level helpers are:
- `promoted-native-build-result`
- `operating-system-from-promoted-native-build-result`
Representative pattern:
```scheme
(define promoted
(promoted-native-build-result
#:store-path "/frx/store/...-fruix-native-build-result-..."))
(define os
(operating-system-from-promoted-native-build-result
promoted
#:host-name "fruix-freebsd"
...))
```
That now gives Fruix a more product-like story:
1. a build runs under some executor policy
2. Fruix records the staged mutable result
3. Fruix promotes it into immutable store identities
4. a later system declaration can point at that promoted result identity
5. Fruix materializes and boots a normal system from that promoted identity
The resulting closure now records that provenance explicitly through:
- `metadata/promoted-native-build-result.scm`
- `metadata/store-layout.scm`
- closure references that retain the selected result-bundle store path
So the operator-facing statement is now:
- “this Fruix system is based on promoted native-base result X”
not only:
- “some earlier build happened and its files were copied somewhere.”
### Native-build executor model
Fruix now has an explicit executor model for native base builds.
Current executor kinds are:
- `host`
- `ssh-guest`
- `self-hosted`
and the intended future extension points are:
- `jail`
- `remote-builder`
The important change is architectural:
- declared source identity stays the same
- expected artifact kinds stay the same
- result/promotion metadata shape stays the same
- only the executor policy changes
So “where the build runs” is now treated as executor policy rather than as a separate native-build architecture each time.
Current end-to-end validated executors for the staged-result-plus-promotion model are:
- `ssh-guest`
- `self-hosted`
Both now converge on the same Fruix-native flow:
1. run the build under a selected executor
2. stage a result root under `/var/lib/fruix/native-builds/...`
3. emit the same promotion/provenance shape
4. promote the result into immutable `/frx/store/...` objects
## Deployment patterns
### 1. Build-first workflow

View File

@@ -1,6 +1,7 @@
(define-module (fruix system freebsd)
#:use-module (fruix system freebsd model)
#:use-module (fruix system freebsd source)
#:use-module (fruix system freebsd executor)
#:use-module (fruix system freebsd build)
#:use-module (fruix system freebsd media)
#:re-export (user-group
@@ -25,14 +26,21 @@
file-system-type
file-system-options
file-system-needed-for-boot?
promoted-native-build-result?
promoted-native-build-result-store-path
promoted-native-build-result-metadata-file
promoted-native-build-result-metadata
promoted-native-build-result-spec
operating-system
operating-system?
operating-system-host-name
operating-system-freebsd-base
operating-system-native-build-result
operating-system-kernel
operating-system-bootloader
operating-system-base-packages
operating-system-development-packages
operating-system-build-packages
operating-system-users
operating-system-groups
operating-system-file-systems
@@ -44,6 +52,25 @@
operating-system-root-authorized-keys
validate-operating-system
materialize-freebsd-source
native-build-executor
native-build-executor?
native-build-executor-ref
native-build-executor-kind
native-build-executor-name
native-build-executor-version
native-build-executor-properties
normalize-native-build-executor
host-native-build-executor
ssh-guest-native-build-executor
self-hosted-native-build-executor
promoted-native-build-result
promoted-native-build-result->freebsd-base
promoted-native-build-result-artifact-store
promoted-native-build-result-kernel-package
promoted-native-build-result-bootloader-package
promoted-native-build-result-base-packages
promoted-native-build-result-development-packages
operating-system-from-promoted-native-build-result
promote-native-build-result
operating-system-closure-spec
operating-system-install-spec

View File

@@ -2,6 +2,7 @@
#:use-module (fruix packages freebsd)
#:use-module (fruix system freebsd model)
#:use-module (fruix system freebsd source)
#:use-module (fruix system freebsd executor)
#:use-module (fruix system freebsd utils)
#:use-module (guix build utils)
#:use-module (ice-9 format)
@@ -10,6 +11,14 @@
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-13)
#:export (host-freebsd-provenance
promoted-native-build-result
promoted-native-build-result->freebsd-base
promoted-native-build-result-artifact-store
promoted-native-build-result-kernel-package
promoted-native-build-result-bootloader-package
promoted-native-build-result-base-packages
promoted-native-build-result-development-packages
operating-system-from-promoted-native-build-result
materialize-freebsd-package
promote-native-build-result
materialize-prefix))
@@ -340,53 +349,55 @@
(append overrides plan)))
(define* (materialize-freebsd-package package store-dir cache #:optional source-cache)
(let* ((source-cache (or source-cache (make-hash-table)))
(input-paths (map (lambda (input)
(materialize-freebsd-package input store-dir cache source-cache))
(freebsd-package-inputs package)))
(prepared-package
(if (freebsd-native-build-package? package)
(let* ((source (plan-freebsd-source (freebsd-package-install-plan package)))
(source-result (materialize-freebsd-source/cached source store-dir source-cache))
(plan (plan-with-materialized-source (freebsd-package-install-plan package)
source-result)))
(package-with-install-plan package plan))
package))
(effective-input-paths
(if (freebsd-native-build-package? package)
(cons (build-plan-ref (freebsd-package-install-plan prepared-package)
'materialized-source-store
#f)
input-paths)
input-paths))
(effective-input-paths (filter identity effective-input-paths))
(manifest (package-manifest-string prepared-package effective-input-paths))
(cache-key (sha256-string manifest))
(cached (hash-ref cache cache-key #f)))
(if cached
cached
(let* ((display-name (string-append (freebsd-package-name prepared-package)
"-"
(freebsd-package-version prepared-package)))
(output-path (make-store-path store-dir display-name manifest
#:kind 'freebsd-package)))
(unless (file-exists? output-path)
(case (freebsd-package-build-system prepared-package)
((copy-build-system)
(mkdir-p output-path)
(for-each (lambda (entry)
(materialize-plan-entry output-path entry))
(freebsd-package-install-plan prepared-package))
(write-file (string-append output-path "/.references")
(string-join effective-input-paths "\n"))
(write-file (string-append output-path "/.fruix-package") manifest))
((freebsd-world-build-system freebsd-kernel-build-system)
(materialize-native-freebsd-package prepared-package effective-input-paths manifest output-path))
(else
(error (format #f "unsupported package build system: ~a"
(freebsd-package-build-system prepared-package))))))
(hash-set! cache cache-key output-path)
output-path))))
(if (existing-store-package? package)
(validate-existing-store-package package)
(let* ((source-cache (or source-cache (make-hash-table)))
(input-paths (map (lambda (input)
(materialize-freebsd-package input store-dir cache source-cache))
(freebsd-package-inputs package)))
(prepared-package
(if (freebsd-native-build-package? package)
(let* ((source (plan-freebsd-source (freebsd-package-install-plan package)))
(source-result (materialize-freebsd-source/cached source store-dir source-cache))
(plan (plan-with-materialized-source (freebsd-package-install-plan package)
source-result)))
(package-with-install-plan package plan))
package))
(effective-input-paths
(if (freebsd-native-build-package? package)
(cons (build-plan-ref (freebsd-package-install-plan prepared-package)
'materialized-source-store
#f)
input-paths)
input-paths))
(effective-input-paths (filter identity effective-input-paths))
(manifest (package-manifest-string prepared-package effective-input-paths))
(cache-key (sha256-string manifest))
(cached (hash-ref cache cache-key #f)))
(if cached
cached
(let* ((display-name (string-append (freebsd-package-name prepared-package)
"-"
(freebsd-package-version prepared-package)))
(output-path (make-store-path store-dir display-name manifest
#:kind 'freebsd-package)))
(unless (file-exists? output-path)
(case (freebsd-package-build-system prepared-package)
((copy-build-system)
(mkdir-p output-path)
(for-each (lambda (entry)
(materialize-plan-entry output-path entry))
(freebsd-package-install-plan prepared-package))
(write-file (string-append output-path "/.references")
(string-join effective-input-paths "\n"))
(write-file (string-append output-path "/.fruix-package") manifest))
((freebsd-world-build-system freebsd-kernel-build-system)
(materialize-native-freebsd-package prepared-package effective-input-paths manifest output-path))
(else
(error (format #f "unsupported package build system: ~a"
(freebsd-package-build-system prepared-package))))))
(hash-set! cache cache-key output-path)
output-path)))))
(define native-build-result-promotion-version "1")
@@ -396,6 +407,32 @@
((_ . value) value)
(#f default)))
(define (native-build-result-executor result)
(let* ((executor (native-build-result-ref result 'executor #f))
(legacy-version (native-build-result-ref result 'executor-version "legacy")))
(cond
((native-build-executor? executor)
executor)
((string? executor)
(let ((normalized (normalize-native-build-executor executor)))
`((kind . ,(native-build-executor-kind normalized))
(name . ,(native-build-executor-name normalized))
(version . ,legacy-version)
(properties . ,(native-build-executor-properties normalized)))))
(else
(native-build-executor #:kind 'unknown
#:name "unknown"
#:version legacy-version)))))
(define (native-build-result-executor-kind result)
(native-build-executor-kind (native-build-result-executor result)))
(define (native-build-result-executor-name result)
(native-build-executor-name (native-build-result-executor result)))
(define (native-build-result-executor-version result)
(native-build-executor-version (native-build-result-executor result)))
(define (read-native-build-result result-root)
(let ((promotion-file (string-append result-root "/promotion.scm")))
(unless (file-exists? promotion-file)
@@ -406,6 +443,237 @@
(error "unsupported native build result promotion version" promotion-file))
result)))
(define existing-store-package-build-system 'existing-store-item-build-system)
(define (existing-store-package? package)
(eq? (freebsd-package-build-system package)
existing-store-package-build-system))
(define (existing-store-package-ref package key default)
(build-plan-ref (freebsd-package-install-plan package) key default))
(define (validate-existing-store-package package)
(let* ((store-path (existing-store-package-ref package 'store-path #f))
(required-file (existing-store-package-ref package 'required-file #f))
(metadata-file (existing-store-package-ref package 'metadata-file #f)))
(unless (and (string? store-path) (file-exists? store-path))
(error "existing-store package is missing a valid store path" package store-path))
(when metadata-file
(unless (file-exists? metadata-file)
(error "existing-store package metadata file is missing" package metadata-file)))
(when required-file
(unless (file-exists? (string-append store-path "/" required-file))
(error "existing-store package is missing required file"
package
(string-append store-path "/" required-file))))
store-path))
(define (normalize-promoted-native-build-result value)
(cond
((promoted-native-build-result? value)
value)
((string? value)
(promoted-native-build-result #:store-path value))
(else
(error "expected a promoted native build result or store path" value))))
(define (read-promoted-native-build-artifact-metadata metadata-file)
(unless (file-exists? metadata-file)
(error "promoted native build artifact metadata file is missing" metadata-file))
(let ((metadata (call-with-input-file metadata-file read)))
(unless (equal? (native-build-result-ref metadata 'native-build-object-version #f)
native-build-result-promotion-version)
(error "unsupported promoted native build object version" metadata-file))
(unless (eq? (native-build-result-ref metadata 'object-kind #f) 'artifact)
(error "promoted native build object is not an artifact" metadata-file))
metadata))
(define (promoted-native-build-result-artifact-entry result artifact-kind)
(let* ((metadata (promoted-native-build-result-metadata result))
(artifacts (native-build-result-ref metadata 'artifacts '()))
(entry (find (lambda (item)
(eq? (native-build-result-ref item 'artifact-kind #f)
artifact-kind))
artifacts)))
(unless entry
(error "promoted native build result is missing artifact entry" artifact-kind))
entry))
(define (promoted-native-build-result-artifact-store result artifact-kind)
(let* ((result (normalize-promoted-native-build-result result))
(entry (promoted-native-build-result-artifact-entry result artifact-kind))
(store-path (native-build-result-ref entry 'store-path #f))
(metadata-file (native-build-result-ref entry 'metadata-file #f))
(artifact-metadata (and metadata-file
(read-promoted-native-build-artifact-metadata metadata-file)))
(required-file (and artifact-metadata
(native-build-result-ref artifact-metadata 'required-file #f))))
(unless (and (string? store-path) (file-exists? store-path))
(error "promoted native build result is missing artifact store" artifact-kind store-path))
(when artifact-metadata
(unless (eq? (native-build-result-ref artifact-metadata 'artifact-kind #f)
artifact-kind)
(error "promoted native build artifact metadata kind mismatch"
artifact-kind
metadata-file)))
(when required-file
(unless (file-exists? (string-append store-path "/" required-file))
(error "promoted native build artifact store is missing required file"
artifact-kind
(string-append store-path "/" required-file))))
store-path))
(define* (promoted-native-build-result #:key store-path)
(unless (and (string? store-path) (file-exists? store-path))
(error "promoted native build result store path does not exist" store-path))
(let* ((metadata-file (string-append store-path "/.fruix-native-build-result.scm")))
(unless (file-exists? metadata-file)
(error "promoted native build result store is missing metadata" metadata-file))
(let* ((metadata (call-with-input-file metadata-file read))
(result (make-promoted-native-build-result store-path metadata-file metadata)))
(unless (equal? (native-build-result-ref metadata 'native-build-result-version #f)
native-build-result-promotion-version)
(error "unsupported promoted native build result version" metadata-file))
(unless (eq? (native-build-result-ref metadata 'object-kind #f) 'result-bundle)
(error "promoted native build result store does not contain a result bundle" metadata-file))
(for-each (lambda (artifact-kind)
(promoted-native-build-result-artifact-store result artifact-kind))
'(world kernel headers bootloader))
result)))
(define (promoted-native-build-result->freebsd-base result)
(let* ((result (normalize-promoted-native-build-result result))
(metadata (promoted-native-build-result-metadata result))
(base (native-build-result-ref metadata 'freebsd-base '()))
(source (native-build-result-ref metadata 'source '()))
(source-root (or (native-build-result-ref source 'source-root #f)
(native-build-result-ref base 'source-root #f)
"/usr/src"))
(source-name (string-append "promoted-native-build-result-source-"
(path-basename
(promoted-native-build-result-store-path result))))
(synthetic-source (freebsd-source #:name source-name
#:kind 'local-tree
#:path source-root)))
(freebsd-base #:name (native-build-result-ref base 'name "promoted-native-build-result")
#:version-label (native-build-result-ref base 'version-label "unknown")
#:release (native-build-result-ref base 'release "unknown")
#:branch (native-build-result-ref base 'branch "unknown")
#:source synthetic-source
#:source-root (native-build-result-ref base 'source-root source-root)
#:target (native-build-result-ref base 'target "amd64")
#:target-arch (native-build-result-ref base 'target-arch "amd64")
#:kernconf (native-build-result-ref base 'kernconf "GENERIC"))))
(define (promoted-native-build-result-artifact-package result artifact-kind
package-name synopsis description)
(let* ((result (normalize-promoted-native-build-result result))
(metadata (promoted-native-build-result-metadata result))
(base (native-build-result-ref metadata 'freebsd-base '()))
(entry (promoted-native-build-result-artifact-entry result artifact-kind))
(store-path (promoted-native-build-result-artifact-store result artifact-kind))
(metadata-file (native-build-result-ref entry 'metadata-file #f))
(artifact-metadata (and metadata-file
(read-promoted-native-build-artifact-metadata metadata-file)))
(required-file (and artifact-metadata
(native-build-result-ref artifact-metadata 'required-file #f))))
(freebsd-package
#:name package-name
#:version (native-build-result-ref base 'version-label "unknown")
#:build-system existing-store-package-build-system
#:home-page "https://www.freebsd.org/"
#:synopsis synopsis
#:description description
#:license 'bsd-2
#:install-plan `((store-path . ,store-path)
(metadata-file . ,metadata-file)
(required-file . ,required-file)
(artifact-kind . ,artifact-kind)
(result-store . ,(promoted-native-build-result-store-path result))))))
(define (promoted-native-build-result-world-package result)
(promoted-native-build-result-artifact-package
result
'world
"freebsd-promoted-world"
"Promoted Fruix-native FreeBSD world artifact"
"FreeBSD world artifact imported from a promoted Fruix native-build result bundle."))
(define (promoted-native-build-result-kernel-package result)
(promoted-native-build-result-artifact-package
result
'kernel
"freebsd-promoted-kernel"
"Promoted Fruix-native FreeBSD kernel artifact"
"FreeBSD kernel artifact imported from a promoted Fruix native-build result bundle."))
(define (promoted-native-build-result-bootloader-package result)
(promoted-native-build-result-artifact-package
result
'bootloader
"freebsd-promoted-bootloader"
"Promoted Fruix-native FreeBSD bootloader artifact"
"FreeBSD bootloader artifact imported from a promoted Fruix native-build result bundle."))
(define (promoted-native-build-result-headers-package result)
(promoted-native-build-result-artifact-package
result
'headers
"freebsd-promoted-headers"
"Promoted Fruix-native FreeBSD headers artifact"
"FreeBSD headers artifact imported from a promoted Fruix native-build result bundle."))
(define (promoted-native-build-result-base-packages result)
(list (promoted-native-build-result-world-package result)))
(define (promoted-native-build-result-development-packages result)
(list (promoted-native-build-result-headers-package result)))
(define* (operating-system-from-promoted-native-build-result result
#:key
(host-name #f)
(freebsd-base #f)
(kernel #f)
(bootloader #f)
(base-packages #f)
(development-packages #f)
(users #f)
(groups #f)
(file-systems #f)
(services #f)
(loader-entries #f)
(rc-conf-entries #f)
(init-mode #f)
(ready-marker #f)
(root-authorized-keys #f))
(let* ((result (normalize-promoted-native-build-result result))
(defaults default-minimal-operating-system)
(fallback (lambda (value thunk)
(if (eq? value #f) (thunk) value))))
(operating-system
#:host-name (fallback host-name (lambda () (operating-system-host-name defaults)))
#:freebsd-base (fallback freebsd-base (lambda ()
(promoted-native-build-result->freebsd-base result)))
#:native-build-result result
#:kernel (fallback kernel (lambda ()
(promoted-native-build-result-kernel-package result)))
#:bootloader (fallback bootloader (lambda ()
(promoted-native-build-result-bootloader-package result)))
#:base-packages (fallback base-packages (lambda ()
(promoted-native-build-result-base-packages result)))
#:development-packages (fallback development-packages (lambda ()
(operating-system-development-packages defaults)))
#:users (fallback users (lambda () (operating-system-users defaults)))
#:groups (fallback groups (lambda () (operating-system-groups defaults)))
#:file-systems (fallback file-systems (lambda () (operating-system-file-systems defaults)))
#:services (fallback services (lambda () (operating-system-services defaults)))
#:loader-entries (fallback loader-entries (lambda () (operating-system-loader-entries defaults)))
#:rc-conf-entries (fallback rc-conf-entries (lambda () (operating-system-rc-conf-entries defaults)))
#:init-mode (fallback init-mode (lambda () (operating-system-init-mode defaults)))
#:ready-marker (fallback ready-marker (lambda () (operating-system-ready-marker defaults)))
#:root-authorized-keys (fallback root-authorized-keys (lambda ()
(operating-system-root-authorized-keys defaults))))))
(define (native-build-artifact-entry result artifact-kind)
(let* ((artifacts (native-build-result-ref result 'artifacts '()))
(entry (assoc artifact-kind artifacts)))
@@ -442,25 +710,31 @@
(define (native-build-artifact-display-name result artifact-kind)
(let* ((base (native-build-result-ref result 'freebsd-base '()))
(version-label (native-build-result-ref base 'version-label "unknown"))
(executor (native-build-result-ref result 'executor "unknown")))
(executor-name (native-build-result-executor-name result)))
(string-append "fruix-native-"
(symbol->string artifact-kind)
"-"
version-label
"-"
executor)))
executor-name)))
(define (native-build-promoted-artifact-metadata result artifact-kind content-signature)
(let* ((entry (native-build-artifact-entry result artifact-kind)))
(let* ((entry (native-build-artifact-entry result artifact-kind))
(executor (native-build-result-executor result))
(build-profile (native-build-result-ref result 'build-profile
(native-build-result-ref result 'development-profile ""))))
`((native-build-object-version . ,native-build-result-promotion-version)
(object-kind . artifact)
(artifact-kind . ,artifact-kind)
(executor . ,(native-build-result-ref result 'executor "unknown"))
(executor-version . ,(native-build-result-ref result 'executor-version "unknown"))
(executor . ,executor)
(executor-kind . ,(native-build-result-executor-kind result))
(executor-name . ,(native-build-result-executor-name result))
(executor-version . ,(native-build-result-executor-version result))
(run-id . ,(native-build-result-ref result 'run-id "unknown"))
(guest-host-name . ,(native-build-result-ref result 'guest-host-name "unknown"))
(closure-path . ,(native-build-result-ref result 'closure-path ""))
(development-profile . ,(native-build-result-ref result 'development-profile ""))
(build-profile . ,build-profile)
(freebsd-base . ,(native-build-result-ref result 'freebsd-base '()))
(source . ,(native-build-result-ref result 'source '()))
(build-policy . ,(native-build-result-ref result 'build-policy '()))
@@ -494,28 +768,34 @@
(define (native-build-result-display-name result)
(let* ((base (native-build-result-ref result 'freebsd-base '()))
(version-label (native-build-result-ref base 'version-label "unknown"))
(executor (native-build-result-ref result 'executor "unknown")))
(string-append "fruix-native-build-result-" version-label "-" executor)))
(executor-name (native-build-result-executor-name result)))
(string-append "fruix-native-build-result-" version-label "-" executor-name)))
(define (native-build-promoted-result-object result promoted-artifacts)
`((native-build-result-version . ,native-build-result-promotion-version)
(object-kind . result-bundle)
(executor . ,(native-build-result-ref result 'executor "unknown"))
(executor-version . ,(native-build-result-ref result 'executor-version "unknown"))
(run-id . ,(native-build-result-ref result 'run-id "unknown"))
(guest-host-name . ,(native-build-result-ref result 'guest-host-name "unknown"))
(closure-path . ,(native-build-result-ref result 'closure-path ""))
(development-profile . ,(native-build-result-ref result 'development-profile ""))
(freebsd-base . ,(native-build-result-ref result 'freebsd-base '()))
(source . ,(native-build-result-ref result 'source '()))
(build-policy . ,(native-build-result-ref result 'build-policy '()))
(artifact-count . ,(length promoted-artifacts))
(artifacts . ,(map (lambda (entry)
`((artifact-kind . ,(assoc-ref entry 'artifact-kind))
(store-path . ,(assoc-ref entry 'store-path))
(content-signature . ,(assoc-ref entry 'content-signature))
(metadata-file . ,(assoc-ref entry 'metadata-file))))
promoted-artifacts))))
(let ((executor (native-build-result-executor result))
(build-profile (native-build-result-ref result 'build-profile
(native-build-result-ref result 'development-profile ""))))
`((native-build-result-version . ,native-build-result-promotion-version)
(object-kind . result-bundle)
(executor . ,executor)
(executor-kind . ,(native-build-result-executor-kind result))
(executor-name . ,(native-build-result-executor-name result))
(executor-version . ,(native-build-result-executor-version result))
(run-id . ,(native-build-result-ref result 'run-id "unknown"))
(guest-host-name . ,(native-build-result-ref result 'guest-host-name "unknown"))
(closure-path . ,(native-build-result-ref result 'closure-path ""))
(development-profile . ,(native-build-result-ref result 'development-profile ""))
(build-profile . ,build-profile)
(freebsd-base . ,(native-build-result-ref result 'freebsd-base '()))
(source . ,(native-build-result-ref result 'source '()))
(build-policy . ,(native-build-result-ref result 'build-policy '()))
(artifact-count . ,(length promoted-artifacts))
(artifacts . ,(map (lambda (entry)
`((artifact-kind . ,(assoc-ref entry 'artifact-kind))
(store-path . ,(assoc-ref entry 'store-path))
(content-signature . ,(assoc-ref entry 'content-signature))
(metadata-file . ,(assoc-ref entry 'metadata-file))))
promoted-artifacts)))))
(define* (promote-native-build-result result-root #:key (store-dir "/frx/store"))
(let* ((result (read-native-build-result result-root))
@@ -544,6 +824,9 @@
(write-file (string-append result-store "/.fruix-native-build-result.scm")
payload))
`((result-root . ,result-root)
(executor-kind . ,(native-build-result-executor-kind result))
(executor-name . ,(native-build-result-executor-name result))
(executor-version . ,(native-build-result-executor-version result))
(result-store . ,result-store)
(result-metadata-file . ,(string-append result-store "/.fruix-native-build-result.scm"))
(artifact-store-count . ,(length promoted-artifacts))

View File

@@ -0,0 +1,121 @@
(define-module (fruix system freebsd executor)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:export (native-build-executor-model-version
native-build-executor
native-build-executor?
native-build-executor-ref
native-build-executor-kind
native-build-executor-name
native-build-executor-version
native-build-executor-properties
normalize-native-build-executor
host-native-build-executor
ssh-guest-native-build-executor
self-hosted-native-build-executor))
(define native-build-executor-model-version "1")
(define (association-list? value)
(and (list? value)
(every pair? value)))
(define (executor-name kind provided-name)
(or provided-name
(symbol->string kind)))
(define* (native-build-executor #:key kind name
(version native-build-executor-model-version)
(properties '()))
(unless (symbol? kind)
(error "native build executor kind must be a symbol" kind))
(unless (string? (executor-name kind name))
(error "native build executor name must be a string" name))
(unless (string? version)
(error "native build executor version must be a string" version))
(unless (association-list? properties)
(error "native build executor properties must be an association list" properties))
`((kind . ,kind)
(name . ,(executor-name kind name))
(version . ,version)
(properties . ,properties)))
(define (native-build-executor-ref executor key default)
(match (assoc key executor)
((_ . value) value)
(#f default)))
(define (native-build-executor? value)
(and (association-list? value)
(symbol? (native-build-executor-ref value 'kind #f))
(string? (native-build-executor-ref value 'name #f))
(string? (native-build-executor-ref value 'version #f))
(association-list? (native-build-executor-ref value 'properties '()))))
(define (native-build-executor-kind executor)
(native-build-executor-ref executor 'kind 'unknown))
(define (native-build-executor-name executor)
(native-build-executor-ref executor 'name "unknown"))
(define (native-build-executor-version executor)
(native-build-executor-ref executor 'version "unknown"))
(define (native-build-executor-properties executor)
(native-build-executor-ref executor 'properties '()))
(define (legacy-executor-kind name)
(cond
((member name '("host")) 'host)
((member name '("ssh-guest" "guest-ssh" "guest-host-initiated")) 'ssh-guest)
((member name '("self-hosted" "guest-self-hosted")) 'self-hosted)
((member name '("jail")) 'jail)
((member name '("remote-builder")) 'remote-builder)
(else 'legacy)))
(define (normalize-native-build-executor value)
(cond
((native-build-executor? value)
value)
((string? value)
(native-build-executor #:kind (legacy-executor-kind value)
#:name value
#:version "legacy"))
(else
(error "unsupported native build executor representation" value))))
(define* (host-native-build-executor #:key (name "host")
host-name working-directory)
(native-build-executor
#:kind 'host
#:name name
#:properties (filter-map identity
`((host-name . ,host-name)
(working-directory . ,working-directory)))))
(define* (ssh-guest-native-build-executor #:key (name "ssh-guest")
transport orchestrator
guest-host-name guest-ip vm-id vdi-id)
(native-build-executor
#:kind 'ssh-guest
#:name name
#:properties (filter-map identity
`((transport . ,(or transport "ssh"))
(orchestrator . ,(or orchestrator "host"))
(guest-host-name . ,guest-host-name)
(guest-ip . ,guest-ip)
(vm-id . ,vm-id)
(vdi-id . ,vdi-id)))))
(define* (self-hosted-native-build-executor #:key (name "self-hosted")
helper-path helper-version
guest-host-name build-root-base result-root-base)
(native-build-executor
#:kind 'self-hosted
#:name name
#:version (or helper-version native-build-executor-model-version)
#:properties (filter-map identity
`((helper-path . ,helper-path)
(guest-host-name . ,guest-host-name)
(build-root-base . ,build-root-base)
(result-root-base . ,result-root-base)))))

View File

@@ -72,14 +72,22 @@
(store-dir "/frx/store")
(guile-prefix "/tmp/guile-freebsd-validate-install")
(guile-extra-prefix "/tmp/guile-gnutls-freebsd-validate-install")
(shepherd-prefix "/tmp/shepherd-freebsd-validate-install"))
(shepherd-prefix "/tmp/shepherd-freebsd-validate-install")
(guile-store-path #f)
(guile-extra-store-path #f)
(shepherd-store-path #f)
(declaration-source #f)
(declaration-origin #f)
(declaration-system-symbol #f))
(validate-operating-system os)
(let* ((cache (make-hash-table))
(source-cache (make-hash-table))
(native-build-result (operating-system-native-build-result os))
(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))
(build-packages (operating-system-build-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)
@@ -89,6 +97,10 @@
(map (lambda (package)
(materialize-freebsd-package package store-dir cache source-cache))
development-packages))
(build-package-stores
(map (lambda (package)
(materialize-freebsd-package package store-dir cache source-cache))
build-packages))
(base-package-pairs (map cons base-packages base-package-stores))
(store-classification
(append (list (cons kernel-package kernel-store)
@@ -109,12 +121,15 @@
("/usr/local/lib/libtasn1.so.6" . "lib/libtasn1.so.6")
("/usr/local/lib/libhogweed.so.6" . "lib/libhogweed.so.6")
("/usr/local/lib/libnettle.so.8" . "lib/libnettle.so.8")))
(guile-store (materialize-prefix guile-prefix "fruix-guile-runtime" "3.0" store-dir
#:extra-files guile-runtime-extra-files))
(guile-extra-store (materialize-prefix guile-extra-prefix "fruix-guile-extra" "3.0" store-dir
#:extra-files (append guile-runtime-extra-files
guile-extra-runtime-files)))
(shepherd-store (materialize-prefix shepherd-prefix "fruix-shepherd-runtime" "1.0.9" store-dir))
(guile-store (or guile-store-path
(materialize-prefix guile-prefix "fruix-guile-runtime" "3.0" store-dir
#:extra-files guile-runtime-extra-files)))
(guile-extra-store (or guile-extra-store-path
(materialize-prefix guile-extra-prefix "fruix-guile-extra" "3.0" store-dir
#:extra-files (append guile-runtime-extra-files
guile-extra-runtime-files))))
(shepherd-store (or shepherd-store-path
(materialize-prefix shepherd-prefix "fruix-shepherd-runtime" "1.0.9" store-dir)))
(host-base-stores
(delete-duplicates
(map cdr
@@ -134,31 +149,76 @@
(delete-duplicates (map (lambda (result)
(assoc-ref result 'source-store-path))
source-materializations)))
(promoted-native-build-result-summary
(and native-build-result
(promoted-native-build-result-spec native-build-result)))
(promoted-native-build-result-store
(and native-build-result
(promoted-native-build-result-store-path native-build-result)))
(promoted-native-build-artifact-stores
(delete-duplicates
(filter identity
(if native-build-result
(map (lambda (artifact-kind)
(promoted-native-build-result-artifact-store native-build-result artifact-kind))
'(world kernel headers bootloader))
'()))))
(declaration-source-text
(or declaration-source
";; Fruix declaration source is unavailable for this closure.\n"))
(declaration-origin-text (or declaration-origin ""))
(declaration-system-text
(cond ((symbol? declaration-system-symbol)
(symbol->string declaration-system-symbol))
((string? declaration-system-symbol)
declaration-system-symbol)
(else "")))
(declaration-info-object
`((available? . ,(not (not declaration-source)))
(system-variable . ,declaration-system-text)))
(metadata-files
`(("metadata/freebsd-base.scm"
. ,(object->string (freebsd-base-spec (operating-system-freebsd-base os))))
("metadata/freebsd-source.scm"
. ,(object->string (freebsd-source-spec (freebsd-base-source (operating-system-freebsd-base os)))))
("metadata/freebsd-source-materializations.scm"
. ,(object->string (map freebsd-source-materialization-spec source-materializations)))
("metadata/host-base-provenance.scm"
. ,(object->string (host-freebsd-provenance)))
("metadata/store-layout.scm"
. ,(object->string
`((freebsd-base . ,(freebsd-base-spec (operating-system-freebsd-base os)))
(freebsd-source . ,(freebsd-source-spec (freebsd-base-source (operating-system-freebsd-base os))))
(materialized-source-store-count . ,(length materialized-source-stores))
(materialized-source-stores . ,materialized-source-stores)
(host-base-store-count . ,(length host-base-stores))
(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)
(init-mode . ,(operating-system-init-mode os)))))))
(append
(list (cons "metadata/freebsd-base.scm"
(object->string (freebsd-base-spec (operating-system-freebsd-base os))))
(cons "metadata/freebsd-source.scm"
(object->string (freebsd-source-spec (freebsd-base-source (operating-system-freebsd-base os)))))
(cons "metadata/freebsd-source-materializations.scm"
(object->string (map freebsd-source-materialization-spec source-materializations)))
(cons "metadata/host-base-provenance.scm"
(object->string (host-freebsd-provenance)))
(cons "metadata/system-declaration.scm"
declaration-source-text)
(cons "metadata/system-declaration-info.scm"
(object->string declaration-info-object))
(cons "metadata/system-declaration-system"
(string-append declaration-system-text "\n"))
(cons "metadata/store-layout.scm"
(object->string
`((freebsd-base . ,(freebsd-base-spec (operating-system-freebsd-base os)))
(freebsd-source . ,(freebsd-source-spec (freebsd-base-source (operating-system-freebsd-base os))))
(system-declaration-available? . ,(not (not declaration-source)))
(system-declaration-system-variable . ,declaration-system-text)
(promoted-native-build-result . ,promoted-native-build-result-summary)
(promoted-native-build-artifact-store-count . ,(length promoted-native-build-artifact-stores))
(promoted-native-build-artifact-stores . ,promoted-native-build-artifact-stores)
(materialized-source-store-count . ,(length materialized-source-stores))
(materialized-source-stores . ,materialized-source-stores)
(host-base-store-count . ,(length host-base-stores))
(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)
(build-package-store-count . ,(length build-package-stores))
(build-package-stores . ,build-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)
(init-mode . ,(operating-system-init-mode os))))))
(if promoted-native-build-result-summary
(list (cons "metadata/promoted-native-build-result.scm"
(object->string promoted-native-build-result-summary)))
'())))
(generated-files (append (operating-system-generated-files os
#:guile-store guile-store
#:guile-extra-store guile-extra-store
@@ -169,10 +229,14 @@
("usr/local/etc/rc.d/fruix-shepherd"
. ,(render-rc-script shepherd-store guile-store guile-extra-store)))))
(references (delete-duplicates
(append materialized-source-stores
(append (if promoted-native-build-result-store
(list promoted-native-build-result-store)
'())
materialized-source-stores
host-base-stores
native-base-stores
development-package-stores
build-package-stores
fruix-runtime-stores)))
(manifest (string-append
"closure-spec=\n"
@@ -189,7 +253,9 @@
(closure-path (make-store-path store-dir display-name manifest
#:kind 'operating-system))
(development-profile-path (and (not (null? development-package-stores))
(string-append closure-path "/development-profile"))))
(string-append closure-path "/development-profile")))
(build-profile-path (and (not (null? build-package-stores))
(string-append closure-path "/build-profile"))))
(unless (file-exists? closure-path)
(mkdir-p closure-path)
(mkdir-p (string-append closure-path "/boot/kernel"))
@@ -212,6 +278,11 @@
(for-each (lambda (output)
(merge-output-into-tree output development-profile-path))
development-package-stores))
(when build-profile-path
(mkdir-p build-profile-path)
(for-each (lambda (output)
(merge-output-into-tree output build-profile-path))
build-package-stores))
(for-each
(lambda (entry)
(write-file (string-append closure-path "/" (car entry)) (cdr entry)))
@@ -225,6 +296,8 @@
(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 "/usr/local/bin/fruix-build-environment"))
(chmod (string-append closure-path "/usr/local/bin/fruix-build-environment") #o555))
(when (file-exists? (string-append closure-path "/usr/local/bin/fruix-self-hosted-native-build"))
(chmod (string-append closure-path "/usr/local/bin/fruix-self-hosted-native-build") #o555))
(when (file-exists? (string-append closure-path "/boot/fruix-pid1"))
@@ -242,7 +315,9 @@
(shepherd-store . ,shepherd-store)
(base-package-stores . ,base-package-stores)
(development-package-stores . ,development-package-stores)
(build-package-stores . ,build-package-stores)
(development-profile-path . ,development-profile-path)
(build-profile-path . ,build-profile-path)
(host-base-stores . ,host-base-stores)
(native-base-stores . ,native-base-stores)
(fruix-runtime-stores . ,fruix-runtime-stores)
@@ -251,7 +326,13 @@
(freebsd-source-materializations-file . ,(string-append closure-path "/metadata/freebsd-source-materializations.scm"))
(materialized-source-stores . ,materialized-source-stores)
(host-base-provenance-file . ,(string-append closure-path "/metadata/host-base-provenance.scm"))
(system-declaration-file . ,(string-append closure-path "/metadata/system-declaration.scm"))
(system-declaration-info-file . ,(string-append closure-path "/metadata/system-declaration-info.scm"))
(system-declaration-system-file . ,(string-append closure-path "/metadata/system-declaration-system"))
(store-layout-file . ,(string-append closure-path "/metadata/store-layout.scm"))
(promoted-native-build-result-file
. ,(and promoted-native-build-result-summary
(string-append closure-path "/metadata/promoted-native-build-result.scm")))
(generated-files . ,(map car generated-files))
(references . ,references))))
@@ -280,6 +361,9 @@
(freebsd-source-materializations-file
. ,(string-append closure-path "/metadata/freebsd-source-materializations.scm"))
(host-base-provenance-file . ,(string-append closure-path "/metadata/host-base-provenance.scm"))
(system-declaration-file . ,(string-append closure-path "/metadata/system-declaration.scm"))
(system-declaration-info-file . ,(string-append closure-path "/metadata/system-declaration-info.scm"))
(system-declaration-system-file . ,(string-append closure-path "/metadata/system-declaration-system"))
(store-layout-file . ,(string-append closure-path "/metadata/store-layout.scm"))
(install-metadata-path . ,install-metadata-path)
(install-spec . ,install-spec)))
@@ -292,6 +376,9 @@
(freebsd-source-materializations-file
. ,(string-append closure-path "/metadata/freebsd-source-materializations.scm"))
(host-base-provenance-file . ,(string-append closure-path "/metadata/host-base-provenance.scm"))
(system-declaration-file . ,(string-append closure-path "/metadata/system-declaration.scm"))
(system-declaration-info-file . ,(string-append closure-path "/metadata/system-declaration-info.scm"))
(system-declaration-system-file . ,(string-append closure-path "/metadata/system-declaration-system"))
(store-layout-file . ,(string-append closure-path "/metadata/store-layout.scm"))))
(define* (populate-system-generation-layout os rootfs closure-path
@@ -378,7 +465,18 @@
(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 "/build-profile"))
(symlink-force "/run/current-system/build-profile"
(string-append rootfs "/run/current-build"))
(when (file-exists? (string-append closure-path "/build-profile/usr/include"))
(symlink-force "/run/current-system/build-profile/usr/include"
(string-append rootfs "/usr/include")))
(when (file-exists? (string-append closure-path "/build-profile/usr/share/mk"))
(symlink-force "/run/current-system/build-profile/usr/share/mk"
(string-append rootfs "/usr/share/mk"))))
(when (and (not (file-exists? (string-append closure-path "/build-profile")))
(file-exists? (string-append closure-path "/development-profile")))
(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")))
@@ -388,6 +486,9 @@
(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")))
(when (file-exists? (string-append closure-path "/usr/local/bin/fruix-build-environment"))
(symlink-force "/run/current-system/usr/local/bin/fruix-build-environment"
(string-append rootfs "/usr/local/bin/fruix-build-environment")))
(when (file-exists? (string-append closure-path "/usr/local/bin/fruix-self-hosted-native-build"))
(symlink-force "/run/current-system/usr/local/bin/fruix-self-hosted-native-build"
(string-append rootfs "/usr/local/bin/fruix-self-hosted-native-build")))
@@ -408,12 +509,18 @@
(store-dir "/frx/store")
(guile-prefix "/tmp/guile-freebsd-validate-install")
(guile-extra-prefix "/tmp/guile-gnutls-freebsd-validate-install")
(shepherd-prefix "/tmp/shepherd-freebsd-validate-install"))
(shepherd-prefix "/tmp/shepherd-freebsd-validate-install")
(declaration-source #f)
(declaration-origin #f)
(declaration-system-symbol #f))
(let* ((closure (materialize-operating-system os
#:store-dir store-dir
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix))
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin declaration-origin
#:declaration-system-symbol declaration-system-symbol))
(closure-path (assoc-ref closure 'closure-path)))
(populate-rootfs-from-closure os rootfs closure-path)))
@@ -754,6 +861,9 @@
(guile-prefix "/tmp/guile-freebsd-validate-install")
(guile-extra-prefix "/tmp/guile-gnutls-freebsd-validate-install")
(shepherd-prefix "/tmp/shepherd-freebsd-validate-install")
(declaration-source #f)
(declaration-origin #f)
(declaration-system-symbol #f)
(efi-size "64m")
(root-size #f)
(disk-capacity #f)
@@ -766,7 +876,10 @@
#:store-dir store-dir
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix))
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin declaration-origin
#:declaration-system-symbol declaration-system-symbol))
(closure-path (assoc-ref closure 'closure-path))
(store-items (store-reference-closure (list closure-path)))
(target-kind (if (string-prefix? "/dev/" target)
@@ -881,6 +994,9 @@
(guile-prefix "/tmp/guile-freebsd-validate-install")
(guile-extra-prefix "/tmp/guile-gnutls-freebsd-validate-install")
(shepherd-prefix "/tmp/shepherd-freebsd-validate-install")
(declaration-source #f)
(declaration-origin #f)
(declaration-system-symbol #f)
(efi-size "64m")
(root-size "256m")
(disk-capacity #f)
@@ -891,7 +1007,10 @@
#:store-dir store-dir
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix))
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin declaration-origin
#:declaration-system-symbol declaration-system-symbol))
(closure-path (assoc-ref closure 'closure-path))
(image-spec (operating-system-image-spec os
#:efi-size efi-size
@@ -934,7 +1053,10 @@
#:store-dir store-dir
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix)
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin declaration-origin
#:declaration-system-symbol declaration-system-symbol)
(copy-rootfs-for-image rootfs image-rootfs)
(copy-store-items-into-rootfs image-rootfs store-dir store-items)
(mkdir-p (string-append esp-stage "/EFI/BOOT"))
@@ -1002,6 +1124,9 @@
(guile-prefix "/tmp/guile-freebsd-validate-install")
(guile-extra-prefix "/tmp/guile-gnutls-freebsd-validate-install")
(shepherd-prefix "/tmp/shepherd-freebsd-validate-install")
(declaration-source #f)
(declaration-origin #f)
(declaration-system-symbol #f)
(install-target-device "/dev/vtbd1")
(efi-size "64m")
(root-size "10g")
@@ -1020,12 +1145,18 @@
#:store-dir store-dir
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix))
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin declaration-origin
#:declaration-system-symbol declaration-system-symbol))
(installer-closure (materialize-operating-system installer-os
#:store-dir store-dir
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix))
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin declaration-origin
#:declaration-system-symbol declaration-system-symbol))
(target-closure-path (assoc-ref target-closure 'closure-path))
(installer-closure-path (assoc-ref installer-closure 'closure-path))
(target-store-items (store-reference-closure (list target-closure-path)))
@@ -1310,6 +1441,9 @@
(guile-prefix "/tmp/guile-freebsd-validate-install")
(guile-extra-prefix "/tmp/guile-gnutls-freebsd-validate-install")
(shepherd-prefix "/tmp/shepherd-freebsd-validate-install")
(declaration-source #f)
(declaration-origin #f)
(declaration-system-symbol #f)
(install-target-device "/dev/vtbd0")
(root-size #f)
(installer-host-name (string-append (operating-system-host-name os)
@@ -1326,12 +1460,18 @@
#:store-dir store-dir
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix))
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin declaration-origin
#:declaration-system-symbol declaration-system-symbol))
(installer-closure (materialize-operating-system installer-os
#:store-dir store-dir
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix))
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin declaration-origin
#:declaration-system-symbol declaration-system-symbol))
(target-closure-path (assoc-ref target-closure 'closure-path))
(installer-closure-path (assoc-ref installer-closure 'closure-path))
(target-closure-store-items (store-reference-closure (list target-closure-path)))

View File

@@ -26,14 +26,22 @@
file-system-type
file-system-options
file-system-needed-for-boot?
make-promoted-native-build-result
promoted-native-build-result?
promoted-native-build-result-store-path
promoted-native-build-result-metadata-file
promoted-native-build-result-metadata
promoted-native-build-result-spec
operating-system
operating-system?
operating-system-host-name
operating-system-freebsd-base
operating-system-native-build-result
operating-system-kernel
operating-system-bootloader
operating-system-base-packages
operating-system-development-packages
operating-system-build-packages
operating-system-users
operating-system-groups
operating-system-file-systems
@@ -95,17 +103,71 @@
(needed-for-boot? #f))
(make-file-system device mount-point type options needed-for-boot?))
(define-record-type <promoted-native-build-result>
(make-promoted-native-build-result store-path metadata-file metadata)
promoted-native-build-result?
(store-path promoted-native-build-result-store-path)
(metadata-file promoted-native-build-result-metadata-file)
(metadata promoted-native-build-result-metadata))
(define (promoted-native-build-result-metadata-ref metadata key default)
(match (assoc key metadata)
((_ . value) value)
(#f default)))
(define (promoted-native-build-result-artifact-spec metadata artifact-kind)
(find (lambda (entry)
(eq? (promoted-native-build-result-metadata-ref entry 'artifact-kind #f)
artifact-kind))
(promoted-native-build-result-metadata-ref metadata 'artifacts '())))
(define (promoted-native-build-result-spec result)
(let* ((metadata (promoted-native-build-result-metadata result))
(base (promoted-native-build-result-metadata-ref metadata 'freebsd-base '()))
(source (promoted-native-build-result-metadata-ref metadata 'source '())))
`((store-path . ,(promoted-native-build-result-store-path result))
(metadata-file . ,(promoted-native-build-result-metadata-file result))
(executor-kind . ,(promoted-native-build-result-metadata-ref metadata 'executor-kind #f))
(executor-name . ,(promoted-native-build-result-metadata-ref metadata 'executor-name #f))
(executor-version . ,(promoted-native-build-result-metadata-ref metadata 'executor-version #f))
(run-id . ,(promoted-native-build-result-metadata-ref metadata 'run-id #f))
(version-label . ,(promoted-native-build-result-metadata-ref base 'version-label #f))
(release . ,(promoted-native-build-result-metadata-ref base 'release #f))
(branch . ,(promoted-native-build-result-metadata-ref base 'branch #f))
(source-store . ,(promoted-native-build-result-metadata-ref source 'store-path #f))
(source-root . ,(promoted-native-build-result-metadata-ref source 'source-root #f))
(artifact-count . ,(promoted-native-build-result-metadata-ref metadata 'artifact-count 0))
(world-store . ,(promoted-native-build-result-metadata-ref
(promoted-native-build-result-artifact-spec metadata 'world)
'store-path
#f))
(kernel-store . ,(promoted-native-build-result-metadata-ref
(promoted-native-build-result-artifact-spec metadata 'kernel)
'store-path
#f))
(headers-store . ,(promoted-native-build-result-metadata-ref
(promoted-native-build-result-artifact-spec metadata 'headers)
'store-path
#f))
(bootloader-store . ,(promoted-native-build-result-metadata-ref
(promoted-native-build-result-artifact-spec metadata 'bootloader)
'store-path
#f)))))
(define-record-type <operating-system>
(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)
(make-operating-system host-name freebsd-base native-build-result kernel bootloader
base-packages development-packages build-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)
(freebsd-base operating-system-freebsd-base)
(native-build-result operating-system-native-build-result)
(kernel operating-system-kernel)
(bootloader operating-system-bootloader)
(base-packages operating-system-base-packages)
(development-packages operating-system-development-packages)
(build-packages operating-system-build-packages)
(users operating-system-users)
(groups operating-system-groups)
(file-systems operating-system-file-systems)
@@ -119,10 +181,12 @@
(define* (operating-system #:key
(host-name "fruix-freebsd")
(freebsd-base %default-freebsd-base)
(native-build-result #f)
(kernel freebsd-kernel)
(bootloader freebsd-bootloader)
(base-packages %freebsd-system-packages)
(development-packages '())
(build-packages '())
(users (list (user-account #:name "root"
#:uid 0
#:group "wheel"
@@ -164,9 +228,10 @@
(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 development-packages
users groups file-systems services loader-entries rc-conf-entries
init-mode ready-marker root-authorized-keys))
(make-operating-system host-name freebsd-base native-build-result kernel bootloader
base-packages development-packages build-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))
@@ -234,8 +299,10 @@
(define (validate-operating-system os)
(let* ((host-name (operating-system-host-name os))
(base (operating-system-freebsd-base os))
(native-build-result (operating-system-native-build-result os))
(base-packages (operating-system-base-packages os))
(development-packages (operating-system-development-packages os))
(build-packages (operating-system-build-packages os))
(users (operating-system-users os))
(groups (operating-system-groups os))
(file-systems (operating-system-file-systems os))
@@ -247,10 +314,15 @@
(error "operating-system host-name must not be empty"))
(unless (freebsd-base? base)
(error "operating-system freebsd-base must be a <freebsd-base> record"))
(when native-build-result
(unless (promoted-native-build-result? native-build-result)
(error "operating-system native-build-result must be a <promoted-native-build-result> 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"))
(unless (every freebsd-package? build-packages)
(error "operating-system build-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)
@@ -303,12 +375,22 @@
"metadata/freebsd-base.scm"
"metadata/host-base-provenance.scm"
"metadata/store-layout.scm"
"metadata/system-declaration.scm"
"metadata/system-declaration-info.scm"
"metadata/system-declaration-system"
"activate"
"shepherd/init.scm"
"share/fruix/node/scripts/fruix.scm"
"usr/local/bin/fruix")
(if (operating-system-native-build-result os)
'("metadata/promoted-native-build-result.scm")
'())
(if (null? (operating-system-development-packages os))
'()
'("usr/local/bin/fruix-development-environment"
'("usr/local/bin/fruix-development-environment"))
(if (null? (operating-system-build-packages os))
'()
'("usr/local/bin/fruix-build-environment"
"usr/local/bin/fruix-self-hosted-native-build"))
(if (pid1-init-mode? os)
'("boot/fruix-pid1")
@@ -325,16 +407,26 @@
(validate-operating-system os)
`((host-name . ,(operating-system-host-name os))
(freebsd-base . ,(freebsd-base-spec (operating-system-freebsd-base os)))
(promoted-native-build-result
. ,(and (operating-system-native-build-result os)
(promoted-native-build-result-spec
(operating-system-native-build-result os))))
(kernel-package . ,(freebsd-package-name (operating-system-kernel os)))
(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)))
(build-package-count . ,(length (operating-system-build-packages os)))
(build-packages . ,(package-names (operating-system-build-packages os)))
(installed-system-command-surface-version . "2")
(bundled-fruix-node-cli-version . "1")
(development-environment-helper-version
. ,(if (null? (operating-system-development-packages os)) #f "1"))
(build-environment-helper-version
. ,(if (null? (operating-system-build-packages os)) #f "1"))
(self-hosted-native-build-helper-version
. ,(if (null? (operating-system-development-packages os)) #f "3"))
. ,(if (null? (operating-system-build-packages os)) #f "5"))
(user-count . ,(length (operating-system-users os)))
(users . ,(map user-account-name (operating-system-users os)))
(group-count . ,(length (operating-system-groups os)))

View File

@@ -4,6 +4,7 @@
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-13)
#:use-module (rnrs io ports)
#:export (operating-system-generated-files
render-activation-rc-script
render-rc-script))
@@ -467,7 +468,57 @@
"}\n\n"
"load_rc_config $name\n"
"run_rc_command \"$1\"\n")))
(define (render-installed-system-fruix os)
(define (path-parent path)
(let ((index (string-rindex path #\/)))
(cond
((not index) ".")
((zero? index) "/")
(else (substring path 0 index)))))
(define (read-source-file-string path)
(call-with-input-file path get-string-all))
(define (bundled-fruix-node-files)
(let* ((repo-root (or (getenv "FRUIX_PROJECT_ROOT")
(let ((render-file (current-filename)))
(and render-file
(path-parent
(path-parent
(path-parent
(path-parent
(path-parent render-file)))))))
(getcwd)))
(guix-root (or (getenv "GUIX_SOURCE_DIR")
(string-append (getenv "HOME") "/repos/guix")))
(specs `((,(string-append repo-root "/scripts/fruix.scm")
. "share/fruix/node/scripts/fruix.scm")
(,(string-append repo-root "/modules/fruix/packages/freebsd.scm")
. "share/fruix/node/modules/fruix/packages/freebsd.scm")
(,(string-append repo-root "/modules/fruix/system/freebsd.scm")
. "share/fruix/node/modules/fruix/system/freebsd.scm")
(,(string-append repo-root "/modules/fruix/system/freebsd/build.scm")
. "share/fruix/node/modules/fruix/system/freebsd/build.scm")
(,(string-append repo-root "/modules/fruix/system/freebsd/executor.scm")
. "share/fruix/node/modules/fruix/system/freebsd/executor.scm")
(,(string-append repo-root "/modules/fruix/system/freebsd/media.scm")
. "share/fruix/node/modules/fruix/system/freebsd/media.scm")
(,(string-append repo-root "/modules/fruix/system/freebsd/model.scm")
. "share/fruix/node/modules/fruix/system/freebsd/model.scm")
(,(string-append repo-root "/modules/fruix/system/freebsd/render.scm")
. "share/fruix/node/modules/fruix/system/freebsd/render.scm")
(,(string-append repo-root "/modules/fruix/system/freebsd/source.scm")
. "share/fruix/node/modules/fruix/system/freebsd/source.scm")
(,(string-append repo-root "/modules/fruix/system/freebsd/utils.scm")
. "share/fruix/node/modules/fruix/system/freebsd/utils.scm")
(,(string-append guix-root "/guix/build/utils.scm")
. "share/fruix/node/guix/guix/build/utils.scm"))))
(map (lambda (entry)
(cons (cdr entry)
(read-source-file-string (car entry))))
specs)))
(define (render-installed-system-fruix os guile-store guile-extra-store shepherd-store)
(string-append
"#!/bin/sh\n"
"set -eu\n"
@@ -485,6 +536,17 @@
"rollback_generation_file=\"$system_root/rollback-generation\"\n"
"gcroots_root=/frx/var/fruix/gcroots\n"
"run_current_link=/run/current-system\n"
"node_root=/run/current-system/share/fruix/node\n"
"node_script=\"$node_root/scripts/fruix.scm\"\n"
"node_module_root=\"$node_root/modules\"\n"
"node_guix_root=\"$node_root/guix\"\n"
"declaration_file=/run/current-system/metadata/system-declaration.scm\n"
"declaration_info_file=/run/current-system/metadata/system-declaration-info.scm\n"
"declaration_system_file=/run/current-system/metadata/system-declaration-system\n"
"default_store_dir=/frx/store\n"
"guile_store='" guile-store "'\n"
"guile_extra_store='" guile-extra-store "'\n"
"shepherd_store='" shepherd-store "'\n"
"layout_version=2\n"
"host_name='" (operating-system-host-name os) "'\n"
"ready_marker='" (operating-system-ready-marker os) "'\n"
@@ -493,6 +555,8 @@
"{\n"
" cat <<'EOF'\n"
"Usage: fruix system status\n"
" fruix system build [DECLARATION [--system NAME] ...]\n"
" fruix system reconfigure [DECLARATION [--system NAME] ...]\n"
" fruix system switch /frx/store/...-fruix-system-...\n"
" fruix system rollback\n"
"EOF\n"
@@ -514,6 +578,10 @@
" tr -d '\\n' < \"$1\"\n"
" fi\n"
"}\n\n"
"default_system_name()\n"
"{\n"
" read_file_maybe \"$declaration_system_file\"\n"
"}\n\n"
"symlink_force()\n"
"{\n"
" target=$1\n"
@@ -537,6 +605,79 @@
" [ -f \"$closure/shepherd/init.scm\" ] || die \"closure is missing shepherd config: $closure\"\n"
" [ -f \"$closure/boot/loader.efi\" ] || die \"closure is missing loader.efi: $closure\"\n"
"}\n\n"
"ensure_default_declaration()\n"
"{\n"
" [ -f \"$declaration_file\" ] || die \"current declaration file is missing: $declaration_file\"\n"
" [ -f \"$declaration_info_file\" ] || die \"current declaration info file is missing: $declaration_info_file\"\n"
" current_system_name=$(default_system_name)\n"
" [ -n \"$current_system_name\" ] || die \"current declaration is missing a system variable name\"\n"
"}\n\n"
"run_node_cli()\n"
"{\n"
" [ -x \"$guile_store/bin/guile\" ] || die \"missing Guile runtime: $guile_store/bin/guile\"\n"
" [ -f \"$node_script\" ] || die \"missing bundled Fruix node CLI: $node_script\"\n"
" [ -d \"$node_module_root\" ] || die \"missing bundled Fruix modules: $node_module_root\"\n"
" [ -d \"$node_guix_root\" ] || die \"missing bundled Guix modules: $node_guix_root\"\n"
" guile_load_path=\"$node_module_root:$node_guix_root:$shepherd_store/share/guile/site/3.0:$guile_extra_store/share/guile/site/3.0\"\n"
" guile_system_path=\"$guile_store/share/guile/3.0:$guile_store/share/guile/site/3.0:$guile_store/share/guile/site:$guile_store/share/guile\"\n"
" guile_system_compiled_path=\"$guile_store/lib/guile/3.0/ccache:$guile_store/lib/guile/3.0/site-ccache\"\n"
" guile_load_compiled_path=\"$shepherd_store/lib/guile/3.0/site-ccache:$guile_extra_store/lib/guile/3.0/site-ccache\"\n"
" guile_system_extensions_path=\"$guile_store/lib/guile/3.0/extensions\"\n"
" guile_extensions_path=\"$guile_extra_store/lib/guile/3.0/extensions\"\n"
" ld_library_path=\"$guile_extra_store/lib:$guile_store/lib:/usr/local/lib\"\n"
" env \\\n"
" GUILE_AUTO_COMPILE=0 \\\n"
" GUILE_SYSTEM_PATH=\"$guile_system_path\" \\\n"
" GUILE_LOAD_PATH=\"$guile_load_path\" \\\n"
" GUILE_SYSTEM_COMPILED_PATH=\"$guile_system_compiled_path\" \\\n"
" GUILE_LOAD_COMPILED_PATH=\"$guile_load_compiled_path\" \\\n"
" GUILE_SYSTEM_EXTENSIONS_PATH=\"$guile_system_extensions_path\" \\\n"
" GUILE_EXTENSIONS_PATH=\"$guile_extensions_path\" \\\n"
" LD_LIBRARY_PATH=\"$ld_library_path\" \\\n"
" GUILE_PREFIX=\"$guile_store\" \\\n"
" GUILE_EXTRA_PREFIX=\"$guile_extra_store\" \\\n"
" SHEPHERD_PREFIX=\"$shepherd_store\" \\\n"
" FRUIX_GUILE_STORE=\"$guile_store\" \\\n"
" FRUIX_GUILE_EXTRA_STORE=\"$guile_extra_store\" \\\n"
" FRUIX_SHEPHERD_STORE=\"$shepherd_store\" \\\n"
" GUIX_SOURCE_DIR=\"$node_guix_root\" \\\n"
" FRUIX_PROJECT_ROOT=\"$node_root\" \\\n"
" \"$guile_store/bin/guile\" --no-auto-compile -s \"$node_script\" \"$@\"\n"
"}\n\n"
"system_build()\n"
"{\n"
" if [ $# -eq 0 ]; then\n"
" ensure_default_declaration\n"
" run_node_cli system build \"$declaration_file\" --system \"$current_system_name\" --store \"$default_store_dir\"\n"
" else\n"
" run_node_cli system build \"$@\"\n"
" fi\n"
"}\n\n"
"reconfigure_system()\n"
"{\n"
" build_output=$(mktemp /tmp/fruix-system-reconfigure.XXXXXX)\n"
" if [ $# -eq 0 ]; then\n"
" ensure_default_declaration\n"
" if ! run_node_cli system build \"$declaration_file\" --system \"$current_system_name\" --store \"$default_store_dir\" > \"$build_output\"; then\n"
" cat \"$build_output\" >&2 || true\n"
" rm -f \"$build_output\"\n"
" exit 1\n"
" fi\n"
" else\n"
" if ! run_node_cli system build \"$@\" > \"$build_output\"; then\n"
" cat \"$build_output\" >&2 || true\n"
" rm -f \"$build_output\"\n"
" exit 1\n"
" fi\n"
" fi\n"
" closure=$(sed -n 's/^closure_path=//p' \"$build_output\" | tail -n 1)\n"
" [ -n \"$closure\" ] || die \"failed to recover closure_path from in-system build output\"\n"
" cat \"$build_output\"\n"
" rm -f \"$build_output\"\n"
" switch_to_closure \"$closure\"\n"
" printf 'reconfigure_closure=%s\\n' \"$closure\"\n"
" printf 'reboot_required=true\\n'\n"
"}\n\n"
"max_generation_number()\n"
"{\n"
" max=0\n"
@@ -726,6 +867,14 @@
" [ $# -eq 2 ] || { usage >&2; exit 1; }\n"
" status\n"
" ;;\n"
" build)\n"
" shift 2\n"
" system_build \"$@\"\n"
" ;;\n"
" reconfigure)\n"
" shift 2\n"
" reconfigure_system \"$@\"\n"
" ;;\n"
" switch)\n"
" [ $# -eq 3 ] || { usage >&2; exit 1; }\n"
" switch_to_closure \"$3\"\n"
@@ -785,6 +934,33 @@
"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 (render-build-environment-script os)
(string-append
"#!/bin/sh\n"
"set -eu\n"
"profile=/run/current-system/build-profile\n"
"[ -d \"$profile\" ] || {\n"
" echo \"fruix-build-environment: build profile is not available\" >&2\n"
" exit 1\n"
"}\n"
"cat <<EOF\n"
"unset MAKEOBJDIRPREFIX MAKEFLAGS CC CXX AR RANLIB NM CPPFLAGS CFLAGS CXXFLAGS LDFLAGS\n"
"unset FRUIX_DEVELOPMENT_PROFILE FRUIX_DEVELOPMENT_INCLUDE FRUIX_DEVELOPMENT_LIB FRUIX_DEVELOPMENT_SHARE_MK\n"
"unset FRUIX_DEVELOPMENT_BIN FRUIX_DEVELOPMENT_USR_BIN FRUIX_CC FRUIX_CXX FRUIX_AR FRUIX_RANLIB FRUIX_NM FRUIX_BMAKE\n"
"export FRUIX_BUILD_PROFILE=\"$profile\"\n"
"export FRUIX_BUILD_INCLUDE=\"$profile/usr/include\"\n"
"export FRUIX_BUILD_LIB=\"$profile/lib\"\n"
"export FRUIX_BUILD_SHARE_MK=\"$profile/usr/share/mk\"\n"
"export FRUIX_BUILD_BIN=\"$profile/bin\"\n"
"export FRUIX_BUILD_USR_BIN=\"$profile/usr/bin\"\n"
"export FRUIX_BUILD_CC=\"$profile/bin/cc\"\n"
"export FRUIX_BUILD_CXX=\"$profile/bin/c++\"\n"
"export FRUIX_BUILD_AR=\"$profile/bin/ar\"\n"
"export FRUIX_BUILD_RANLIB=\"$profile/bin/ranlib\"\n"
"export FRUIX_BUILD_NM=\"$profile/bin/nm\"\n"
"export FRUIX_BMAKE=\"make\"\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 (render-self-hosted-native-build-script os)
(let* ((base-spec (freebsd-base-spec (operating-system-freebsd-base os)))
(base-name (assoc-ref base-spec 'name))
@@ -807,25 +983,26 @@
"#!/bin/sh\n"
"set -eu\n"
"umask 022\n"
"profile=/run/current-system/development-profile\n"
"profile=/run/current-system/build-profile\n"
"guest_host_name='" (operating-system-host-name os) "'\n"
"[ -d \"$profile\" ] || {\n"
" echo \"fruix-self-hosted-native-build: development profile is not available\" >&2\n"
" echo \"fruix-self-hosted-native-build: build profile is not available\" >&2\n"
" exit 1\n"
"}\n"
"[ -x /usr/local/bin/fruix-development-environment ] || {\n"
" echo \"fruix-self-hosted-native-build: development environment helper is missing\" >&2\n"
"[ -x /usr/local/bin/fruix-build-environment ] || {\n"
" echo \"fruix-self-hosted-native-build: build environment helper is missing\" >&2\n"
" exit 1\n"
"}\n"
"eval \"$(/usr/local/bin/fruix-build-environment)\"\n"
"[ \"${FRUIX_BUILD_PROFILE:-}\" = \"$profile\" ] || {\n"
" echo \"fruix-self-hosted-native-build: build environment helper exported an unexpected profile\" >&2\n"
" exit 1\n"
"}\n"
"PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin\n"
"unset MAKEOBJDIRPREFIX MAKEFLAGS CC CXX AR RANLIB NM CPPFLAGS CFLAGS CXXFLAGS LDFLAGS\n"
"unset FRUIX_DEVELOPMENT_PROFILE FRUIX_DEVELOPMENT_INCLUDE FRUIX_DEVELOPMENT_LIB FRUIX_DEVELOPMENT_SHARE_MK\n"
"unset FRUIX_DEVELOPMENT_BIN FRUIX_DEVELOPMENT_USR_BIN FRUIX_CC FRUIX_CXX FRUIX_AR FRUIX_RANLIB FRUIX_NM FRUIX_BMAKE\n"
"[ -L /usr/include ] || {\n"
" echo \"fruix-self-hosted-native-build: /usr/include compatibility link is missing\" >&2\n"
" exit 1\n"
"}\n"
"[ \"$(readlink /usr/include)\" = \"/run/current-system/development-profile/usr/include\" ] || {\n"
"[ \"$(readlink /usr/include)\" = \"/run/current-system/build-profile/usr/include\" ] || {\n"
" echo \"fruix-self-hosted-native-build: /usr/include points at the wrong target\" >&2\n"
" exit 1\n"
"}\n"
@@ -833,10 +1010,11 @@
" echo \"fruix-self-hosted-native-build: /usr/share/mk compatibility link is missing\" >&2\n"
" exit 1\n"
"}\n"
"[ \"$(readlink /usr/share/mk)\" = \"/run/current-system/development-profile/usr/share/mk\" ] || {\n"
"[ \"$(readlink /usr/share/mk)\" = \"/run/current-system/build-profile/usr/share/mk\" ] || {\n"
" echo \"fruix-self-hosted-native-build: /usr/share/mk points at the wrong target\" >&2\n"
" exit 1\n"
"}\n"
"make_cmd=${FRUIX_BMAKE:-make}\n"
"jobs=${FRUIX_SELF_HOSTED_NATIVE_BUILD_JOBS:-$(sysctl -n hw.ncpu)}\n"
"case \"$jobs\" in\n"
" ''|*[!0-9]*)\n"
@@ -887,11 +1065,11 @@
"}\n"
"mkdir -p \"$world_artifact\" \"$headers_artifact/usr\" \"$kernel_artifact/boot\" \"$bootloader_artifact/boot\"\n"
"export MAKEOBJDIRPREFIX=\"$build_root/obj\"\n"
"make -j\"$jobs\" -C \"$source_root\" " build-common " buildworld > \"$logdir/buildworld.log\" 2>&1\n"
"make -j\"$jobs\" -C \"$source_root\" " build-common " buildkernel > \"$logdir/buildkernel.log\" 2>&1\n"
"make -C \"$source_root\" " install-common " DESTDIR=\"$world_stage\" installworld > \"$logdir/installworld.log\" 2>&1\n"
"make -C \"$source_root\" " install-common " DESTDIR=\"$world_stage\" distribution > \"$logdir/distribution.log\" 2>&1\n"
"make -C \"$source_root\" " install-common " DESTDIR=\"$kernel_stage\" installkernel > \"$logdir/installkernel.log\" 2>&1\n"
"\"$make_cmd\" -j\"$jobs\" -C \"$source_root\" " build-common " buildworld > \"$logdir/buildworld.log\" 2>&1\n"
"\"$make_cmd\" -j\"$jobs\" -C \"$source_root\" " build-common " buildkernel > \"$logdir/buildkernel.log\" 2>&1\n"
"\"$make_cmd\" -C \"$source_root\" " install-common " DESTDIR=\"$world_stage\" installworld > \"$logdir/installworld.log\" 2>&1\n"
"\"$make_cmd\" -C \"$source_root\" " install-common " DESTDIR=\"$world_stage\" distribution > \"$logdir/distribution.log\" 2>&1\n"
"\"$make_cmd\" -C \"$source_root\" " install-common " DESTDIR=\"$kernel_stage\" installkernel > \"$logdir/installkernel.log\" 2>&1\n"
"cp -a \"$world_stage/.\" \"$world_artifact/\"\n"
"cp -a \"$kernel_stage/boot/kernel\" \"$kernel_artifact/boot/kernel\"\n"
"cp -a \"$world_stage/usr/include\" \"$headers_artifact/usr/include\"\n"
@@ -928,12 +1106,17 @@
"ln -s \"$result_root\" \"$latest_link\"\n"
"cat >\"$promotion_file\" <<EOF\n"
"((native-build-result-version . \"1\")\n"
" (executor . \"guest-self-hosted\")\n"
" (executor-version . \"3\")\n"
" (executor . ((kind . self-hosted)\n"
" (name . \"guest-self-hosted\")\n"
" (version . \"5\")\n"
" (properties . ((helper-path . \"/usr/local/bin/fruix-self-hosted-native-build\")\n"
" (guest-host-name . \"$guest_host_name\")\n"
" (build-root-base . \"$build_root_base\")\n"
" (result-root-base . \"$result_root_base\")))))\n"
" (run-id . \"$run_id\")\n"
" (guest-host-name . \"$guest_host_name\")\n"
" (closure-path . \"$closure\")\n"
" (development-profile . \"$profile\")\n"
" (build-profile . \"$profile\")\n"
" (freebsd-base . ((name . \"" base-name "\")\n"
" (version-label . \"" version-label "\")\n"
" (release . \"" release "\")\n"
@@ -961,10 +1144,13 @@
"EOF\n"
"cat >\"$metadata_file\" <<EOF\n"
"run_id=$run_id\n"
"helper_version=3\n"
"helper_version=5\n"
"executor_kind=self-hosted\n"
"executor_name=guest-self-hosted\n"
"executor_version=5\n"
"closure_path=$closure\n"
"guest_host_name=$guest_host_name\n"
"development_profile=$profile\n"
"build_profile=$profile\n"
"source_store=$source_store\n"
"source_root=$source_root\n"
"build_jobs=$jobs\n"
@@ -1022,11 +1208,17 @@
#:guile-extra-store guile-extra-store
#:shepherd-store shepherd-store))
("shepherd/init.scm" . ,(render-shepherd-config os))
("usr/local/bin/fruix" . ,(render-installed-system-fruix os)))
("usr/local/bin/fruix"
. ,(render-installed-system-fruix os guile-store guile-extra-store shepherd-store)))
(bundled-fruix-node-files)
(if (null? (operating-system-development-packages os))
'()
`(("usr/local/bin/fruix-development-environment"
. ,(render-development-environment-script os))
. ,(render-development-environment-script os))))
(if (null? (operating-system-build-packages os))
'()
`(("usr/local/bin/fruix-build-environment"
. ,(render-build-environment-script os))
("usr/local/bin/fruix-self-hosted-native-build"
. ,(render-self-hosted-native-build-script os))))
(if (pid1-init-mode? os)

View File

@@ -6,7 +6,8 @@
(ice-9 format)
(ice-9 match)
(srfi srfi-1)
(srfi srfi-13))
(srfi srfi-13)
(rnrs io ports))
(define (usage code)
(format (if (= code 0) #t (current-error-port))
@@ -69,6 +70,9 @@ Common options:\n\
(format #t "~a=~a~%" (car field) (stringify (cdr field))))
fields))
(define (read-file-string file)
(call-with-input-file file get-string-all))
(define (lookup-bound-value module symbol)
(let ((var (module-variable module symbol)))
(and var (variable-ref var))))
@@ -580,6 +584,9 @@ Common options:\n\
`((action . "promote")
(result_root . ,result-root)
(store_dir . ,store-dir)
(executor_kind . ,(assoc-ref result 'executor-kind))
(executor_name . ,(assoc-ref result 'executor-name))
(executor_version . ,(assoc-ref result 'executor-version))
(result_store . ,(assoc-ref result 'result-store))
(result_metadata_file . ,(assoc-ref result 'result-metadata-file))
(artifact_store_count . ,(assoc-ref result 'artifact-store-count))
@@ -626,7 +633,11 @@ Common options:\n\
(lambda (os resolved-symbol)
(let* ((guile-prefix (or (getenv "GUILE_PREFIX") "/tmp/guile-freebsd-validate-install"))
(guile-extra-prefix (or (getenv "GUILE_EXTRA_PREFIX") "/tmp/guile-gnutls-freebsd-validate-install"))
(shepherd-prefix (or (getenv "SHEPHERD_PREFIX") "/tmp/shepherd-freebsd-validate-install")))
(shepherd-prefix (or (getenv "SHEPHERD_PREFIX") "/tmp/shepherd-freebsd-validate-install"))
(guile-store-path (getenv "FRUIX_GUILE_STORE"))
(guile-extra-store-path (getenv "FRUIX_GUILE_EXTRA_STORE"))
(shepherd-store-path (getenv "FRUIX_SHEPHERD_STORE"))
(declaration-source (read-file-string os-file)))
(cond
((string=? action "build")
(emit-system-build-metadata
@@ -635,7 +646,13 @@ Common options:\n\
#:store-dir store-dir
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix)))
#:shepherd-prefix shepherd-prefix
#:guile-store-path guile-store-path
#:guile-extra-store-path guile-extra-store-path
#:shepherd-store-path shepherd-store-path
#:declaration-source declaration-source
#:declaration-origin os-file
#:declaration-system-symbol resolved-symbol)))
((string=? action "rootfs")
(unless rootfs
(error "rootfs action requires ROOTFS-DIR or --rootfs DIR"))
@@ -643,7 +660,10 @@ Common options:\n\
#:store-dir store-dir
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix)))
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin os-file
#:declaration-system-symbol resolved-symbol)))
(emit-metadata
`((action . "rootfs")
(os_file . ,os-file)
@@ -661,6 +681,9 @@ Common options:\n\
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin os-file
#:declaration-system-symbol resolved-symbol
#:root-size (or root-size "256m")
#:disk-capacity disk-capacity)))
((string=? action "installer")
@@ -671,6 +694,9 @@ Common options:\n\
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin os-file
#:declaration-system-symbol resolved-symbol
#:install-target-device (or install-target-device "/dev/vtbd1")
#:root-size (or root-size "10g")
#:disk-capacity disk-capacity)))
@@ -682,6 +708,9 @@ Common options:\n\
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin os-file
#:declaration-system-symbol resolved-symbol
#:install-target-device (or install-target-device "/dev/vtbd0")
#:root-size root-size)))
((string=? action "install")
@@ -695,6 +724,9 @@ Common options:\n\
#:guile-prefix guile-prefix
#:guile-extra-prefix guile-extra-prefix
#:shepherd-prefix shepherd-prefix
#:declaration-source declaration-source
#:declaration-origin os-file
#:declaration-system-symbol resolved-symbol
#:root-size root-size
#:disk-capacity disk-capacity))))))))))
((string=? command "source")

View File

@@ -9,6 +9,8 @@
#:base-packages %freebsd-native-system-packages
#:development-packages (list freebsd-native-headers
freebsd-clang-toolchain)
#:build-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)

View File

@@ -0,0 +1,73 @@
(use-modules (fruix system freebsd)
(fruix packages freebsd))
(define phase20-promoted-native-build-result
(promoted-native-build-result
#:store-path "__PROMOTED_RESULT_STORE__"))
(define phase20-promoted-native-base-operating-system
(operating-system-from-promoted-native-build-result
phase20-promoted-native-build-result
#:host-name "fruix-freebsd"
#: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__")))

View File

@@ -0,0 +1,73 @@
(use-modules (fruix system freebsd)
(fruix packages freebsd))
(define postphase20-promoted-native-build-result
(promoted-native-build-result
#:store-path "__PROMOTED_RESULT_STORE__"))
(define postphase20-installed-node-operating-system
(operating-system-from-promoted-native-build-result
postphase20-promoted-native-build-result
#:host-name "__HOST_NAME__"
#: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__")))

View File

@@ -50,8 +50,10 @@ 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
build_profile_path=$closure_path/build-profile
runtime_profile_path=$closure_path/profile
development_env_script=$closure_path/usr/local/bin/fruix-development-environment
build_env_script=$closure_path/usr/local/bin/fruix-build-environment
[ "$shepherd_pid" = 1 ] || { echo "shepherd was not PID 1" >&2; exit 1; }
[ "$sshd_status" = running ] || { echo "sshd is not running" >&2; exit 1; }
@@ -68,7 +70,11 @@ for path in \
"$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"
"$build_profile_path/bin/cc" \
"$build_profile_path/usr/include/sys/param.h" \
"$build_profile_path/usr/share/mk/bsd.prog.mk" \
"$development_env_script" \
"$build_env_script"
do
[ -e "$path" ] || {
echo "required development environment path missing: $path" >&2
@@ -96,8 +102,11 @@ guest_dev_metadata=$(ssh -i "$root_ssh_private_key_file" \
root@"$guest_ip" 'sh -s' <<'EOF'
set -eu
[ -x /usr/local/bin/fruix-development-environment ]
[ -x /usr/local/bin/fruix-build-environment ]
[ -L /run/current-development ]
[ "$(readlink /run/current-development)" = "/run/current-system/development-profile" ]
[ -L /run/current-build ]
[ "$(readlink /run/current-build)" = "/run/current-system/build-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
@@ -109,6 +118,20 @@ eval "$exports"
[ -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 }')
build_exports=$(/usr/local/bin/fruix-build-environment)
printf '%s\n' "$build_exports" | grep '^unset MAKEOBJDIRPREFIX MAKEFLAGS CC CXX AR RANLIB NM CPPFLAGS CFLAGS CXXFLAGS LDFLAGS$' >/dev/null
printf '%s\n' "$build_exports" | grep '^export FRUIX_BUILD_PROFILE="/run/current-system/build-profile"$' >/dev/null
eval "$build_exports"
[ -d "$FRUIX_BUILD_PROFILE" ]
[ -f "$FRUIX_BUILD_INCLUDE/sys/param.h" ]
[ -f "$FRUIX_BUILD_SHARE_MK/bsd.prog.mk" ]
[ "${MAKEFLAGS-unset}" = unset ]
[ "${CPPFLAGS-unset}" = unset ]
[ "${CFLAGS-unset}" = unset ]
[ "${LDFLAGS-unset}" = unset ]
[ "$FRUIX_BMAKE" = make ]
build_profile_value=$FRUIX_BUILD_PROFILE
eval "$exports"
tmp=/tmp/fruix-phase20-development-env
rm -rf "$tmp"
mkdir -p "$tmp/direct" "$tmp/mk"
@@ -142,25 +165,34 @@ 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 'build_profile=%s\n' "$build_profile_value"
printf 'cc_version=%s\n' "$cc_version"
printf 'hello_direct=%s\n' "$hello_direct"
printf 'hello_make=%s\n' "$hello_make"
build_exports_flat=$(printf '%s' "$build_exports" | tr '\n' ' ')
printf 'exports=%s\n' "$exports_flat"
printf 'build_exports=%s\n' "$build_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')
build_profile=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^build_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')
build_exports=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^build_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
}
[ "$build_profile" = "/run/current-system/build-profile" ] || {
echo "unexpected guest build profile path: $build_profile" >&2
exit 1
}
case "$cc_version" in
*"FreeBSD clang version"*) : ;;
*) echo "unexpected cc version output: $cc_version" >&2; exit 1 ;;
@@ -177,6 +209,10 @@ 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
case "$build_exports" in
*'export FRUIX_BUILD_PROFILE="/run/current-system/build-profile"'*) : ;;
*) echo "build environment exports do not include FRUIX_BUILD_PROFILE" >&2; exit 1 ;;
esac
cat >"$metadata_file" <<EOF
workdir=$workdir
@@ -189,16 +225,20 @@ vdi_id=$vdi_id
guest_ip=$guest_ip
root_size=$root_size
development_profile_path=$development_profile_path
build_profile_path=$build_profile_path
development_env_script=$development_env_script
build_env_script=$build_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
build_profile_guest=$build_profile
cc_version=$cc_version
hello_direct=$hello_direct
hello_make=$hello_make
development_exports=$development_exports
build_exports=$build_exports
make_log_tail=$make_log_tail
boot_backend=xcp-ng-xo-cli
init_mode=shepherd-pid1

View File

@@ -0,0 +1,239 @@
#!/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}
store_dir=${STORE_DIR:-/frx/store}
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-host-initiated-native-build-store-promotion-xcpng.XXXXXX)
cleanup=1
fi
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
cleanup=0
fi
inner_metadata=$workdir/phase20-host-initiated-native-build-store-promotion-inner-metadata.txt
promotion_out=$workdir/native-build-promote.txt
metadata_file=$workdir/phase20-host-initiated-native-build-store-promotion-xcpng-metadata.txt
import_root=$workdir/import
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-host-initiated-native-build-xcpng.sh"
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")
closure_path=$(sed -n 's/^closure_path=//p' "$inner_metadata")
closure_base=$(sed -n 's/^closure_base=//p' "$inner_metadata")
run_id=$(sed -n 's/^run_id=//p' "$inner_metadata")
source_store=$(sed -n 's/^source_store=//p' "$inner_metadata")
result_root=$(sed -n 's/^result_root=//p' "$inner_metadata")
promotion_file=$(sed -n 's/^promotion_file=//p' "$inner_metadata")
world_artifact=$(sed -n 's/^world_artifact=//p' "$inner_metadata")
kernel_artifact=$(sed -n 's/^kernel_artifact=//p' "$inner_metadata")
headers_artifact=$(sed -n 's/^headers_artifact=//p' "$inner_metadata")
bootloader_artifact=$(sed -n 's/^bootloader_artifact=//p' "$inner_metadata")
sha_kernel=$(sed -n 's/^sha_kernel=//p' "$inner_metadata")
sha_loader=$(sed -n 's/^sha_loader=//p' "$inner_metadata")
sha_param=$(sed -n 's/^sha_param=//p' "$inner_metadata")
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" "$@"
}
mkdir -p "$import_root"
result_base=$(basename "$result_root")
ssh -i "$root_ssh_private_key_file" \
-o BatchMode=yes \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o ConnectTimeout=5 \
root@"$guest_ip" "tar -C '$(dirname "$result_root")' -cf - '$result_base'" | tar -C "$import_root" -xf -
local_result_root=$import_root/$result_base
[ -d "$local_result_root" ] || { echo "failed to import native build result root" >&2; exit 1; }
[ -f "$local_result_root/promotion.scm" ] || { echo "imported result is missing promotion.scm" >&2; exit 1; }
[ -f "$local_result_root/artifacts/world/bin/sh" ] || { echo "imported result is missing world artifact" >&2; exit 1; }
[ -f "$local_result_root/artifacts/kernel/boot/kernel/kernel" ] || { echo "imported result is missing kernel artifact" >&2; exit 1; }
[ -f "$local_result_root/artifacts/headers/usr/include/sys/param.h" ] || { echo "imported result is missing headers artifact" >&2; exit 1; }
[ -f "$local_result_root/artifacts/bootloader/boot/loader.efi" ] || { echo "imported result is missing bootloader artifact" >&2; exit 1; }
action_env() {
sudo env \
HOME="$HOME" \
GUILE_AUTO_COMPILE=0 \
GUIX_SOURCE_DIR="${GUIX_SOURCE_DIR:-$HOME/repos/guix}" \
GUILE_BIN="${GUILE_BIN:-/tmp/guile-freebsd-validate-install/bin/guile}" \
GUILE_EXTRA_PREFIX="${GUILE_EXTRA_PREFIX:-/tmp/guile-gnutls-freebsd-validate-install}" \
SHEPHERD_PREFIX="${SHEPHERD_PREFIX:-/tmp/shepherd-freebsd-validate-install}" \
"$@"
}
action_env "$repo_root/bin/fruix" native-build promote "$local_result_root" --store "$store_dir" >"$promotion_out"
field() {
sed -n "s/^$1=//p" "$promotion_out" | tail -n 1
}
executor_kind=$(field executor_kind)
executor_name=$(field executor_name)
executor_version=$(field executor_version)
result_store=$(field result_store)
result_metadata_file=$(field result_metadata_file)
artifact_store_count=$(field artifact_store_count)
artifact_stores=$(field artifact_stores)
world_store=$(field world_store)
kernel_store=$(field kernel_store)
headers_store=$(field headers_store)
bootloader_store=$(field bootloader_store)
[ "$executor_kind" = ssh-guest ] || { echo "unexpected executor kind: $executor_kind" >&2; exit 1; }
[ "$executor_name" = ssh-guest ] || { echo "unexpected executor name: $executor_name" >&2; exit 1; }
[ "$executor_version" = 1 ] || { echo "unexpected executor version: $executor_version" >&2; exit 1; }
[ "$artifact_store_count" = 4 ] || { echo "unexpected artifact store count: $artifact_store_count" >&2; exit 1; }
case "$result_store" in
/frx/store/*-fruix-native-build-result-*-ssh-guest) : ;;
*) echo "unexpected result store path: $result_store" >&2; exit 1 ;;
esac
case "$world_store" in
/frx/store/*-fruix-native-world-*-ssh-guest) : ;;
*) echo "unexpected world store path: $world_store" >&2; exit 1 ;;
esac
case "$kernel_store" in
/frx/store/*-fruix-native-kernel-*-ssh-guest) : ;;
*) echo "unexpected kernel store path: $kernel_store" >&2; exit 1 ;;
esac
case "$headers_store" in
/frx/store/*-fruix-native-headers-*-ssh-guest) : ;;
*) echo "unexpected headers store path: $headers_store" >&2; exit 1 ;;
esac
case "$bootloader_store" in
/frx/store/*-fruix-native-bootloader-*-ssh-guest) : ;;
*) echo "unexpected bootloader store path: $bootloader_store" >&2; exit 1 ;;
esac
[ -f "$result_metadata_file" ] || { echo "missing result metadata file: $result_metadata_file" >&2; exit 1; }
[ -f "$world_store/.fruix-native-build-object.scm" ] || { echo "missing world store metadata" >&2; exit 1; }
[ -f "$kernel_store/.fruix-native-build-object.scm" ] || { echo "missing kernel store metadata" >&2; exit 1; }
[ -f "$headers_store/.fruix-native-build-object.scm" ] || { echo "missing headers store metadata" >&2; exit 1; }
[ -f "$bootloader_store/.fruix-native-build-object.scm" ] || { echo "missing bootloader store metadata" >&2; exit 1; }
[ -L "$result_store/artifacts/world" ] || { echo "missing promoted world artifact link" >&2; exit 1; }
[ -L "$result_store/artifacts/kernel" ] || { echo "missing promoted kernel artifact link" >&2; exit 1; }
[ -L "$result_store/artifacts/headers" ] || { echo "missing promoted headers artifact link" >&2; exit 1; }
[ -L "$result_store/artifacts/bootloader" ] || { echo "missing promoted bootloader artifact link" >&2; exit 1; }
[ "$(readlink "$result_store/artifacts/world")" = "$world_store" ] || { echo "world artifact link mismatch" >&2; exit 1; }
[ "$(readlink "$result_store/artifacts/kernel")" = "$kernel_store" ] || { echo "kernel artifact link mismatch" >&2; exit 1; }
[ "$(readlink "$result_store/artifacts/headers")" = "$headers_store" ] || { echo "headers artifact link mismatch" >&2; exit 1; }
[ "$(readlink "$result_store/artifacts/bootloader")" = "$bootloader_store" ] || { echo "bootloader artifact link mismatch" >&2; exit 1; }
[ -f "$world_store/bin/sh" ] || { echo "promoted world store missing /bin/sh" >&2; exit 1; }
[ -f "$kernel_store/boot/kernel/kernel" ] || { echo "promoted kernel store missing kernel" >&2; exit 1; }
[ -f "$headers_store/usr/include/sys/param.h" ] || { echo "promoted headers store missing param.h" >&2; exit 1; }
[ -f "$bootloader_store/boot/loader.efi" ] || { echo "promoted bootloader store missing loader.efi" >&2; exit 1; }
promoted_kernel_sha=$(sha256 -q "$kernel_store/boot/kernel/kernel")
promoted_loader_sha=$(sha256 -q "$bootloader_store/boot/loader.efi")
promoted_param_sha=$(sha256 -q "$headers_store/usr/include/sys/param.h")
[ "$promoted_kernel_sha" = "$sha_kernel" ] || { echo "kernel sha mismatch after promotion" >&2; exit 1; }
[ "$promoted_loader_sha" = "$sha_loader" ] || { echo "loader sha mismatch after promotion" >&2; exit 1; }
[ "$promoted_param_sha" = "$sha_param" ] || { echo "param.h sha mismatch after promotion" >&2; exit 1; }
grep -F '(executor-kind . ssh-guest)' "$result_metadata_file" >/dev/null || {
echo "result metadata file is missing ssh-guest executor kind" >&2
exit 1
}
grep -F '(executor-name . "ssh-guest")' "$result_metadata_file" >/dev/null || {
echo "result metadata file is missing ssh-guest executor name" >&2
exit 1
}
grep -F "$source_store" "$result_metadata_file" >/dev/null || {
echo "result metadata file is missing source store provenance" >&2
exit 1
}
grep -F '(artifact-kind . kernel)' "$kernel_store/.fruix-native-build-object.scm" >/dev/null || {
echo "kernel store metadata is missing artifact kind" >&2
exit 1
}
grep -F '(artifact-kind . world)' "$world_store/.fruix-native-build-object.scm" >/dev/null || {
echo "world store metadata is missing artifact kind" >&2
exit 1
}
cat >"$metadata_file" <<EOF
workdir=$workdir
inner_metadata=$inner_metadata
promotion_out=$promotion_out
closure_path=$closure_path
closure_base=$closure_base
vm_id=$vm_id
vdi_id=$vdi_id
guest_ip=$guest_ip
root_size=$root_size
run_id=$run_id
source_store=$source_store
guest_result_root=$result_root
guest_promotion_file=$promotion_file
guest_world_artifact=$world_artifact
guest_kernel_artifact=$kernel_artifact
guest_headers_artifact=$headers_artifact
guest_bootloader_artifact=$bootloader_artifact
local_result_root=$local_result_root
store_dir=$store_dir
executor_kind=$executor_kind
executor_name=$executor_name
executor_version=$executor_version
result_store=$result_store
result_metadata_file=$result_metadata_file
artifact_store_count=$artifact_store_count
artifact_stores=$artifact_stores
world_store=$world_store
kernel_store=$kernel_store
headers_store=$headers_store
bootloader_store=$bootloader_store
sha_kernel=$sha_kernel
sha_loader=$sha_loader
sha_param=$sha_param
promoted_kernel_sha=$promoted_kernel_sha
promoted_loader_sha=$promoted_loader_sha
promoted_param_sha=$promoted_param_sha
boot_backend=xcp-ng-xo-cli
init_mode=shepherd-pid1
host_initiated_native_build_store_promotion=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-store-promotion-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"

View File

@@ -70,49 +70,76 @@ case "$guest_build_jobs" in
;;
esac
native_build_metadata=$(ssh_guest env BUILD_JOBS="$guest_build_jobs" sh -s <<'EOF'
native_build_metadata=$(ssh_guest env BUILD_JOBS="$guest_build_jobs" GUEST_IP="$guest_ip" VM_ID="$vm_id" VDI_ID="$vdi_id" sh -s <<'EOF'
set -eu
[ -L /run/current-development ]
[ -L /run/current-build ]
[ -L /usr/include ]
[ "$(readlink /usr/include)" = "/run/current-system/development-profile/usr/include" ]
[ "$(readlink /usr/include)" = "/run/current-system/build-profile/usr/include" ]
[ -L /usr/share/mk ]
[ "$(readlink /usr/share/mk)" = "/run/current-system/development-profile/usr/share/mk" ]
[ "$(readlink /usr/share/mk)" = "/run/current-system/build-profile/usr/share/mk" ]
guest_host_name=$(hostname)
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_store=$(sed -n 's/.*"\(\/frx\/store\/[^"]*-freebsd-source-[^"]*\)".*/\1/p' "$closure/metadata/store-layout.scm" | head -n 1)
source_root="$source_store/tree"
build_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'
install_common="$build_common DB_FROM_SRC=yes"
build_root=/var/tmp/fruix-phase20-native-build
run_id=${FRUIX_HOST_INITIATED_NATIVE_BUILD_ID:-$(date -u +%Y%m%dT%H%M%SZ)}
result_root_base=/var/lib/fruix/native-builds
result_root=$result_root_base/$run_id
logdir=$build_root/logs
status_file=$result_root/status
guest_metadata_file=$result_root/metadata.txt
promotion_file=$result_root/promotion.scm
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"
world_artifact=$result_root/artifacts/world
kernel_artifact=$result_root/artifacts/kernel
headers_artifact=$result_root/artifacts/headers
bootloader_artifact=$result_root/artifacts/bootloader
latest_link=$result_root_base/latest
rm -rf "$build_root" "$result_root"
mkdir -p "$logdir" "$result_root" "$world_artifact" "$kernel_artifact/boot" "$headers_artifact/usr" "$bootloader_artifact/boot"
printf 'running\n' > "$status_file"
fail_mark() {
rc=$?
if [ "$rc" -ne 0 ]; then
printf 'failed\n' > "$status_file"
fi
}
trap fail_mark EXIT HUP INT TERM
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
make -C "$source_root" $install_common DESTDIR="$world_stage" installworld > "$logdir/installworld.log" 2>&1
make -C "$source_root" $install_common DESTDIR="$world_stage" distribution > "$logdir/distribution.log" 2>&1
make -C "$source_root" $install_common DESTDIR="$kernel_stage" installkernel > "$logdir/installkernel.log" 2>&1
mkdir -p "$headers_stage/usr" "$bootloader_stage/boot"
cp -a "$world_stage/." "$world_artifact/"
cp -a "$kernel_stage/boot/kernel" "$kernel_artifact/boot/kernel"
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 "$headers_stage/usr/." "$headers_artifact/usr/"
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")
cp -a "$bootloader_stage/boot/." "$bootloader_artifact/boot/"
[ -f "$world_artifact/bin/sh" ]
[ -f "$kernel_artifact/boot/kernel/kernel" ]
[ -f "$headers_artifact/usr/include/sys/param.h" ]
[ -f "$headers_artifact/usr/share/mk/bsd.prog.mk" ]
[ -f "$bootloader_artifact/boot/loader.efi" ]
[ -f "$bootloader_artifact/boot/defaults/loader.conf" ]
[ -f "$bootloader_artifact/boot/lua/loader.lua" ]
sha_kernel=$(sha256 -q "$kernel_artifact/boot/kernel/kernel")
sha_loader=$(sha256 -q "$bootloader_artifact/boot/loader.efi")
sha_param=$(sha256 -q "$headers_artifact/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' ' ')
@@ -120,46 +147,131 @@ 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}')
result_root_size=$(du -sh "$result_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"
world_artifact_size=$(du -sh "$world_artifact" | awk '{print $1}')
kernel_artifact_size=$(du -sh "$kernel_artifact" | awk '{print $1}')
headers_artifact_size=$(du -sh "$headers_artifact" | awk '{print $1}')
bootloader_artifact_size=$(du -sh "$bootloader_artifact" | awk '{print $1}')
rm -f "$latest_link"
ln -s "$result_root" "$latest_link"
cat >"$promotion_file" <<EOF2
((native-build-result-version . "1")
(executor . ((kind . ssh-guest)
(name . "ssh-guest")
(version . "1")
(properties . ((transport . "ssh")
(orchestrator . "host")
(guest-host-name . "$guest_host_name")
(guest-ip . "$GUEST_IP")
(vm-id . "$VM_ID")
(vdi-id . "$VDI_ID")))))
(run-id . "$run_id")
(guest-host-name . "$guest_host_name")
(closure-path . "$closure")
(build-profile . "/run/current-system/build-profile")
(freebsd-base . ((name . "default")
(version-label . "15.0-STABLE")
(release . "15.0-STABLE")
(branch . "stable/15")
(source-root . "/usr/src")
(target . "amd64")
(target-arch . "amd64")
(kernconf . "GENERIC")))
(source . ((store-path . "$source_store")
(source-root . "$source_root")))
(build-policy . ((jobs . "$BUILD_JOBS")
(build-common . "$build_common")
(install-common . "$install_common")))
(artifacts . ((world . ((path . "artifacts/world")
(required-file . "bin/sh")))
(kernel . ((path . "artifacts/kernel")
(required-file . "boot/kernel/kernel")
(recorded-sha256 . "$sha_kernel")))
(headers . ((path . "artifacts/headers")
(required-file . "usr/include/sys/param.h")
(recorded-sha256 . "$sha_param")))
(bootloader . ((path . "artifacts/bootloader")
(required-file . "boot/loader.efi")
(recorded-sha256 . "$sha_loader"))))))
EOF2
cat >"$guest_metadata_file" <<EOF2
run_id=$run_id
executor_kind=ssh-guest
executor_name=ssh-guest
executor_version=1
guest_host_name=$guest_host_name
closure_path=$closure
source_store=$source_store
source_root=$source_root
build_jobs=$BUILD_JOBS
build_common=$build_common
install_common=$install_common
build_root=$build_root
result_root=$result_root
logdir=$logdir
status_file=$status_file
metadata_file=$guest_metadata_file
promotion_file=$promotion_file
world_stage=$world_stage
kernel_stage=$kernel_stage
headers_stage=$headers_stage
bootloader_stage=$bootloader_stage
world_artifact=$world_artifact
kernel_artifact=$kernel_artifact
headers_artifact=$headers_artifact
bootloader_artifact=$bootloader_artifact
latest_link=$latest_link
root_df=$root_df
build_root_size=$build_root_size
result_root_size=$result_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
world_artifact_size=$world_artifact_size
kernel_artifact_size=$kernel_artifact_size
headers_artifact_size=$headers_artifact_size
bootloader_artifact_size=$bootloader_artifact_size
buildworld_log=$logdir/buildworld.log
buildkernel_log=$logdir/buildkernel.log
installworld_log=$logdir/installworld.log
distribution_log=$logdir/distribution.log
installkernel_log=$logdir/installkernel.log
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
host_initiated_native_build=ok
EOF2
printf 'ok\n' > "$status_file"
cat "$guest_metadata_file"
EOF
)
build_jobs=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^build_jobs=//p')
run_id=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^run_id=//p')
executor_kind=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^executor_kind=//p')
executor_name=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^executor_name=//p')
executor_version=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^executor_version=//p')
guest_host_name=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^guest_host_name=//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_jobs=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^build_jobs=//p')
build_common=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^build_common=//p')
install_common=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^install_common=//p')
build_root=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^build_root=//p')
result_root=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^result_root=//p')
logdir=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^logdir=//p')
status_file=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^status_file=//p')
guest_metadata_file=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^metadata_file=//p')
promotion_file=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^promotion_file=//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')
@@ -169,12 +281,22 @@ world_stage=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^world_stage=//
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')
world_artifact=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^world_artifact=//p')
kernel_artifact=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^kernel_artifact=//p')
headers_artifact=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^headers_artifact=//p')
bootloader_artifact=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^bootloader_artifact=//p')
latest_link=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^latest_link=//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')
result_root_size=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^result_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')
world_artifact_size=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^world_artifact_size=//p')
kernel_artifact_size=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^kernel_artifact_size=//p')
headers_artifact_size=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^headers_artifact_size=//p')
bootloader_artifact_size=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^bootloader_artifact_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')
@@ -184,6 +306,17 @@ installworld_tail=$(printf '%s\n' "$native_build_metadata" | sed -n 's/^installw
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')
status_value=$(ssh_guest "cat '$status_file'")
latest_target=$(ssh_guest "readlink '$latest_link'")
ssh_guest "[ -f '$promotion_file' ]"
ssh_guest "[ -f '$world_artifact/bin/sh' ]"
[ "$executor_kind" = ssh-guest ] || { echo "unexpected executor kind: $executor_kind" >&2; exit 1; }
[ "$executor_name" = ssh-guest ] || { echo "unexpected executor name: $executor_name" >&2; exit 1; }
[ "$executor_version" = 1 ] || { echo "unexpected executor version: $executor_version" >&2; exit 1; }
[ "$status_value" = ok ] || { echo "host-initiated build status is not ok: $status_value" >&2; exit 1; }
[ "$latest_target" = "$result_root" ] || { echo "latest link target mismatch: $latest_target" >&2; exit 1; }
case "$source_store" in
/frx/store/*-freebsd-source-*) : ;;
*) echo "unexpected source store path: $source_store" >&2; exit 1 ;;
@@ -196,6 +329,10 @@ case "$build_root" in
/var/tmp/fruix-phase20-native-build) : ;;
*) echo "unexpected build root: $build_root" >&2; exit 1 ;;
esac
case "$result_root" in
/var/lib/fruix/native-builds/*) : ;;
*) echo "unexpected result root: $result_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
@@ -227,11 +364,22 @@ vm_id=$vm_id
vdi_id=$vdi_id
guest_ip=$guest_ip
root_size=$root_size
run_id=$run_id
executor_kind=$executor_kind
executor_name=$executor_name
executor_version=$executor_version
guest_host_name=$guest_host_name
build_jobs=$build_jobs
source_store=$source_store
source_root=$source_root
build_common=$build_common
install_common=$install_common
build_root=$build_root
result_root=$result_root
logdir=$logdir
status_file=$status_file
guest_metadata_file=$guest_metadata_file
promotion_file=$promotion_file
buildworld_log=$buildworld_log
buildkernel_log=$buildkernel_log
installworld_log=$installworld_log
@@ -241,12 +389,24 @@ world_stage=$world_stage
kernel_stage=$kernel_stage
headers_stage=$headers_stage
bootloader_stage=$bootloader_stage
world_artifact=$world_artifact
kernel_artifact=$kernel_artifact
headers_artifact=$headers_artifact
bootloader_artifact=$bootloader_artifact
latest_link=$latest_link
latest_target=$latest_target
status_value=$status_value
root_df=$root_df
build_root_size=$build_root_size
result_root_size=$result_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
world_artifact_size=$world_artifact_size
kernel_artifact_size=$kernel_artifact_size
headers_artifact_size=$headers_artifact_size
bootloader_artifact_size=$bootloader_artifact_size
sha_kernel=$sha_kernel
sha_loader=$sha_loader
sha_param=$sha_param

View File

@@ -104,6 +104,9 @@ field() {
sed -n "s/^$1=//p" "$promotion_out" | tail -n 1
}
executor_kind=$(field executor_kind)
executor_name=$(field executor_name)
executor_version=$(field executor_version)
result_store=$(field result_store)
result_metadata_file=$(field result_metadata_file)
artifact_store_count=$(field artifact_store_count)
@@ -113,6 +116,9 @@ kernel_store=$(field kernel_store)
headers_store=$(field headers_store)
bootloader_store=$(field bootloader_store)
[ "$executor_kind" = self-hosted ] || { echo "unexpected executor kind: $executor_kind" >&2; exit 1; }
[ "$executor_name" = guest-self-hosted ] || { echo "unexpected executor name: $executor_name" >&2; exit 1; }
[ "$executor_version" = 5 ] || { echo "unexpected executor version: $executor_version" >&2; exit 1; }
[ "$artifact_store_count" = 4 ] || { echo "unexpected artifact store count: $artifact_store_count" >&2; exit 1; }
case "$result_store" in
/frx/store/*-fruix-native-build-result-*-guest-self-hosted) : ;;
@@ -160,14 +166,22 @@ promoted_param_sha=$(sha256 -q "$headers_store/usr/include/sys/param.h")
[ "$promoted_loader_sha" = "$sha_loader" ] || { echo "loader sha mismatch after promotion" >&2; exit 1; }
[ "$promoted_param_sha" = "$sha_param" ] || { echo "param.h sha mismatch after promotion" >&2; exit 1; }
grep -F '(executor . "guest-self-hosted")' "$result_metadata_file" >/dev/null || {
echo "result metadata file is missing guest-self-hosted executor" >&2
grep -F '(executor-kind . self-hosted)' "$result_metadata_file" >/dev/null || {
echo "result metadata file is missing self-hosted executor kind" >&2
exit 1
}
grep -F '(executor-name . "guest-self-hosted")' "$result_metadata_file" >/dev/null || {
echo "result metadata file is missing guest-self-hosted executor name" >&2
exit 1
}
grep -F "$source_store" "$result_metadata_file" >/dev/null || {
echo "result metadata file is missing source store provenance" >&2
exit 1
}
grep -F '(build-profile . "/run/current-system/build-profile")' "$result_metadata_file" >/dev/null || {
echo "result metadata file is missing build-profile provenance" >&2
exit 1
}
grep -F '(artifact-kind . kernel)' "$kernel_store/.fruix-native-build-object.scm" >/dev/null || {
echo "kernel store metadata is missing artifact kind" >&2
exit 1
@@ -197,6 +211,9 @@ guest_headers_artifact=$headers_artifact
guest_bootloader_artifact=$bootloader_artifact
local_result_root=$local_result_root
store_dir=$store_dir
executor_kind=$executor_kind
executor_name=$executor_name
executor_version=$executor_version
result_store=$result_store
result_metadata_file=$result_metadata_file
artifact_store_count=$artifact_store_count

View File

@@ -0,0 +1,227 @@
#!/bin/sh
set -eu
repo_root=${PROJECT_ROOT:-$(pwd)}
os_template=${OS_TEMPLATE:-$repo_root/tests/system/phase20-promoted-native-base-operating-system.scm.in}
system_name=${SYSTEM_NAME:-phase20-promoted-native-base-operating-system}
root_size=${ROOT_SIZE:-12g}
result_store=${RESULT_STORE:-}
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-promoted-native-base-xcpng.XXXXXX)
cleanup=1
fi
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
cleanup=0
fi
promotion_metadata=$workdir/phase20-promoted-native-base-promotion-metadata.txt
prepared_template=$workdir/phase20-promoted-native-base-operating-system.scm.in
inner_metadata=$workdir/phase20-promoted-native-base-inner-metadata.txt
metadata_file=$workdir/phase20-promoted-native-base-declaration-xcpng-metadata.txt
cleanup_workdir() {
if [ "$cleanup" -eq 1 ]; then
rm -rf "$workdir"
fi
}
trap cleanup_workdir EXIT INT TERM
if [ -z "$result_store" ]; then
KEEP_WORKDIR=1 WORKDIR="$workdir/promotion" METADATA_OUT="$promotion_metadata" \
ROOT_AUTHORIZED_KEY_FILE="$root_authorized_key_file" \
ROOT_SSH_PRIVATE_KEY_FILE="$root_ssh_private_key_file" \
ROOT_SIZE=20g \
"$repo_root/tests/system/run-phase20-host-initiated-native-build-store-promotion-xcpng.sh"
result_store=$(sed -n 's/^result_store=//p' "$promotion_metadata")
fi
[ -n "$result_store" ] || { echo "missing promoted result store" >&2; exit 1; }
[ -d "$result_store" ] || { echo "promoted result store does not exist: $result_store" >&2; exit 1; }
[ -f "$result_store/.fruix-native-build-result.scm" ] || {
echo "promoted result store is missing .fruix-native-build-result.scm" >&2
exit 1
}
[ -L "$result_store/artifacts/world" ] || { echo "promoted result store is missing world artifact link" >&2; exit 1; }
[ -L "$result_store/artifacts/kernel" ] || { echo "promoted result store is missing kernel artifact link" >&2; exit 1; }
[ -L "$result_store/artifacts/headers" ] || { echo "promoted result store is missing headers artifact link" >&2; exit 1; }
[ -L "$result_store/artifacts/bootloader" ] || { echo "promoted result store is missing bootloader artifact link" >&2; exit 1; }
world_store=$(readlink "$result_store/artifacts/world")
kernel_store=$(readlink "$result_store/artifacts/kernel")
headers_store=$(readlink "$result_store/artifacts/headers")
bootloader_store=$(readlink "$result_store/artifacts/bootloader")
result_metadata_file=$result_store/.fruix-native-build-result.scm
executor_kind=$(grep -o '(executor-kind \. [^)]*)' "$result_metadata_file" | head -n 1 | sed 's/(executor-kind \. //; s/)$//')
executor_name=$(grep -o '(executor-name \. "[^"]*")' "$result_metadata_file" | head -n 1 | sed 's/(executor-name \. "//; s/")$//')
executor_version=$(grep -o '(executor-version \. "[^"]*")' "$result_metadata_file" | head -n 1 | sed 's/(executor-version \. "//; s/")$//')
[ -f "$world_store/bin/sh" ] || { echo "promoted world store is missing /bin/sh" >&2; exit 1; }
[ -f "$kernel_store/boot/kernel/kernel" ] || { echo "promoted kernel store is missing kernel" >&2; exit 1; }
[ -f "$headers_store/usr/include/sys/param.h" ] || { echo "promoted headers store is missing param.h" >&2; exit 1; }
[ -f "$bootloader_store/boot/loader.efi" ] || { echo "promoted bootloader store is missing loader.efi" >&2; exit 1; }
sed "s|__PROMOTED_RESULT_STORE__|$result_store|g" "$os_template" > "$prepared_template"
action_metadata=${promotion_metadata:-}
ROOT_SIZE="$root_size" KEEP_WORKDIR=1 WORKDIR="$workdir/boot" METADATA_OUT="$inner_metadata" \
ROOT_AUTHORIZED_KEY_FILE="$root_authorized_key_file" \
ROOT_SSH_PRIVATE_KEY_FILE="$root_ssh_private_key_file" \
OS_TEMPLATE="$prepared_template" SYSTEM_NAME="$system_name" \
"$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")
promoted_result_file=$closure_path/metadata/promoted-native-build-result.scm
store_layout_file=$closure_path/metadata/store-layout.scm
[ "$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
[ -f "$promoted_result_file" ] || { echo "closure is missing promoted native build result metadata" >&2; exit 1; }
[ -f "$store_layout_file" ] || { echo "closure is missing store layout metadata" >&2; exit 1; }
grep -F "$result_store" "$promoted_result_file" >/dev/null || {
echo "closure promoted result metadata does not reference the selected result store" >&2
exit 1
}
grep -F "$result_store" "$closure_path/.references" >/dev/null || {
echo "closure references do not retain the promoted result store" >&2
exit 1
}
grep -F "$kernel_store" "$promoted_result_file" >/dev/null || {
echo "closure promoted result metadata does not reference the promoted kernel store" >&2
exit 1
}
grep -F "$bootloader_store" "$promoted_result_file" >/dev/null || {
echo "closure promoted result metadata does not reference the promoted bootloader store" >&2
exit 1
}
grep -F "$result_store" "$store_layout_file" >/dev/null || {
echo "store layout metadata does not record the promoted result store" >&2
exit 1
}
kernel_link=$(readlink "$closure_path/boot/kernel/kernel")
bootloader_link=$(readlink "$closure_path/boot/loader.efi")
[ "$kernel_link" = "$kernel_store/boot/kernel/kernel" ] || {
echo "closure kernel link does not target the promoted kernel store" >&2
exit 1
}
[ "$bootloader_link" = "$bootloader_store/boot/loader.efi" ] || {
echo "closure bootloader link does not target the promoted bootloader store" >&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_promoted_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
[ -f /run/current-system/metadata/promoted-native-build-result.scm ]
grep -F '$result_store' /run/current-system/metadata/promoted-native-build-result.scm >/dev/null
grep -F '$result_store' /run/current-system/.references >/dev/null
kernel_link=$(readlink /run/current-system/boot/kernel/kernel)
bootloader_link=$(readlink /run/current-system/boot/loader.efi)
[ "$kernel_link" = "$kernel_store/boot/kernel/kernel" ]
[ "$bootloader_link" = "$bootloader_store/boot/loader.efi" ]
[ -x /bin/sh ]
[ -x /usr/sbin/sshd ]
printf 'kernel_link=%s\n' "$kernel_link"
printf 'bootloader_link=%s\n' "$bootloader_link"
printf 'uname=%s\n' "$(uname -sr)"
printf 'promoted_result_store=%s\n' '$result_store'
EOF
)
guest_kernel_link=$(printf '%s\n' "$guest_promoted_metadata" | sed -n 's/^kernel_link=//p')
guest_bootloader_link=$(printf '%s\n' "$guest_promoted_metadata" | sed -n 's/^bootloader_link=//p')
guest_uname=$(printf '%s\n' "$guest_promoted_metadata" | sed -n 's/^uname=//p')
[ "$guest_kernel_link" = "$kernel_store/boot/kernel/kernel" ] || {
echo "guest kernel link does not target the promoted kernel store" >&2
exit 1
}
[ "$guest_bootloader_link" = "$bootloader_store/boot/loader.efi" ] || {
echo "guest bootloader link does not target the promoted bootloader store" >&2
exit 1
}
cat >"$metadata_file" <<EOF
workdir=$workdir
promotion_metadata=$promotion_metadata
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
result_store=$result_store
result_metadata_file=$result_metadata_file
executor_kind=$executor_kind
executor_name=$executor_name
executor_version=$executor_version
world_store=$world_store
kernel_store=$kernel_store
headers_store=$headers_store
bootloader_store=$bootloader_store
promoted_result_file=$promoted_result_file
store_layout_file=$store_layout_file
kernel_link=$kernel_link
bootloader_link=$bootloader_link
guest_kernel_link=$guest_kernel_link
guest_bootloader_link=$guest_bootloader_link
guest_uname=$guest_uname
boot_backend=xcp-ng-xo-cli
init_mode=shepherd-pid1
promoted_native_base_declaration=ok
EOF
if [ -n "$metadata_target" ]; then
mkdir -p "$(dirname "$metadata_target")"
cp "$metadata_file" "$metadata_target"
fi
printf 'PASS phase20-promoted-native-base-declaration-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"

View File

@@ -70,7 +70,9 @@ case "$guest_build_jobs" in
;;
esac
ssh_guest '[ -x /usr/local/bin/fruix-build-environment ]'
ssh_guest '[ -x /usr/local/bin/fruix-self-hosted-native-build ]'
ssh_guest '[ -L /run/current-build ]'
ssh_guest '[ -L /usr/include ]'
ssh_guest '[ -L /usr/share/mk ]'
@@ -78,6 +80,10 @@ self_hosted_metadata=$(ssh_guest env FRUIX_SELF_HOSTED_NATIVE_BUILD_JOBS="$guest
run_id=$(printf '%s\n' "$self_hosted_metadata" | sed -n 's/^run_id=//p')
helper_version=$(printf '%s\n' "$self_hosted_metadata" | sed -n 's/^helper_version=//p')
executor_kind=$(printf '%s\n' "$self_hosted_metadata" | sed -n 's/^executor_kind=//p')
executor_name=$(printf '%s\n' "$self_hosted_metadata" | sed -n 's/^executor_name=//p')
executor_version=$(printf '%s\n' "$self_hosted_metadata" | sed -n 's/^executor_version=//p')
build_profile=$(printf '%s\n' "$self_hosted_metadata" | sed -n 's/^build_profile=//p')
source_store=$(printf '%s\n' "$self_hosted_metadata" | sed -n 's/^source_store=//p')
source_root=$(printf '%s\n' "$self_hosted_metadata" | sed -n 's/^source_root=//p')
build_jobs=$(printf '%s\n' "$self_hosted_metadata" | sed -n 's/^build_jobs=//p')
@@ -118,7 +124,11 @@ latest_target=$(ssh_guest "readlink '$latest_link'")
ssh_guest "[ -f '$promotion_file' ]"
ssh_guest "[ -f '$world_artifact/bin/sh' ]"
[ "$helper_version" = 3 ] || { echo "unexpected helper version: $helper_version" >&2; exit 1; }
[ "$helper_version" = 5 ] || { echo "unexpected helper version: $helper_version" >&2; exit 1; }
[ "$executor_kind" = self-hosted ] || { echo "unexpected executor kind: $executor_kind" >&2; exit 1; }
[ "$executor_name" = guest-self-hosted ] || { echo "unexpected executor name: $executor_name" >&2; exit 1; }
[ "$executor_version" = 5 ] || { echo "unexpected executor version: $executor_version" >&2; exit 1; }
[ "$build_profile" = /run/current-system/build-profile ] || { echo "unexpected build profile: $build_profile" >&2; exit 1; }
[ "$build_jobs" = "$guest_build_jobs" ] || { echo "unexpected build job count: $build_jobs" >&2; exit 1; }
[ "$status_value" = ok ] || { echo "self-hosted build status is not ok: $status_value" >&2; exit 1; }
[ "$latest_target" = "$result_root" ] || { echo "latest link target mismatch: $latest_target" >&2; exit 1; }
@@ -197,7 +207,11 @@ guest_ip=$guest_ip
root_size=$root_size
run_id=$run_id
helper_version=$helper_version
executor_kind=$executor_kind
executor_name=$executor_name
executor_version=$executor_version
build_jobs=$build_jobs
build_profile=$build_profile
source_store=$source_store
source_root=$source_root
build_common=$build_common

View File

@@ -104,11 +104,11 @@ image_size_bytes=$(stat -f '%z' "$disk_image")
closure_base=$(basename "$closure_path")
case "$image_store_path" in
/frx/store/*-fruix-bhyve-image-fruix-freebsd) : ;;
/frx/store/*-fruix-bhyve-image-*) : ;;
*) echo "unexpected image store path: $image_store_path" >&2; exit 1 ;;
esac
case "$disk_image" in
/frx/store/*-fruix-bhyve-image-fruix-freebsd/disk.img) : ;;
/frx/store/*-fruix-bhyve-image-*/disk.img) : ;;
*) echo "unexpected disk image path: $disk_image" >&2; exit 1 ;;
esac
@@ -142,7 +142,7 @@ if [ -L "$mnt_root/etc/master.passwd" ]; then master_passwd_kind=symlink; elif [
loader_conf_image=$mnt_root/frx/store/$closure_base/boot/loader.conf
rc_conf_image=$mnt_root/frx/store/$closure_base/etc/rc.conf
grep -F 'comconsole' "$loader_conf_image" >/dev/null || { echo "loader.conf is missing serial console config" >&2; exit 1; }
grep -F 'hostname="fruix-freebsd"' "$rc_conf_image" >/dev/null || { echo "rc.conf is missing hostname" >&2; exit 1; }
grep -E '^hostname=".+"$' "$rc_conf_image" >/dev/null || { echo "rc.conf is missing hostname" >&2; exit 1; }
[ -f "$host_base_provenance_file" ] || { echo "missing host base provenance file: $host_base_provenance_file" >&2; exit 1; }
[ -f "$store_layout_file" ] || { echo "missing store layout file: $store_layout_file" >&2; exit 1; }
[ -n "$host_freebsd_version" ] || { echo "missing host freebsd version provenance" >&2; exit 1; }

View File

@@ -0,0 +1,271 @@
#!/bin/sh
set -eu
repo_root=${PROJECT_ROOT:-$(pwd)}
os_template=${OS_TEMPLATE:-$repo_root/tests/system/postphase20-installed-node-operating-system.scm.in}
system_name=${SYSTEM_NAME:-postphase20-installed-node-operating-system}
result_store=${RESULT_STORE:-/frx/store/ffe44f5d1ba576e1f811ad3fe3a526a242b5c4a5-fruix-native-build-result-15.0-STABLE-ssh-guest}
root_size=${ROOT_SIZE:-12g}
current_host_name=${CURRENT_HOST_NAME:-fruix-node-current}
candidate_host_name=${CANDIDATE_HOST_NAME:-fruix-node-canary}
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-postphase20-installed-node-xcpng.XXXXXX)
cleanup=1
fi
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
cleanup=0
fi
current_os_file=$workdir/current-operating-system.scm
candidate_os_file=$workdir/candidate-operating-system.scm
inner_metadata=$workdir/postphase20-installed-node-inner-metadata.txt
current_build_out=$workdir/current-build.txt
candidate_build_out=$workdir/candidate-build.txt
reconfigure_out=$workdir/reconfigure.txt
rollback_out=$workdir/rollback.txt
post_reconfigure_status=$workdir/post-reconfigure-status.txt
post_boot_candidate_status=$workdir/post-boot-candidate-status.txt
post_boot_rollback_status=$workdir/post-boot-rollback-status.txt
metadata_file=$workdir/postphase20-installed-node-build-reconfigure-xcpng-metadata.txt
cleanup_workdir() {
if [ "$cleanup" -eq 1 ]; then
rm -rf "$workdir"
fi
}
trap cleanup_workdir EXIT INT TERM
[ -f "$os_template" ] || { echo "missing operating-system template: $os_template" >&2; exit 1; }
[ -f "$root_authorized_key_file" ] || { echo "missing root authorized key file: $root_authorized_key_file" >&2; exit 1; }
[ -f "$root_ssh_private_key_file" ] || { echo "missing root SSH private key file: $root_ssh_private_key_file" >&2; exit 1; }
[ -d "$result_store" ] || { echo "promoted result store does not exist: $result_store" >&2; exit 1; }
root_authorized_key=$(tr -d '\n' < "$root_authorized_key_file")
render_os() {
output=$1
host_name=$2
sed \
-e "s|__PROMOTED_RESULT_STORE__|$result_store|g" \
-e "s|__HOST_NAME__|$host_name|g" \
-e "s|__ROOT_AUTHORIZED_KEY__|$root_authorized_key|g" \
"$os_template" > "$output"
}
render_os "$current_os_file" "$current_host_name"
render_os "$candidate_os_file" "$candidate_host_name"
KEEP_WORKDIR=1 WORKDIR="$workdir/boot" METADATA_OUT="$inner_metadata" \
ROOT_AUTHORIZED_KEY_FILE="$root_authorized_key_file" \
ROOT_SSH_PRIVATE_KEY_FILE="$root_ssh_private_key_file" \
OS_TEMPLATE="$current_os_file" 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")
[ "$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 \
"$closure_path/metadata/system-declaration.scm" \
"$closure_path/metadata/system-declaration-info.scm" \
"$closure_path/metadata/system-declaration-system" \
"$closure_path/share/fruix/node/scripts/fruix.scm" \
"$closure_path/share/fruix/node/modules/fruix/system/freebsd/render.scm" \
"$closure_path/share/fruix/node/guix/guix/build/utils.scm"
do
[ -f "$path" ] || {
echo "required installed-node path missing: $path" >&2
exit 1
}
done
grep -F "$current_host_name" "$closure_path/metadata/system-declaration.scm" >/dev/null || {
echo "embedded declaration does not mention current host name" >&2
exit 1
}
grep -F "$system_name" "$closure_path/metadata/system-declaration-system" >/dev/null || {
echo "embedded declaration system name is missing" >&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" "$@"
}
scp_guest() {
scp -O -i "$root_ssh_private_key_file" \
-o BatchMode=yes \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o ConnectTimeout=5 \
"$@"
}
wait_for_ssh() {
for attempt in $(jot 120 1 120); do
if ssh_guest 'service sshd onestatus >/dev/null 2>&1' >/dev/null 2>&1; then
return 0
fi
sleep 2
done
return 1
}
reboot_guest() {
ssh_guest 'shutdown -r now >/dev/null 2>&1 || reboot >/dev/null 2>&1 || true' >/dev/null 2>&1 || true
sleep 5
wait_for_ssh || {
echo "guest did not return over SSH after reboot" >&2
exit 1
}
}
ssh_guest 'sh -s' <<EOF
set -eu
[ -x /usr/local/bin/fruix ]
[ -f /run/current-system/metadata/system-declaration.scm ]
[ -f /run/current-system/metadata/system-declaration-info.scm ]
[ -f /run/current-system/metadata/system-declaration-system ]
[ -f /run/current-system/share/fruix/node/scripts/fruix.scm ]
[ -f /run/current-system/share/fruix/node/modules/fruix/system/freebsd/media.scm ]
[ -f /run/current-system/share/fruix/node/guix/guix/build/utils.scm ]
grep -F '$system_name' /run/current-system/metadata/system-declaration-system >/dev/null
EOF
ssh_guest '/usr/local/bin/fruix system build' > "$current_build_out"
current_built_closure=$(sed -n 's/^closure_path=//p' "$current_build_out" | tail -n 1)
[ -n "$current_built_closure" ] || { echo "missing closure_path from in-system build output" >&2; exit 1; }
case "$current_built_closure" in
/frx/store/*-fruix-system-$current_host_name) : ;;
*)
echo "in-system build of current declaration produced an unexpected closure path: $current_built_closure" >&2
exit 1
;;
esac
scp_guest "$candidate_os_file" root@"$guest_ip":/root/candidate.scm >/dev/null
ssh_guest "/usr/local/bin/fruix system build /root/candidate.scm --system $system_name" > "$candidate_build_out"
candidate_closure=$(sed -n 's/^closure_path=//p' "$candidate_build_out" | tail -n 1)
[ -n "$candidate_closure" ] || { echo "missing candidate closure_path from in-system build output" >&2; exit 1; }
[ "$candidate_closure" != "$closure_path" ] || {
echo "candidate closure unexpectedly matches current closure" >&2
exit 1
}
ssh_guest "/usr/local/bin/fruix system reconfigure /root/candidate.scm --system $system_name" > "$reconfigure_out"
reconfigure_closure=$(sed -n 's/^reconfigure_closure=//p' "$reconfigure_out" | tail -n 1)
reconfigure_current_generation=$(sed -n 's/^current_generation=//p' "$reconfigure_out" | tail -n 1)
reconfigure_current_closure=$(sed -n 's/^current_closure=//p' "$reconfigure_out" | tail -n 1)
reconfigure_rollback_generation=$(sed -n 's/^rollback_generation=//p' "$reconfigure_out" | tail -n 1)
reconfigure_rollback_closure=$(sed -n 's/^rollback_closure=//p' "$reconfigure_out" | tail -n 1)
[ "$reconfigure_closure" = "$candidate_closure" ] || { echo "reconfigure closure mismatch" >&2; exit 1; }
[ "$reconfigure_current_generation" = 2 ] || { echo "unexpected current generation after reconfigure: $reconfigure_current_generation" >&2; exit 1; }
[ "$reconfigure_current_closure" = "$candidate_closure" ] || { echo "unexpected current closure after reconfigure" >&2; exit 1; }
[ "$reconfigure_rollback_generation" = 1 ] || { echo "unexpected rollback generation after reconfigure: $reconfigure_rollback_generation" >&2; exit 1; }
[ "$reconfigure_rollback_closure" = "$closure_path" ] || { echo "unexpected rollback closure after reconfigure" >&2; exit 1; }
ssh_guest '/usr/local/bin/fruix system status' > "$post_reconfigure_status"
reboot_guest
candidate_hostname=$(ssh_guest 'hostname')
candidate_run_current=$(ssh_guest 'readlink /run/current-system')
ssh_guest '/usr/local/bin/fruix system status' > "$post_boot_candidate_status"
[ "$candidate_hostname" = "$candidate_host_name" ] || { echo "unexpected host name after candidate boot: $candidate_hostname" >&2; exit 1; }
[ "$candidate_run_current" = "$candidate_closure" ] || { echo "unexpected current closure after candidate boot: $candidate_run_current" >&2; exit 1; }
ssh_guest '/usr/local/bin/fruix system rollback' > "$rollback_out"
rollback_current_generation=$(sed -n 's/^current_generation=//p' "$rollback_out" | tail -n 1)
rollback_current_closure=$(sed -n 's/^current_closure=//p' "$rollback_out" | tail -n 1)
rollback_rollback_generation=$(sed -n 's/^rollback_generation=//p' "$rollback_out" | tail -n 1)
rollback_rollback_closure=$(sed -n 's/^rollback_closure=//p' "$rollback_out" | tail -n 1)
[ "$rollback_current_generation" = 1 ] || { echo "unexpected current generation after rollback: $rollback_current_generation" >&2; exit 1; }
[ "$rollback_current_closure" = "$closure_path" ] || { echo "unexpected current closure after rollback" >&2; exit 1; }
[ "$rollback_rollback_generation" = 2 ] || { echo "unexpected rollback generation after rollback: $rollback_rollback_generation" >&2; exit 1; }
[ "$rollback_rollback_closure" = "$candidate_closure" ] || { echo "unexpected rollback closure after rollback" >&2; exit 1; }
reboot_guest
rollback_hostname=$(ssh_guest 'hostname')
rollback_run_current=$(ssh_guest 'readlink /run/current-system')
ssh_guest '/usr/local/bin/fruix system status' > "$post_boot_rollback_status"
[ "$rollback_hostname" = "$current_host_name" ] || { echo "unexpected host name after rollback boot: $rollback_hostname" >&2; exit 1; }
[ "$rollback_run_current" = "$closure_path" ] || { echo "unexpected current closure after rollback boot: $rollback_run_current" >&2; exit 1; }
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
result_store=$result_store
current_host_name=$current_host_name
candidate_host_name=$candidate_host_name
current_build_out=$current_build_out
current_built_closure=$current_built_closure
candidate_build_out=$candidate_build_out
candidate_closure=$candidate_closure
reconfigure_out=$reconfigure_out
reconfigure_closure=$reconfigure_closure
reconfigure_current_generation=$reconfigure_current_generation
reconfigure_current_closure=$reconfigure_current_closure
reconfigure_rollback_generation=$reconfigure_rollback_generation
reconfigure_rollback_closure=$reconfigure_rollback_closure
candidate_hostname=$candidate_hostname
candidate_run_current=$candidate_run_current
rollback_out=$rollback_out
rollback_current_generation=$rollback_current_generation
rollback_current_closure=$rollback_current_closure
rollback_rollback_generation=$rollback_rollback_generation
rollback_rollback_closure=$rollback_rollback_closure
rollback_hostname=$rollback_hostname
rollback_run_current=$rollback_run_current
boot_backend=xcp-ng-xo-cli
init_mode=shepherd-pid1
installed_node_build_reconfigure=ok
EOF
if [ -n "$metadata_target" ]; then
mkdir -p "$(dirname "$metadata_target")"
cp "$metadata_file" "$metadata_target"
fi
printf 'PASS postphase20-installed-node-build-reconfigure-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"