max / alloy
7 files changed,
+186 insertions,
-0 deletions
| @@ -25,6 +25,7 @@ | |||
| 25 | 25 | # Usage: | |
| 26 | 26 | # build/build-image.sh # build image + raw disk image | |
| 27 | 27 | # build/build-image.sh --type qcow2 # build image + qcow2 disk image | |
| 28 | + | # build/build-image.sh --host fw13 # mint with fw13's recipe | |
| 28 | 29 | # build/build-image.sh --skip-build # reuse the current image, just run bib | |
| 29 | 30 | # build/build-image.sh --write-only --write /dev/sdX # write what is already built | |
| 30 | 31 | # build/build-image.sh --write /dev/sdX # also dd the artifact to a device | |
| @@ -48,6 +49,11 @@ | |||
| 48 | 49 | # shellcheck source=build/build-stamp.sh | |
| 49 | 50 | . "$REPO_ROOT/build/build-stamp.sh" | |
| 50 | 51 | ||
| 52 | + | # host_recipe_args. The per-machine dials, so a build host is asked for by name | |
| 53 | + | # rather than by remembering four flags; see the header of build/host-recipe.sh. | |
| 54 | + | # shellcheck source=build/host-recipe.sh | |
| 55 | + | . "$REPO_ROOT/build/host-recipe.sh" | |
| 56 | + | ||
| 51 | 57 | IMAGE="localhost/alloy:local" | |
| 52 | 58 | BIB_IMAGE="quay.io/centos-bootc/bootc-image-builder:latest" | |
| 53 | 59 | DEF="$REPO_ROOT/build/bib-defs.yaml" | |
| @@ -65,6 +71,9 @@ | |||
| 65 | 71 | ||
| 66 | 72 | TYPE="raw" | |
| 67 | 73 | WRITE_DEV="" | |
| 74 | + | # Empty means no per-machine recipe: the Containerfile's own defaults, which | |
| 75 | + | # carry no compiler and no database. See build/host-recipe.sh. | |
| 76 | + | HOST="" | |
| 68 | 77 | SKIP_BUILD=0 | |
| 69 | 78 | SKIP_BIB=0 | |
| 70 | 79 | ||
| @@ -95,11 +104,17 @@ | |||
| 95 | 104 | # the gate lives there rather than here: a bad value has to fail the | |
| 96 | 105 | # build whether it came from `alloy image` or from a hand-typed flag. | |
| 97 | 106 | --build-arg) BUILD_ARGS+=(--build-arg "${2:?--build-arg needs KEY=VALUE}"); shift 2 ;; | |
| 107 | + | # The machine this image is for, read from build/hosts/<name>.env. Its | |
| 108 | + | # dials go in ahead of anything typed here, so an explicit --build-arg is | |
| 109 | + | # the last value podman sees and overrides the recipe. | |
| 110 | + | --host) HOST="${2:?--host needs a machine name}"; shift 2 ;; | |
| 98 | 111 | -h|--help) usage 0 ;; | |
| 99 | 112 | *) die "unknown argument: $1 (see --help)" ;; | |
| 100 | 113 | esac | |
| 101 | 114 | done | |
| 102 | 115 | ||
| 116 | + | [ -z "$HOST" ] || host_recipe_args "$HOST" BUILD_ARGS | |
| 117 | + | ||
| 103 | 118 | # Refuse the ISO types outright. Both of bib's spellings compose Anaconda, and | |
| 104 | 119 | # an artifact from either one installs a machine nobody can log into, so the | |
| 105 | 120 | # failure has to land here at the argument rather than an hour later at a boot. |
| @@ -26,6 +26,7 @@ | |||
| 26 | 26 | # build/build-iso.sh --fast # iteration: cheap compression, keeps source | |
| 27 | 27 | # build/build-iso.sh --fast --skip-source # boot chain only, cannot install | |
| 28 | 28 | # build/build-iso.sh --update-target host:5000/alloy:43 # updates come from there | |
| 29 | + | # build/build-iso.sh --host astra # mint with astra's recipe, arch included | |
| 29 | 30 | # build/build-iso.sh --arch aarch64 # override the detected architecture | |
| 30 | 31 | # build/build-iso.sh --write /dev/sdX # build, then write to a device | |
| 31 | 32 | # build/build-iso.sh --write-only --write /dev/sdX # write the ISO already built | |
| @@ -47,6 +48,11 @@ | |||
| 47 | 48 | # shellcheck source=build/build-stamp.sh | |
| 48 | 49 | . "$REPO_ROOT/build/build-stamp.sh" | |
| 49 | 50 | ||
| 51 | + | # host_recipe_args. The per-machine dials, so a build host is asked for by name | |
| 52 | + | # rather than by remembering four flags; see the header of build/host-recipe.sh. | |
| 53 | + | # shellcheck source=build/host-recipe.sh | |
| 54 | + | . "$REPO_ROOT/build/host-recipe.sh" | |
| 55 | + | ||
| 50 | 56 | IMAGE="localhost/alloy:local" | |
| 51 | 57 | BUILDER="localhost/alloy-iso-builder:local" | |
| 52 | 58 | OUTPUT="$REPO_ROOT/output" | |
| @@ -74,6 +80,9 @@ | |||
| 74 | 80 | # the medium and its contents are the same architecture by construction. | |
| 75 | 81 | # See --arch below. | |
| 76 | 82 | ARCH="" | |
| 83 | + | # Empty means no per-machine recipe: the Containerfile's own defaults, which | |
| 84 | + | # carry no compiler and no database. See build/host-recipe.sh. | |
| 85 | + | HOST="" | |
| 77 | 86 | ||
| 78 | 87 | BUILD_ARGS=() | |
| 79 | 88 | ||
| @@ -88,6 +97,10 @@ | |||
| 88 | 97 | # the gate lives there rather than here: a bad value has to fail the | |
| 89 | 98 | # build whether it came from `alloy image` or from a hand-typed flag. | |
| 90 | 99 | --build-arg) BUILD_ARGS+=(--build-arg "${2:?--build-arg needs KEY=VALUE}"); shift 2 ;; | |
| 100 | + | # The machine this medium is for, read from build/hosts/<name>.env. Its | |
| 101 | + | # dials go in ahead of anything typed here, and an ARCH line in the recipe | |
| 102 | + | # sets the medium's architecture unless --arch says otherwise. | |
| 103 | + | --host) HOST="${2:?--host needs a machine name}"; shift 2 ;; | |
| 91 | 104 | --skip-source) SKIP_SOURCE=1; shift ;; | |
| 92 | 105 | # Iteration mode: reuse the image and compress cheaply, but still carry | |
| 93 | 106 | # the install source. Measured on fw13, 2026-08-15: level 19 costs 192s | |
| @@ -124,6 +137,14 @@ | |||
| 124 | 137 | esac | |
| 125 | 138 | done | |
| 126 | 139 | ||
| 140 | + | if [ -n "$HOST" ]; then | |
| 141 | + | host_recipe_args "$HOST" BUILD_ARGS | |
| 142 | + | # --arch wins over the recipe: the flag is how a one-off test of the boot | |
| 143 | + | # chain asks for the other architecture, and the recipe is what the machine | |
| 144 | + | # normally takes. | |
| 145 | + | [ -n "$ARCH" ] || ARCH="$HOST_ARCH" | |
| 146 | + | fi | |
| 147 | + | ||
| 127 | 148 | ARTIFACT="$OUTPUT/install.iso" | |
| 128 | 149 | ||
| 129 | 150 | # --write-only skips every build step, including the output rotation, and |
| @@ -255,6 +255,35 @@ | |||
| 255 | 255 | browser and both are the product; the point is that every large thing in this image is | |
| 256 | 256 | either explained above or gone. | |
| 257 | 257 | ||
| 258 | + | ## Per-machine mint recipes | |
| 259 | + | ||
| 260 | + | The dials that decide what a machine can do all default to the smallest thing | |
| 261 | + | that boots. `LANGS` defaults to empty, so a default mint carries no language | |
| 262 | + | toolchain and no C compiler; `DB` defaults to `none`; `PROFILE` defaults to | |
| 263 | + | `client`. That is right for an image whose job is to run software, and it is a | |
| 264 | + | trap for the three machines that build it: a forgotten flag produces a build | |
| 265 | + | host that boots, looks healthy, and cannot compile. | |
| 266 | + | ||
| 267 | + | So each machine's dials live in `build/hosts/<name>.env`, and both builders take | |
| 268 | + | `--host <name>`: | |
| 269 | + | ||
| 270 | + | build/build-image.sh --host fw13 | |
| 271 | + | build/build-iso.sh --host astra | |
| 272 | + | ||
| 273 | + | Format is `KEY=VALUE`, one per line, with `#` comments. Every key must name an | |
| 274 | + | `ARG` the Containerfile declares, checked against the Containerfile itself when | |
| 275 | + | the recipe is read, because podman drops an unknown build arg silently and a | |
| 276 | + | typo would mint exactly the image the file exists to prevent. `ARCH` is the one | |
| 277 | + | reserved key: `build-iso.sh` uses it to pick the medium's architecture and | |
| 278 | + | `build-image.sh` ignores it, since nothing here cross-compiles. | |
| 279 | + | ||
| 280 | + | Recipe values go in ahead of anything typed on the command line, so an explicit | |
| 281 | + | `--build-arg` overrides the recipe rather than racing it. | |
| 282 | + | ||
| 283 | + | What the three recipes say today: fw12 is a stock client mint and sets nothing; | |
| 284 | + | fw13 takes `LANGS=rust,js` and `DB=postgres16` for the build-host role; astra | |
| 285 | + | takes those plus `PROFILE=server` and `ARCH=aarch64`. | |
| 286 | + | ||
| 258 | 287 | ## Version fields | |
| 259 | 288 | ||
| 260 | 289 | Three numbers describe an Alloy machine, and they move on three different clocks. That is why os-release carries three fields rather than one, and why `alloy --version` prints all of them: |
| @@ -1,0 +1,76 @@ | |||
| 1 | + | #!/usr/bin/env bash | |
| 2 | + | # | |
| 3 | + | # host-recipe.sh — the per-machine mint recipe both image builders read. | |
| 4 | + | # | |
| 5 | + | # Sourced by build/build-image.sh and build/build-iso.sh alongside | |
| 6 | + | # build/build-stamp.sh and build/privilege.sh. | |
| 7 | + | # | |
| 8 | + | # The dials that decide what a machine can do (PROFILE, LANGS, DB) all default | |
| 9 | + | # to the smallest thing that boots. LANGS defaults to empty, so a default mint | |
| 10 | + | # carries no compiler at all, and the C toolchain leaves with it. That is the | |
| 11 | + | # right default for an image whose job is to run software, and it is a trap for | |
| 12 | + | # the three machines in this tree that build it: a forgotten flag produces a | |
| 13 | + | # build host that boots, looks healthy, and cannot compile. | |
| 14 | + | # | |
| 15 | + | # So the dials live in a file per machine. The difference between two machines | |
| 16 | + | # is then a diff rather than a memory, and `--host fw13` is what a mint is | |
| 17 | + | # asked for rather than four flags somebody has to remember in the right order. | |
| 18 | + | # | |
| 19 | + | # Format: KEY=VALUE, one per line. Blank lines and # comments are ignored, and | |
| 20 | + | # so is trailing whitespace. Quotes are not stripped, because no dial takes a | |
| 21 | + | # value that needs them. | |
| 22 | + | # | |
| 23 | + | # Every key must name an ARG the Containerfile declares. That is checked here, | |
| 24 | + | # against the Containerfile itself rather than a list kept in step by hand, | |
| 25 | + | # because a typo'd key is silently dropped by podman and would mint exactly the | |
| 26 | + | # image this file exists to prevent. | |
| 27 | + | # | |
| 28 | + | # ARCH is the one reserved key, and it is not a build arg. build/build-iso.sh | |
| 29 | + | # consumes it to pick the medium's architecture; build/build-image.sh ignores | |
| 30 | + | # it and builds natively, per the standing rule that nothing here | |
| 31 | + | # cross-compiles. | |
| 32 | + | ||
| 33 | + | # Read a recipe into a BUILD_ARGS array, and set HOST_ARCH from a recipe that | |
| 34 | + | # names one. Recipe args go in before anything the caller typed, so an explicit | |
| 35 | + | # `--build-arg` on the command line is the last value podman sees and wins. | |
| 36 | + | # | |
| 37 | + | # Usage: host_recipe_args NAME BUILD_ARGS (the array's NAME, not its contents) | |
| 38 | + | host_recipe_args() { | |
| 39 | + | local host="$1" | |
| 40 | + | local -n out="$2" | |
| 41 | + | local file="$REPO_ROOT/build/hosts/$host.env" | |
| 42 | + | local containerfile="$REPO_ROOT/Containerfile" | |
| 43 | + | ||
| 44 | + | [ -f "$file" ] || { | |
| 45 | + | printf 'error: no recipe for host %s at %s\n' "$host" "$file" >&2 | |
| 46 | + | printf 'known hosts: %s\n' "$(cd "$REPO_ROOT/build/hosts" 2>/dev/null \ | |
| 47 | + | && ls -1 ./*.env 2>/dev/null | sed 's|^\./||; s|\.env$||' | tr '\n' ' ')" >&2 | |
| 48 | + | return 1 | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | HOST_ARCH="" | |
| 52 | + | local recipe=() | |
| 53 | + | local line key value | |
| 54 | + | while IFS= read -r line || [ -n "$line" ]; do | |
| 55 | + | line="${line%%#*}" | |
| 56 | + | line="${line%"${line##*[![:space:]]}"}" | |
| 57 | + | [ -n "$line" ] || continue | |
| 58 | + | case "$line" in | |
| 59 | + | *=*) ;; | |
| 60 | + | *) printf 'error: %s: not KEY=VALUE: %s\n' "$file" "$line" >&2; return 1 ;; | |
| 61 | + | esac | |
| 62 | + | key="${line%%=*}" | |
| 63 | + | value="${line#*=}" | |
| 64 | + | if [ "$key" = "ARCH" ]; then | |
| 65 | + | HOST_ARCH="$value" | |
| 66 | + | continue | |
| 67 | + | fi | |
| 68 | + | grep -q "^ARG $key=" "$containerfile" || { | |
| 69 | + | printf 'error: %s: %s is not an ARG the Containerfile declares\n' "$file" "$key" >&2 | |
| 70 | + | return 1 | |
| 71 | + | } | |
| 72 | + | recipe+=(--build-arg "$key=$value") | |
| 73 | + | done < "$file" | |
| 74 | + | ||
| 75 | + | out=("${recipe[@]}" "${out[@]}") | |
| 76 | + | } |
| @@ -1,0 +1,19 @@ | |||
| 1 | + | # astra — ASRock Rack Ampere Altra, aarch64, 96 cores. Service host, the only | |
| 2 | + | # aarch64 builder, and the machine the nightly evidence tier runs on. | |
| 3 | + | ||
| 4 | + | # The only server-profile machine in the tree. It has no session, tty1 belongs | |
| 5 | + | # to the panel, and the profile's absence assertions are what keep the desktop | |
| 6 | + | # estate off it. | |
| 7 | + | PROFILE=server | |
| 8 | + | ||
| 9 | + | # A build host is a mint-time choice rather than a property of the profile: | |
| 10 | + | # astra builds the aarch64 images, runs the sweep, and hosts PoM's runner. | |
| 11 | + | LANGS=rust,js | |
| 12 | + | ||
| 13 | + | # Same reasoning as fw13: PoM's runner and the CI scratch databases want the | |
| 14 | + | # major production runs, not the one Fedora ships. | |
| 15 | + | DB=postgres16 | |
| 16 | + | ||
| 17 | + | # Reserved key, read by build/build-iso.sh and ignored by build/build-image.sh. | |
| 18 | + | # Not a cross-build: astra builds its own medium, on aarch64 hardware. | |
| 19 | + | ARCH=aarch64 |
| @@ -1,0 +1,7 @@ | |||
| 1 | + | # fw12 — Framework 12, Intel, second daily driver. | |
| 2 | + | # | |
| 3 | + | # Deliberately a stock client mint. fw12 builds nothing and carries no service, | |
| 4 | + | # so it is the machine that proves the image an ordinary person would install, | |
| 5 | + | # and every dial it sets is a way that proof stops being about that image. | |
| 6 | + | # | |
| 7 | + | # Nothing here overrides a default. The file exists so that stays a decision. |
| @@ -1,0 +1,19 @@ | |||
| 1 | + | # fw13 — Framework 13, AMD Strix Point, primary dev box and the tree's only | |
| 2 | + | # x86_64 build host. | |
| 3 | + | # | |
| 4 | + | # Four roles ride on this machine: Bento's local x86_64 builder, Bento's | |
| 5 | + | # controller for astra, mbp and windows-x86, the crates.io publish host, and | |
| 6 | + | # Sando's controller with the local PostgreSQL its gates restore into. | |
| 7 | + | ||
| 8 | + | # rust for the tree; js because Sando's code_smoke gate builds two frontends | |
| 9 | + | # with `npm run build` before it creates a database or boots anything, and that | |
| 10 | + | # is the one place a frontend failure is fatal. The C toolchain arrives with | |
| 11 | + | # rust: a mint without it has no gcc, no binutils and no make. | |
| 12 | + | LANGS=rust,js | |
| 13 | + | ||
| 14 | + | # Sando's migration_dry_run and cargo_test gates restore production dumps into | |
| 15 | + | # a local scratch cluster over a unix socket. Production is PostgreSQL 16, so a | |
| 16 | + | # scratch cluster on Fedora's own postgresql-server (18.4) would test a | |
| 17 | + | # database nobody deploys. Binaries only: creating the cluster is the machine's | |
| 18 | + | # business, not the image's. | |
| 19 | + | DB=postgres16 |