Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1383cab
Build a generic aarch64 ISO on Arch Linux ARM's kernel
emirb Oct 8, 2026
6e19b54
Build the generic aarch64 ISO on a Namespace ARM runner
emirb Oct 8, 2026
220b615
Run the Python unit tests from their own directory on the ARM runner
emirb Oct 8, 2026
8da1354
Run the unit tests on an x86 runner, where the installer tests pass
emirb Oct 8, 2026
613e535
Run the unit tests as quattro does, in an Arch container
emirb Oct 8, 2026
fcc33c5
Give the unit test container cmp
emirb Oct 8, 2026
d716a69
Keep the offline mirror's packages between ARM builds
emirb Oct 8, 2026
95ce390
Boot the generic aarch64 ISO under KVM
emirb Oct 8, 2026
592bb20
Compress the generic image's live root with lz4
emirb Oct 8, 2026
201cf27
Build the generic image's Omarchy packages from the n1x branches
emirb Oct 8, 2026
045a0c8
Install the generic aarch64 ISO unattended and boot what it installed
emirb Oct 8, 2026
0eeaa23
Recognise an installed kernel that records no pkgbase
emirb Oct 8, 2026
2dccdb8
Leave out of the initramfs the modules the installed kernel lacks
emirb Oct 8, 2026
c764864
Install test: see why an install stopped, and check the desktop
emirb Oct 8, 2026
f0c97b8
Build the generic image from omarchy's arm64-generic branch
emirb Oct 8, 2026
b0edb4c
Give the unit test workflow the tools the aarch64 tests call
emirb Oct 9, 2026
29d8c51
Accept a Linux boot entry when Limine disables UKIs (#211)
jdvmi00 Oct 10, 2026
5af498d
Pin the boot validation fixtures to x86_64
birkskyum Oct 10, 2026
044a265
Let the launcher run with the bash and grep that macOS ships
birkskyum Oct 10, 2026
72773f6
Build one aarch64 image for every board
birkskyum Oct 11, 2026
ff4c45d
Save a Snapdragon laptop's firmware from Windows before the disk is w…
birkskyum Oct 10, 2026
2e7e0eb
Start the DSP on the live Yoga Slim 7x once the root is in RAM
birkskyum Oct 10, 2026
409dd6f
Describe the one aarch64 image in the README
birkskyum Oct 11, 2026
941bd97
Boot a Snapdragon laptop's live system on a kernel that builds its pl…
birkskyum Oct 11, 2026
1d18dd3
Build the aarch64 image on linux-omarchy, by default
emirb Oct 13, 2026
62d151d
Build the aarch64 image on linux-omarchy only
emirb Oct 13, 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
243 changes: 243 additions & 0 deletions .github/workflows/arm64-iso.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
name: ARM64 ISO

# Builds the aarch64 ISO on a native ARM runner, on Arch Linux ARM's kernel and
# the published edge packages: the one image for every board that kernel runs.
# A VM can boot it, so CI can go on to install and test it.
#
# The push trigger exists while this lives on a branch: a manual run needs the
# workflow on the default branch first.
on:
push:
branches: [arm64-generic]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: arm64-iso-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Build the generic ISO
runs-on: namespace-profile-linux-arm64-16x32
timeout-minutes: 120
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
submodules: recursive
persist-credentials: false

- name: Runner
run: |
uname -srm
echo "$(nproc) CPUs, $(free -h | awk 'NR==2 {print $2}') memory, $(df -h --output=avail . | tail -1 | tr -d ' ') free disk"
echo "LSMs: $(cat /sys/kernel/security/lsm 2>/dev/null || echo unreadable)"
echo "KVM: $([[ -e /dev/kvm ]] && echo present || echo absent)"
docker version --format 'docker {{.Server.Version}}' || sudo docker version --format 'docker {{.Server.Version}}'

# The packages the offline mirror is built from: 3.3 GB that otherwise
# come down again on every run. The build prunes what it no longer needs.
# Skipped, not failed, on a runner profile without a cache volume.
- name: Package cache (Namespace)
continue-on-error: true
uses: namespacelabs/nscloud-cache-action@c5f8dab7560444c4bf8dbc64f1b203431873c547 # v1.6.1
with:
path: ~/.cache/omarchy

# The Omarchy packages are built from source, as the N1x image builds
# them: the published ones carry no aarch64 pacman configuration and none
# of the platform support the build asks for, and until omarchy#14637
# reaches edge their installer looks for an x64 Node tarball on every
# architecture. omarchy's n1x branch has all of it.
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
repository: omacom/omarchy
ref: n1x
path: src/omarchy
fetch-depth: 0
persist-credentials: false
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
repository: omacom/omarchy-pkgs
ref: n1x
path: src/omarchy-pkgs
persist-credentials: false

- name: Build the ISO
run: |
start=$(date +%s)
./bin/omarchy-iso-make --arch aarch64 --no-boot-offer \
--local-source src/omarchy src/omarchy-pkgs
echo "Built in $(( $(date +%s) - start )) s" | tee -a "$GITHUB_STEP_SUMMARY"

- name: Inspect the ISO
run: |
set -euo pipefail
command -v bsdtar >/dev/null || { sudo apt-get update -qq && sudo apt-get install -y -qq libarchive-tools >/dev/null; }
iso=$(ls release/*.iso)
(cd release && sha256sum "$(basename "$iso")" | tee "$(basename "$iso").sha256")
bsdtar -tvf "$iso" >listing.txt
# What the firmware starts, what GRUB loads and what the installer installs from.
for want in EFI/BOOT/BOOTAA64.EFI arch/boot/aarch64/vmlinuz-linux-aarch64 \
arch/boot/aarch64/initramfs-linux-aarch64.img arch/aarch64/airootfs.sfs; do
grep -Eiq " $want\$" listing.txt || { echo "::error::$iso has no $want"; exit 1; }
done
{
echo "\`$(basename "$iso")\`, $(( $(stat -c %s "$iso") / 1048576 )) MiB"
echo
echo '```'
grep -E ' (arch/boot/aarch64|EFI/BOOT|arch/aarch64)/[^/]+$' listing.txt | awk '{print $5, $NF}'
echo '```'
} | tee -a "$GITHUB_STEP_SUMMARY"

# For the boot job: Namespace's own artifact store is in the same place
# as its runners. GitHub's took 3 min 20 s to hand the ISO back.
- name: Keep the ISO in Namespace's artifact store
id: nsc
continue-on-error: true
run: |
command -v nsc >/dev/null || { curl -fsSL https://get.namespace.so/cloud/install.sh | sh; export PATH="$HOME/.local/bin:$PATH"; }
cd release
iso=$(ls ./*.iso); iso=${iso#./}
printf '%s\n' "$iso" >NAME
for f in NAME "$iso" "$iso.sha256"; do
nsc artifact upload "$f" "arm64-iso/${{ github.run_id }}/$f" --expires_in 48h
done
rm NAME

- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: omarchy-arm64-generic-${{ github.run_number }}
path: |
release/*.iso
release/*.sha256
compression-level: 0
retention-days: 3
if-no-files-found: error

# The image through its own boot path: firmware, GRUB, the live kernel and
# initramfs, the live root. Apple-backed runners are the ARM ones with KVM.
boot:
name: Boot the live ISO under KVM
needs: build
runs-on: namespace-profile-arm64-linux-apple-12x32
timeout-minutes: 40
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
persist-credentials: false

- name: Runner
run: |
uname -srm
echo "$(nproc) CPUs, $(free -h | awk 'NR==2 {print $2}') memory, $(df -h --output=avail . | tail -1 | tr -d ' ') free disk"
grep -m2 -E 'CPU implementer|CPU part' /proc/cpuinfo || true
ls -l /dev/kvm
[[ -r /dev/kvm && -w /dev/kvm ]] || sudo chmod 666 /dev/kvm

- name: QEMU and firmware
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq qemu-system-arm qemu-efi-aarch64 >/dev/null
qemu-system-aarch64 --version | head -1

- name: ISO from Namespace's artifact store
id: fast
continue-on-error: true
run: |
command -v nsc >/dev/null || { curl -fsSL https://get.namespace.so/cloud/install.sh | sh; export PATH="$HOME/.local/bin:$PATH"; }
mkdir -p iso && cd iso
nsc artifact download "arm64-iso/${{ github.run_id }}/NAME" NAME
iso=$(cat NAME)
time nsc artifact download "arm64-iso/${{ github.run_id }}/$iso" "$iso"
nsc artifact download "arm64-iso/${{ github.run_id }}/$iso.sha256" "$iso.sha256"
rm NAME

- name: ISO from GitHub's artifact store (fallback, slow)
if: steps.fast.outcome != 'success'
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: omarchy-arm64-generic-${{ github.run_number }}
path: iso

- name: Boot it
run: |
(cd iso && sha256sum -c ./*.sha256)
./test/arm64-live-boot iso/*.iso evidence
echo "The live system offered a login after $(cut -d= -f2 evidence/timing) s." >>"$GITHUB_STEP_SUMMARY"

- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
if: always()
with:
name: arm64-live-boot-${{ github.run_number }}
path: evidence
retention-days: 7
if-no-files-found: warn

# The whole path with nobody at the keyboard: the installer answers from a
# cidata drive, writes the disk, and the machine restarts into what it wrote.
install:
name: Install it unattended and boot the result
needs: build
runs-on: namespace-profile-arm64-linux-apple-12x32
timeout-minutes: 60
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
persist-credentials: false

- name: Runner
run: |
uname -srm
echo "$(nproc) CPUs, $(free -h | awk 'NR==2 {print $2}') memory, $(df -h --output=avail . | tail -1 | tr -d ' ') free disk"
grep -m2 -E 'CPU implementer|CPU part' /proc/cpuinfo || true
ls -l /dev/kvm
[[ -r /dev/kvm && -w /dev/kvm ]] || sudo chmod 666 /dev/kvm

- name: QEMU and firmware
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq qemu-system-arm qemu-efi-aarch64 qemu-utils \
mtools dosfstools jq openssl openssh-client tesseract-ocr >/dev/null
qemu-system-aarch64 --version | head -1

- name: ISO from Namespace's artifact store
id: fast
continue-on-error: true
run: |
command -v nsc >/dev/null || { curl -fsSL https://get.namespace.so/cloud/install.sh | sh; export PATH="$HOME/.local/bin:$PATH"; }
mkdir -p iso && cd iso
nsc artifact download "arm64-iso/${{ github.run_id }}/NAME" NAME
iso=$(cat NAME)
time nsc artifact download "arm64-iso/${{ github.run_id }}/$iso" "$iso"
nsc artifact download "arm64-iso/${{ github.run_id }}/$iso.sha256" "$iso.sha256"
rm NAME

- name: ISO from GitHub's artifact store (fallback, slow)
if: steps.fast.outcome != 'success'
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: omarchy-arm64-generic-${{ github.run_number }}
path: iso

- name: Install, reboot, check
run: |
(cd iso && sha256sum -c ./*.sha256)
./test/arm64-install iso/*.iso evidence
{
echo "Installed, rebooted and checked in $(cut -d= -f2 evidence/timing) s."
echo
echo '```'
cat evidence/checks
echo '```'
} >>"$GITHUB_STEP_SUMMARY"

- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
if: always()
with:
name: arm64-install-${{ github.run_number }}
path: evidence
retention-days: 7
if-no-files-found: warn
2 changes: 1 addition & 1 deletion .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
# mirror that drops one file must not fail the tests.
sed -i "1i Server = https://mirror.omarchy.org/\$repo/os/\$arch" /etc/pacman.d/mirrorlist
for attempt in 1 2 3; do
pacman -Syu --noconfirm --needed python parted diffutils jq zip unzip >/dev/null && break
pacman -Syu --noconfirm --needed python parted diffutils jq zip unzip openssh patch git >/dev/null && break
[ "$attempt" -lt 3 ] || { echo "::error::the test container could not install its tools"; exit 1; }
sleep 15
done
Expand Down
18 changes: 9 additions & 9 deletions N1X.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ There are two images:

| Image | File name | Use it for |
| --- | --- | --- |
| Normal | `omarchy-<date>-aarch64-n1x-local.iso` | Everyday installs. No remote access, like any Omarchy ISO; the installed system leaves sshd off. |
| Dev | `omarchy-<date>-aarch64-n1x-devssh-local.iso` | Debugging. Adds key-only SSH for the public key it was built with, on the live USB and the installed system, and verbose installer output. |
| Normal | `omarchy-<date>-aarch64-local.iso` | Everyday installs. No remote access, like any Omarchy ISO; the installed system leaves sshd off. |
| Dev | `omarchy-<date>-aarch64-devssh-local.iso` | Debugging. Adds key-only SSH for the public key it was built with, on the live USB and the installed system, and verbose installer output. |

Both install the same Omarchy. Only the dev image opens remote access, so do not hand a dev image, or a machine installed from one, to anyone else.

## What is different from stock Omarchy

- **Kernel:** `linux-omarchy-n1x`, the `linux-omarchy` kernel with NVIDIA's N1x platform support and Omarchy's N1x patches (see `pkgbuilds/linux-omarchy-n1x` in omarchy-pkgs).
- **Kernel:** `linux-omarchy`, the `linux-omarchy` kernel with NVIDIA's N1x platform support and Omarchy's N1x patches (see `pkgbuilds/linux-omarchy` in omarchy-pkgs).
- **GPU:** the NVIDIA open driver from Omarchy's aarch64 repository, loaded early so the disk passphrase prompt and the desktop are drawn by the GPU. On the Dell's pre-release firmware (0.x) the driver stays unloaded and the desktop renders in software.
- **Console:** every boot entry carries `console=tty0 acpi=nospcr`. Without it the firmware's serial console takes over and the screen stays black, the disk passphrase prompt included.
- **Boot splash:** Plymouth, as on every Omarchy machine, with the panel lit for the passphrase prompt.
Expand All @@ -31,7 +31,7 @@ Both install the same Omarchy. Only the dev image opens remote access, so do not
1. Write the image to a USB stick of 8 GB or more, replacing `/dev/sdX` with the stick (check with `lsblk`; everything on it is erased):

```bash
sudo dd if=omarchy-<date>-aarch64-n1x-local.iso of=/dev/sdX bs=4M status=progress oflag=sync
sudo dd if=omarchy-<date>-aarch64-local.iso of=/dev/sdX bs=4M status=progress oflag=sync
```

2. Plug the stick into the laptop, power it on, open the firmware's boot menu and pick the stick. It boots straight into the installer; the internal keyboard works there.
Expand All @@ -49,8 +49,8 @@ The **USB stick** boots the installer without showing a menu. Press Esc while it

The **installed system** (Limine) offers:

- **linux-omarchy-n1x**, the normal desktop. It boots by default.
- **linux-omarchy-n1x-fallback**, the rescue entry. It boots the same system to a text login with the NVIDIA driver blocked and Plymouth off; use it if the desktop comes up black. It is rebuilt with every kernel update.
- **linux-omarchy**, the normal desktop. It boots by default.
- **linux-omarchy-fallback**, the rescue entry. It boots the same system to a text login with the NVIDIA driver blocked and Plymouth off; use it if the desktop comes up black. It is rebuilt with every kernel update.
- **Snapshots**, once Omarchy has taken some.

`sudo omarchy-n1x-probe` records hardware and boot details to `/var/log/omarchy-n1x-probe.log` on demand.
Expand Down Expand Up @@ -87,16 +87,16 @@ sudo ufw delete allow ssh

Builds run natively on an aarch64 host with Docker, from sibling checkouts of omarchy, omarchy-pkgs and this repository. They need a platform package directory with the packages no repository publishes yet, built from omarchy-pkgs, and a `SHA256SUMS` covering them:

- `linux-omarchy-n1x` and `linux-omarchy-n1x-headers`
- `linux-omarchy` and `linux-omarchy-headers`
- `asus-proart-p14-speaker-firmware`

```bash
# Normal image
bin/omarchy-iso-make --arch aarch64 --platform n1x \
bin/omarchy-iso-make --arch aarch64 \
--package-dir <platform-packages> --local-source ../omarchy-installer ../omarchy-pkgs

# Dev image: adds key-only SSH for your public key and verbose installer output
bin/omarchy-iso-make --arch aarch64 --platform n1x --dev-ssh ~/.ssh/id_ed25519.pub --debug \
bin/omarchy-iso-make --arch aarch64 --dev-ssh ~/.ssh/id_ed25519.pub --debug \
--package-dir <platform-packages> --local-source ../omarchy-installer ../omarchy-pkgs
```

Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,20 @@ 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.

### aarch64

```bash
./bin/omarchy-iso-make --arch aarch64 --edge --package-dir <platform-packages> --local-source ../omarchy-installer ../omarchy-pkgs
```

One aarch64 image serves every board that installs from a USB stick. Apple Silicon Macs are not among them, since they are installed from macOS. The build chooses nothing. The installer asks the machine which platform it is on, with the runtime's own `omarchy-hw-platform`, and installs that platform's packages from the offline mirror, which carries them for every platform.

The kernel is `linux-omarchy`, as on x86_64, and it is the only one an aarch64 image is built on. No repository publishes it for aarch64 yet, so the image is built with the package bundle that carries it (`--package-dir`), as [N1X.md](N1X.md) describes.

Snapdragon laptops get no device tree from their firmware, so the live system also carries a UKI with one for every supported laptop, built from the device trees the kernel package ships. GRUB boots it by default where the processor is a Snapdragon, and systemd-stub picks the device tree by the machine's SMBIOS data. Every other machine boots the kernel directly.

The runtime the image installs must have the platform detector and the Snapdragon hardware setup (`install/hardware/qualcomm/`), which lists those device trees for the installed system's own UKI. The build refuses a runtime without them. On a laptop that still has Windows, the installer copies the vendor-signed firmware off the Windows partition before the disk is written.

## Testing the ISO

Run `./bin/omarchy-iso-boot [release/omarchy.iso]`.
Expand Down
Loading