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
6 changes: 6 additions & 0 deletions THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ code.
- **Hyprland** — BSD-3-Clause; the reviewed v0.56.1 source and rounded-border
coverage backport are pinned in `guest/spec.json`. The guest package retains
Hyprland's upstream license and dependency metadata.
- **aquamarine** — BSD-3-Clause; Arch Linux ARM currently publishes only
`libaquamarine.so=14`, while the pinned Hyprland still requires `.so=13`. The
factory build therefore vendors the reviewed `aquamarine 0.14.0-2` aarch64
package under `guest/pinned-packages/` (digest-pinned in `guest/spec.json`)
for empty-root resolve/pacstrap only. The finished guest holds aquamarine on
`IgnorePkg` with Hyprland so Omarchy updates cannot pull the ABI break.
- **Glaze** — MIT; the pinned v7.2.0 headers are used by the Hyprland build, and
their verified upstream license is retained in the rebuilt guest package.
- **ANGLE, VirGLRenderer, libepoxy, SDL, libslirp, GLib, Pixman, and other QEMU
Expand Down
5 changes: 4 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,10 @@ creates the account on first boot.
- The guest normally consumes upstream Arch Linux ARM packages. Hyprland is the
documented exception: an upstream package is reproducibly rebuilt with a
guarded rounded-border coverage patch for the VM graphics path, then held in
the guest's immutable local repository.
the guest's immutable local repository. While that pin still needs
`libaquamarine.so=13`, the factory also vendors `aquamarine 0.14.0-2` for
empty-root resolve (ALARM mirrors only publish `.so=14`) and holds aquamarine
on `IgnorePkg` in the finished guest so Omarchy updates cannot pull the break.
- The final Arch Linux ARM pacman files live under `/usr/share/try-omarchy/`.
An Omarchy-supported `pre-refresh-pacman` hook restores them after a channel
refresh writes its x86_64 templates to `/etc`; the upstream templates remain
Expand Down
52 changes: 28 additions & 24 deletions guest/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ fi
root=$(mktemp -d "$work/rootfs.XXXXXX")
resolution_db=$(mktemp -d "$work/pacman-db.XXXXXX")
pinned_repo=""
abi_pin_repo=""
chmod 0755 "$resolution_db"
cleanup() {
if (( keep_rootfs )); then
Expand All @@ -102,6 +103,9 @@ cleanup() {
if [[ -n $pinned_repo ]]; then
rm -rf "$pinned_repo"
fi
if [[ -n $abi_pin_repo ]]; then
rm -rf "$abi_pin_repo"
fi
}
trap cleanup EXIT

Expand Down Expand Up @@ -162,30 +166,30 @@ if ((${#pinned_records[@]})); then
"$pinned_repo/"*.pkg.tar.zst >/dev/null
fi

# Preserve the pinned repository configuration exactly, adding only builder
# options. The generated file is temporary build input; configure-rootfs later
# installs Omarchy's unmodified pacman configuration into the guest.
options_sections=0
pinned_repo_inserted=0
while IFS= read -r line || [[ -n $line ]]; do
if [[ -n $pinned_repo && $line =~ ^\[[^]]+\]$ && $line != "[options]" && $pinned_repo_inserted == 0 ]]; then
printf '[try-omarchy-pinned-cache]\n'
printf 'SigLevel = Required DatabaseOptional\n'
printf 'Server = file://%s\n\n' "$pinned_repo"
pinned_repo_inserted=1
fi
printf '%s\n' "$line"
if [[ $line == "[options]" ]]; then
printf 'CacheDir = %s\n' "$package_cache"
if [[ ${OMARCHY_PACMAN_DISABLE_SANDBOX:-0} == "1" ]]; then
printf 'DisableSandbox\n'
fi
options_sections=$((options_sections + 1))
fi
done <"$upstream_pacman_config" >"$pacman_config"
(( options_sections == 1 )) || fail "pinned pacman configuration must contain one [options] section"
(( ${#pinned_records[@]} == 0 || pinned_repo_inserted == 1 )) \
|| fail "pinned pacman configuration does not contain a repository section"
# Guest pacman.conf is installed unchanged by configure-rootfs. The builder copy
# may add reviewed ABI pins (packages mirrors no longer publish) and must drop
# those names from IgnorePkg so the empty-root transaction can install them once.
abi_pin_count=$(python3 -c 'import json,sys; print(len(json.load(open(sys.argv[1])).get("inputs", {}).get("abiPackagePins", [])))' "$spec")
builder_conf_args=(
python3 "$guest_dir/scripts/write-builder-pacman-conf.py"
--spec "$spec"
--guest-dir "$guest_dir"
--guest-config "$upstream_pacman_config"
--package-lock "$package_lock_file"
--output "$pacman_config"
--package-cache "$package_cache"
)
if [[ ${OMARCHY_PACMAN_DISABLE_SANDBOX:-0} == "1" ]]; then
builder_conf_args+=(--disable-sandbox)
fi
if (( abi_pin_count > 0 )); then
abi_pin_repo=$(mktemp -d "$work/abi-pin-repo.XXXXXX")
builder_conf_args+=(--abi-repo "$abi_pin_repo")
fi
if [[ -n $pinned_repo ]]; then
builder_conf_args+=(--pinned-cache-repo "$pinned_repo")
fi
"${builder_conf_args[@]}" || fail "could not derive the factory builder pacman configuration"

# Resolve against an empty target database and require the reviewed transitive
# version lock before any multi-gigabyte package transaction begins.
Expand Down
70 changes: 35 additions & 35 deletions guest/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"abseil-cpp": "20260817.0-2",
"acl": "2.4.0-1",
"adwaita-cursors": "50.0-1",
"adwaita-fonts": "50.0-1",
"adwaita-fonts": "51.0-2",
"adwaita-icon-theme": "50.0-1",
"adwaita-icon-theme-legacy": "46.2-3",
"alsa-card-profiles": "1:1.6.8-1",
Expand All @@ -14,7 +14,7 @@
"aom": "3.15.0-1",
"appstream": "1.2.0-1",
"aquamarine": "0.14.0-2",
"archlinux-keyring": "20260727-1",
"archlinux-keyring": "20260902-1",
"archlinuxarm-keyring": "20240419-2",
"at-spi2-core": "2.60.6-1",
"attr": "2.6.0-1",
Expand All @@ -36,11 +36,11 @@
"ca-certificates-utils": "20240618-1",
"cairo": "1.18.4-1",
"cfitsio": "1:4.7.0-1",
"chromium": "152.0.7977.64-1",
"chromium": "152.0.7977.75-1",
"coreutils": "9.11-2",
"cpptrace": "1.0.4-2",
"cryptsetup": "2.8.7-1",
"curl": "8.21.0-1",
"curl": "8.22.0-1",
"dav1d": "1.5.4-1",
"db5.3": "5.3.28-7",
"dbus": "1.16.2-1",
Expand All @@ -60,20 +60,20 @@
"elfutils": "0.196-1",
"exempi": "2.6.6-3",
"exiv2": "0.28.9-1",
"expat": "2.8.3-1",
"expat": "2.8.4-1",
"eza": "0.23.5-2",
"fakeroot": "1:1.37.2-3",
"fastfetch": "2.67.1-1",
"fastfetch": "2.68.1-1",
"fcft": "3.3.3-1",
"fd": "10.5.0-2",
"fd": "10.5.0-3",
"ffmpeg": "2:9.0.1-4",
"fftw": "3.3.11-1",
"file": "5.48-1",
"filesystem": "2025.10.12-1",
"findutils": "4.11.0-1",
"flac": "1.5.0-1",
"fontconfig": "2:2.18.3-2",
"foot": "1.27.0-1",
"foot": "1.28.0-1",
"freeglut": "3.8.0-1",
"freetype2": "2.14.3-1",
"fribidi": "1.0.16-2",
Expand Down Expand Up @@ -101,7 +101,7 @@
"glycin": "2.1.5-2",
"glycin-gtk4": "2.1.5-2",
"gmp": "6.3.0-3",
"gnome-autoar": "0.5.1-1",
"gnome-autoar": "0.5.2-1",
"gnome-desktop-4": "1:44.5-1",
"gnome-desktop-common": "1:44.5-1",
"gnome-keyring": "1:50.0-1",
Expand All @@ -110,8 +110,8 @@
"gnutls": "3.8.13-2",
"gobject-introspection-runtime": "1.86.0-2",
"gperftools": "2.18.1-1",
"gpgme": "2.1.2-1",
"gpgmepp": "2.1.0-1",
"gpgme": "2.2.0-1",
"gpgmepp": "2.2.0-1",
"gpm": "1.20.7.r38.ge82d1a6-6",
"graphene": "1.10.8-2.1",
"graphite": "1:1.3.15-1",
Expand Down Expand Up @@ -147,15 +147,15 @@
"hyprland": "0.56.1-3",
"hyprland-guiutils": "0.2.2-2",
"hyprlang": "0.6.8-5",
"hyprpicker": "0.4.7-3",
"hyprpicker": "0.4.7-4",
"hyprsunset": "0.4.0-3",
"hyprtoolkit": "0.5.4-4",
"hyprtoolkit": "0.5.4-5",
"hyprutils": "0.14.1-1",
"hyprwayland-scanner": "0.4.6-1",
"hyprwire": "0.3.1-3",
"iana-etc": "20260617-1",
"icu": "78.3-1",
"imagemagick": "7.1.2.30-1",
"imagemagick": "7.1.2.31-1",
"imath": "3.2.3-1",
"imv": "5.0.1-2",
"iniparser": "4.2.6-2",
Expand Down Expand Up @@ -211,14 +211,14 @@
"libcap-ng": "0.9.5-1",
"libcdio": "2.4.0-1",
"libcdio-paranoia": "10.2+2.0.2-3",
"libcgif": "0.5.3-1",
"libcgif": "0.5.4-1",
"libcloudproviders": "0.4.1-1",
"libcolord": "1.4.8-1",
"libcue": "2.3.0-1",
"libcups": "2:2.4.19-1",
"libdaemon": "0.14-6",
"libdatrie": "0.2.14-1",
"libde265": "1.1.1-1",
"libde265": "1.1.2-1",
"libdecor": "0.2.5-1",
"libdeflate": "1.26-1",
"libdisplay-info": "0.3.0-1",
Expand Down Expand Up @@ -251,7 +251,7 @@
"libgsf": "1.14.58-1",
"libgudev": "238-3",
"libgxps": "0.3.2-5",
"libheif": "1.23.2-1",
"libheif": "1.23.3-1",
"libice": "1.1.2-1",
"libidn2": "2.3.8-1",
"libiec61883": "1.2.0-9",
Expand Down Expand Up @@ -285,7 +285,7 @@
"libnetfilter_conntrack": "1.1.1-1",
"libnewt": "0.52.25-2",
"libnfnetlink": "1.0.2-2",
"libnftnl": "1.3.1-1",
"libnftnl": "1.3.2-1",
"libnghttp2": "1.70.0-1",
"libnghttp3": "1.18.0-1",
"libngtcp2": "1.25.0-1",
Expand Down Expand Up @@ -346,7 +346,7 @@
"libubsan": "16.1.1+r12+g301eb08fa2c5-1",
"libunibreak": "7.0-1",
"libunistring": "1.4.2-1",
"libunwind": "1.8.2-1",
"libunwind": "1.8.3-1",
"liburing": "2.15-1",
"libusb": "1.0.30-1",
"libusbmuxd": "2.1.1-2",
Expand All @@ -362,7 +362,7 @@
"libvterm": "0.3.3-2",
"libwacom": "2.19.1-1",
"libwebp": "1.6.0-2",
"libwireplumber": "0.5.16-1",
"libwireplumber": "0.5.17-1",
"libx11": "1.8.13-1",
"libxau": "1.0.12-1",
"libxcb": "1.17.0-1",
Expand All @@ -381,7 +381,7 @@
"libxkbcommon": "1.13.2-1",
"libxkbcommon-x11": "1.13.2-1",
"libxkbfile": "1.2.0-1",
"libxml2": "2.15.3-1",
"libxml2": "2.15.4-1",
"libxmlb": "0.3.29-1",
"libxmu": "1.3.1-1",
"libxpresent": "1.0.2-1",
Expand All @@ -398,8 +398,8 @@
"libzip": "1.11.4-1",
"licenses": "20240728-1",
"lilv": "0.28.0-1",
"linux-aarch64": "7.2.2-2",
"linux-aarch64-headers": "7.2.2-2",
"linux-aarch64": "7.2.3-1",
"linux-aarch64-headers": "7.2.3-1",
"linux-api-headers": "7.2-1",
"llhttp": "9.3.1-1",
"llvm-libs": "22.1.8-2",
Expand All @@ -409,14 +409,14 @@
"lua": "5.5.1-1",
"lua51-lpeg": "1.1.0-5",
"lua54": "5.4.9-1",
"luajit": "2.1.1787165859+1ee778a-1",
"luajit": "2.1.1788460057+24c20c9-1",
"lv2": "1.18.10-2",
"lz4": "1:1.10.0-2",
"lzo": "2.10-5",
"make": "4.4.1-3",
"md4c": "0.5.3-1",
"mdadm": "4.6-2",
"mesa": "1:26.2.1-1",
"mesa": "1:26.2.2-1",
"minizip": "1:1.3.2-3",
"mkinitcpio": "41.1-1",
"mkinitcpio-busybox": "1.36.1-1",
Expand All @@ -434,7 +434,7 @@
"neovim": "0.12.5-1",
"nettle": "4.0-1",
"networkmanager": "1.58.1-1",
"nftables": "1:1.1.6-3",
"nftables": "1:1.1.7-3",
"noto-fonts": "1:2026.09.01-1",
"noto-fonts-emoji": "1:2.051-1",
"npth": "1.8-1",
Expand Down Expand Up @@ -466,7 +466,7 @@
"patch": "2.8-1",
"pciutils": "3.15.0-1",
"pcre": "8.45-5",
"pcre2": "10.47-1",
"pcre2": "10.48-1",
"pcsclite": "2.5.1-1",
"perl": "5.42.2-2",
"perl-error": "0.17030-3",
Expand Down Expand Up @@ -557,22 +557,22 @@
"unibilium": "2.1.4-1",
"unzip": "6.0-23",
"upower": "1.91.3-1",
"util-linux": "2.42.2-1",
"util-linux-libs": "2.42.2-1",
"util-linux": "2.42.3-1",
"util-linux-libs": "2.42.3-1",
"uwsm": "0.26.7-1",
"v4l-utils": "1.32.0-2",
"v4l2loopback-dkms": "0.15.4-2",
"vapoursynth": "77-2",
"vid.stab": "1.1.1-3",
"vapoursynth": "79-1",
"vid.stab": "1.1.2-1",
"vmaf": "3.2.0-1",
"volume_key": "0.3.12-12",
"vulkan-icd-loader": "1.4.357.0-1",
"vulkan-mesa-implicit-layers": "1:26.2.1-1",
"vulkan-swrast": "1:26.2.1-1",
"vulkan-mesa-implicit-layers": "1:26.2.2-1",
"vulkan-swrast": "1:26.2.2-1",
"wayland": "1.26.0-1",
"wayland-protocols": "1.49-1",
"webrtc-audio-processing-1": "1.3-5",
"wireplumber": "0.5.16-1",
"wireplumber": "0.5.17-1",
"wl-clipboard": "1:2.3.0-1",
"woff2-font-awesome": "7.3.1-1",
"wpa_supplicant": "2:2.12-1",
Expand All @@ -588,7 +588,7 @@
"xcb-util-wm": "0.4.2-2",
"xdg-desktop-portal": "1.22.1-2",
"xdg-desktop-portal-gtk": "1.15.3-1",
"xdg-desktop-portal-hyprland": "1.4.1-1",
"xdg-desktop-portal-hyprland": "1.4.1-2",
"xdg-user-dirs": "0.20-1",
"xdg-user-dirs-gtk": "0.16-1",
"xdg-utils": "1.2.1-2",
Expand Down
6 changes: 4 additions & 2 deletions guest/pacman.aarch64.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ HoldPkg = pacman glibc
# QEMU direct-boots the kernel and initramfs paired with this VM disk. Keep its
# installed modules and DKMS headers on that exact ABI. Hyprland is likewise
# held until upstream includes our rounded-border coverage backport; otherwise
# a routine full-system update can silently restore the defect.
IgnorePkg = linux-aarch64 linux-aarch64-headers hyprland
# a routine full-system update can silently restore the defect. Keep aquamarine
# on the same hold: newer ALARM aquamarine bumps libaquamarine.so past what the
# pinned Hyprland was built against and aborts Omarchy's updater mid-transaction.
IgnorePkg = linux-aarch64 linux-aarch64-headers hyprland aquamarine
Architecture = auto
CheckSpace
ParallelDownloads = 5
Expand Down
9 changes: 9 additions & 0 deletions guest/pinned-packages/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Reviewed ABI package pins

Archives here are factory-build inputs for packages Arch Linux ARM no longer
publishes at the SONAME the locked Hyprland stack still requires.

They are digested in `guest/spec.json` (`inputs.abiPackagePins`), served only
through the disposable `[try-omarchy-abi-pins]` builder repository, and must not
appear in the finished guest's pacman configuration. The guest holds matching
runtime packages on `IgnorePkg` instead.
Binary file not shown.
20 changes: 18 additions & 2 deletions guest/scripts/refresh-package-lock.sh
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,26 @@ chmod 0755 "$temporary"
mkdir -p "$temporary/db"
chmod 0755 "$temporary/db"
config="$temporary/pacman.conf"
cp "$upstream_pacman_config" "$config"
package_lock_input=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["inputs"]["packageLock"])' "$spec")
package_lock_file="$guest_dir/$package_lock_input"
[[ -f $package_lock_file ]] || fail "package lock not found: $package_lock_file"

builder_conf_args=(
python3 "$script_dir/write-builder-pacman-conf.py"
--spec "$spec"
--guest-dir "$guest_dir"
--guest-config "$upstream_pacman_config"
--package-lock "$package_lock_file"
--output "$config"
)
if [[ ${OMARCHY_PACMAN_DISABLE_SANDBOX:-0} == "1" ]]; then
sed -i '/^\[options\]$/a DisableSandbox' "$config"
builder_conf_args+=(--disable-sandbox)
fi
abi_pin_count=$(python3 -c 'import json,sys; print(len(json.load(open(sys.argv[1])).get("inputs", {}).get("abiPackagePins", [])))' "$spec")
if (( abi_pin_count > 0 )); then
builder_conf_args+=(--abi-repo "$temporary/abi-pin-repo")
fi
"${builder_conf_args[@]}" || fail "could not derive the factory builder pacman configuration"

pacman -Syy --noconfirm --config "$config" \
--dbpath "$temporary/db" --logfile "$temporary/pacman.log"
Expand Down
Loading
Loading