You've already forked fruix-bootstrap
bootstrap: load pinned fruix checkout
This commit is contained in:
@@ -1,17 +1,56 @@
|
||||
# Fruix
|
||||
# Fruix Bootstrap
|
||||
|
||||
Fruix is a Guix-like system running on FreeBSD but not GNU, using Shepherd, building GNU packages, with a BSD userland, and a functional store similar to Nix but not Nix.
|
||||
`fruix-bootstrap` is the foreign-FreeBSD bring-up layer for Fruix.
|
||||
|
||||
In Fruix, the FreeBSD platform is represented as foundational store artifacts and updated through the same generation mechanism as the rest of the system.
|
||||
Its job is to turn a plain FreeBSD installation into a **Fruix builder**: an environment capable of evaluating and materializing a pinned Fruix checkout.
|
||||
|
||||
Fruix is a system where everything that exists on the machine exists for a reason that can be explained.
|
||||
## What belongs here
|
||||
|
||||
Every Fruix system must remain fully understandable and recoverable using only text files, a shell, and standard system tools.
|
||||
Bootstrap-only responsibilities such as:
|
||||
|
||||
- Every host has a local config repository.
|
||||
- Every host has a persistent system identity key.
|
||||
- Every applied change corresponds to a commit and a generation.
|
||||
- Secrets are declared in config but realized only at runtime.
|
||||
- Secrets are encrypted to explicit recipients derived from host/user identity.
|
||||
- Services explicitly declare their secret dependencies.
|
||||
- The orchestration layer operates only through these primitives.
|
||||
- host environment checks
|
||||
- locating or building bootstrap Guile / support bits
|
||||
- wrapper entrypoints that invoke a pinned Fruix checkout
|
||||
- validation of the path from vanilla FreeBSD to first Fruix artifact
|
||||
|
||||
## What does not belong here
|
||||
|
||||
Long-lived Fruix product logic should live in the canonical `fruix` repo/channel instead:
|
||||
|
||||
- package definitions
|
||||
- system definitions
|
||||
- installer logic
|
||||
- deployment logic
|
||||
- installed-node lifecycle behavior
|
||||
- long-lived metadata semantics
|
||||
|
||||
## Default channel checkout
|
||||
|
||||
Bootstrap currently assumes a sibling canonical Fruix checkout at:
|
||||
|
||||
- `../fruix`
|
||||
|
||||
and bakes in the original channel repo URL as the default origin reference:
|
||||
|
||||
- `https://git.teralink.net/self/fruix.git`
|
||||
|
||||
Override the checkout location with:
|
||||
|
||||
- `FRUIX_CHANNEL_DIR=/path/to/fruix`
|
||||
|
||||
## Design docs
|
||||
|
||||
- `docs/bootstrap.md` — boundary definition
|
||||
- `docs/PLAN_5.md` — migration plan
|
||||
|
||||
## Intended lifecycle
|
||||
|
||||
```text
|
||||
plain FreeBSD
|
||||
-> fruix-bootstrap
|
||||
-> Fruix builder
|
||||
-> pinned fruix checkout
|
||||
-> build installer ISO / VM image / system closure
|
||||
-> booted Fruix node
|
||||
-> future lifecycle managed by fruix
|
||||
```
|
||||
|
||||
@@ -1,12 +1,32 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
project_root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
||||
bootstrap_root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
||||
default_channel_dir=$bootstrap_root/../fruix
|
||||
if [ -d "$default_channel_dir" ]; then
|
||||
default_channel_dir=$(CDPATH= cd -- "$default_channel_dir" && pwd)
|
||||
fi
|
||||
|
||||
fruix_channel_dir=${FRUIX_CHANNEL_DIR:-$default_channel_dir}
|
||||
fruix_channel_url=${FRUIX_CHANNEL_URL:-https://git.teralink.net/self/fruix.git}
|
||||
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}
|
||||
script=$project_root/scripts/fruix.scm
|
||||
script=$fruix_channel_dir/scripts/fruix.scm
|
||||
modules_dir=$fruix_channel_dir/modules
|
||||
|
||||
if [ ! -d "$fruix_channel_dir" ]; then
|
||||
echo "Fruix channel checkout not found: $fruix_channel_dir" >&2
|
||||
echo "Set FRUIX_CHANNEL_DIR or clone $fruix_channel_url" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$script" ] || [ ! -d "$modules_dir" ]; then
|
||||
echo "Fruix channel checkout is missing scripts/ or modules/: $fruix_channel_dir" >&2
|
||||
echo "Expected canonical Fruix content from $fruix_channel_url" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$guile_bin" ]; then
|
||||
echo "Guile binary is not executable: $guile_bin" >&2
|
||||
@@ -20,11 +40,11 @@ ensure_built() {
|
||||
GUILE_EXTENSIONS_PATH="$guile_extra_prefix/lib/guile/3.0/extensions${GUILE_EXTENSIONS_PATH:+:$GUILE_EXTENSIONS_PATH}" \
|
||||
LD_LIBRARY_PATH="$guile_extra_prefix/lib:/tmp/guile-freebsd-validate-install/lib:/usr/local/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \
|
||||
"$guile_bin" -c '(catch #t (lambda () (use-modules (fibers)) (display "ok") (newline)) (lambda _ (display "missing") (newline)))' | grep -qx ok; then
|
||||
METADATA_OUT= ENV_OUT= "$project_root/tests/shepherd/build-local-guile-fibers.sh"
|
||||
METADATA_OUT= ENV_OUT= "$bootstrap_root/tests/shepherd/build-local-guile-fibers.sh"
|
||||
fi
|
||||
|
||||
if [ ! -x "$shepherd_prefix/bin/shepherd" ] || [ ! -x "$shepherd_prefix/bin/herd" ]; then
|
||||
METADATA_OUT= ENV_OUT= GUILE_EXTRA_PREFIX="$guile_extra_prefix" "$project_root/tests/shepherd/build-local-shepherd.sh"
|
||||
METADATA_OUT= ENV_OUT= GUILE_EXTRA_PREFIX="$guile_extra_prefix" "$bootstrap_root/tests/shepherd/build-local-shepherd.sh"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -34,9 +54,9 @@ guile_prefix=$(CDPATH= cd -- "$(dirname "$guile_bin")/.." && pwd)
|
||||
guile_lib_dir=$guile_prefix/lib
|
||||
|
||||
if [ -n "${GUILE_LOAD_PATH:-}" ]; then
|
||||
guile_load_path="$project_root/modules:$guix_source_dir:$GUILE_LOAD_PATH"
|
||||
guile_load_path="$modules_dir:$guix_source_dir:$GUILE_LOAD_PATH"
|
||||
else
|
||||
guile_load_path="$project_root/modules:$guix_source_dir"
|
||||
guile_load_path="$modules_dir:$guix_source_dir"
|
||||
fi
|
||||
|
||||
exec env \
|
||||
@@ -47,5 +67,8 @@ exec env \
|
||||
GUILE_EXTRA_PREFIX="$guile_extra_prefix" \
|
||||
SHEPHERD_PREFIX="$shepherd_prefix" \
|
||||
GUIX_SOURCE_DIR="$guix_source_dir" \
|
||||
FRUIX_PROJECT_ROOT="$project_root" \
|
||||
FRUIX_PROJECT_ROOT="$fruix_channel_dir" \
|
||||
FRUIX_BOOTSTRAP_ROOT="$bootstrap_root" \
|
||||
FRUIX_CHANNEL_DIR="$fruix_channel_dir" \
|
||||
FRUIX_CHANNEL_URL="$fruix_channel_url" \
|
||||
"$guile_bin" --no-auto-compile -s "$script" "$@"
|
||||
|
||||
+357
@@ -0,0 +1,357 @@
|
||||
# Plan 5: split Fruix bootstrap from canonical Fruix
|
||||
|
||||
This plan turns the boundary from `docs/bootstrap.md` into a concrete migration sequence.
|
||||
|
||||
The intended end state is:
|
||||
|
||||
- `fruix-bootstrap` prepares a plain FreeBSD host to act as a **Fruix builder**
|
||||
- `fruix` is the canonical repo/channel for:
|
||||
- packages
|
||||
- systems
|
||||
- installers
|
||||
- deployment
|
||||
- node lifecycle
|
||||
- a booted Fruix system no longer depends on `fruix-bootstrap`
|
||||
|
||||
## Primary goal
|
||||
|
||||
Reach the point where a plain FreeBSD machine can:
|
||||
|
||||
1. use `fruix-bootstrap` to become a Fruix builder
|
||||
2. use that builder to evaluate a pinned `fruix` checkout or revision
|
||||
3. build a Fruix artifact from `fruix`
|
||||
- installer ISO
|
||||
- VM image
|
||||
- system closure
|
||||
4. boot or install that artifact
|
||||
5. continue operating using only `fruix`
|
||||
|
||||
## Guiding rules
|
||||
|
||||
1. `fruix` is the only canonical home for long-lived Fruix logic.
|
||||
2. `fruix-bootstrap` may wrap `fruix`, but should not fork it.
|
||||
3. If a booted Fruix node should understand it, it belongs in `fruix`.
|
||||
4. Bootstrap-only foreign-host glue belongs in `fruix-bootstrap`.
|
||||
5. Prefer temporary wrappers over duplicated logic.
|
||||
|
||||
## What should move to `fruix` first
|
||||
|
||||
These are the highest-priority items to live in `fruix`, because they define the product rather than the bootstrap path.
|
||||
|
||||
### 1. Package definitions
|
||||
|
||||
Move first:
|
||||
|
||||
- `modules/fruix/packages/...`
|
||||
|
||||
Why:
|
||||
|
||||
- package definitions are canonical Fruix content
|
||||
- they are part of the future channel story
|
||||
- booted Fruix systems should use these directly
|
||||
|
||||
### 2. FreeBSD system model and system artifact logic
|
||||
|
||||
Move early:
|
||||
|
||||
- `modules/fruix/system/freebsd/model.scm`
|
||||
- `modules/fruix/system/freebsd/source.scm`
|
||||
- `modules/fruix/system/freebsd/build.scm`
|
||||
- `modules/fruix/system/freebsd/render.scm`
|
||||
- `modules/fruix/system/freebsd/media.scm`
|
||||
- `modules/fruix/system/freebsd/utils.scm`
|
||||
- `modules/fruix/system/freebsd/executor.scm`
|
||||
- the thin facade `modules/fruix/system/freebsd.scm`
|
||||
|
||||
Why:
|
||||
|
||||
- these define what a Fruix system is
|
||||
- they define build/promotion/deploy behavior
|
||||
- they must remain stable across bootstrap and post-bootstrap operation
|
||||
|
||||
### 3. Fruix CLI semantics
|
||||
|
||||
Move early:
|
||||
|
||||
- `scripts/fruix.scm`
|
||||
- the long-lived CLI behavior it implements
|
||||
|
||||
Why:
|
||||
|
||||
- command semantics belong to Fruix, not bootstrap
|
||||
- installed nodes should match the host CLI model
|
||||
|
||||
### 4. Installed-node lifecycle logic
|
||||
|
||||
Move early:
|
||||
|
||||
- node helper/module tree logic
|
||||
- `build`
|
||||
- `build-base`
|
||||
- `deploy`
|
||||
- `reconfigure`
|
||||
- `status`
|
||||
- `switch`
|
||||
- `rollback`
|
||||
|
||||
Why:
|
||||
|
||||
- these are core Fruix product actions after first boot
|
||||
|
||||
### 5. Installer and future TUI installer
|
||||
|
||||
Move early and keep there:
|
||||
|
||||
- installer environment logic
|
||||
- installer ISO logic
|
||||
- future TUI installer application
|
||||
|
||||
Why:
|
||||
|
||||
- the installer is a Fruix product artifact
|
||||
- Fruix should be able to rebuild its own installer
|
||||
|
||||
### 6. Metadata formats
|
||||
|
||||
Move early and treat as canonical:
|
||||
|
||||
- system generation metadata
|
||||
- deployment metadata
|
||||
- promoted native-build result metadata
|
||||
- any future Fruix revision/pin metadata
|
||||
|
||||
Why:
|
||||
|
||||
- metadata is part of Fruix identity
|
||||
- bootstrap should consume these formats, not redefine them
|
||||
|
||||
## What can temporarily remain in `fruix-bootstrap`
|
||||
|
||||
These items are allowed to remain here while the split is in progress.
|
||||
|
||||
### 1. Bootstrap wrapper entrypoints
|
||||
|
||||
Examples:
|
||||
|
||||
- shell wrappers that locate the pinned `fruix` checkout
|
||||
- shell wrappers that set `GUILE_LOAD_PATH`, `GUILE_PREFIX`, etc.
|
||||
- entrypoints that invoke the real Fruix CLI from the foreign host
|
||||
|
||||
These should become thin wrappers around `fruix`, not alternate implementations.
|
||||
|
||||
### 2. Foreign-host dependency bring-up
|
||||
|
||||
Examples:
|
||||
|
||||
- locating or building Guile
|
||||
- locating or building Shepherd support pieces
|
||||
- preparing the host environment so the Fruix CLI can run
|
||||
|
||||
This is bootstrap territory.
|
||||
|
||||
### 3. Foreign-host validation harnesses
|
||||
|
||||
Examples:
|
||||
|
||||
- tests for plain-FreeBSD -> Fruix-builder bring-up
|
||||
- tests that validate local bootstrap assumptions
|
||||
- docs for first-time setup on vanilla FreeBSD
|
||||
|
||||
These stay useful here even after most Fruix logic moves out.
|
||||
|
||||
### 4. Temporary compatibility glue
|
||||
|
||||
Examples:
|
||||
|
||||
- wrappers that still point from bootstrap into `../fruix`
|
||||
- temporary environment shims needed while the split settles
|
||||
|
||||
These are acceptable if they are clearly transitional and do not redefine Fruix semantics.
|
||||
|
||||
## What should not remain in `fruix-bootstrap`
|
||||
|
||||
Do not let this repo become a second home for:
|
||||
|
||||
- canonical package definitions
|
||||
- canonical system semantics
|
||||
- deployment semantics
|
||||
- installer semantics
|
||||
- long-lived metadata definitions
|
||||
- installed-node behavior after first boot
|
||||
|
||||
If bootstrap needs those capabilities, it should invoke them from `fruix`.
|
||||
|
||||
## Proposed migration sequence
|
||||
|
||||
## Milestone 1: make `fruix-bootstrap` call `fruix`
|
||||
|
||||
Goal:
|
||||
|
||||
- this repo stops being the canonical implementation and becomes a launcher/bootstrap layer
|
||||
|
||||
Deliverables:
|
||||
|
||||
- define a pinned `fruix` checkout input for bootstrap
|
||||
- initially a local path such as `../fruix` is acceptable
|
||||
- bootstrap wrappers invoke Fruix modules/scripts from that checkout
|
||||
- package/system logic is no longer edited here as the primary source of truth
|
||||
|
||||
Success signal:
|
||||
|
||||
- running the bootstrap entrypoint clearly uses `../fruix` as the canonical Fruix source
|
||||
|
||||
## Milestone 2: move canonical modules into `fruix`
|
||||
|
||||
Goal:
|
||||
|
||||
- all long-lived package/system logic lives in `fruix`
|
||||
|
||||
Deliverables:
|
||||
|
||||
- `modules/fruix/packages/...` live in `fruix`
|
||||
- `modules/fruix/system/...` live in `fruix`
|
||||
- `scripts/fruix.scm` lives in `fruix`
|
||||
- this repo only wraps/invokes those files
|
||||
|
||||
Success signal:
|
||||
|
||||
- the same Fruix code is used both:
|
||||
- from a foreign host via bootstrap
|
||||
- from a booted Fruix node
|
||||
|
||||
## Milestone 3: record a pinned Fruix identity in artifacts
|
||||
|
||||
Goal:
|
||||
|
||||
- built artifacts know which Fruix revision produced them
|
||||
|
||||
Deliverables:
|
||||
|
||||
- define minimal Fruix identity metadata, such as:
|
||||
- checkout path for local development
|
||||
- git commit when available
|
||||
- optional dirty/clean marker
|
||||
- record that identity in:
|
||||
- image metadata
|
||||
- installer metadata
|
||||
- deployed generation metadata
|
||||
- installed-node metadata
|
||||
|
||||
Success signal:
|
||||
|
||||
- a system can answer: “which Fruix revision built me?”
|
||||
|
||||
## Milestone 4: first self-contained Fruix install from bootstrap
|
||||
|
||||
Goal:
|
||||
|
||||
- prove that bootstrap is needed only to create the first Fruix artifact
|
||||
|
||||
Deliverables:
|
||||
|
||||
- from plain FreeBSD + bootstrap, build either:
|
||||
- a Fruix installer ISO, or
|
||||
- a Fruix VM image
|
||||
- boot/install the resulting system
|
||||
- validate that the running Fruix node can perform post-boot actions using `fruix` only:
|
||||
- `fruix system build`
|
||||
- `fruix system build-base`
|
||||
- `fruix system reconfigure`
|
||||
- `fruix system deploy`
|
||||
- `fruix system rollback`
|
||||
|
||||
Success signal:
|
||||
|
||||
- no runtime dependence on the bootstrap checkout is required after first boot
|
||||
|
||||
## Milestone 5: build the first TUI installer in `fruix`
|
||||
|
||||
Goal:
|
||||
|
||||
- the user-facing installation experience is defined by Fruix itself
|
||||
|
||||
Deliverables:
|
||||
|
||||
- minimal text/TUI installer application in `fruix`
|
||||
- built into the Fruix installer environment / installer ISO
|
||||
- opinionated input surface, for example:
|
||||
- target disk
|
||||
- hostname
|
||||
- root SSH key or password
|
||||
- optional operator user
|
||||
- optional profile/template selection
|
||||
- generated declaration and install metadata recorded by Fruix
|
||||
|
||||
Success signal:
|
||||
|
||||
- bootstrap builds the installer, but Fruix defines the installer behavior
|
||||
|
||||
## First split-validation milestone
|
||||
|
||||
The first major proof should be this:
|
||||
|
||||
### Bootstrap builds Fruix, then Fruix continues alone
|
||||
|
||||
Concretely:
|
||||
|
||||
1. start from plain FreeBSD
|
||||
2. use `fruix-bootstrap` to create a Fruix builder
|
||||
3. point it at pinned `../fruix`
|
||||
4. build a Fruix VM image or installer ISO from `fruix`
|
||||
5. boot the resulting Fruix system
|
||||
6. on the running Fruix system, perform at least:
|
||||
- `fruix system build`
|
||||
- `fruix system reconfigure`
|
||||
- `fruix system rollback`
|
||||
7. verify none of those steps need the bootstrap checkout
|
||||
|
||||
This is the first meaningful architectural victory of the split.
|
||||
|
||||
## Recommended immediate tasks
|
||||
|
||||
1. add a bootstrap configuration variable for the pinned Fruix checkout
|
||||
- initially defaulting to `../fruix`
|
||||
2. change bootstrap wrappers to load Fruix code from that checkout
|
||||
3. stop treating this repo as the canonical edit location for Fruix modules
|
||||
4. move package/system/CLI logic into `fruix`
|
||||
5. add minimal Fruix revision metadata recording to built artifacts
|
||||
6. then proceed to the Fruix-defined TUI installer
|
||||
|
||||
## Temporary practical compromises
|
||||
|
||||
The split does not require perfection on day one.
|
||||
|
||||
Temporarily acceptable:
|
||||
|
||||
- local-path pinning to `../fruix`
|
||||
- wrapper scripts in bootstrap that export environment variables for Fruix
|
||||
- some bootstrap-era host assumptions while the builder path stabilizes
|
||||
|
||||
Not acceptable long-term:
|
||||
|
||||
- duplicated package definitions in both repos
|
||||
- duplicated system semantics in both repos
|
||||
- booted Fruix nodes depending on bootstrap logic to keep operating
|
||||
|
||||
## Open questions to resolve later
|
||||
|
||||
These do not block the boundary itself:
|
||||
|
||||
- exact Fruix channel/lock file format
|
||||
- exact user-facing `upgrade` policy
|
||||
- binary publication/substitution design
|
||||
- local `jail` executor for native base builds
|
||||
|
||||
Those are important, but they should be built on top of the split rather than baked into the boundary prematurely.
|
||||
|
||||
## Summary
|
||||
|
||||
The migration priority is:
|
||||
|
||||
1. make bootstrap a thin launcher for pinned `fruix`
|
||||
2. move canonical logic into `fruix`
|
||||
3. record Fruix identity in artifacts
|
||||
4. prove a booted Fruix node can continue without bootstrap
|
||||
5. build the TUI installer in `fruix`
|
||||
|
||||
If this plan is followed, `fruix-bootstrap` stays small and generic, while `fruix` becomes the real long-term system source of truth.
|
||||
@@ -0,0 +1,239 @@
|
||||
# Fruix bootstrap boundary
|
||||
|
||||
This document defines the architectural boundary between:
|
||||
|
||||
- `fruix`
|
||||
- `fruix-bootstrap`
|
||||
|
||||
The goal is to keep Fruix itself small, self-hosting, and canonical, while isolating the foreign-host bring-up logic required to get the first Fruix-capable environment running on plain FreeBSD.
|
||||
|
||||
## Short version
|
||||
|
||||
- `fruix` is the canonical source of Fruix package, system, installer, deployment, and node-management logic.
|
||||
- `fruix-bootstrap` is a thin foreign-host layer that turns a plain FreeBSD installation into a **Fruix builder**.
|
||||
- A booted Fruix system must be able to continue operating from `fruix` alone, without depending on `fruix-bootstrap`.
|
||||
|
||||
## Core concept: the Fruix builder
|
||||
|
||||
`fruix-bootstrap` should not be thought of as a permanent alternate Fruix implementation or a long-lived compatibility layer.
|
||||
|
||||
Its job is to create a **Fruix builder**: an environment capable of evaluating and materializing a pinned `fruix` revision on a non-Fruix FreeBSD host.
|
||||
|
||||
A Fruix builder should be able to:
|
||||
|
||||
- run the Fruix CLI and evaluator
|
||||
- load a pinned `fruix` checkout or channel revision
|
||||
- materialize Fruix package outputs
|
||||
- build Fruix system closures
|
||||
- build Fruix installer ISOs and VM images
|
||||
- install or deploy the first Fruix system
|
||||
|
||||
That builder may run:
|
||||
|
||||
- on a plain FreeBSD machine
|
||||
- in CI
|
||||
- in a jail
|
||||
- on a later Fruix node
|
||||
|
||||
The important point is that the builder is generic. It is a build/evaluation environment for Fruix, not a second product identity.
|
||||
|
||||
## Ownership boundary
|
||||
|
||||
### `fruix` owns
|
||||
|
||||
Anything that should still matter after first Fruix boot belongs in `fruix`.
|
||||
|
||||
This includes:
|
||||
|
||||
- package definitions
|
||||
- system definitions
|
||||
- source objects and source provenance logic
|
||||
- native-build and promotion logic
|
||||
- executor model
|
||||
- system artifact materializers:
|
||||
- closures
|
||||
- root filesystems
|
||||
- disk images
|
||||
- installers
|
||||
- installer ISOs
|
||||
- installed-node management logic:
|
||||
- build
|
||||
- build-base
|
||||
- deploy
|
||||
- reconfigure
|
||||
- switch
|
||||
- rollback
|
||||
- later upgrade
|
||||
- installer application / TUI logic
|
||||
- publication/substitution logic when added later
|
||||
- metadata formats that define Fruix identity and lifecycle
|
||||
|
||||
Rule of thumb:
|
||||
|
||||
> If a booted Fruix node should conceptually understand it, it belongs in `fruix`.
|
||||
|
||||
### `fruix-bootstrap` owns
|
||||
|
||||
Anything only required to turn plain FreeBSD into a Fruix-capable builder belongs in `fruix-bootstrap`.
|
||||
|
||||
This includes:
|
||||
|
||||
- host environment checks
|
||||
- locating or building bootstrap tool dependencies
|
||||
- foreign-host setup glue
|
||||
- wrapper entrypoints for invoking a pinned `fruix` revision
|
||||
- initial bootstrap documentation
|
||||
- tests for the path from vanilla FreeBSD to first Fruix-capable builder or first Fruix artifact
|
||||
|
||||
Rule of thumb:
|
||||
|
||||
> If it is only needed before Fruix exists as Fruix, it belongs in `fruix-bootstrap`.
|
||||
|
||||
## Dependency direction
|
||||
|
||||
The dependency direction must remain one-way.
|
||||
|
||||
Allowed:
|
||||
|
||||
- `fruix-bootstrap` depends on a pinned `fruix` revision
|
||||
- `fruix-bootstrap` invokes `fruix` to build packages, systems, installers, and images
|
||||
|
||||
Not allowed:
|
||||
|
||||
- `fruix` depending on `fruix-bootstrap`
|
||||
- a booted Fruix node needing `fruix-bootstrap` in order to keep building or upgrading itself
|
||||
|
||||
This keeps `fruix` canonical and prevents the bootstrap repo from becoming a second source of truth.
|
||||
|
||||
## Canonical source of truth
|
||||
|
||||
`fruix` is the only canonical home for Fruix product logic.
|
||||
|
||||
In particular, these should not be duplicated long-term in `fruix-bootstrap`:
|
||||
|
||||
- canonical package definitions
|
||||
- canonical system logic
|
||||
- installer workflow semantics
|
||||
- deployment semantics
|
||||
- long-lived metadata definitions
|
||||
- installed-node lifecycle behavior
|
||||
|
||||
`fruix-bootstrap` may temporarily wrap or seed those capabilities, but it should consume them from `fruix`, not fork them.
|
||||
|
||||
## Pinning
|
||||
|
||||
`fruix-bootstrap` should operate on a clear Fruix identity, not an ambient checkout with unclear provenance.
|
||||
|
||||
Initially, that identity can be simple:
|
||||
|
||||
- a local checkout path
|
||||
- a git commit
|
||||
- a tag
|
||||
- a branch plus locked commit
|
||||
|
||||
Later, this can become a proper Fruix channel lock/update model.
|
||||
|
||||
Whatever form is used, the important property is:
|
||||
|
||||
> the first Fruix artifact is built from a known `fruix` identity.
|
||||
|
||||
That identity should eventually be recorded in:
|
||||
|
||||
- installer metadata
|
||||
- image metadata
|
||||
- deployed generation metadata
|
||||
- installed-node metadata
|
||||
|
||||
## Product flow
|
||||
|
||||
The intended lifecycle is:
|
||||
|
||||
1. start from plain FreeBSD
|
||||
2. use `fruix-bootstrap` to create a Fruix builder
|
||||
3. point that builder at a pinned `fruix` revision
|
||||
4. materialize artifacts from `fruix`, such as:
|
||||
- package outputs
|
||||
- system closures
|
||||
- installer ISOs
|
||||
- VM images
|
||||
- installed systems
|
||||
5. boot or install the resulting Fruix system
|
||||
6. from that point onward, use `fruix` alone to move the system forward
|
||||
|
||||
In short:
|
||||
|
||||
```text
|
||||
plain FreeBSD
|
||||
-> fruix-bootstrap
|
||||
-> Fruix builder
|
||||
-> pinned fruix revision
|
||||
-> build/install/deploy Fruix artifacts
|
||||
-> booted Fruix node
|
||||
-> future lifecycle managed by fruix
|
||||
```
|
||||
|
||||
## Installer implication
|
||||
|
||||
The installer UI and workflow belong to `fruix`, not `fruix-bootstrap`.
|
||||
|
||||
Why:
|
||||
|
||||
- the installer is part of the Fruix product surface
|
||||
- Fruix should be able to build its own installer artifacts
|
||||
- installed systems should be traceable to a pinned Fruix revision
|
||||
- later Fruix nodes should be able to rebuild the installer without returning to bootstrap-only logic
|
||||
|
||||
So the split should be:
|
||||
|
||||
- `fruix-bootstrap`: makes it possible to build the installer
|
||||
- `fruix`: defines the installer artifact and the TUI installer behavior
|
||||
|
||||
## Success criteria
|
||||
|
||||
The boundary is working when all of the following are true:
|
||||
|
||||
1. a plain FreeBSD host can become a Fruix builder using `fruix-bootstrap`
|
||||
2. that builder can build a Fruix installer ISO or VM image from a pinned `fruix` revision
|
||||
3. the resulting Fruix system boots without requiring `fruix-bootstrap`
|
||||
4. the booted Fruix system can keep using `fruix` for:
|
||||
- build
|
||||
- build-base
|
||||
- reconfigure
|
||||
- deploy
|
||||
- rollback
|
||||
- later upgrade
|
||||
5. package and system evolution happens in `fruix`, not in duplicated bootstrap logic
|
||||
|
||||
## Non-goals
|
||||
|
||||
This split does not mean:
|
||||
|
||||
- `fruix-bootstrap` becomes a permanent parallel Fruix distribution
|
||||
- booted Fruix nodes should keep consulting bootstrap state
|
||||
- bootstrap should own package or system semantics long-term
|
||||
|
||||
It also does not require all historical bootstrap leakage to disappear immediately. Some transitional host assumptions may remain for a while, but they should be treated as technical debt against this boundary.
|
||||
|
||||
## Working rule for future refactors
|
||||
|
||||
When deciding where code belongs, ask:
|
||||
|
||||
> Should a booted Fruix node still care about this?
|
||||
|
||||
- If yes, it belongs in `fruix`.
|
||||
- If no, and it only exists to bring up Fruix from foreign FreeBSD, it belongs in `fruix-bootstrap`.
|
||||
|
||||
## Current direction
|
||||
|
||||
Near-term work should follow this boundary:
|
||||
|
||||
- keep bootstrap generic and thin
|
||||
- move as much canonical logic as possible into `fruix`
|
||||
- make the first user-facing installer a Fruix-defined product artifact
|
||||
- ensure installed nodes record and operate from a pinned Fruix identity
|
||||
|
||||
That gives Fruix a clean product story:
|
||||
|
||||
- bootstrap creates a Fruix builder
|
||||
- Fruix builds Fruix
|
||||
- Fruix systems move forward using Fruix
|
||||
Reference in New Issue
Block a user