Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 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
55b6135
Build a Snapdragon image as --platform qualcomm
birkskyum Oct 10, 2026
b110755
Save a Snapdragon laptop's firmware from Windows before the disk is w…
birkskyum Oct 10, 2026
6badae8
Start the DSP on the live Yoga Slim 7x once the root is in RAM
birkskyum Oct 10, 2026
d9c3cfd
Pin the boot validation fixtures to x86_64
birkskyum Oct 10, 2026
57a4ac3
Let the launcher run with the bash and grep that macOS ships
birkskyum Oct 10, 2026
fdd0dff
Describe the Snapdragon image in the README
birkskyum Oct 10, 2026
a3883fe
Build a GB10 image as --platform gb10
jdvmi00 Oct 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
244 changes: 244 additions & 0 deletions .github/workflows/arm64-iso.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
name: ARM64 ISO

# Builds the generic aarch64 ISO on a native ARM runner: Arch Linux ARM's
# kernel, the published edge packages, nothing board-specific. This is the
# image a VM can boot, so it is the one CI can go on to install and test.
#
# 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
# until omarchy#14637 reaches edge their installer looks for an x64 Node
# tarball on every architecture. omarchy's arm64-generic branch is n1x
# plus one line, the installed system's mirrorlist pointed at Omarchy's
# own mirror; it goes back to n1x when that line lands there.
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
repository: omacom/omarchy
ref: arm64-generic
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 --platform generic --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: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,24 @@ 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.

### Snapdragon laptops

```bash
./bin/omarchy-iso-make --arch aarch64 --platform qualcomm --edge --local-source ../omarchy-installer ../omarchy-pkgs
```

The Snapdragon image is the generic aarch64 image with one addition. These laptops get no device tree from their firmware, so the live system boots a UKI that carries one for every supported laptop, and systemd-stub picks by the machine's SMBIOS data. The N1x image is described in [N1X.md](N1X.md).

The runtime the image installs must have 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 it. On a laptop that still has Windows, the installer copies the vendor-signed firmware off the Windows partition before the disk is written.

### NVIDIA GB10 desktops

```bash
./bin/omarchy-iso-make --arch aarch64 --platform gb10 --edge --local-source ../omarchy-installer ../omarchy-pkgs
```

The GB10 image (DGX Spark, ASUS Ascent GX10) is the generic aarch64 image with the runtime's GB10 packages (`install/omarchy-aarch64-gb10.packages`) installed offline: the NVIDIA driver, the container toolkit, `rdma-core` and NVIDIA's profilers. The build refuses a runtime without that list.

## Testing the ISO

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