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:
commit
75d00a2036
14 files changed
+409
-618
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -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))
|
||||
Reference in new issue
Block a user