Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f9cfc1c
builder: build for the host architecture instead of hardcoding x86_64
oceanapplications Aug 24, 2026
795c3b0
configs: filter x86-only repositories out of the pacman config on aar…
oceanapplications Aug 24, 2026
19281f0
boot: make the live ISO bootable on aarch64
oceanapplications Aug 24, 2026
91dbf61
installer: fix the phases that assume an x86 target
oceanapplications Aug 24, 2026
e687c36
configs: add an aarch64 package list for the live environment
oceanapplications Aug 24, 2026
a6415af
boot: fail the build when the live initramfs is not produced
oceanapplications Aug 24, 2026
c391885
boot: document what customize_airootfs.sh is actually responsible for
oceanapplications Aug 24, 2026
5171b65
builder: filter GRUB modules not built for the target platform
oceanapplications Aug 25, 2026
21aa8f3
builder: define _filter_grubmodules in the carried archiso patch
oceanapplications Aug 26, 2026
2766ac6
installer: keep the distribution's mirrorlist on aarch64
oceanapplications Aug 26, 2026
8340b6b
builder: enumerate offline mirror packages before repo-add
oceanapplications Aug 26, 2026
c9c705a
Build the aarch64 ISO from an Arch Linux ARM image with a local [omar…
JimmayVV Aug 25, 2026
c917d4e
Add the aarch64 ISO workflow on GitHub arm64 runners
JimmayVV Aug 25, 2026
052152c
Chainload a live UKI carrying the Windows-on-ARM device trees on arm64
JimmayVV Aug 25, 2026
178a7b0
Kernel parameters and Qualcomm firmware the live ISO needs on Snapdra…
JimmayVV Aug 25, 2026
0c81c10
Install the pkgbase shim and the Adreno Vulkan driver on aarch64 targets
JimmayVV Aug 26, 2026
2d89268
Stage the Qualcomm firmware from Windows before the disk is written
JimmayVV Aug 26, 2026
dfedc49
Make the carried archiso patch whitespace-clean
birkskyum Aug 27, 2026
8eefea4
Point the ARM ISO at the upstream repository builder
birkskyum Aug 27, 2026
7d79158
Actually include the live UKI in aarch64 ISOs
birkskyum Aug 27, 2026
67539f3
Remove x86 microcode from the aarch64 profile
birkskyum Aug 27, 2026
14f17c1
Build the ALARM kernel image shim for the ISO
birkskyum Aug 27, 2026
b2fa0ac
Keep the ALARM keyring in the offline ISO mirror
birkskyum Aug 28, 2026
1734848
Trim aarch64 installer comments
birkskyum Aug 28, 2026
a675f33
Fail ARM ISO builds when the live initramfs is incomplete
birkskyum Sep 5, 2026
165de8e
Make ARM bootstrap and staged firmware handoff retry-safe
birkskyum Sep 5, 2026
a380b01
Use the canonical ARM repository workflow
birkskyum Sep 5, 2026
3b1b66a
Keep carried archiso patches concise and whitespace-clean
birkskyum Sep 5, 2026
fcc4d92
Share architecture filtering for ISO package lists
birkskyum Sep 6, 2026
dc166d7
Fix ARM ISO failures found in native live-media build
birkskyum Sep 6, 2026
e62f150
Read keyboard layouts from the installed target
maralcbr Sep 3, 2026
0d887de
Consolidate ARM media targets and hardware profiles
birkskyum Sep 10, 2026
50951ca
Merge quattro and retain the Broadcom package rename
birkskyum Sep 10, 2026
4d47dd6
Start live Yoga DSP only after the root is in RAM
birkskyum Sep 10, 2026
f67b090
Document the automatic Yoga live DSP boot test
birkskyum Sep 10, 2026
4a6149a
Keep ARM usage instructions in the README
birkskyum Sep 10, 2026
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
123 changes: 123 additions & 0 deletions .github/workflows/aarch64-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: aarch64 ISO Build

# Build the aarch64 package repository and ISO on a native ARM runner.

on:
workflow_dispatch:
inputs:
pkgs_ref:
description: "omarchy-pkgs branch, tag or SHA to build the [omarchy] repo from"
type: string
default: master
media_target:
description: "ARM live-media boot path"
type: choice
default: aarch64/snapdragon
options:
- aarch64/snapdragon
- aarch64/generic
iso_make_args:
description: "Extra bin/omarchy-iso-make arguments (e.g. --edge)"
type: string
default: ""

jobs:
packages:
uses: omacom/omarchy-pkgs/.github/workflows/build-aarch64.yml@master
with:
pkgs_repository: omacom/omarchy-pkgs
pkgs_ref: ${{ inputs.pkgs_ref || 'master' }}
# Packages installed from the local aarch64 Omarchy repository.
packages: >-
omarchy-keyring ttf-jetbrains-mono-nerd-basic limine-mkinitcpio-hook limine-snapper-sync
linux-aarch64-pkgbase-shim qcom-firmware-extract omarchy-settings omarchy omarchy-nvim
aether cliamp herdr localsend-bin mise-bin omacalc omacut omawrite omarchy-chromium-bin
ttfx yay tobi-try ttf-ia-writer ufw-docker xdg-terminal-exec yaru-icon-theme
tzupdate tensaku hyprtoolkit hyprland-guiutils hyprland hyprland-preview-share-picker

build-iso:
needs: packages
runs-on: ubuntu-24.04-arm
timeout-minutes: 330
env:
ISO_MAKE_ARGS: ${{ inputs.iso_make_args }}
MEDIA_TARGET: ${{ inputs.media_target || 'aarch64/snapdragon' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true

- name: Runner facts
run: |
set -x
uname -a
nproc
free -h
df -h
docker version --format '{{.Server.Version}}'
docker info --format '{{.Driver}} {{.DockerRootDir}}'

- name: Test ARM live build configuration
run: |
bash test/unit/arch-package-filter-test.sh
bash test/unit/aarch64-initramfs-test.sh
python3 -m unittest discover -s test/unit -p test_local_repo_config.py
python3 -m unittest discover -s test/unit -p test_iso_architecture.py
python3 -m unittest discover -s test/unit -p test_live_dsp.py
python3 -m unittest discover -s test/unit -p test_hardware_platforms.py
python3 -m unittest discover -s test/unit -p test_keyboard.py

# The hosted runner's root disk is too small for both build stages.
- name: Free Disk Space
uses: jlumbroso/free-disk-space@v1.3.1
continue-on-error: true

- name: Disk after cleanup
run: df -h

- name: Download the aarch64 [omarchy] repository
uses: actions/download-artifact@v4
with:
name: ${{ needs.packages.outputs.artifact }}
path: omarchy-repo

- name: Inspect the repository
run: |
test -f omarchy-repo/omarchy.db
ls -la omarchy-repo | sed -n '1,50p'
du -sh omarchy-repo

- name: Build ISO
run: |
re='^[A-Za-z0-9 ._=/-]*$'
[[ $ISO_MAKE_ARGS =~ $re ]] || { echo "invalid iso_make_args" >&2; exit 1; }
start=$(date +%s)
NO_BOOT_OFFER=1 ./bin/omarchy-iso-make --keep-pkg-cache --local-repo "$PWD/omarchy-repo" $ISO_MAKE_ARGS --arch aarch64 --media-target "$MEDIA_TARGET"
echo "ISO build wall-clock: $(( $(date +%s) - start ))s"

- name: Inspect ISO
run: |
sudo apt-get install -y -qq xorriso >/dev/null
ls -l release/
for iso in release/*.iso; do
echo "== $iso"
sha256sum "$iso"
xorriso -indev "$iso" -ls /EFI/BOOT/ 2>/dev/null
xorriso -indev "$iso" -lsl /arch/boot/aarch64/ 2>/dev/null
if [[ $MEDIA_TARGET == aarch64/snapdragon ]]; then
xorriso -indev "$iso" -ls /arch/boot/aarch64/omarchy-live.efi 2>/dev/null \
|| { echo "ISO is missing the DTB-carrying live UKI" >&2; exit 1; }
else
[[ $iso == release/omarchy-generic-*-aarch64-*.iso ]]
fi
done
df -h

- name: Upload newly built ISO artifact
uses: actions/upload-artifact@v4
with:
name: omarchy-iso-aarch64-${{ github.run_number }}
path: ./release/*.iso
if-no-files-found: error
retention-days: 3
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,27 @@ Despite the local folder name, the first argument is the Omarchy source checkout

Use `--dev` or `--rc` to build against those package channels. Both `--dev` and `--edge` select the dev packages from the edge mirror.

### ARM media

The build defaults to the host architecture, with Snapdragon media selected on ARM hosts. Use `--arch` and `--media-target` to select explicitly. Docker must support the selected architecture; cross-architecture builds require container emulation.

```bash
./bin/omarchy-iso-make --arch aarch64 --media-target aarch64/snapdragon --edge --keep-pkg-cache --no-boot-offer
./bin/omarchy-iso-make --arch aarch64 --media-target aarch64/generic --edge --keep-pkg-cache --no-boot-offer
```

Snapdragon media uses a UKI with hardware-matched Qualcomm device trees. Generic ARM media uses GRUB and firmware-provided hardware tables, and must be selected explicitly. Neither target supports every ARM board; Apple Silicon and Raspberry Pi boot support are not included. The existing `omarchy-iso-boot` helper is x86-only.

Both targets require an ARM package repository. Use `--local-repo <repo-dir>` for a prebuilt repository or `--local-source` as above to build from local checkouts. Offline caches are separated by channel, architecture and media target. Generic image filenames start with `omarchy-generic-`.

[configs/aarch64/platforms.json](configs/aarch64/platforms.json) supplies model-specific packages and installed boot arguments through exact vendor/product matches. Both targets use the package-owned `linux-aarch64-pkgbase-shim` for kernel-image handling.

### Snapdragon live DSP startup

The DSP driver stays disabled in the initramfs because loading it can reset USB-C installation media. On the Yoga Slim 7x, `omarchy-live-dsp.service` starts it later to enable USB hotplug and battery status, but only after verifying that the live filesystem is entirely in RAM, with no mounted block devices or active swap. Other boards and the installed system are unchanged.

Add `omarchy.live_dsp=0` to the live boot arguments to disable this service when troubleshooting. Inspect its decisions with `journalctl -b -u omarchy-live-dsp.service`, or run `omarchy-live-dsp --check` to validate the guards without loading the driver. An unsafe or unfamiliar storage layout leaves the driver disabled.

## Autoinstall

The shipped ISO installs itself with no keyboard when it finds its configuration on a second drive. Attach a drive labeled `cidata` alongside the ISO and the installer copies the config off it and skips the configurator; with no such drive, nothing changes and the wizard runs as usual. No rebuild, no extra boot entry.
Expand Down
63 changes: 50 additions & 13 deletions bin/omarchy-iso-make
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,29 @@ NO_CACHE=""
KEEP_PKG_CACHE=""
LOCAL_OMARCHY_PATH=""
LOCAL_PKGS_PATH=""
LOCAL_REPO_PATH=""
while [[ $# -gt 0 ]]; do
case $1 in
--arch|--media-target)
if [[ -z ${2:-} || $2 == --* ]]; then
echo "Error: $1 requires a value" >&2
exit 1
fi
if [[ $1 == --arch ]]; then
OMARCHY_ARCH=$2
else
OMARCHY_MEDIA_TARGET=$2
fi
shift 2
;;
--local-repo)
LOCAL_REPO_PATH="${2:-}"
if [[ -z $LOCAL_REPO_PATH ]]; then
echo "Error: --local-repo requires a directory holding a built [omarchy] repo (omarchy.db + packages)" >&2
exit 1
fi
shift 2
;;
--no-cache)
NO_CACHE=1
shift
Expand Down Expand Up @@ -57,27 +78,35 @@ while [[ $# -gt 0 ]]; do
;;
*)
echo "Unknown option: $1"
echo "Usage: $0 [--no-cache] [--keep-pkg-cache] [--no-boot-offer] [--debug] [--edge] [--dev|--rc] [--local-source <omarchy-checkout> <pkgs-checkout>]"
echo "Usage: $0 [--arch x86_64|aarch64] [--media-target x86_64/pc|aarch64/snapdragon|aarch64/generic] [--no-cache] [--keep-pkg-cache] [--no-boot-offer] [--debug] [--edge] [--dev|--rc] [--local-source <omarchy-checkout> <pkgs-checkout>] [--local-repo <repo-dir>]"
exit 1
;;
esac
done

BUILD_ROOT=$(cd "${BASH_SOURCE[0]%/*}/.." && pwd)
source "$BUILD_ROOT/builder/architecture.sh"

if [[ -n $LOCAL_OMARCHY_PATH ]]; then
[[ -d $LOCAL_OMARCHY_PATH ]] || { echo "Error: Omarchy checkout not found at $LOCAL_OMARCHY_PATH" >&2; exit 1; }
[[ -d $LOCAL_PKGS_PATH ]] || { echo "Error: pkgs checkout not found at $LOCAL_PKGS_PATH" >&2; exit 1; }
LOCAL_OMARCHY_PATH=$(realpath "$LOCAL_OMARCHY_PATH")
LOCAL_PKGS_PATH=$(realpath "$LOCAL_PKGS_PATH")
fi

if [[ -n $LOCAL_REPO_PATH ]]; then
[[ -f $LOCAL_REPO_PATH/omarchy.db ]] || { echo "Error: no omarchy.db in $LOCAL_REPO_PATH (build one with omarchy-pkgs: bin/repo build + bin/repo update)" >&2; exit 1; }
LOCAL_REPO_PATH=$(realpath "$LOCAL_REPO_PATH")
fi

cd "$BUILD_ROOT"

lint_file_permissions() {
local profiledef="configs/profiledef.sh"
local -A declared=()
local declared
local missing=()

while IFS= read -r path; do
[[ -n $path ]] && declared["$path"]=1
done < <(grep -oP '^\s*\["\K[^"]+(?="\])' "$profiledef" || true)
declared=$(sed -n 's/^[[:space:]]*\["\([^"]*\)"\].*/\1/p' "$profiledef")

while IFS= read -r f; do
[[ -f $f ]] || continue
Expand All @@ -90,7 +119,7 @@ lint_file_permissions() {
continue
fi

if [[ -z ${declared[$rel]+x} ]]; then
if ! grep -Fxq -- "$rel" <<<"$declared"; then
missing+=("$rel")
fi
done < <(find configs/airootfs/usr/local/bin configs/airootfs/root -type f 2>/dev/null)
Expand All @@ -107,13 +136,12 @@ lint_file_permissions

# Purging the host package cache needs interactive sudo; --keep-pkg-cache
# skips it so unattended builds (test rigs, agents) can run without a prompt.
if [[ -z $KEEP_PKG_CACHE ]]; then
if [[ -z $KEEP_PKG_CACHE && $(uname -s) == Linux && $(uname -m) == "$ISO_ARCH" ]]; then
sudo rm -rf /var/cache/pacman/pkg/*
fi
git submodule update --init --recursive --jobs=8

BUILD_SCRIPT="builder/build-iso.sh"
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
BUILD_RELEASE_PATH="$BUILD_ROOT/release"
mkdir -p "$BUILD_RELEASE_PATH"

Expand All @@ -123,6 +151,9 @@ OMARCHY_MIRROR="${OMARCHY_MIRROR:-stable}"
DOCKER_ARGS=(
--rm
--privileged
--platform "$DOCKER_PLATFORM"
-e "OMARCHY_ARCH=$OMARCHY_ARCH"
-e "OMARCHY_MEDIA_TARGET=$OMARCHY_MEDIA_TARGET"
-e "OMARCHY_ISO_REF=$OMARCHY_ISO_REF"
-e "OMARCHY_MIRROR=$OMARCHY_MIRROR"
-e "OMARCHY_INSTALL_DEBUG=${OMARCHY_INSTALL_DEBUG:-}"
Expand All @@ -142,14 +173,19 @@ if [[ -n $LOCAL_OMARCHY_PATH ]]; then
)
fi

if [[ -d /var/cache/pacman/pkg ]]; then
# Mount a locally built Omarchy package repository when requested.
if [[ -n $LOCAL_REPO_PATH ]]; then
DOCKER_ARGS+=(-v "$LOCAL_REPO_PATH:/omarchy-repo:ro")
fi

if [[ $(uname -s) == Linux && $(uname -m) == "$ISO_ARCH" && -d /var/cache/pacman/pkg ]]; then
DOCKER_ARGS+=(-v "/var/cache/pacman/pkg:/var/cache/pacman/pkg")
fi

# Mount the build cache directory where packages are downloaded. Channels use
# different package snapshots and must never share an offline mirror cache.
if [[ -z "$NO_CACHE" ]]; then
OFFLINE_REPO_BUILD_CACHE_DIR="$HOME/.cache/omarchy/iso_${OMARCHY_MIRROR}/airootfs/var/cache/omarchy"
OFFLINE_REPO_BUILD_CACHE_DIR="$HOME/.cache/omarchy/iso_${OMARCHY_MIRROR}/${OMARCHY_MEDIA_TARGET}/airootfs/var/cache/omarchy"
mkdir -p "$OFFLINE_REPO_BUILD_CACHE_DIR"
DOCKER_ARGS+=(-v "$OFFLINE_REPO_BUILD_CACHE_DIR:/var/cache/airootfs/var/cache/omarchy")
else
Expand All @@ -166,13 +202,14 @@ if ! docker version &>/dev/null; then
DOCKER=(sudo docker)
fi

"${DOCKER[@]}" run "${DOCKER_ARGS[@]}" archlinux/archlinux:latest /$BUILD_SCRIPT
"${DOCKER[@]}" run "${DOCKER_ARGS[@]}" "$BUILD_IMAGE" /$BUILD_SCRIPT

latest_iso=$(\ls -t "$BUILD_RELEASE_PATH"/*.iso | head -n1)
latest_iso=$(\ls -t "$BUILD_RELEASE_PATH/$OMARCHY_ISO_NAME"-????.??.??-"$ISO_ARCH".iso | head -n1)
[[ -n $latest_iso ]] || { echo "No $OMARCHY_MEDIA_TARGET ISO produced" >&2; exit 1; }
iso_ref="${latest_iso%.*}-$OMARCHY_ISO_REF.iso"
mv -f "$latest_iso" "$iso_ref"

if [[ -z "${NO_BOOT_OFFER:-}" ]]; then
if [[ $ISO_ARCH == x86_64 && -z "${NO_BOOT_OFFER:-}" ]]; then
echo
gum confirm "Boot $iso_ref?" && "$BUILD_ROOT/bin/omarchy-iso-boot" "$iso_ref"
fi
42 changes: 42 additions & 0 deletions builder/architecture.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/bin/bash
# shellcheck disable=SC2034

# Shared by the host launcher and the build container. Keep the existing
# Snapdragon image as the ARM default; generic media uses firmware tables.
OMARCHY_ARCH=${OMARCHY_ARCH:-$(uname -m)}
[[ $OMARCHY_ARCH == arm64 ]] && OMARCHY_ARCH=aarch64
case "$OMARCHY_ARCH" in
x86_64)
OMARCHY_MEDIA_TARGET=${OMARCHY_MEDIA_TARGET:-x86_64/pc}
ISO_NODE_ARCH=x64
ISO_KERNEL=linux-t2
BUILD_IMAGE=archlinux/archlinux:latest
DOCKER_PLATFORM=linux/amd64
;;
aarch64)
OMARCHY_MEDIA_TARGET=${OMARCHY_MEDIA_TARGET:-aarch64/snapdragon}
ISO_NODE_ARCH=arm64
ISO_KERNEL=linux-aarch64
BUILD_IMAGE=menci/archlinuxarm:base-devel
DOCKER_PLATFORM=linux/arm64
;;
*)
echo "Unsupported ISO architecture: $OMARCHY_ARCH" >&2
return 1
;;
esac

case "$OMARCHY_ARCH:$OMARCHY_MEDIA_TARGET" in
x86_64:x86_64/pc|aarch64:aarch64/snapdragon|aarch64:aarch64/generic) ;;
*)
echo "Unsupported architecture/media target: $OMARCHY_ARCH:$OMARCHY_MEDIA_TARGET" >&2
return 1
;;
esac

ISO_ARCH=$OMARCHY_ARCH
OMARCHY_ISO_NAME=omarchy
if [[ $OMARCHY_MEDIA_TARGET == aarch64/generic ]]; then
OMARCHY_ISO_NAME=omarchy-generic
fi
export OMARCHY_ARCH OMARCHY_MEDIA_TARGET OMARCHY_ISO_NAME
Loading