Merge omacom/omarchy-iso#241: region from the timezone in one ISO

Drop our --region build, prepare-region.sh, region skel, the preselected
China timezone, the explicit region in the configurator's JSON, Rime
bundling and its pin.
This commit is contained in:
ZacharyZhang-NY committed 2026-10-09 00:03:17 -04:00
commit 75d00a2036
14 files changed
+409 -618

No files matched your search

+2 -1
View File
@@ -145,7 +145,8 @@ if [[ -f /root/defer-provisioning ]] ||
export OMARCHY_UI_DEFER_PROVISIONING=yes
fi
if [[ -n ${OMARCHY_CJK_CONSOLE:-} && "$(jq -r '.omarchy_install.region // "global"' /root/user_configuration.json)" == "cn" ]]; then
# Only Chinese reaches the install from the CJK console; English returns to the VT.
if [[ -n ${OMARCHY_CJK_CONSOLE:-} ]]; then
export OMARCHY_INSTALL_LANG=zh
fi
+14 -17
View File
@@ -91,13 +91,6 @@ OMARCHY_ZH+=(
["Everything will be overwritten. There is no recovery possible."]="磁盘上的一切都会被覆盖,无法恢复。"
["Confirm overwriting %s"]="确认覆盖 %s"
)
# China preselects the bundled cn profile's timezone when geo guessing fails.
REGION_TIMEZONE="${REGION_TIMEZONE:-/usr/share/omarchy-iso/region/timezone}"
region_timezone() {
if [[ $region == "cn" ]]; then
head -n 1 "$REGION_TIMEZONE"
fi
}
# Partition creation helpers, shared with test/partition-numbering-test.sh so
# the numbering logic can be exercised without a real disk.
@@ -370,7 +363,7 @@ user_form() {
omarchy_prompt_identity || return $?
omarchy_prompt_hostname || return $?
omarchy_prompt_timezone "$(region_timezone)" || return $?
omarchy_prompt_timezone || return $?
}
# Returns 0 on confirmed account details, or as soon as the keyboard screen
@@ -390,19 +383,25 @@ user_step() {
continue
fi
# The timezone selects the region (mirrors and repositories), using the
# same lookup as the orchestrator. Global installs get no extra row.
region=$(cd /usr/share/omarchy-iso 2>/dev/null && python -m orchestrator.region "$timezone") || region="global"
region_row=""
if [[ $region != "global" ]]; then
region_row="\n$(omarchy_text Region),$region"
fi
# Add manual padding since gum table -p doesn't respect padding
local skipped region_label=Global
local skipped
skipped=$(omarchy_text "[Skipped]")
[[ $region == "cn" ]] && region_label=China
echo -e "$(omarchy_text Field),$(omarchy_text Value)
$(omarchy_text Username),$username
$(omarchy_text Password),$(printf "%${#password}s" | tr ' ' '*')
$(omarchy_text "Full name"),${full_name:-$skipped}
$(omarchy_text "Email address"),${email_address:-$skipped}
$(omarchy_text Hostname),$hostname
$(omarchy_text Timezone),$timezone
$(omarchy_text Keyboard),$keyboard
$(omarchy_text Region),$(omarchy_text "$region_label")" |
$(omarchy_text Timezone),$timezone$region_row
$(omarchy_text Keyboard),$keyboard" |
gum table -s "," -p | sed "s/^/${PADDING_LEFT_SPACES}/"
echo
@@ -907,7 +906,6 @@ run_partition_execute() {
"input_method": "${keyboard_input_method:-none}",
"input_xkb_layout": "${keyboard_xkb_layout:-}",
"mode": "protected",
"region": "$region",
"target_mount": "/mnt",
"boot": {
"esp_mount": "$esp_mount_in_target",
@@ -1141,8 +1139,8 @@ select_installation() {
defer_provisioning=false
install_target="full_disk"
# A rerun arrives with the region chosen; otherwise the ISO's default.
region="${OMARCHY_INSTALL_REGION:-$(<"${ISO_REGION_FILE:-/root/omarchy_region}")}"
# A rerun arrives with the console language chosen; otherwise Global.
region="${OMARCHY_INSTALL_REGION:-global}"
# Let the live VT reach its settled width before the first screen so the logo
# isn't measured against the transient 80-column boot console.
@@ -1256,7 +1254,6 @@ cat <<-_EOF_ >user_configuration.json
"input_method": "${keyboard_input_method:-none}",
"input_xkb_layout": "${keyboard_xkb_layout:-}",
"mode": "full_disk",
"region": "$region",
"defer_provisioning": $defer_provisioning,
"target_mount": "/mnt",
"boot": {
@@ -12,6 +12,7 @@ from pathlib import Path
from typing import Any
from .keyboard import validate_input_selection
from .region import GLOBAL, resolve_region
@dataclass
@@ -29,6 +30,7 @@ class InstallContext:
arch_config_path: Path
omarchy_install: dict[str, Any]
defer_provisioning: bool = False
region: str = GLOBAL
target: Path = Path("/mnt")
omarchy_path: Path = Path("/usr/share/omarchy")
@@ -61,6 +63,11 @@ class InstallContext:
validate_input_selection(omarchy_install.get("input_method"), omarchy_install.get("input_xkb_layout", ""))
# Resolved before any disk is touched, so an autoinstall naming a
# region this ISO lacks fails without wiping anything.
region = resolve_region(omarchy_install, user_configuration.get("timezone"))
omarchy_install["region"] = region
# Deferred provisioning: the whole system installs but user creation is deferred to
# first boot. Selected by the configurator (omarchy_install.defer_provisioning) or by
# an `defer-provisioning` marker file on an autoinstall drive, which also replaces the
@@ -69,12 +76,6 @@ class InstallContext:
defer_provisioning = bool(omarchy_install.get("defer_provisioning")) or defer_provisioning_marker is not None
omarchy_install["defer_provisioning"] = defer_provisioning
# Autoinstall configs written before regions existed install Global.
region = omarchy_install.get("region", "global")
if region not in ("global", "cn"):
raise RuntimeError(f"omarchy_install.region must be global or cn, not {region!r}")
omarchy_install["region"] = region
if creds_path.exists():
user_credentials = json.loads(creds_path.read_text())
elif defer_provisioning:
@@ -131,6 +132,7 @@ class InstallContext:
arch_config_path=arch_config_path,
omarchy_install=omarchy_install,
defer_provisioning=defer_provisioning,
region=region,
state_dir=state_dir,
)
disk_config = user_configuration.get("disk_config", {})
@@ -36,35 +36,10 @@ from . import archinstall_adapter as arch
from .command import capture, capture_identifier, require_text
from .context import InstallContext
from .keyboard import configure_keyboard
from .region import GLOBAL, region_keyrings, region_packages
from .ui import error, info
ISO_REGION_PAYLOAD = Path("/usr/share/omarchy-iso/region")
def _region_packages(ctx: InstallContext) -> list[str]:
if ctx.omarchy_install["region"] != "cn":
return []
lines = (ISO_REGION_PAYLOAD / "packages").read_text().splitlines()
return [s for s in (raw.strip() for raw in lines) if s and not s.startswith("#")]
def _stage_region_defaults(ctx: InstallContext) -> None:
"""Seed the target before useradd, including deferred first-boot users.
Region is a bootstrap default, not a locale or a user-level policy.
Existing home directories are never rewritten by this step.
"""
region = ctx.omarchy_install["region"]
skel = ISO_REGION_PAYLOAD / "skel"
if region == "cn" and skel.is_dir():
shutil.copytree(skel, ctx.target / "etc/skel", dirs_exist_ok=True)
marker = ctx.target / "etc/omarchy/region"
marker.parent.mkdir(parents=True, exist_ok=True)
marker.write_text(region + "\n")
marker.chmod(0o644)
# Package targets are written by builder/build-iso.sh. Stable ISOs use the
# stable package names, while dev/local-source ISOs install the dev package
# names explicitly instead of relying on provides=omarchy resolution.
@@ -313,7 +288,6 @@ def arch_install_system(ctx: InstallContext) -> None:
_install_early_packages(installer)
_configure_limine_boot(ctx, installer, config)
_stage_region_defaults(ctx)
info("› creating user (with /etc/skel populated)")
if config.auth_config and config.auth_config.users:
installer.create_users(config.auth_config.users)
@@ -325,6 +299,11 @@ def arch_install_system(ctx: InstallContext) -> None:
info("› installing Omarchy runtime + omarchy-base.packages")
installer.add_additional_packages(_runtime_package_list(ctx))
if region_pkgs := region_packages(ctx.region):
info(f"› installing {ctx.region} region packages: {', '.join(region_pkgs)}")
installer.add_additional_packages(region_pkgs)
_write_region_marker(ctx)
# Tailscale is bundled in the offline mirror but only installed
# when an autoinstall drive staged an auth key; must happen here,
# while the mirror is still bind-mounted, not in the phase that
@@ -755,6 +734,17 @@ def _unmask_mkinitcpio_pacman_hooks(
info(f"warning: failed to restore pacman hook mask for {name}: {exc}")
def _write_region_marker(ctx: InstallContext) -> None:
"""Tell the runtime's pacman finalizer which profile to apply. Global
installs write nothing, so they stay identical to a region-less install."""
if ctx.region == GLOBAL:
return
marker = ctx.target / "etc/omarchy/region"
marker.parent.mkdir(parents=True, exist_ok=True)
marker.write_text(ctx.region + "\n")
marker.chmod(0o644)
def _runtime_package_list(ctx: InstallContext) -> list[str]:
"""Selected Omarchy runtime package + every package in the ISO-bundled
base package list that isn't already installed early."""
@@ -774,10 +764,6 @@ def _runtime_package_list(ctx: InstallContext) -> list[str]:
continue
if s not in already_installed and s not in pkgs:
pkgs.append(s)
pkgs += [p for p in _region_packages(ctx) if p not in pkgs]
# Local builds live only in this ISO's mirror; a later region switch could not fetch them.
if PINNED_PACKAGES.exists():
pkgs += [p for p in PINNED_PACKAGES.read_text().split() if p not in already_installed and p not in pkgs]
return pkgs
@@ -1129,7 +1115,6 @@ def _run_target_setup_command(ctx: InstallContext, cmd: list[str], *, user: str
f"OMARCHY_USER_NAME={ctx.full_name}",
f"OMARCHY_USER_EMAIL={ctx.email}",
f"OMARCHY_MIRROR={mirror_channel}",
f"OMARCHY_REGION={ctx.omarchy_install['region']}",
f"OMARCHY_ISO_REF={_iso_ref()}",
f"OMARCHY_RUNTIME_PACKAGE={_omarchy_runtime_package()}",
f"OMARCHY_SETTINGS_PACKAGE={_omarchy_settings_package()}",
@@ -1167,12 +1152,13 @@ def _run_target_setup_command(ctx: InstallContext, cmd: list[str], *, user: str
def run_system_finalizer(ctx: InstallContext) -> None:
# The runtime package transaction has installed the regional keyring from
# the ISO. Initialize the target's trust database before enabling the online
# community repository; the build container's GPG state is not inherited.
if ctx.omarchy_install["region"] == "cn":
# The region's keyring packages are installed by now, but the build
# container's GPG state is not inherited. Initialize the target's trust
# database with only this region's keys before the finalizer enables its
# online repositories; global installs never trust them.
if keyrings := region_keyrings(ctx.region):
_run_target_setup_command(ctx, ["pacman-key", "--init"])
_run_target_setup_command(ctx, ["pacman-key", "--populate", "archlinux", "archlinuxcn"])
_run_target_setup_command(ctx, ["pacman-key", "--populate", "archlinux", *keyrings])
if ctx.defer_provisioning:
cmd = ["/usr/bin/omarchy-apply-system", "--defer-provisioning", "--first-install"]
@@ -1414,7 +1400,6 @@ def run_chroot_finalizer(ctx: InstallContext) -> None:
)
PINNED_PACKAGES = Path("/usr/share/omarchy-iso/pinned-packages")
PIN_HOOK_SCRIPT = """#!/bin/bash
@@ -0,0 +1,68 @@
"""Region selection: which of the runtime's region profiles a target gets.
The builder copies every profile from the bundled runtime's default/regions/
into REGIONS_DIR, and their packages into the offline mirror. Nobody picks a
region by hand: the timezone the user already chose selects it, and an
autoinstall config may name one explicitly in omarchy_install.region. Run as
`python -m orchestrator.region <timezone>` to print the region the installer
would use, which is how the configurator fills its summary.
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
REGIONS_DIR = Path("/usr/share/omarchy-iso/regions")
GLOBAL = "global"
def _entries(path: Path) -> list[str]:
if not path.is_file():
return []
lines = (line.strip() for line in path.read_text().splitlines())
return [line for line in lines if line and not line.startswith("#")]
def region_for_timezone(timezone: str | None, regions_dir: Path | None = None) -> str:
regions_dir = regions_dir or REGIONS_DIR
if timezone and regions_dir.is_dir():
for profile in sorted(regions_dir.iterdir()):
if timezone in _entries(profile / "timezones"):
return profile.name
return GLOBAL
def resolve_region(omarchy_install: dict, timezone: str | None, regions_dir: Path | None = None) -> str:
regions_dir = regions_dir or REGIONS_DIR
region = omarchy_install.get("region")
if region is None or region == "":
return region_for_timezone(timezone, regions_dir)
# Profiles are named by ISO 3166-1 alpha-2 code; checking the shape first
# keeps a path like ".." from passing the directory test.
if not isinstance(region, str) or (
region != GLOBAL and not (re.fullmatch(r"[a-z]{2}", region) and (regions_dir / region).is_dir())
):
raise RuntimeError(f"Unsupported region: {region!r}")
return region
def region_packages(region: str, regions_dir: Path | None = None) -> list[str]:
if region == GLOBAL:
return []
return _entries((regions_dir or REGIONS_DIR) / region / "packages")
def region_keyrings(region: str, regions_dir: Path | None = None) -> list[str]:
"""Pacman keyrings the profile ships, named the Arch way: <name>-keyring
installs /usr/share/pacman/keyrings/<name>.gpg."""
return [
package.removesuffix("-keyring")
for package in region_packages(region, regions_dir)
if package.endswith("-keyring")
]
if __name__ == "__main__":
print(region_for_timezone(sys.argv[1] if len(sys.argv) > 1 else None))