From 27b7e76c5ab51c19cd5a741475117efaec2faa05 Mon Sep 17 00:00:00 2001 From: Kros Dai Date: Thu, 8 Oct 2026 15:32:46 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(build):=20read=20region=20pr?= =?UTF-8?q?ofiles=20from=20settings=20package?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - :bug: default/ ships in omarchy-settings, so published builds silently bundled no regions - :white_check_mark: cover the published package path, a package without profiles, and an unreadable one Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QuATgjU4mnqpqSLAKMQ14g --- builder/build-iso.sh | 18 +++++++--- builder/prepare-regions.sh | 19 ++++++++-- test/unit/test_region.py | 74 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 101 insertions(+), 10 deletions(-) diff --git a/builder/build-iso.sh b/builder/build-iso.sh index 17a50f7..5007aaf 100755 --- a/builder/build-iso.sh +++ b/builder/build-iso.sh @@ -142,7 +142,7 @@ sed -i -E '/^(linux|broadcom-wl)$/d' "$build_cache_dir/packages.x86_64" # already in the mirror and we filter them out below. Without it, pacman -Syw # pulls the published omarchy* from the network mirror like any other package. if [[ -d /omarchy-source ]]; then - runtime_root=/omarchy-source + regions_source=/omarchy-source/default/regions base_pkg_lists=(/omarchy-source/install/omarchy-base.packages /omarchy-source/install/omarchy-other.packages) setup_form=/omarchy-source/install/provisioning/setup-form.sh else @@ -165,9 +165,17 @@ else # build here (set -e) with a bare "Not found in archive" instead of the # actionable error below. bsdtar -xf "$omarchy_pkg" -C /tmp/omarchy-pkglists usr/share/omarchy/install/provisioning/setup-form.sh 2>/dev/null || true - # Likewise optional: a runtime predating region profiles ships none. - bsdtar -xf "$omarchy_pkg" -C /tmp/omarchy-pkglists usr/share/omarchy/default/regions 2>/dev/null || true - runtime_root=/tmp/omarchy-pkglists/usr/share/omarchy + # Region profiles live under default/, which the settings package ships, + # not the runtime. Its own cache dir keeps the runtime lookup above from + # ever matching it. + settings_cache_dir="$bootstrap_cache_dir/settings" + mkdir -p "$settings_cache_dir" + pacman --config /configs/pacman-online-${OMARCHY_MIRROR}.conf --noconfirm -Syw "$OMARCHY_SETTINGS_PACKAGE" --cachedir "$settings_cache_dir" --dbpath /tmp/offlinedb-bootstrap >/dev/null + regions_source=$(find "$settings_cache_dir" -maxdepth 1 -type f -name "$OMARCHY_SETTINGS_PACKAGE-*.pkg.tar.zst" | sort | head -1) + if [[ -z $regions_source ]]; then + echo "ERROR: downloaded package for $OMARCHY_SETTINGS_PACKAGE not found in $settings_cache_dir" >&2 + exit 1 + fi setup_form=/tmp/omarchy-pkglists/usr/share/omarchy/install/provisioning/setup-form.sh fi @@ -198,7 +206,7 @@ cp "$setup_form" "$build_cache_dir/airootfs/usr/share/omarchy-iso/setup-form.sh" # repositories join a writable copy of the online config to download them. online_config=/tmp/omarchy-pacman-online.conf cp "/configs/pacman-online-${OMARCHY_MIRROR}.conf" "$online_config" -bash /builder/prepare-regions.sh "$runtime_root/default/regions" \ +bash /builder/prepare-regions.sh "$regions_source" \ "$build_cache_dir/airootfs/usr/share/omarchy-iso" "$online_config" # Collect every package we want available in the offline mirror. diff --git a/builder/prepare-regions.sh b/builder/prepare-regions.sh index e7743b0..c668048 100644 --- a/builder/prepare-regions.sh +++ b/builder/prepare-regions.sh @@ -6,12 +6,25 @@ # defaults stay in the runtime, not in a second set of ISO-specific templates. set -euo pipefail -regions="$1" +regions_source="$1" iso_payload="$2" online_config="$3" -# A runtime predating region profiles ships none; the ISO then installs -# global targets only, exactly as before. +# The source is a checkout's default/regions, or the published settings +# package that ships default/. One predating region profiles ships none; the +# ISO then installs global targets only, exactly as before. Anything else that +# fails here, such as an unreadable package, aborts the build. +regions="$regions_source" +if [[ -f $regions_source ]]; then + member=usr/share/omarchy/default/regions + listing=$(bsdtar -tf "$regions_source") + if grep -q "^$member/" <<<"$listing"; then + extracted=$(mktemp -d) + trap 'rm -rf "$extracted"' EXIT + bsdtar -xf "$regions_source" -C "$extracted" "$member" + regions="$extracted/$member" + fi +fi if [[ ! -d $regions ]]; then echo "Runtime ships no region profiles; every install will be global." exit 0 diff --git a/test/unit/test_region.py b/test/unit/test_region.py index 6a9bbcd..6119bc9 100644 --- a/test/unit/test_region.py +++ b/test/unit/test_region.py @@ -7,6 +7,7 @@ import sys import tempfile import types import unittest +from contextlib import ExitStack from pathlib import Path from unittest import mock @@ -26,7 +27,8 @@ def write_profiles(root: Path) -> Path: (cn / "timezones").write_text("# China\nAsia/Shanghai\n\n Asia/Urumqi \n") (cn / "packages").write_text("# keyring first\narchlinuxcn-keyring\nextra-tool\n") (cn / "pacman/pacman.conf.append").write_text("[archlinuxcn]\nServer = https://example.invalid/$arch\n") - (cn / "pacman/mirrorlist.append").write_text("Server = https://example.invalid/$repo/os/$arch\n") + for channel in ("stable", "rc", "edge"): + (cn / f"pacman/mirrorlist-{channel}.prepend").write_text(f"Server = https://{channel}.example.invalid/$repo/os/$arch\n") return regions @@ -62,7 +64,7 @@ class RegionSelectionTest(unittest.TestCase): self.assertEqual(region.resolve_region({"region": "cn"}, "UTC"), "cn") self.assertEqual(region.resolve_region({"region": "global"}, "Asia/Shanghai"), "global") self.assertEqual(region.resolve_region({}, "Asia/Shanghai"), "cn") - for bad in ("us", "CN", "..", "cn/../cn"): + for bad in ("us", "CN", "..", "cn/../cn", True, 1, ["cn"]): with self.subTest(region=bad), self.assertRaises(RuntimeError): region.resolve_region({"region": bad}, "UTC") @@ -104,6 +106,41 @@ class RegionSelectionTest(unittest.TestCase): self.assertEqual(marker.read_text(), "cn\n") self.assertEqual(marker.stat().st_mode & 0o777, 0o644) + def test_region_packages_install_while_the_offline_mirror_is_mounted(self): + for selected, expected_packages in [("global", []), ("cn", [["archlinuxcn-keyring", "extra-tool"]])]: + with self.subTest(region=selected), ExitStack() as stack: + events = [] + installer = mock.MagicMock() + installer.add_additional_packages.side_effect = lambda packages: events.append(packages) + config = types.SimpleNamespace( + kernels=["linux-omarchy"], locale_config=None, mirror_config=None, + swap=None, auth_config=None, app_config=None, timezone=None, + ntp=False, hostname="test", pacman_config="/etc/pacman.conf", + ) + ctx = types.SimpleNamespace( + state={"arch_config_handler": types.SimpleNamespace(config=config), "mirror_handler": None}, + target=self.root / "target", tailscale_authkey_path=None, omarchy_install={}, + region=selected, + ) + for name in ("_mount_offline_package_cache", "_mask_mkinitcpio_pacman_hooks", + "_unmask_mkinitcpio_pacman_hooks", "_configure_limine_boot", + "_write_pre_mounted_fstab", "_install_early_packages"): + stack.enter_context(mock.patch.object(phases_impl, name)) + stack.enter_context(mock.patch.object( + phases_impl, "_unmount_offline_package_cache", side_effect=lambda _: events.append("unmount"))) + stack.enter_context(mock.patch.object(phases_impl, "configure_keyboard", return_value=True)) + stack.enter_context(mock.patch.object(phases_impl, "_runtime_package_list", return_value=["omarchy"])) + for name, value in (("is_pre_mount", True), ("root_user", None)): + stack.enter_context(mock.patch.object(phases_impl.arch, name, return_value=value, create=True)) + stack.enter_context(mock.patch.object(phases_impl.arch, "sanity_check", create=True)) + opened = stack.enter_context(mock.patch.object(phases_impl.arch, "open_installer", create=True)) + opened.return_value.__enter__.return_value = installer + + phases_impl.arch_install_system(ctx) + + self.assertEqual(events, [["linux-omarchy-headers"], ["omarchy"], *expected_packages, "unmount"]) + self.assertEqual((ctx.target / "etc/omarchy/region").exists(), selected != "global") + def test_finalizer_trusts_only_the_selected_region(self): for selected, expected in [ ("global", []), @@ -153,6 +190,39 @@ class PrepareRegionsTest(unittest.TestCase): "pacman-key --populate archlinuxcn", ]) + def settings_package(self, root: Path, with_regions: bool) -> Path: + tree = root / "pkgroot" + (tree / "usr/share/omarchy/default/pacman").mkdir(parents=True) + if with_regions: + write_profiles(root / "src") + (root / "src/regions").rename(tree / "usr/share/omarchy/default/regions") + package = root / "omarchy-settings-1-1-any.pkg.tar.zst" + subprocess.run(["bsdtar", "--zstd", "-cf", str(package), "-C", str(tree), "usr"], check=True) + return package + + def test_published_build_reads_profiles_from_the_settings_package(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + payload, online, calls = self.run_prepare(self.settings_package(root, with_regions=True), root) + self.assertEqual((payload / "regions/cn/packages").read_text(), "# keyring first\narchlinuxcn-keyring\nextra-tool\n") + self.assertIn("[archlinuxcn]", online.read_text()) + self.assertIn("pacman-key --populate archlinuxcn", calls) + + def test_settings_package_without_profiles_builds_a_global_iso(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + payload, online, calls = self.run_prepare(self.settings_package(root, with_regions=False), root) + self.assertFalse((payload / "regions").exists()) + self.assertEqual(calls, "") + + def test_unreadable_settings_package_fails_the_build(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + package = root / "omarchy-settings-1-1-any.pkg.tar.zst" + package.write_text("not an archive") + with self.assertRaises(subprocess.CalledProcessError): + self.run_prepare(package, root) + def test_runtime_without_profiles_builds_a_global_iso(self): with tempfile.TemporaryDirectory() as tmp: root = Path(tmp)