✨ feat(install): pick the region from the timezone
- ✨ the timezone the user already chose selects the region, so no new prompt is needed - 🔒 populate only the selected region's keys so global installs never trust them - ✅ cover the mapping, autoinstall override, marker, keyring setup, and builder staging 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
1c912658aa
commit
e121561851
6 files changed
+275
-1
No files matched your search
@@ -0,0 +1,166 @@
|
||||
"""The target's region comes from its timezone, using the runtime's profiles."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import types
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT / "configs/airootfs/usr/share/omarchy-iso"))
|
||||
sys.modules.setdefault(
|
||||
"orchestrator.archinstall_adapter",
|
||||
types.ModuleType("orchestrator.archinstall_adapter"),
|
||||
)
|
||||
from orchestrator import context, phases_impl, region # noqa: E402
|
||||
|
||||
|
||||
def write_profiles(root: Path) -> Path:
|
||||
regions = root / "regions"
|
||||
cn = regions / "cn"
|
||||
(cn / "pacman").mkdir(parents=True)
|
||||
(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")
|
||||
return regions
|
||||
|
||||
|
||||
class RegionSelectionTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
tmp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(tmp.cleanup)
|
||||
self.root = Path(tmp.name)
|
||||
self.regions = write_profiles(self.root)
|
||||
patcher = mock.patch.object(region, "REGIONS_DIR", self.regions)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_timezone_selects_region(self):
|
||||
for timezone, expected in [
|
||||
("Asia/Shanghai", "cn"),
|
||||
("Asia/Urumqi", "cn"),
|
||||
("Asia/Hong_Kong", "global"),
|
||||
("America/New_York", "global"),
|
||||
("UTC", "global"),
|
||||
("# China", "global"),
|
||||
("", "global"),
|
||||
(None, "global"),
|
||||
]:
|
||||
with self.subTest(timezone=timezone):
|
||||
self.assertEqual(region.region_for_timezone(timezone), expected)
|
||||
|
||||
def test_iso_without_profiles_is_global(self):
|
||||
self.assertEqual(region.region_for_timezone("Asia/Shanghai", self.root / "missing"), "global")
|
||||
self.assertEqual(region.resolve_region({}, "Asia/Shanghai", self.root / "missing"), "global")
|
||||
|
||||
def test_autoinstall_region_overrides_timezone(self):
|
||||
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"):
|
||||
with self.subTest(region=bad), self.assertRaises(RuntimeError):
|
||||
region.resolve_region({"region": bad}, "UTC")
|
||||
|
||||
def test_profile_packages_and_keyrings(self):
|
||||
self.assertEqual(region.region_packages("cn"), ["archlinuxcn-keyring", "extra-tool"])
|
||||
self.assertEqual(region.region_keyrings("cn"), ["archlinuxcn"])
|
||||
self.assertEqual(region.region_packages("global"), [])
|
||||
self.assertEqual(region.region_keyrings("global"), [])
|
||||
|
||||
def from_env(self, config: dict) -> context.InstallContext:
|
||||
config_path = self.root / "config.json"
|
||||
creds_path = self.root / "creds.json"
|
||||
config_path.write_text(json.dumps(config))
|
||||
creds_path.write_text(json.dumps({"users": [{"username": "test"}]}))
|
||||
with mock.patch.dict(os.environ, {
|
||||
"OMARCHY_INSTALL_CONFIG": str(config_path),
|
||||
"OMARCHY_INSTALL_CREDS": str(creds_path),
|
||||
"OMARCHY_INSTALL_STATE_DIR": str(self.root / "state"),
|
||||
}, clear=True), mock.patch.object(context, "_default_kernel", return_value="linux-omarchy"):
|
||||
return context.InstallContext.from_env()
|
||||
|
||||
def test_context_resolves_region_before_install(self):
|
||||
self.assertEqual(self.from_env({"timezone": "Asia/Shanghai"}).region, "cn")
|
||||
self.assertEqual(self.from_env({"timezone": "Europe/Berlin"}).region, "global")
|
||||
# Deferred installs carry UTC until first boot, so they are global
|
||||
# unless the autoinstall config names a region.
|
||||
self.assertEqual(self.from_env({"timezone": "UTC"}).region, "global")
|
||||
self.assertEqual(self.from_env({"timezone": "UTC", "omarchy_install": {"region": "cn"}}).region, "cn")
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.from_env({"omarchy_install": {"region": "zz"}})
|
||||
|
||||
def test_marker_is_written_only_for_regional_targets(self):
|
||||
target = self.root / "target"
|
||||
phases_impl._write_region_marker(types.SimpleNamespace(target=target, region="global"))
|
||||
self.assertFalse((target / "etc/omarchy/region").exists())
|
||||
|
||||
phases_impl._write_region_marker(types.SimpleNamespace(target=target, region="cn"))
|
||||
marker = target / "etc/omarchy/region"
|
||||
self.assertEqual(marker.read_text(), "cn\n")
|
||||
self.assertEqual(marker.stat().st_mode & 0o777, 0o644)
|
||||
|
||||
def test_finalizer_trusts_only_the_selected_region(self):
|
||||
for selected, expected in [
|
||||
("global", []),
|
||||
("cn", [["pacman-key", "--init"], ["pacman-key", "--populate", "archlinux", "archlinuxcn"]]),
|
||||
]:
|
||||
with self.subTest(region=selected), \
|
||||
mock.patch.object(phases_impl, "_run_target_setup_command") as run, \
|
||||
mock.patch.object(phases_impl, "_mask_mkinitcpio_pacman_hooks"), \
|
||||
mock.patch.object(phases_impl, "_unmask_mkinitcpio_pacman_hooks"):
|
||||
ctx = types.SimpleNamespace(region=selected, defer_provisioning=True, target=Path("/unused"))
|
||||
phases_impl.run_system_finalizer(ctx)
|
||||
commands = [call.args[1] for call in run.call_args_list]
|
||||
self.assertEqual(commands[:-1], expected)
|
||||
self.assertEqual(commands[-1][0], "/usr/bin/omarchy-apply-system")
|
||||
|
||||
|
||||
class PrepareRegionsTest(unittest.TestCase):
|
||||
def run_prepare(self, regions: Path, root: Path) -> tuple[Path, Path, str]:
|
||||
payload = root / "payload"
|
||||
payload.mkdir()
|
||||
online = root / "online.conf"
|
||||
online.write_text("[core]\nInclude = /etc/pacman.d/mirrorlist\n")
|
||||
stubs = root / "bin"
|
||||
stubs.mkdir()
|
||||
log = root / "calls.log"
|
||||
for name in ("pacman", "pacman-key"):
|
||||
stub = stubs / name
|
||||
stub.write_text(f'#!/bin/bash\necho "{name} $*" >> "{log}"\n')
|
||||
stub.chmod(0o755)
|
||||
subprocess.run(
|
||||
["bash", str(ROOT / "builder/prepare-regions.sh"), str(regions), str(payload), str(online)],
|
||||
env={**os.environ, "PATH": f"{stubs}:{os.environ['PATH']}"},
|
||||
check=True, capture_output=True, text=True,
|
||||
)
|
||||
return payload, online, log.read_text() if log.exists() else ""
|
||||
|
||||
def test_ships_every_profile_and_bootstraps_its_keyring(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
regions = write_profiles(root)
|
||||
payload, online, calls = self.run_prepare(regions, root)
|
||||
self.assertEqual((payload / "regions/cn/timezones").read_text(), (regions / "cn/timezones").read_text())
|
||||
self.assertIn("[archlinuxcn]", online.read_text())
|
||||
self.assertTrue(online.read_text().startswith("[core]\n"))
|
||||
self.assertEqual(calls.splitlines(), [
|
||||
f"pacman --config {online} --noconfirm -Sy --needed archlinuxcn-keyring",
|
||||
"pacman-key --populate archlinuxcn",
|
||||
])
|
||||
|
||||
def test_runtime_without_profiles_builds_a_global_iso(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
payload, online, calls = self.run_prepare(root / "missing", root)
|
||||
self.assertFalse((payload / "regions").exists())
|
||||
self.assertNotIn("archlinuxcn", online.read_text())
|
||||
self.assertEqual(calls, "")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user