Skip to main content

max / alloy

5.5 KB · 129 lines History Blame Raw
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 # A value of the form @path reads the file instead. That exists for
24 # ALLOY_SSH_KEY: a machine that is reinstalled repeatedly should not need its
25 # key pasted in at each mint, and the recipe is committed to a public repo, so
26 # it names where the key lives rather than carrying it. ~ is expanded; a
27 # missing file is an error, since a medium minted with no key silently becomes
28 # one that needs somebody at the keyboard.
29 #
30 # Every key must name an ARG the Containerfile declares. That is checked here,
31 # against the Containerfile itself rather than a list kept in step by hand,
32 # because a typo'd key is silently dropped by podman and would mint exactly the
33 # image this file exists to prevent.
34 #
35 # Two reserved keys are not build args, because what they name is not something
36 # the Containerfile can be told. Both are consumed by build/build-iso.sh and
37 # ignored by build/build-image.sh.
38 #
39 # ARCH the medium's architecture. Not a cross-build: a machine builds its
40 # own medium, per the standing rule that nothing here cross-compiles.
41 # UPDATE_TARGET where `bootc upgrade` fetches from on a machine installed
42 # from this medium. Without one the installer writes the compiled-in
43 # public registry, which install/image.rs calls a deliberate dead
44 # end, and updates_scheduled() leaves the timer disabled because it
45 # reads the same absent kernel parameter. So a machine minted without
46 # it cannot be fixed without a `bootc switch` typed at the machine or
47 # a second medium, at the moment it is newest. It was a flag anyone
48 # could forget; a recipe key is a machine's own answer, kept.
49 # NVIDIA yes to install the derived alloy-nvidia image rather than Alloy
50 # itself. The NVIDIA module is not a package the shipped Containerfile
51 # can add: it has to be compiled against one exact kernel, so it lives
52 # in a thin image `FROM` that kernel's digest (build/build-nvidia.sh,
53 # ruled 2026-08-30 on alloy `2a382a75`). A dial would kernel-lock the
54 # shipped image; a recipe key says which machine's medium carries the
55 # derived one.
56
57 # Read a recipe into a BUILD_ARGS array, and set HOST_ARCH from a recipe that
58 # names one. Recipe args go in before anything the caller typed, so an explicit
59 # `--build-arg` on the command line is the last value podman sees and wins.
60 #
61 # Usage: host_recipe_args NAME BUILD_ARGS (the array's NAME, not its contents)
62 host_recipe_args() {
63 local host="$1"
64 local -n out="$2"
65 local file="$REPO_ROOT/build/hosts/$host.env"
66 local containerfile="$REPO_ROOT/Containerfile"
67
68 [ -f "$file" ] || {
69 printf 'error: no recipe for host %s at %s\n' "$host" "$file" >&2
70 printf 'known hosts: %s\n' "$(cd "$REPO_ROOT/build/hosts" 2>/dev/null \
71 && ls -1 ./*.env 2>/dev/null | sed 's|^\./||; s|\.env$||' | tr '\n' ' ')" >&2
72 return 1
73 }
74
75 HOST_ARCH=""
76 HOST_NVIDIA=""
77 HOST_UPDATE_TARGET=""
78 local recipe=()
79 local line key value
80 while IFS= read -r line || [ -n "$line" ]; do
81 line="${line%%#*}"
82 line="${line%"${line##*[![:space:]]}"}"
83 [ -n "$line" ] || continue
84 case "$line" in
85 *=*) ;;
86 *) printf 'error: %s: not KEY=VALUE: %s\n' "$file" "$line" >&2; return 1 ;;
87 esac
88 key="${line%%=*}"
89 value="${line#*=}"
90 case "$value" in
91 @*)
92 local path="${value#@}"
93 case "$path" in "~/"*) path="$HOME/${path#\~/}" ;; esac
94 [ -f "$path" ] || {
95 printf 'error: %s: %s reads %s, which does not exist\n' "$file" "$key" "$path" >&2
96 return 1
97 }
98 value="$(tr -d '\r\n' < "$path")"
99 [ -n "$value" ] || {
100 printf 'error: %s: %s reads %s, which is empty\n' "$file" "$key" "$path" >&2
101 return 1
102 }
103 ;;
104 esac
105 if [ "$key" = "ARCH" ]; then
106 HOST_ARCH="$value"
107 continue
108 fi
109 if [ "$key" = "UPDATE_TARGET" ]; then
110 HOST_UPDATE_TARGET="$value"
111 continue
112 fi
113 if [ "$key" = "NVIDIA" ]; then
114 case "$value" in
115 yes|no) HOST_NVIDIA="$value" ;;
116 *) printf 'error: %s: NVIDIA is %s; expected yes or no\n' "$file" "$value" >&2; return 1 ;;
117 esac
118 continue
119 fi
120 grep -q "^ARG $key=" "$containerfile" || {
121 printf 'error: %s: %s is not an ARG the Containerfile declares\n' "$file" "$key" >&2
122 return 1
123 }
124 recipe+=(--build-arg "$key=$value")
125 done < "$file"
126
127 out=("${recipe[@]}" "${out[@]}")
128 }
129