# Plan: aarch64 (Generic UEFI ARM64) build for omarchy-iso ## Context omarchy-iso currently produces a single x86_64 Arch Linux live ISO. The build is x86-baked from top to bottom — profile arch, package list filename, microcode, kernel choice (`linux-t2`), bootloader configs (BIOS syslinux + UEFI GRUB), squashfs BCJ filter, Node.js download URL, QEMU smoke-test, release filename, and CI runner. Target: a parallel **generic UEFI aarch64** ISO — boots on Ampere servers, AWS Graviton VMs, Snapdragon X laptops, ARM dev kits. Anything that exposes vanilla UEFI + ACPI. Apple Silicon (Asahi) and SBCs (U-Boot/rpi-firmware) are out of scope. **Assumption:** cross-repo dependencies are someone else's problem to land first. This plan only covers omarchy-iso. The hard prerequisites are listed up front so it's clear what blocks the first green build. --- ## Hard prerequisites (NOT in this plan, but block any green build) These exist outside omarchy-iso. Flagged for visibility — without them, nothing here boots: 1. **aarch64 base packages must exist somewhere we can pacman from.** Vanilla Arch (`geo.mirror.pkgbuild.com`) is x86_64-only — there is no `core/os/aarch64`. The realistic source is **Arch Linux ARM** (`mirror.archlinuxarm.org`) for `core`/`extra`/`alarm`/`aur`. Decision required: target Arch Linux ARM as the aarch64 base distribution. 2. **`pkgs.omarchy.org/{stable,edge}/aarch64/`** must serve a real repo. Probed today, both return 404. omarchy-pkgs already has multi-arch build support per its README, so this is a publish step, not a port. 3. **Omarchy installer guard** at `~/.local/share/omarchy/install/preflight/guard.sh:25` hard-aborts on `uname -m != x86_64`. Must be relaxed to allow aarch64 (or scoped behind a flag). 4. **archinstall + Limine** must work end-to-end on aarch64. archinstall supports it; Limine supports aarch64 UEFI. Worth a manual verification before committing to this bootloader path on ARM. --- ## Approach Add an `--arch aarch64` flag to `bin/omarchy-iso-make`. Branch on it everywhere x86_64 is currently assumed. Don't fork the profile directory — overlay arch-specific files at build time so the two architectures share one source of truth. Output artifact naming already uses no arch suffix until release-time renaming, so two parallel builds coexist cleanly. --- ## Concrete changes ### 1. Build entrypoint — `bin/omarchy-iso-make` - Add `--arch x86_64|aarch64` flag (default `x86_64` for backward compat). - Pass `OMARCHY_ARCH` env var into the container. - Switch the docker image / platform per arch: - x86_64: `archlinux/archlinux:latest` (unchanged) - aarch64: `archlinuxarm/archlinuxarm:latest` (or `menci/archlinuxarm` — pick whichever publishes a recent multi-arch image), with `--platform linux/arm64`. Native arm64 host preferred; QEMU emulation works but is slow. - Don't change pacman cache mount on the host: pacman keeps per-arch subdirs, so the cache is safe to share. ### 2. Profile — `configs/profiledef.sh` Change `arch` and the squashfs BCJ filter at runtime. Either: - Keep one `profiledef.sh` and have `builder/build-iso.sh` `sed` the arch line + BCJ filter (`x86` → `arm`) before `mkarchiso` runs, **or** - Source `OMARCHY_ARCH` directly inside `profiledef.sh` (mkarchiso sources this file with bash, so env access is fine). `bootmodes` also needs to drop `bios.syslinux` for aarch64 (BIOS boot is x86-only) — `bootmodes=('uefi.grub')` on ARM. ### 3. Build script — `builder/build-iso.sh` - **Line 56–57**: Node.js URL grep. Branch on `OMARCHY_ARCH`: - `x86_64` → `linux-x64.tar.gz` - `aarch64` → `linux-arm64.tar.gz` - **Line 73**: package additions. Drop `linux-t2` for aarch64 (T2-only x86 kernel) — use plain `linux`. Write to `packages.${OMARCHY_ARCH}` instead of hardcoded `packages.x86_64`. - **Line 77**: read same `packages.${OMARCHY_ARCH}` for offline mirror enumeration. - **archiso releng overlay** (`cp -r /archiso/configs/releng/*`): the upstream `releng` profile ships only `packages.x86_64`. For aarch64, copy `packages.x86_64` to `packages.aarch64` and prune obviously x86-only entries (`memtest86+`, `intel-ucode`, `amd-ucode`, `edk2-shell` x64 binary, `syslinux`). Land this as a small fixup step in `build-iso.sh` rather than a full fork of `releng`. ### 4. archinstall package list — `builder/archinstall.packages` Drop microcode for aarch64 — `intel-ucode` and `amd-ucode` don't exist for ARM. Either: - Two lists (`archinstall.packages.x86_64`, `archinstall.packages.aarch64`), or - One list with sentinel comments and a filter step in `build-iso.sh:80` that drops microcode lines when `OMARCHY_ARCH=aarch64`. The second is less duplication for one-line drift. ### 5. Bootloader configs aarch64 has no BIOS — only UEFI. Drop the syslinux path entirely on ARM. - **Skip on aarch64**: `configs/syslinux/*` (BIOS syslinux), `configs/grub/*` references to `shellx64.efi`/`shellia32.efi` (lines 81–85 of `grub.cfg`). - **`configs/efiboot/loader/loader.conf`**: change `default 01-archiso-x86_64-linux.conf` to a per-arch entry. Either two loader.conf files (selected by build-iso.sh) or rename the entry generically (`01-archiso-linux.conf`) and keep one file. - **`configs/efiboot/loader/entries/01-archiso-x86_64-linux.conf`**: produce an aarch64 sibling (`01-archiso-aarch64-linux.conf`) at build time — same template, drop microcode initrd lines, swap title. The kernel/initrd paths use `%ARCH%` already in `grub.cfg` but are hardcoded `x86_64` in this file (line 3–4). - **`configs/grub/grub.cfg` and `loopback.cfg`**: remove or guard the x86_64 conditionals. The `%ARCH%` placeholder gets substituted by mkarchiso from `profiledef.sh:arch`, so the kernel/initrd paths flip automatically. Manually-written `grub_cpu == 'x86_64'` blocks (lines 34–39, 68, 81–85) should drop on aarch64 builds — easiest done with a build-time sed for the aarch64 path, or by extracting them into a separate `grub-x86_64.cfg.fragment` only included on x86 builds. The cleanest cut: keep one `grub.cfg` shared, strip the x86 shell/memtest fragments at build time when `OMARCHY_ARCH=aarch64`. Don't fork the file. ### 6. mkinitcpio preset — `configs/airootfs/etc/mkinitcpio.d/linux.preset` References `vmlinuz-linux-t2` and `initramfs-linux-t2.img` (lines 5, 8). For aarch64, swap to `vmlinuz-linux` / `initramfs-linux.img`. Generate the file at build time from a template, keyed on arch. ### 7. Configurator — `configs/airootfs/root/configurator` Lines 320–325: ```bash if lspci -nn 2>/dev/null | grep -q "106b:180[12]"; then kernel_choice="linux-t2" else kernel_choice="linux" fi ``` T2 detection is harmless on aarch64 (`lspci` returns no match), so `kernel_choice` falls through to `linux`. **No change strictly required**, but clearer to wrap the lspci probe in `[[ $(uname -m) == "x86_64" ]]` so the intent reads correctly. Cheap. The archinstall JSON's `mirror_config.custom_servers` (lines 422–424) points at `mirror.omarchy.org`, `mirror.rackspace.com/archlinux`, `geo.mirror.pkgbuild.com` — none of these serve aarch64. For aarch64 the list must be `mirror.archlinuxarm.org` and friends. Branch the JSON template on `OMARCHY_ARCH` (or `uname -m` at runtime, since this file runs on the live ISO). ### 8. pacman configs — `configs/pacman-online-{stable,rc,edge,offline}.conf` The `$arch` placeholder is pacman-resolved (matches `Architecture = auto`), so `https://stable-mirror.omarchy.org/$repo/os/$arch` adapts automatically — **as long as the mirror serves aarch64**. The omarchy mirror is the bottleneck (prerequisite #2). The `arch-mact2` repo is x86-only by definition; gate it behind arch on aarch64 builds (drop the section). ### 9. Smoke-test scripts — `bin/omarchy-iso-boot`, `bin/omarchy-vm` Detect arch from the ISO filename (or accept a flag) and switch: - x86_64: `qemu-system-x86_64`, `/usr/share/edk2/x64/OVMF_CODE.4m.fd` + `OVMF_VARS.4m.fd` (unchanged). - aarch64: `qemu-system-aarch64`, `-machine virt -cpu max`, `/usr/share/edk2/aarch64/QEMU_CODE.fd` + `QEMU_VARS.fd` (Arch package: `edk2-armvirt`). Both scripts hardcode the OVMF path; small, mechanical fix. ### 10. Release script — `bin/omarchy-iso-release:52` ```bash latest_iso=$(\ls -t "$BUILD_RELEASE_PATH"/*x86_64-"$INSTALLER_REF".iso | head -n1) ``` Hardcoded `x86_64` glob. Take an arch arg, or do `*${OMARCHY_ARCH}-${INSTALLER_REF}.iso`. The ISO filename already contains arch (set by archiso from `profiledef.sh`), so this just needs the glob parameterized. ### 11. CI — `.github/workflows/nightly-build.yml` Add a matrix: ```yaml strategy: matrix: arch: [x86_64, aarch64] include: - arch: x86_64 runner: ubuntu-latest - arch: aarch64 runner: ubuntu-24.04-arm # GitHub-hosted ARM runner, GA ``` Pass `--arch ${{ matrix.arch }}` to the build. Upload artifacts named per-arch. If GitHub-hosted ARM runners aren't available on this org's plan, fall back to QEMU emulation on `ubuntu-latest` (slow, ~3-4× build time) or self-hosted. --- ## Files to modify (summary) Code: - `bin/omarchy-iso-make` — add `--arch` flag, branch docker image/platform - `bin/omarchy-iso-boot` — branch QEMU binary + OVMF path - `bin/omarchy-vm` — same as iso-boot - `bin/omarchy-iso-release` — parameterize ISO glob - `builder/build-iso.sh` — main branching: Node.js URL, package list filename, releng overlay fixups, kernel package - `builder/archinstall.packages` — filter microcode on aarch64 (sentinel comments + filter step) - `configs/profiledef.sh` — env-driven `arch` + BCJ filter + bootmodes - `configs/efiboot/loader/loader.conf` — per-arch default entry - `configs/efiboot/loader/entries/01-archiso-x86_64-linux.conf` — generate aarch64 sibling at build time - `configs/grub/grub.cfg`, `loopback.cfg` — strip x86-only fragments on aarch64 - `configs/airootfs/etc/mkinitcpio.d/linux.preset` — template by arch (drop linux-t2 on aarch64) - `configs/airootfs/root/configurator` — branch archinstall JSON mirror list on `uname -m`; optionally guard lspci T2 probe - `.github/workflows/nightly-build.yml` — matrix x86_64/aarch64 No new files unless we go the dual-list route on archinstall.packages or efiboot loader entries; preferred path is build-time templating to keep one source of truth. --- ## Verification **Local (host is x86_64):** 1. `bin/omarchy-iso-make --arch x86_64` — must produce a byte-similar ISO to today's nightly (smoke test for regressions in the refactor). 2. `bin/omarchy-iso-make --arch aarch64` — succeeds (slow under QEMU binfmt; expect 30-60 min). 3. `bin/omarchy-iso-boot release/omarchy-*-aarch64-*.iso` — boots to the configurator under `qemu-system-aarch64 -machine virt`. Walk through the picker, confirm archinstall lays down a working system, reboot into the installed system. 4. Live-iso shell: `pacman -Sy && pacman -Si linux` returns an aarch64 package from `mirror.archlinuxarm.org`. **On real hardware (post green QEMU run):** - AWS Graviton VM (`c7g.medium`, dd ISO to a volume, attach as boot) — fastest cloud check, no hardware needed. - One physical ARM64 UEFI box if available (Ampere dev kit, Snapdragon X laptop, etc.) — the long pole. **CI:** - Both matrix legs go green on a manual `workflow_dispatch` before merging. - Nightly produces both ISOs as artifacts. --- ## Open risks (call out before implementation) 1. **mkarchiso on aarch64 is less-trodden ground.** It accepts `arch="aarch64"`, but the upstream Arch project doesn't dogfood it. Expect to file/patch around small bugs in archiso's helper scripts. Keep the archiso submodule pin tight so a regression doesn't surprise nightly. 2. **Limine + LUKS + Btrfs + Snapper on aarch64** — every one of these works individually on ARM, but the combination is what omarchy ships. Worth one manual end-to-end pass before declaring done. 3. **Apple T2 / linux-t2 / `arch-mact2` repo** are silently dropped on aarch64; users mistakenly trying to install the aarch64 ISO on a T2 Mac will get an obviously-wrong result. The configurator could refuse to install when arch mismatch is detected, but that's outside this plan.