🐛 fix(build): read region profiles from settings package
- 🐛 default/ ships in omarchy-settings, so published builds silently bundled no regions - ✅ cover the published package path, a package without profiles, and an unreadable one Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuATgjU4mnqpqSLAKMQ14g
This commit is contained in:
1 parent
9dcfdb035a
commit
27b7e76c5a
3 files changed
+101
-10
No files matched your search
+13
-5
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user