Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/image-producer.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Build order inside the container:

1. A base system and `omarchy-settings`, alone, so its pacman platform guard (omacom/omarchy-mac#539) is resident before any platform package.
2. The root-owned image-target manifest `/var/lib/omarchy/image/target` (`format=1`, `platform=apple-silicon`), which deferred hardware setup (omacom/omarchy-mac#528) and the pacman platform guard (#539) both read. It also records `candidate_set`, `candidate_source_commit`, `builder_commit`, `builder_tree_clean` and `image_profile` (`lab` for `--lab-access`, `test` for a candidate-only set, else `release`), which the runtime ignores; after first boot the file is `target.booted`, so a booted Mac names the image it came from. Once the package set is recorded (step 5), the builder appends `package_set_sha256` and `built` (UTC, `YYYY-MM-DDTHH:MM:SSZ`), the values `IMAGE` and `PROVENANCE` record, before `@factory` is sealed, so a hardware report maps to one build's release assets (`image-builder/README.md`, Build identity). The image's own sha256 is not in it (root.img holds the file): `IMAGE`, `PROVENANCE` and the catalog carry it.
3. The runtime with `omarchy-base.packages`, then the Apple set: `omarchy-apple-silicon.packages` (an older runtime's `omarchy-apple.packages`), the kernel, m1n1, U-Boot and the Limine hook, plus `alsa-ucm-conf-asahi` and `asahi-audio` from the pinned asahi-alarm database. Every candidate set package is installed by its `omarchy-candidates/` name, so `[asahi-alarm]`, ordered before `[omarchy]` in the Apple profile, cannot supply `uboot-asahi` or `m1n1`.
3. The runtime with `omarchy-base.packages`, then the Apple set: `omarchy-aarch64-apple.packages` (an older runtime's `omarchy-apple-silicon.packages` or `omarchy-apple.packages`), the kernel, m1n1, U-Boot and the Limine hook, plus `alsa-ucm-conf-asahi` and `asahi-audio` from the pinned asahi-alarm database. Every candidate set package is installed by its `omarchy-candidates/` name, so `[asahi-alarm]`, ordered before `[omarchy]` in the Apple profile, cannot supply `uboot-asahi` or `m1n1`.
4. The runtime's `omarchy-apply-system --defer-provisioning --first-install`. The build chroot never sees the build host's hardware: a device tree naming the image's platform, UEFI without EFI variables, no PCI, USB, input or DMI devices. The host kernel must run with a device tree for the image's platform checks to find one. A runtime with #528 queues its hardware steps for first boot from the manifest; the first candidate set's runtime (`073e489b5`) predates it and runs them here, and its first boot runs only the Limine leaf (`mac-first-boot/deferred-steps`).
5. The image's GRUB defaults (the Limine command line's source, with `plymouth.ignore-serial-consoles` as on mx-mac) and Plymouth's `omarchy` theme, presets (`80-omarchy-mac*.preset`, so omarchy-mac's audio preset too), Node.js for owner provisioning, `mkinitcpio -P`, GRUB, `update-m1n1` under `LC_ALL=C`, the runtime's GRUB compatibility and Limine leaves, and first boot armed by the boot package's own `arm` command.
6. Seal (snapper's `/.snapshots` stays a btrfs subvolume through the copy), inspect, package. `PROVENANCE` and `IMAGE` record the builder commit, the set's receipt, manifest, source commit and signer, each installed package's repository and archive sha256, `package_set_sha256`, and `built`, the UTC time the build recorded its identity in the image-target manifest (the same `package_set_sha256` and `built` the manifest carries).
Expand Down
4 changes: 2 additions & 2 deletions image-builder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ A build takes about 15 minutes and 25 GB of disk. On a host with a desktop sessi

## What a build does

1. Installs a base system and `omarchy-settings` first, so its pacman platform guard is resident, then writes the root-owned image-target manifest (`/var/lib/omarchy/image/target`: `format=1`, `platform=apple-silicon`, read by deferred hardware setup and the platform guard, then the image's provenance, which the runtime ignores: `candidate_set`, `candidate_source_commit`, `builder_commit`, `builder_tree_clean` and `image_profile` of `lab`, `test` for a candidate-only set, or `release`), then installs the runtime with `omarchy-base.packages` and `omarchy-aarch64.packages` (as `omarchy-pkg-defaults apple-silicon` composes them) and, last, the Apple set: the runtime's Apple list (`omarchy-apple-silicon.packages`, or an older runtime's `omarchy-apple.packages`; a runtime with neither stops the build), the Aurora kernel, m1n1, U-Boot, the Limine hook, and the speaker stack's model profiles and DSP chain (`alsa-ucm-conf-asahi`, `asahi-audio`), which the runtime's audio step would otherwise fetch on a first boot that may have no network. Set packages are installed by their `omarchy-candidates/` name, so no other repository can supply them. Base names the pinned repositories lack are recorded as `unavailable=` in `PROVENANCE`.
1. Installs a base system and `omarchy-settings` first, so its pacman platform guard is resident, then writes the root-owned image-target manifest (`/var/lib/omarchy/image/target`: `format=1`, `platform=apple-silicon`, read by deferred hardware setup and the platform guard, then the image's provenance, which the runtime ignores: `candidate_set`, `candidate_source_commit`, `builder_commit`, `builder_tree_clean` and `image_profile` of `lab`, `test` for a candidate-only set, or `release`), then installs the runtime with `omarchy-base.packages` and `omarchy-aarch64.packages` (as `omarchy-pkg-defaults apple-silicon` composes them) and, last, the Apple set: the runtime's Apple list (`omarchy-aarch64-apple.packages`, or an older runtime's `omarchy-apple-silicon.packages` or `omarchy-apple.packages`; a runtime with none stops the build), the Aurora kernel, m1n1, U-Boot, the Limine hook, and the speaker stack's model profiles and DSP chain (`alsa-ucm-conf-asahi`, `asahi-audio`), which the runtime's audio step would otherwise fetch on a first boot that may have no network. Set packages are installed by their `omarchy-candidates/` name, so no other repository can supply them. Base names the pinned repositories lack are recorded as `unavailable=` in `PROVENANCE`.
2. Runs the runtime's own `omarchy-apply-system --defer-provisioning --first-install` in an isolated chroot. The chroot never sees the build host's hardware: its device tree names the image's platform, it has UEFI (as U-Boot provides) without EFI variables, and no PCI, USB, input or DMI devices, so hardware setup installs the same packages on any aarch64 host whose kernel runs with a device tree (an Apple Silicon Mac, most arm64 boards; on an ACPI-only host the image's platform checks find no device tree and the build stops at the Limine activation). A runtime with deferred hardware setup (omacom/omarchy-mac#528) queues its hardware steps from the manifest; an older one runs them here for the image's platform.
3. Sets the kernel command line in `/etc/default/grub`, which the Limine command line is derived from; the image installs no GRUB (`quiet splash`, and `plymouth.ignore-serial-consoles` as on mx-mac, since the Mac's device tree registers a serial console), and Plymouth's `omarchy` theme, which omarchy-settings leaves to Arch Linux ARM's `bgrt` on Apple Silicon. Applies the Apple presets (`80-omarchy-mac*.preset`, including omarchy-mac's audio preset), stages owner provisioning with the pinned Node.js, rebuilds the initramfs, runs `update-m1n1` with `LC_ALL=C` so the device trees go into m1n1's stage 2 in one order, activates Limine through omarchy-mac-boot's `setup-boot` operation of the runtime's lifecycle dispatcher (once before the Limine gate is set, for the console settings, and once after, for the activation; a runtime whose dispatcher has no `setup-boot`, or a boot package without it, keeps the runtime's own `install/hardware/apple` console and Limine leaves), and arms first boot with the boot package's own `arm` command. Once the installed package set is recorded, it appends the build's identity to the image-target manifest (see [Build identity](#build-identity)).
4. Copies the root into a fresh image, keeping snapper's `/.snapshots` a btrfs subvolume (a file copy would flatten it into a plain directory and snapper would fail on the Mac), seals a snapshot of it into `@factory`, then inspects the images against the set's archives and packages them.
Expand All @@ -40,7 +40,7 @@ A build takes about 15 minutes and 25 GB of disk. On a host with a desktop sessi
- the pacman hooks that rebuild the UKI and redeploy Limine come from their packages, the Apple gate included
- the image-target manifest (its provenance matching the set, the build's profile and `@factory`'s copy; `mac-image-check provenance` matches it to `PROVENANCE`), first boot, owner provisioning, the Limine gate and the fresh-image `deferred-steps` contract; the hardware queue never runs an older runtime's `install/hardware/apple/pacman.sh`, which adds the unsigned `[omarchy-aarch64]`
- `/.snapshots` is an empty btrfs subvolume under snapper's root configuration, or absent with a deferred hardware step queued to create it; a plain directory fails
- the installed pacman configuration is omarchy-mac's Apple Silicon template for the channel (`/usr/share/omarchy-mac/pacman`; on an older set the runtime's `default/pacman/apple-silicon`, or `aarch64` on an older runtime still) with the runtime's aarch64 mirror list (`aarch64/mirrorlist-<channel>`, or a single `mirrorlist-aarch64`), plus the test image's pin below, and the installed-system checks (`builder/verify_installed_system.py`) pass, `alsa-ucm-conf-asahi`, `asahi-audio`, `vulkan-asahi` and `asahi-bless` included, and no `[omarchy-aarch64]` section or `TrustAll` SigLevel; Bluetooth counts as enabled when its hardware step is queued for first boot
- the installed pacman configuration is omarchy-mac's Apple Silicon template for the channel (`/usr/share/omarchy-mac/pacman`; on an older set the runtime's `default/pacman/aarch64-apple`, or `apple-silicon` or `aarch64` on an older runtime still) with the runtime's aarch64 mirror list (`aarch64/mirrorlist-<channel>`, or a single `mirrorlist-aarch64`), plus the test image's pin below, and the installed-system checks (`builder/verify_installed_system.py`) pass, `alsa-ucm-conf-asahi`, `asahi-audio`, `vulkan-asahi` and `asahi-bless` included, and no `[omarchy-aarch64]` section or `TrustAll` SigLevel; Bluetooth counts as enabled when its hardware step is queued for first boot
- `@factory` is sealed for the set without fresh-image or owner state

`bin/mac-image-check` also holds the payload to the installer engine's contract and checks `PROVENANCE` and `IMAGE` against the bytes beside them.
Expand Down
13 changes: 7 additions & 6 deletions image-builder/bin/build-mac-image
Original file line number Diff line number Diff line change
Expand Up @@ -643,19 +643,19 @@ runtime_list() {
}

# The runtime's Apple list, read once before anything installs:
# omarchy-apple-silicon.packages, else an older runtime's
# omarchy-apple.packages. A link is passed over: bsdtar reads it as empty, and
# the list it names is a member of its own.
# omarchy-aarch64-apple.packages, else an older runtime's
# omarchy-apple-silicon.packages or omarchy-apple.packages. A link is passed
# over: bsdtar reads it as empty, and the list it names is a member of its own.
read_apple_list() {
local archive name list="" text
archive=$candidates/$(candidate_archive omarchy)
for name in omarchy-apple-silicon omarchy-apple; do
for name in omarchy-aarch64-apple omarchy-apple-silicon omarchy-apple; do
if [[ $(bsdtar -tvf "$archive" "usr/share/omarchy/install/$name.packages" 2>/dev/null) == -* ]]; then
list=usr/share/omarchy/install/$name.packages
break
fi
done
[[ -n $list ]] || fail "the runtime ships no Apple package list (omarchy-apple-silicon.packages or omarchy-apple.packages)"
[[ -n $list ]] || fail "the runtime ships no Apple package list (omarchy-aarch64-apple.packages, omarchy-apple-silicon.packages or omarchy-apple.packages)"
text=$(bsdtar -xOf "$archive" "$list") || fail "could not read the runtime's ${list##*/}"
mapfile -t apple_names < <(awk 'NF && $1 !~ /^#/ { print $1 }' <<<"$text")
((${#apple_names[@]})) || fail "the runtime's ${list##*/} names no package"
Expand Down Expand Up @@ -846,7 +846,7 @@ release_build_pacman_config() {

# The installed configuration for an Apple Silicon Mac: omarchy-mac's
# template for the channel the inputs name (an older set's runtime
# apple-silicon one, or an even older runtime's aarch64 one), with the
# aarch64-apple or apple-silicon one, or an even older runtime's aarch64 one), with the
# runtime's aarch64 mirror list (per channel, or the single
# mirrorlist-aarch64 of a runtime that keeps its own repositories). TEST IMAGES ONLY: a
# test candidate set's runtime sorts below the channel's omarchy, so its
Expand All @@ -857,6 +857,7 @@ write_installed_config() {
local channel templates=$target/usr/share/omarchy/default/pacman repositories mirrorlist
channel=$(field omarchy_channel)
repositories=$target/usr/share/omarchy-mac/pacman
[[ -f $repositories/pacman-$channel.conf ]] || repositories=$templates/aarch64-apple
[[ -f $repositories/pacman-$channel.conf ]] || repositories=$templates/apple-silicon
[[ -f $repositories/pacman-$channel.conf ]] || repositories=$templates/aarch64
mirrorlist=$templates/aarch64/mirrorlist-$channel
Expand Down
7 changes: 4 additions & 3 deletions image-builder/builder/candidate_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@
HEX40 = re.compile(r'[0-9a-f]{40}')
HEX64 = re.compile(r'[0-9a-f]{64}')
ARCHIVE = re.compile(r'[A-Za-z0-9@._+:-]+\.pkg\.tar\.(xz|zst)')
# The runtime's Apple package list, by upstream's name, then an older runtime's.
APPLE_LISTS = ('usr/share/omarchy/install/omarchy-apple-silicon.packages',
# The runtime's Apple package list, by upstream's name, then older runtimes'.
APPLE_LISTS = ('usr/share/omarchy/install/omarchy-aarch64-apple.packages',
'usr/share/omarchy/install/omarchy-apple-silicon.packages',
'usr/share/omarchy/install/omarchy-apple.packages')
# Where each runtime package records the commit it was built from.
REVISION_FILES = {
Expand Down Expand Up @@ -183,7 +184,7 @@ def apple_list(package):
entry = subprocess.run(['bsdtar', '-tvf', str(package), name], capture_output=True, text=True)
if entry.returncode == 0 and entry.stdout.startswith('-'):
return name, member(package, name).decode()
raise ValueError('the runtime ships no Apple package list (omarchy-apple-silicon.packages or omarchy-apple.packages)')
raise ValueError('the runtime ships no Apple package list (omarchy-aarch64-apple.packages, omarchy-apple-silicon.packages or omarchy-apple.packages)')


def payload_paths(package):
Expand Down
14 changes: 8 additions & 6 deletions image-builder/builder/inspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,13 @@ def check_runtime_sources(root: Path, candidates: Candidates, report: dict) -> s

def check_apple_packages(root: Path, report: dict) -> str:
"""Every package the image's Apple list names is installed, reading the list
build-mac-image took: omarchy-apple-silicon.packages, else an older
runtime's omarchy-apple.packages, never a link."""
build-mac-image took: omarchy-aarch64-apple.packages, else an older
runtime's omarchy-apple-silicon.packages or omarchy-apple.packages, never
a link."""
path = next((root / name for name in candidate_set.APPLE_LISTS
if (root / name).is_file() and not (root / name).is_symlink()), None)
require(path is not None,
"the image ships no Apple package list (omarchy-apple-silicon.packages or omarchy-apple.packages)")
"the image ships no Apple package list (omarchy-aarch64-apple.packages, omarchy-apple-silicon.packages or omarchy-apple.packages)")
names = [fields[0] for fields in map(str.split, path.read_text().splitlines())
if fields and not fields[0].startswith("#")]
require(bool(names), f"{path.name} names no package")
Expand Down Expand Up @@ -517,11 +518,12 @@ def check_first_boot(root: Path, report: dict) -> str:


def pacman_templates(root: Path, channel: str) -> tuple[str, Path, Path]:
# The Apple template as build-mac-image picks it: omarchy-mac's, else an
# older runtime's apple-silicon one, else an even older runtime's aarch64
# one; and the runtime's aarch64 mirror list, per channel or single.
# The Apple template as build-mac-image picks it: omarchy-mac's, else the
# runtime's aarch64-apple one, else an older runtime's apple-silicon one, else
# an even older runtime's aarch64 one; and the runtime's aarch64 mirror list.
runtime = root / "usr/share/omarchy/default/pacman"
choices = (("omarchy-mac's apple-silicon", root / f"usr/share/omarchy-mac/pacman/pacman-{channel}.conf"),
("the runtime's aarch64-apple", runtime / f"aarch64-apple/pacman-{channel}.conf"),
("the runtime's apple-silicon", runtime / f"apple-silicon/pacman-{channel}.conf"),
("the runtime's aarch64", runtime / f"aarch64/pacman-{channel}.conf"))
kind, template = next((choice for choice in choices if choice[1].is_file()), choices[-1])
Expand Down
14 changes: 9 additions & 5 deletions image-builder/test/build-mac-image-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pass "the runtime's base list, then its aarch64 additions, as omarchy-pkg-defaul
# The Apple list by upstream's name, else an older runtime's; never a link, which bsdtar reads as empty.
install_dir=$scratch/runtime/usr/share/omarchy/install
apple_layout() {
rm -f "$install_dir"/omarchy-apple*.packages
rm -f "$install_dir"/omarchy-apple*.packages "$install_dir"/omarchy-aarch64-apple.packages
while (($#)); do
if [[ $2 == @* ]]; then
ln -s "${2#@}" "$install_dir/$1"
Expand All @@ -108,8 +108,12 @@ apple_layout() {
chosen_apple() {
(fail() { builder_fail "$@"; }; read_apple_list && echo "${apple_names[*]}")
}
apple_layout omarchy-aarch64-apple.packages 'omarchy-mac wf-recorder'
[[ $(chosen_apple) == "omarchy-mac wf-recorder" ]] || fail "an upstream runtime's omarchy-aarch64-apple.packages is the Apple list"
apple_layout omarchy-aarch64-apple.packages 'omarchy-mac wf-recorder' omarchy-apple-silicon.packages omarchy-mac
[[ $(chosen_apple) == "omarchy-mac wf-recorder" ]] || fail "upstream's name wins over the name before the platform rename"
apple_layout omarchy-apple-silicon.packages 'omarchy-mac wf-recorder'
[[ $(chosen_apple) == "omarchy-mac wf-recorder" ]] || fail "an upstream runtime's omarchy-apple-silicon.packages is the Apple list"
[[ $(chosen_apple) == "omarchy-mac wf-recorder" ]] || fail "a runtime's omarchy-apple-silicon.packages from before the rename is the Apple list"
apple_layout omarchy-apple.packages omarchy-mac
[[ $(chosen_apple) == omarchy-mac ]] || fail "an older runtime's omarchy-apple.packages is the Apple list"
apple_layout omarchy-apple-silicon.packages 'omarchy-mac wf-recorder' omarchy-apple.packages omarchy-mac
Expand All @@ -118,7 +122,7 @@ apple_layout omarchy-apple-silicon.packages 'omarchy-mac wf-recorder' omarchy-ap
[[ $(chosen_apple) == "omarchy-mac wf-recorder" ]] || fail "a compatibility link beside the list changes nothing"
apple_layout omarchy-apple.packages omarchy-mac omarchy-apple-silicon.packages @omarchy-apple.packages
[[ $(chosen_apple) == omarchy-mac ]] || fail "a link by upstream's name is passed over for the list it names"
pass "the Apple list is omarchy-apple-silicon.packages, else an older runtime's omarchy-apple.packages, never a link"
pass "the Apple list is omarchy-aarch64-apple.packages, else an older runtime's omarchy-apple-silicon.packages or omarchy-apple.packages, never a link"
refused_apple() {
local output
if output=$(chosen_apple 2>&1); then
Expand All @@ -127,10 +131,10 @@ refused_apple() {
[[ $output == "build-mac-image: $1" ]] || fail "$2 is refused with: $1 (got: $output)"
}
apple_layout
refused_apple "the runtime ships no Apple package list (omarchy-apple-silicon.packages or omarchy-apple.packages)" \
refused_apple "the runtime ships no Apple package list (omarchy-aarch64-apple.packages, omarchy-apple-silicon.packages or omarchy-apple.packages)" \
"a runtime with no Apple list"
apple_layout omarchy-apple.packages @omarchy-apple-silicon.packages
refused_apple "the runtime ships no Apple package list (omarchy-apple-silicon.packages or omarchy-apple.packages)" \
refused_apple "the runtime ships no Apple package list (omarchy-aarch64-apple.packages, omarchy-apple-silicon.packages or omarchy-apple.packages)" \
"a runtime whose only Apple list is a link"
apple_layout omarchy-apple-silicon.packages ''
refused_apple "the runtime's omarchy-apple-silicon.packages names no package" "a runtime whose Apple list names nothing"
Expand Down
Loading
Loading