Compare commits
45
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75d9ec05e9
|
||
|
|
45a27629de
|
||
|
|
d6bea2aeba
|
||
|
|
70fb355250
|
||
|
|
c9494b27ec
|
||
|
|
a77ea36fe5
|
||
|
|
e9a3f13b9a
|
||
|
|
e2dab6ef14
|
||
|
|
7dbaa4914f
|
||
|
|
1dbe97d9b7
|
||
|
|
84450ffc19
|
||
|
|
b4de1c0d34
|
||
|
|
c90afe24dc
|
||
|
|
f5ab724a1c
|
||
|
|
096982fe69
|
||
|
|
96e2effbe1
|
||
|
|
846f7d491f
|
||
|
|
16faf06fcf
|
||
|
|
466c2ca14a
|
||
|
|
f3b236ae82
|
||
|
|
ec4579a461 | ||
|
|
d9a781e3c9 | ||
|
|
a6c3480ff8 | ||
|
|
545190596d | ||
|
|
b381093e32 | ||
|
|
9d74c813f1 | ||
|
|
fce85abf48 | ||
|
|
3c68a1bb04 | ||
|
|
b89def6c7f | ||
|
|
0de49b231a | ||
|
|
3e00a48c00 | ||
|
|
a1bdc46119 | ||
|
|
7a60c8dea4 | ||
|
|
6be88d14a5 | ||
|
|
50a26be6a7 | ||
|
|
d5ef1c1d15 | ||
|
|
d024c82a9d | ||
|
|
d7f17a0424 | ||
|
|
87a2931209 | ||
|
|
647979bde7 | ||
|
|
123535ea14 | ||
|
|
645cfc2a36 | ||
|
|
e989960895 | ||
|
|
48f7c411c0 | ||
|
|
7727d40c92 |
No files matched your search
@@ -130,6 +130,13 @@ Exceptions are allowed for migration and package-helper scripts where the helper
|
||||
- Chinese menu labels live in `default/omarchy/omarchy-menu.zh-cn.jsonc`, merged when `LANG` starts with `zh_CN`; give a new menu row with translatable words a label there.
|
||||
- Shell text goes through `I18n.tr("English %1", arg)` from `qs.Commons`, with its Chinese in `shell/Commons/I18nZh.js` keyed by the English. Notifications and OSD messages are worded by their sender with `omarchy-text "English %1" "$arg"` (Python: `text()`, QML: `I18n.tr`), which reads the same table; the shell shows them as given, so user text is never translated. `test/shell.d/shell-i18n-test.sh` fails on a missing entry.
|
||||
|
||||
# Region
|
||||
|
||||
- `/etc/omarchy/region` holds the region the installer derived from the timezone (`cn`); no file means Global. The profile lives in `default/regions/cn/`, its contract in `docs/regions.md`.
|
||||
- `install/config/region.sh` links the `cn` developer mirrors into `/etc` and `/.cargo` and refuses to replace a regular file it did not create. It serves both the install (the marker) and `omarchy region cn|global` (`OMARCHY_REGION`), which also records or removes the marker; Global removes only the links it made.
|
||||
- `omarchy-region` prints the region (`Global` or `China`) and switches it. Nothing in `omarchy update` or migrations may change the region.
|
||||
- China-only menu rows carry `"when":"[[ \"$(omarchy-region)\" == \"China\" ]]"` on every row, not only the submenu, because search checks each row's own guard. User-facing docs: `manual/52-china.md`.
|
||||
|
||||
# Config Structure
|
||||
|
||||
- `config/` - default configs copied to `~/.config/`
|
||||
|
||||
@@ -71,6 +71,7 @@ The manual lives in [`manual/`](manual/), which is its authoritative source.
|
||||
- [Omarchy on...](manual/49-omarchy-on.md)
|
||||
- [Dual Boot Install](manual/50-dual-boot-install.md)
|
||||
- [Unattended Installs](manual/51-unattended-installs.md)
|
||||
- [China](manual/52-china.md)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ GROUP_DESCRIPTIONS[plymouth]="Plymouth boot theme management"
|
||||
GROUP_DESCRIPTIONS[power]="Power supply detection"
|
||||
GROUP_DESCRIPTIONS[powerprofiles]="Power profile management"
|
||||
GROUP_DESCRIPTIONS[refresh]="Reset config to defaults"
|
||||
GROUP_DESCRIPTIONS[region]="Region-specific package sources and setup"
|
||||
GROUP_DESCRIPTIONS[reinstall]="Reinstall and reset workflows"
|
||||
GROUP_DESCRIPTIONS[reminder]="Desktop notification reminders"
|
||||
GROUP_DESCRIPTIONS[remove]="Removal workflows"
|
||||
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Restore channel and regional pacman defaults in an installed target
|
||||
# omarchy:args=<stable|rc|edge> [target-root] | --render <stable|rc|edge> <output-dir> [target-root]
|
||||
# omarchy:requires-sudo=true
|
||||
# omarchy:hidden=true
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
echo "Usage: omarchy-apply-pacman <stable|rc|edge> [target-root]" >&2
|
||||
echo " omarchy-apply-pacman --render <stable|rc|edge> <output-dir> [target-root]" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --render only builds the two files, so an unprivileged caller can hand root
|
||||
# nothing but plain copies of the finished result.
|
||||
render_dir=""
|
||||
if [[ ${1:-} == "--render" ]]; then
|
||||
(( $# == 3 || $# == 4 )) || usage
|
||||
channel="$2"
|
||||
render_dir="$3"
|
||||
[[ -d $render_dir ]] || { echo "Render directory not found: $render_dir" >&2; exit 1; }
|
||||
etc="${4:-/}/etc"
|
||||
else
|
||||
(( $# >= 1 && $# <= 2 )) || usage
|
||||
channel="$1"
|
||||
etc="${2:-/}/etc"
|
||||
fi
|
||||
|
||||
case "$channel" in
|
||||
stable|rc|edge) ;;
|
||||
*) echo "Invalid pacman channel: $channel" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
# The ISO writes this plain-text marker before target finalization. Missing
|
||||
# markers keep existing installations global; never source it as shell code.
|
||||
region=global
|
||||
if [[ -e $etc/omarchy/region ]]; then
|
||||
region=$(<"$etc/omarchy/region")
|
||||
fi
|
||||
case "$region" in
|
||||
global|cn) ;;
|
||||
*) echo "Unsupported region: $region (expected global or cn)" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
# Build both files before replacing either. Explicit refreshes retain backups;
|
||||
# ordinary updates do not call this command or enforce regional defaults.
|
||||
if [[ -n $render_dir ]]; then
|
||||
staging="$render_dir"
|
||||
else
|
||||
staging=$(mktemp -d)
|
||||
trap 'rm -rf "$staging"' EXIT
|
||||
fi
|
||||
cp "$OMARCHY_PATH/default/pacman/pacman-$channel.conf" "$staging/pacman.conf"
|
||||
if [[ $region == "global" ]]; then
|
||||
cp "$OMARCHY_PATH/default/pacman/mirrorlist-$channel" "$staging/mirrorlist"
|
||||
else
|
||||
profile="$OMARCHY_PATH/default/regions/$region/pacman"
|
||||
printf '\n' >> "$staging/pacman.conf"
|
||||
cat "$profile/pacman.conf.append" >> "$staging/pacman.conf"
|
||||
# pacman tries servers in order, so the regional mirror leads and the
|
||||
# channel's own mirror stays as the fallback.
|
||||
cat "$profile/mirrorlist-$channel.prepend" "$OMARCHY_PATH/default/pacman/mirrorlist-$channel" > "$staging/mirrorlist"
|
||||
fi
|
||||
|
||||
if [[ -z $render_dir ]]; then
|
||||
mkdir -p "$etc/pacman.d"
|
||||
for file in pacman.conf pacman.d/mirrorlist; do
|
||||
if [[ -e $etc/$file ]]; then
|
||||
cp -f "$etc/$file" "$etc/$file.bak"
|
||||
fi
|
||||
done
|
||||
# Copying over an existing file keeps its owner and mode, as refresh always has.
|
||||
cp -f "$staging/pacman.conf" "$etc/pacman.conf"
|
||||
cp -f "$staging/mirrorlist" "$etc/pacman.d/mirrorlist"
|
||||
fi
|
||||
@@ -47,9 +47,31 @@ validate_dev_checkout() {
|
||||
fi
|
||||
}
|
||||
|
||||
# The dev channel refreshes pacman with the checkout's own tools. A checkout
|
||||
# without this machine's region profile would silently drop its repositories.
|
||||
validate_dev_region() {
|
||||
local checkout="$1" region_file="${OMARCHY_REGION_FILE:-/etc/omarchy/region}" region=global
|
||||
|
||||
[[ -f $region_file ]] && region=$(<"$region_file")
|
||||
[[ $region == "global" ]] && return 0
|
||||
|
||||
# The refresh after linking needs a runnable helper and every fragment, not
|
||||
# just a directory with the region's name: dev refreshes edge, and leaving
|
||||
# dev later refreshes stable or rc with this checkout's tools.
|
||||
local profile="$checkout/default/regions/$region/pacman" fragment supported=true
|
||||
[[ -x $checkout/bin/omarchy-apply-pacman ]] || supported=false
|
||||
for fragment in pacman.conf.append mirrorlist-stable.prepend mirrorlist-rc.prepend mirrorlist-edge.prepend; do
|
||||
[[ -r $profile/$fragment ]] || supported=false
|
||||
done
|
||||
if ! $supported; then
|
||||
fail "$checkout does not support the '$region' region, so switching would drop its package repositories. Check out a branch with default/regions/$region, or remove $region_file to use global defaults."
|
||||
fi
|
||||
}
|
||||
|
||||
link_dev_checkout() {
|
||||
local checkout="$1" required
|
||||
[[ -d $checkout/.git ]] || git clone https://github.com/omacom/omarchy.git "$checkout"
|
||||
validate_dev_region "$checkout"
|
||||
|
||||
# Check the destination before changing /etc/omarchy.conf or sudo's path.
|
||||
# An existing checkout is not pulled automatically and may predate this policy.
|
||||
|
||||
@@ -42,11 +42,29 @@ if [[ ! -f $templates/pacman-$channel.conf || ! -f $templates/mirrorlist-$channe
|
||||
exit 1
|
||||
fi
|
||||
|
||||
conf=$templates/pacman-$channel.conf mirrorlist=$templates/mirrorlist-$channel
|
||||
# Regions render x86 pacman only (omarchy #13690).
|
||||
if [[ $platform == x86 ]]; then
|
||||
# Render the channel templates plus this machine's regional additions as the
|
||||
# caller, so root only copies finished files. The scratch directory goes on
|
||||
# every exit, including a failed render, a refused sudo prompt, or a signal;
|
||||
# revoking the sudo timestamp stays the last thing the exit path does.
|
||||
rendered=$(mktemp -d)
|
||||
cleanup_refresh() {
|
||||
local status=$?
|
||||
rm -rf "$rendered"
|
||||
omarchy_security_exit_with_revoked_sudo "$status"
|
||||
}
|
||||
trap cleanup_refresh EXIT
|
||||
"$OMARCHY_PATH/bin/omarchy-apply-pacman" --render "$channel" "$rendered"
|
||||
conf=$rendered/pacman.conf mirrorlist=$rendered/mirrorlist
|
||||
fi
|
||||
|
||||
sudo cp -f /etc/pacman.conf /etc/pacman.conf.bak
|
||||
sudo cp -f /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak
|
||||
echo "Setting channel to $channel"
|
||||
sudo cp -f "$templates/pacman-$channel.conf" /etc/pacman.conf
|
||||
sudo cp -f "$templates/mirrorlist-$channel" /etc/pacman.d/mirrorlist
|
||||
sudo cp -f "$conf" /etc/pacman.conf
|
||||
sudo cp -f "$mirrorlist" /etc/pacman.d/mirrorlist
|
||||
|
||||
# Allow user customization of /etc/pacman.conf before the upgrade runs, so
|
||||
# custom repositories and IgnorePkg entries shape the downgrade-capable
|
||||
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Show or switch the region this machine is set up for
|
||||
# omarchy:args=[global|cn]
|
||||
# omarchy:examples=omarchy region | omarchy region cn
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
region_file=/etc/omarchy/region
|
||||
|
||||
if (($# == 0)); then
|
||||
if [[ ! -e $region_file ]]; then
|
||||
echo "Global"
|
||||
elif [[ $(<"$region_file") == "cn" ]]; then
|
||||
echo "China"
|
||||
else
|
||||
echo "Unknown region in $region_file: $(<"$region_file")" >&2
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ $1 != "global" && $1 != "cn" ]]; then
|
||||
echo "Usage: omarchy-region [global|cn]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
channel=$(omarchy-version-channel)
|
||||
if [[ $channel != "stable" && $channel != "rc" && $channel != "edge" ]]; then
|
||||
echo "Cannot tell this machine's package channel ($channel); run omarchy channel set first" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The cn profile renders x86 pacman only (omarchy #13690).
|
||||
if [[ $1 == "cn" && $(omarchy-hw-platform) != "x86" ]]; then
|
||||
echo "The China region needs an x86 machine; $(omarchy-hw-platform) has no China profile yet" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $1 == "cn" ]]; then
|
||||
# Upgrade first: the keyring install below syncs new databases.
|
||||
omarchy-update-pacman -Syu --noconfirm
|
||||
sudo env OMARCHY_REGION=cn OMARCHY_PATH="$OMARCHY_PATH" bash -e "$OMARCHY_PATH/install/config/region.sh"
|
||||
# Trust ArchLinuxCN before the refresh upgrades through it.
|
||||
rendered=$(mktemp -d)
|
||||
trap 'rm -rf "$rendered"' EXIT
|
||||
omarchy-apply-pacman --render "$channel" "$rendered"
|
||||
mapfile -t profile_packages < <(grep -Ev '^[[:space:]]*(#|$)' "$OMARCHY_PATH/default/regions/cn/packages")
|
||||
sudo pacman --config "$rendered/pacman.conf" -Sy --noconfirm --needed "${profile_packages[@]}"
|
||||
sudo pacman-key --populate archlinuxcn
|
||||
omarchy-refresh-pacman "$channel"
|
||||
else
|
||||
sudo env OMARCHY_REGION=global OMARCHY_PATH="$OMARCHY_PATH" bash -e "$OMARCHY_PATH/install/config/region.sh"
|
||||
omarchy-refresh-pacman "$channel"
|
||||
fi
|
||||
|
||||
# The developer mirrors reach only a new session, so offer one.
|
||||
if [[ -t 0 && -t 1 ]] && gum confirm --affirmative "$(omarchy-text "Log out now")" --negative "$(omarchy-text "Later")" "$(omarchy-text "Log out now to switch the developer mirrors?")"; then
|
||||
omarchy-system-logout
|
||||
else
|
||||
omarchy-text "Log back in to switch the developer mirrors."
|
||||
fi
|
||||
@@ -1,6 +1,298 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||
<fontconfig>
|
||||
<!-- CJK: fold the language tag onto one tag per Noto CJK variant -->
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="eq">
|
||||
<string>zh</string>
|
||||
</test>
|
||||
<edit name="lang" mode="assign">
|
||||
<string>zh-cn</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="eq">
|
||||
<string>zh-hant</string>
|
||||
</test>
|
||||
<edit name="lang" mode="assign">
|
||||
<string>zh-tw</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-hant-hk</string>
|
||||
</test>
|
||||
<edit name="lang" mode="assign">
|
||||
<string>zh-hk</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-hant-mo</string>
|
||||
</test>
|
||||
<edit name="lang" mode="assign">
|
||||
<string>zh-hk</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-hant</string>
|
||||
</test>
|
||||
<edit name="lang" mode="assign">
|
||||
<string>zh-tw</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-hans</string>
|
||||
</test>
|
||||
<edit name="lang" mode="assign">
|
||||
<string>zh-cn</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-sg</string>
|
||||
</test>
|
||||
<edit name="lang" mode="assign">
|
||||
<string>zh-cn</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-mo</string>
|
||||
</test>
|
||||
<edit name="lang" mode="assign">
|
||||
<string>zh-hk</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="eq">
|
||||
<string>yue</string>
|
||||
</test>
|
||||
<edit name="lang" mode="assign">
|
||||
<string>zh-hk</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>yue-hans</string>
|
||||
</test>
|
||||
<edit name="lang" mode="assign">
|
||||
<string>zh-cn</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>yue-cn</string>
|
||||
</test>
|
||||
<edit name="lang" mode="assign">
|
||||
<string>zh-cn</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>yue</string>
|
||||
</test>
|
||||
<edit name="lang" mode="assign">
|
||||
<string>zh-hk</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<!-- Then append that variant after the generic family it falls back from -->
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-cn</string>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>sans-serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Sans CJK SC</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-cn</string>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Serif CJK SC</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-cn</string>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>monospace</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Sans Mono CJK SC</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-hk</string>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>sans-serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Sans CJK HK</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-hk</string>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Serif CJK HK</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-hk</string>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>monospace</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Sans Mono CJK HK</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-tw</string>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>sans-serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Sans CJK TC</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-tw</string>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Serif CJK TC</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<string>zh-tw</string>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>monospace</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Sans Mono CJK TC</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<langset><string>ja</string></langset>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>sans-serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Sans CJK JP</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<langset><string>ja</string></langset>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Serif CJK JP</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<langset><string>ja</string></langset>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>monospace</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Sans Mono CJK JP</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<langset><string>ko</string></langset>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>sans-serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Sans CJK KR</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<langset><string>ko</string></langset>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>serif</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Serif CJK KR</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="lang" qual="first" compare="contains">
|
||||
<langset><string>ko</string></langset>
|
||||
</test>
|
||||
<test name="family">
|
||||
<string>monospace</string>
|
||||
</test>
|
||||
<edit name="family" mode="append" binding="strong">
|
||||
<string>Noto Sans Mono CJK KR</string>
|
||||
</edit>
|
||||
</match>
|
||||
|
||||
<match target="pattern">
|
||||
<test name="family" qual="any">
|
||||
<string>sans-serif</string>
|
||||
|
||||
@@ -215,6 +215,9 @@
|
||||
"setup.language": {"icon":"","label":"Language"},
|
||||
"setup.language.en": {"icon":"","label":"English","checked":"[[ \"$(omarchy-language)\" == \"en_US.UTF-8\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-language en'"},
|
||||
"setup.language.zh": {"icon":"","label":"简体中文","checked":"[[ \"$(omarchy-language)\" == \"zh_CN.UTF-8\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-language zh'"},
|
||||
"setup.region": {"icon":"","label":"Region"},
|
||||
"setup.region.global": {"icon":"","label":"Global","checked":"[[ \"$(omarchy-region)\" == \"Global\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-region global'"},
|
||||
"setup.region.china": {"icon":"","label":"China","checked":"[[ \"$(omarchy-region)\" == \"China\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-region cn'"},
|
||||
"setup.plugin": {"icon":"","label":"Plugins","aliases":["plugin","plugins"]},
|
||||
"setup.plugin.enable": {"icon":"","label":"Enable Plugin","action":"omarchy-menu-plugin enable"},
|
||||
"setup.plugin.disable": {"icon":"","label":"Disable Plugin","action":"omarchy-menu-plugin disable"},
|
||||
@@ -258,6 +261,16 @@
|
||||
"install.tui": {"icon":"","label":"TUI","action":"omarchy-launch-floating-terminal-with-presentation omarchy-tui-install"},
|
||||
"install.windows": {"icon":"","label":"Windows","when":"omarchy-hw-x86","disabled":"[[ -f $HOME/.local/share/applications/windows-vm.desktop ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-windows-vm install'"},
|
||||
"install.aur": {"icon":"","label":"AUR","action":"xdg-terminal-exec --app-id=org.omarchy.terminal omarchy-pkg-aur-install"},
|
||||
"install.china": {"icon":"","label":"China Apps","when":"[[ \"$(omarchy-region)\" == \"China\" ]]"},
|
||||
"install.china.wechat": {"icon":"","label":"WeChat","when":"[[ \"$(omarchy-region)\" == \"China\" ]]","disabled":"omarchy-pkg-present wechat-universal-bwrap","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-pkg-aur-add wechat-universal-bwrap'"},
|
||||
"install.china.qq": {"icon":"","label":"QQ","when":"[[ \"$(omarchy-region)\" == \"China\" ]]","disabled":"omarchy-pkg-present linuxqq","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-pkg-aur-add linuxqq'"},
|
||||
"install.china.feishu": {"icon":"","label":"Feishu","when":"[[ \"$(omarchy-region)\" == \"China\" ]]","disabled":"omarchy-pkg-present feishu-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-pkg-aur-add feishu-bin'"},
|
||||
"install.china.dingtalk": {"icon":"","label":"DingTalk","when":"[[ \"$(omarchy-region)\" == \"China\" ]]","disabled":"omarchy-pkg-present dingtalk-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-pkg-aur-add dingtalk-bin'"},
|
||||
"install.china.wemeet": {"icon":"","label":"Tencent Meeting","when":"[[ \"$(omarchy-region)\" == \"China\" ]]","disabled":"omarchy-pkg-present wemeet-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-pkg-aur-add wemeet-bin'"},
|
||||
"install.china.wps": {"icon":"","label":"WPS Office","when":"[[ \"$(omarchy-region)\" == \"China\" ]]","disabled":"omarchy-pkg-present wps-office-cn","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-pkg-aur-add wps-office-cn'"},
|
||||
"install.china.tencent-docs": {"icon":"","label":"Tencent Docs","when":"[[ \"$(omarchy-region)\" == \"China\" ]]","disabled":"[[ -f \"$HOME/.local/share/applications/腾讯文档.desktop\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-webapp-install 腾讯文档 https://docs.qq.com \"\"'"},
|
||||
"install.china.yuque": {"icon":"","label":"Yuque","when":"[[ \"$(omarchy-region)\" == \"China\" ]]","disabled":"[[ -f \"$HOME/.local/share/applications/语雀.desktop\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-webapp-install 语雀 https://www.yuque.com/dashboard \"\"'"},
|
||||
"install.china.shimo": {"icon":"","label":"Shimo","when":"[[ \"$(omarchy-region)\" == \"China\" ]]","disabled":"[[ -f \"$HOME/.local/share/applications/石墨文档.desktop\" ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-webapp-install 石墨文档 https://shimo.im/desktop \"\"'"},
|
||||
"install.preinstalls": {"icon":"","label":"Preinstalls","disabled":"[[ ! -f $HOME/.local/state/omarchy/preinstalls-removed ]]","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-preinstalls"},
|
||||
"install.browser.chrome": {"icon":"","label":"Chrome","disabled":"omarchy-pkg-present google-chrome","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser chrome'"},
|
||||
"install.browser.edge": {"icon":"","label":"Edge","when":"omarchy-hw-x86","disabled":"omarchy-pkg-present microsoft-edge-stable-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-install-browser edge'"},
|
||||
|
||||
@@ -189,4 +189,15 @@
|
||||
"setup.typing": {"label":"输入"},
|
||||
"setup.typing.keyboard": {"label":"键盘布局"},
|
||||
"setup.typing.input": {"label":"输入法"},
|
||||
"setup.region": {"label":"地区"},
|
||||
"setup.region.global": {"label":"全球"},
|
||||
"setup.region.china": {"label":"中国"},
|
||||
"install.china": {"label":"国内应用"},
|
||||
"install.china.wechat": {"label":"微信"},
|
||||
"install.china.feishu": {"label":"飞书"},
|
||||
"install.china.dingtalk": {"label":"钉钉"},
|
||||
"install.china.wemeet": {"label":"腾讯会议"},
|
||||
"install.china.tencent-docs": {"label":"腾讯文档"},
|
||||
"install.china.yuque": {"label":"语雀"},
|
||||
"install.china.shimo": {"label":"石墨文档"},
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
[source.crates-io]
|
||||
replace-with = "aliyun"
|
||||
|
||||
[source.aliyun]
|
||||
registry = "sparse+https://mirrors.aliyun.com/crates.io-index/"
|
||||
@@ -0,0 +1,8 @@
|
||||
NPM_CONFIG_REGISTRY=https://registry.npmmirror.com
|
||||
PIP_INDEX_URL=https://mirrors.aliyun.com/pypi/simple/
|
||||
UV_DEFAULT_INDEX=https://mirrors.aliyun.com/pypi/simple/
|
||||
GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
|
||||
RUSTUP_DIST_SERVER=https://mirrors.aliyun.com/rustup
|
||||
RUSTUP_UPDATE_ROOT=https://mirrors.aliyun.com/rustup/rustup
|
||||
BUNDLE_MIRROR__ALL=https://mirrors.aliyun.com/rubygems/
|
||||
MAVEN_ARGS=--global-settings /usr/share/omarchy/default/regions/cn/maven-settings.xml
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
:sources:
|
||||
- https://mirrors.aliyun.com/rubygems/
|
||||
@@ -0,0 +1,9 @@
|
||||
<settings>
|
||||
<mirrors>
|
||||
<mirror>
|
||||
<id>aliyun</id>
|
||||
<mirrorOf>central</mirrorOf>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
</mirror>
|
||||
</mirrors>
|
||||
</settings>
|
||||
@@ -0,0 +1,2 @@
|
||||
# Installed from the ISO's offline repository before enabling ArchLinuxCN.
|
||||
archlinuxcn-keyring
|
||||
@@ -0,0 +1 @@
|
||||
Server = https://mirror.omarchy.cn/$repo/os/$arch
|
||||
@@ -0,0 +1 @@
|
||||
Server = https://rc-mirror.omarchy.cn/$repo/os/$arch
|
||||
@@ -0,0 +1 @@
|
||||
Server = https://stable-mirror.omarchy.cn/$repo/os/$arch
|
||||
@@ -0,0 +1,2 @@
|
||||
[archlinuxcn]
|
||||
Server = https://mirrors.ustc.edu.cn/archlinuxcn/$arch
|
||||
@@ -0,0 +1,9 @@
|
||||
# Installs in these timezones get this region. The installer's timezone list
|
||||
# includes tzdata's backward-compatible names, so they are listed too.
|
||||
Asia/Shanghai
|
||||
Asia/Urumqi
|
||||
Asia/Chongqing
|
||||
Asia/Chungking
|
||||
Asia/Harbin
|
||||
Asia/Kashgar
|
||||
PRC
|
||||
@@ -0,0 +1,49 @@
|
||||
# Regional defaults
|
||||
|
||||
Regions customize bootstrap defaults independently of update channels and of the installer's language. Country identifiers are lowercase, officially assigned ISO 3166-1 alpha-2 codes. `global` is a reserved value for no country-specific additions. Only implemented profiles are accepted; currently `global` and `cn`.
|
||||
|
||||
## Runtime configuration
|
||||
|
||||
The ISO installer writes the region it derived to `/etc/omarchy/region` before target finalization. It is plain text, not executable configuration, owned by root and world-readable (`0644`), because refresh renders as the calling user. A missing file means `global`; an unsupported value is an error.
|
||||
|
||||
`omarchy-apply-pacman <stable|rc|edge> [target-root]` restores the selected channel's two pacman templates, appends the region's `pacman.conf.append` to `pacman.conf`, and puts the region's `mirrorlist-<channel>.prepend` ahead of the channel's mirrorlist. Both files are generated before either live file is replaced. Existing files are backed up with `.bak`. Dev uses the edge pacman channel, as before.
|
||||
|
||||
Both installation finalization and `omarchy-refresh-pacman` use this command. Explicit refreshes and channel changes restore regional defaults; ordinary updates do not enforce them. The existing `pre-refresh-pacman` user hook runs after regeneration and before the update, so users can maintain overrides there. Switching to the dev channel refuses a checkout that lacks this machine's region profile, because that checkout's own refresh would otherwise drop the regional repositories. Users can edit the files directly or set the region to `global` before a refresh to stop applying regional repository defaults. This does not uninstall packages or rewrite anything in existing home directories.
|
||||
|
||||
Refresh renders both files as the calling user and gives root only the finished copies, so the sudo boundary stays command-scoped. That boundary does not defend against other code already running as the same user: such code could change the rendered files while refresh waits for the password, as it could alter that user's own environment in other ways.
|
||||
|
||||
The China profile puts Omarchy's China replica of the channel's Arch mirror first (`stable-mirror.omarchy.cn`, `rc-mirror.omarchy.cn`, or `mirror.omarchy.cn` for edge and dev) and keeps the channel's `.org` mirror as the fallback. pacman tries servers in order and moves to the next one when a download fails, including a 404. After three failed downloads from one server, it skips that server for the rest of the transaction. It also takes the package databases from the first server that answers, without comparing them, so the China replicas must carry complete, current metadata. The profile also adds the ArchLinuxCN community repository from USTC's mirror, run by the University of Science and Technology of China. It does not change the `[omarchy]` package repository. ArchLinuxCN is rolling and built against current Arch, while stable and RC are snapshots, so its packages can need newer libraries than a stable machine has.
|
||||
|
||||
## Profile layout
|
||||
|
||||
Each profile lives in `default/regions/<code>/`:
|
||||
|
||||
- `timezones`: the timezones that select this region, one per line, with blank lines and `#` comments allowed. List tzdata's backward-compatible names too, since the installer's timezone list includes them. A timezone may belong to at most one profile.
|
||||
- `packages`: additional target packages, in the same format. Every ISO carries every profile's packages in its offline repository; the installer installs them only on targets in that region. A package named `<name>-keyring` is the profile's trust anchor: the builder installs it through Arch's existing trust chain, and a regional target populates the `<name>` keyring.
|
||||
- `pacman/`: `pacman.conf.append`, added after the channel's `pacman.conf`, and one `mirrorlist-<channel>.prepend` per channel (`stable`, `rc`, `edge`), placed ahead of the channel's mirrorlist so the regional mirror is tried first.
|
||||
|
||||
## ISO installer contract
|
||||
|
||||
There is one ISO for every region. The companion `omarchy-iso` repository reads the profiles from the runtime it bundles, so a region and its repository defaults can never disagree.
|
||||
|
||||
- **Build:** every profile's `packages` go into the offline repository, but not into the base package list. The builder adds each profile's community repository to its writable online config to download them; it does not change its Arch channel mirrors.
|
||||
- **Install:** the installer maps the timezone the user picked to a region through the `timezones` files; a timezone no profile lists means `global`. It shows the result on the summary screen. Unattended installs can set the region explicitly instead. For a regional install it installs the profile's packages from the offline repository and writes `/etc/omarchy/region`; the runtime finalizer then applies the fragments.
|
||||
- **Deferred installs** have no timezone until first boot, so they are `global` unless the unattended configuration sets a region.
|
||||
|
||||
For China, the builder installs `archlinuxcn-keyring` using the existing Arch trust chain before downloading ArchLinuxCN packages. Signature or keyring failures abort the build; no unverified keyserver trust or online signature bypass is added. On a China target the installer installs the keyring from the offline repository, then initializes and populates it before the online repositories are enabled. Global targets never populate ArchLinuxCN's keys. The ISO's existing offline repository verification policy is unchanged.
|
||||
|
||||
## Scope of the China profile
|
||||
|
||||
The China profile provides repository defaults and developer mirrors. Language is a separate installer choice, not part of a region: a Chinese speaker outside China keeps global mirrors, and a China install in English still gets the regional ones.
|
||||
|
||||
## Developer mirrors
|
||||
|
||||
The `cn` profile also points the common package managers at mirrors inside China: `environment.conf` (npm, PyPI and uv, Go, rustup, Bundler, and Maven through `MAVEN_ARGS`), `cargo.toml` for crates.io, `gemrc` for RubyGems, and `maven-settings.xml`, which Maven reads in place. During installation `install/config/region.sh` links the first three to `/etc/environment.d/90-omarchy-china.conf`, `/.cargo/config.toml` and `/etc/gemrc` when the marker says `cn`. It refuses to replace a regular file it did not create. The environment reaches sessions started after login.
|
||||
|
||||
`omarchy region cn|global` (_Setup > Region_) switches an installed machine. `cn` upgrades, installs and trusts the profile's keyring through a rendered China config, records the marker and links the developer mirrors through the same script, then refreshes pacman. `global` removes the marker and only the links that point into the profile, then refreshes. The region also shows _Install > China Apps_.
|
||||
|
||||
## Remaining release validation
|
||||
|
||||
Unit tests cover region/channel independence, the timezone mapping, generated pacman contents, repeat application, and refresh ordering; refresh itself is exercised only inside the sudo-boundary sandbox. They do not establish live mirror availability, real package trust, or successful offline installation. Before distribution, build from matching local checkouts on an x86_64 builder and run the disposable-VM acceptance workflow in `agents/skills/acceptance-tests.md`.
|
||||
|
||||
The acceptance run must install without network access in both directions: a non-China timezone must produce pacman files byte for byte identical to the channel templates, and `Asia/Shanghai` must produce the China profile, boot to a new user, and keep the regional mirrors, repository, marker, and keyring trust through `stable`, `rc`, and `edge` refreshes.
|
||||
@@ -1,3 +1,4 @@
|
||||
run_logged "$OMARCHY_INSTALL/config/region.sh"
|
||||
run_logged "$OMARCHY_INSTALL/config/theme-system.sh"
|
||||
run_logged "$OMARCHY_INSTALL/config/browser-policy.sh"
|
||||
run_logged "$OMARCHY_INSTALL/config/increase-lockout-limit.sh"
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
cn_links=(
|
||||
"environment.conf /etc/environment.d/90-omarchy-china.conf"
|
||||
"cargo.toml /.cargo/config.toml"
|
||||
"gemrc /etc/gemrc"
|
||||
)
|
||||
|
||||
# The installer writes the marker from the timezone; omarchy-region passes the region it switches to.
|
||||
region=global
|
||||
if [[ -e /etc/omarchy/region ]]; then
|
||||
region=$(</etc/omarchy/region)
|
||||
fi
|
||||
region=${OMARCHY_REGION:-$region}
|
||||
|
||||
case $region in
|
||||
global)
|
||||
rm -f /etc/omarchy/region
|
||||
for link in "${cn_links[@]}"; do
|
||||
read -r _ target <<<"$link"
|
||||
if [[ -L $target && $(readlink "$target") == */default/regions/cn/* ]]; then
|
||||
rm "$target"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
cn)
|
||||
for link in "${cn_links[@]}"; do
|
||||
read -r _ target <<<"$link"
|
||||
if [[ -e $target && ! -L $target ]]; then
|
||||
echo "Refusing to replace $target, which Omarchy did not create" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
mkdir -p /etc/omarchy
|
||||
echo cn >/etc/omarchy/region
|
||||
for link in "${cn_links[@]}"; do
|
||||
read -r source target <<<"$link"
|
||||
mkdir -p "${target%/*}"
|
||||
ln -sfn "$OMARCHY_PATH/default/regions/cn/$source" "$target"
|
||||
done
|
||||
;;
|
||||
*)
|
||||
echo "Unknown region: $region" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -21,8 +21,13 @@ fi
|
||||
# rc on aarch64) gets the platform's default channel instead.
|
||||
channel=${OMARCHY_MIRROR:-}
|
||||
[[ -n $channel && -f $templates/pacman-$channel.conf ]] || channel=$(omarchy_pacman_default_channel "$platform")
|
||||
cp -f "$templates/pacman-$channel.conf" /etc/pacman.conf
|
||||
cp -f "$templates/mirrorlist-$channel" /etc/pacman.d/mirrorlist
|
||||
# Regions render x86 pacman only (omarchy #13690).
|
||||
if [[ $platform == x86 && -e /etc/omarchy/region ]]; then
|
||||
"$OMARCHY_PATH/bin/omarchy-apply-pacman" "$channel"
|
||||
else
|
||||
cp -f "$templates/pacman-$channel.conf" /etc/pacman.conf
|
||||
cp -f "$templates/mirrorlist-$channel" /etc/pacman.d/mirrorlist
|
||||
fi
|
||||
|
||||
# An image build leaves the keyring to each machine's first boot, so no two
|
||||
# share a master key (install/helpers/image-target.sh).
|
||||
|
||||
@@ -20,6 +20,10 @@ Now you're ready to Omarchy!
|
||||
|
||||
The full-disk encryption won't allow you to enter the password from a Bluetooth keyboard at startup. Just like you can't use a Bluetooth keyboard to enter the BIOS on a PC. You'll need a keyboard that either uses a 2.4ghz dongle or a cable (which is much nicer for latency anyway!). I personally love the [Lofree Flow84](https://www.lofree.co/products/lofree-flow-the-smoothest-mechanical-keyboard)!
|
||||
|
||||
### Installing in China
|
||||
|
||||
The installer's first screen asks for your language; pick _Chinese_ for a Chinese installer and system. The timezone you pick later decides the region: `Asia/Shanghai` brings Chinese package and developer mirrors and the China apps. See [China](52-china.md).
|
||||
|
||||
### Installing for another owner
|
||||
|
||||
If you're setting up a machine for someone else — a family member, a new employee, a buyer — you shouldn't be answering the personal questions on their behalf. Hit `Ctrl + C` on the very first screen of the installer (the language and keyboard selection), and Omarchy will offer to prepare the machine for another owner instead. The system installs right away, but all the personal setup — keyboard layout, username, password — is deferred until the machine boots for the first time. The drive is still encrypted by default, and the password the new owner picks on that first boot becomes the encryption password too. (A machine you've already been using can be handed over without a reinstall too — see [resetting the computer](48-security.md).)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# China
|
||||
|
||||
Omarchy works in mainland China without a VPN. Two installer answers set a machine up for China, and each stands on its own:
|
||||
|
||||
- **Language.** The installer's first screen asks for English or Simplified Chinese. Chinese puts the installer, the system language (`LANG=zh_CN.UTF-8`), the menus and the desktop shell in Chinese.
|
||||
- **Region.** The timezone you pick decides the region. `Asia/Shanghai`, `Asia/Urumqi` and their older names select China; any other timezone is Global. The summary screen shows _Region: cn_ before anything touches the disk.
|
||||
|
||||
A Chinese speaker abroad keeps the global mirrors, and an English install in Shanghai still gets the China mirrors.
|
||||
|
||||
## What the China region sets up
|
||||
|
||||
| Area | China | Global |
|
||||
|------|-------|--------|
|
||||
| Arch packages | The channel's `.omarchy.cn` mirror first, its `.omarchy.org` mirror behind it, and the ArchLinuxCN repository with its keyring | `.omarchy.org` mirrors |
|
||||
| Omarchy packages | `pkgs.omarchy.org` | `pkgs.omarchy.org` |
|
||||
| Developer mirrors | npm, PyPI and uv, Go, rustup and crates.io, RubyGems and Bundler, Maven | Upstream registries |
|
||||
| Software | _Install > China Apps_ | Hidden |
|
||||
|
||||
pacman tries the `.cn` mirror first. If it is unreachable or lacks a package, pacman falls through to `.org`, which works for anyone with their own VPN. The installer carries the China packages on the ISO, so a China install needs no network.
|
||||
|
||||
## Chinese input
|
||||
|
||||
After choosing Chinese, the installer's keyboard screen opens on _Chinese (Simplified, Pinyin)_; keep it and Fcitx5 Pinyin is ready at first login. Press `Super + I` to switch to Pinyin, and tap `Shift` to step between Chinese and Latin letters. Add or change an input method later under _Setup > Typing > Input Methods_. See [Keyboard, Mouse, Trackpad](34-keyboard-mouse-trackpad.md).
|
||||
|
||||
Chinese text renders in the right Noto CJK variant everywhere: Simplified for Chinese, Traditional for Taiwan and Hong Kong, Japanese and Korean for those languages.
|
||||
|
||||
## Developer mirrors
|
||||
|
||||
China machines point the common package managers at mirrors inside China. Environment variables in `/etc/environment.d/90-omarchy-china.conf` cover npm, PyPI and uv, Go, rustup, Bundler and Maven; they reach everything started from the desktop after your next login (not SSH or TTY shells), and they take precedence over npm, pip, uv and Go config files, so unset the variable in a shell to opt out. `/.cargo/config.toml` points crates.io at the mirror unless a project's `.cargo/config.toml` says otherwise, and `/etc/gemrc` sets the RubyGems source unless your `~/.gemrc` sets another. Omarchy never replaces one of those two files if it is a regular file you created. Toolchain downloads by mise, such as Node.js or Go themselves, still come from upstream.
|
||||
|
||||
## China Apps
|
||||
|
||||
_Install > China Apps_ installs WeChat, QQ, Feishu, DingTalk, Tencent Meeting and WPS Office from the AUR, and sets up Tencent Docs, Yuque and Shimo as web apps.
|
||||
|
||||
## Switching later
|
||||
|
||||
_Setup > Region_, or `omarchy region cn` and `omarchy region global`, switches the region. China runs a full system update, trusts the ArchLinuxCN keyring, records the region, moves pacman to the `.cn` mirrors and links the developer mirrors. Global moves pacman back to `.org`, removes the developer mirrors and hides _Install > China Apps_; apps you installed from it stay. The developer mirrors change at the next login, so the switch offers to log out.
|
||||
|
||||
_Setup > Language_, or `omarchy language zh` and `omarchy language en`, switches the system language. The menus and the desktop shell follow at the next login, so it offers to log out too.
|
||||
|
||||
Neither switch touches the other, the input methods or the CJK fonts. `omarchy update` never changes the region or the language.
|
||||
|
||||
A machine prepared for another owner keeps the language picked in the installer but installs Global, since its timezone is only set on first boot. Switch it under _Setup > Region_ once it is online.
|
||||
@@ -0,0 +1,141 @@
|
||||
# OmarchyCN task list
|
||||
|
||||
Progress source of truth for the `omarchycn` branches of `omarchy`, `omarchy-iso` and `omarchy-pkgs`. Work top to bottom, one item at a time. Each item closes only after a codex review (GPT 6.1 Sol, xhigh, in a herdr split) passes; at most 5 reviews per item.
|
||||
|
||||
## Decisions (confirmed with the owner, 2026-10-05)
|
||||
|
||||
- Region is machine state in `/etc/omarchy/region` (`china`); absent means Global.
|
||||
- Arch and Omarchy repos follow the domain plan: every `*.omarchy.org` server gets a `*.omarchy.cn` twin listed first. `pkgs.omarchy.cn` is live; `stable-mirror`, `rc-mirror` and `mirror.omarchy.cn` do not resolve yet, so pacman falls through to `.org`.
|
||||
- China means a Chinese UI: `LANG=zh_CN.UTF-8` and Chinese menu labels. Global means `en_US.UTF-8`. `omarchy update` never touches the locale.
|
||||
- Input method: rime-ice-installer (omarchy-pkgs PR 265, not merged yet). The ISO bundles the package, the Rime Ice `full.zip` and the Wanxiang grammar model; the install finishes with Rime and US keyboard ready, Ctrl+Space toggles. The installer gets a local patch: `--asset-dir` and no sudo when its packages are present.
|
||||
- China to Global: switch sources and language, keep CJK fonts and the input method, remove the Claude Code third-party provider entry and the env it wrote, hide the China software entry. Dim and Kimi stay for every region.
|
||||
- Deferred provisioning and factory reset get the region's system setup but no offline input method (deferred).
|
||||
|
||||
## Decisions (owner and maintainers, 2026-10-08)
|
||||
|
||||
These replace the 2026-10-05 decisions where they disagree.
|
||||
|
||||
- Region and language are separate. Region comes from the timezone the user picks (omarchy #13690, omarchy-iso #241); the installer adds no region screen. Language is the first installer screen.
|
||||
- The `.cn` pacman mirrors are #13690's `mirrorlist-<channel>.prepend` files. Our `servers.sed` twins and the USTC Arch fallback go; `[omarchy]` keeps only `pkgs.omarchy.org`, as in #13690.
|
||||
- Input methods come from dhh's #14433 and omarchy-iso #237. Rime, `rime-ice-installer` and its bundled assets go.
|
||||
- The language picker offers English and Simplified Chinese. Chinese reruns the installer in cage + foot with fonts unpacked from the offline repository's `noto-fonts-cjk`; the target gets `LANG=zh_CN.UTF-8`.
|
||||
- Setup > Region switches mirrors, developer mirrors, the China apps entry and the Claude Code API entry. Setup > Language switches `LANG` and the Chinese UI. Neither touches the other.
|
||||
|
||||
## Decisions (owner, 2026-10-11, after the maintainers tested the ISO)
|
||||
|
||||
- Drop _Setup > Claude Code API_ (`omarchy-setup-claude-api`) and the Dim/Kimi agent roster and migration. `omarchy region global` no longer touches Claude settings.
|
||||
- Drop the ISO's local-build pin (`pin_local_packages`, `IgnorePkg` and its hooks).
|
||||
- Drop the `omarchy-pkg-add` change; it served Rime.
|
||||
- PRs carry only our changes and never overlap Yunjie Dai's (#13690, iso #241) or David's (#14433, iso #237). Region work stacks on #13690; ISO work stacks on iso #241.
|
||||
|
||||
## Shipping prerequisites
|
||||
|
||||
- The region profile and timezone mapping wait on omarchy #13690 and omarchy-iso #241; input methods (omarchy #14433, omarchy-iso #237) are merged. The language screen, CJK console, Chinese UI, developer mirrors and China apps exist only on the `omarchycn` branches: build a test ISO with `./bin/omarchy-iso-make --local-source ../omarchy ../omarchy-pkgs` from `omarchy-iso/`.
|
||||
- A local-source ISO builds `omarchy-dev` and `omarchy-settings-dev` under the published version number and does not pin them, so the next published release replaces them on `omarchy update`. Local test ISOs are for testing only.
|
||||
- `stable-mirror.omarchy.cn`, `rc-mirror.omarchy.cn` and `mirror.omarchy.cn` resolve and serve `core.db` (checked 2026-10-09).
|
||||
|
||||
## PR branches (T32, 2026-10-12)
|
||||
|
||||
omarchy, each a fork branch:
|
||||
|
||||
- `cjk-variant-selection` on upstream `quattro`: the Noto CJK variant fontconfig.
|
||||
- `chinese-language` on upstream `quattro`: `omarchy language`, Setup > Language, the installer's language prompt, the Chinese menu overlay, shell and notification text.
|
||||
- `cn-developer-mirrors` on #13690: the `cn` developer mirrors at install.
|
||||
- `cn-region-switch` on `cn-developer-mirrors`: `omarchy region`, Setup > Region, China Apps, `manual/52-china.md`; English only.
|
||||
|
||||
omarchy-iso: `chinese-installer` on iso #241 (the language screen, the CJK console, the translated installer, the target locale, the summary's keyboard label); `omarchycn` merges it onto iso `quattro`. omarchy-pkgs: `omarchycn` is upstream `master`.
|
||||
|
||||
omarchy `omarchycn` merges them onto `quattro` and adds what no PR can carry yet: the Chinese wording for Region and China Apps and the language half of the China manual (needs both language and region), and, until #13690 rebases onto upstream's ARM templates, an integration-only pacman resolution and a guard that refuses `omarchy region cn` off x86. Known failure there: `pacman-templates-test` expects x86 refresh to copy templates directly, which #13690 replaces with a render.
|
||||
|
||||
## Tasks
|
||||
|
||||
- [x] T0 Branches and this list
|
||||
- [x] T1 China package sources (PRD 1): region state, `omarchy-region` reader, `default/region/china/pacman.sed`, applied by `omarchy-refresh-pacman` and `install/post-install/pacman.sh`; tests
|
||||
- [x] T2 Region choice in the installer (PRD 2): `omarchy_prompt_region` in `setup-form.sh`, ISO configurator screen, orchestrator passes `OMARCHY_REGION`, `install/config/region.sh` records it
|
||||
- [x] T3 Input method (PRD 3): rime-ice-installer patch and PKGBUILD in omarchy-pkgs, IME packages in `omarchy-other.packages`, install-time package add, user step runs the installer from bundled assets, ISO build bundles assets and the package
|
||||
- [x] T4 CJK fontconfig fix (PRD 4): rebuild PR 9693 on the current base
|
||||
- [x] T5 AI (PRD 5): Dim and Kimi Code for every region (rebuild PR 9694), Claude Code third-party provider setup shown only in China
|
||||
- [x] T6 Developer mirrors (PRD 6): npm, PyPI/uv, Go, rustup, crates.io, RubyGems, Maven via system drop-ins under `default/region/china/`
|
||||
- [x] T7 China software (PRD 7): Install menu entry shown only in China
|
||||
- [x] T8 Region switching and Chinese UI (PRD 8): `omarchy-region china|global`, Setup > Region, locale, Chinese menu overlay chosen by `LANG`
|
||||
- [x] T9 Logo (PRD 9): stock Omarchy logo everywhere
|
||||
- [x] T10 Docs: `AGENTS.md`, manual
|
||||
- [x] T11 Test ISO build (local source)
|
||||
- [x] T12 QEMU install and verification: China and Global installs, region switch, `omarchy update` keeps region and language
|
||||
- [x] T13 Acceptance checklist
|
||||
- [x] T14 Chinese installer (owner request, parity with the old OmarchyCN ISO): choosing China on the VT reruns the installer in cage + foot with CJK fonts and a US keyboard, falling back to the English VT; there the region screen is bilingual and Global returns to the VT; every later installer screen, the shared setup form and the install dashboard read in Chinese
|
||||
- [x] T15 Global to China fails on local-source installs (owner report): `rime-ice-installer` lives only in the ISO's offline mirror, so a Global install cannot fetch it later; local-source installs install every pinned local build, and `install/config/region.sh` installs the China packages before it writes any region state
|
||||
- [x] T16 A region switch offers to log out at once, since the language and the developer mirrors only reach a new session (owner request)
|
||||
- [x] T17 The desktop shell reads in Chinese under `zh_CN` (owner report): notifications, the bar panels (agents, network, display and the rest), the menu's search prompt — review cap reached: five codex rounds; the round-5 findings (user text translated at display, custom reminder list lines, reminder tooltips and errors) are fixed by wording every notification at its sender and verified by tests and in the China VM, without a sixth review
|
||||
- [x] T18 Merge the official region-profile PRs (omarchy #13690, #14399; omarchy-iso #206, #236) and align to them: region code `cn` in `/etc/omarchy/region`, China files under `default/regions/cn/`, pacman rendered by `omarchy-apply-pacman` (`.cn` twins first, USTC fallback, ArchLinuxCN with its keyring), offline timezone `Asia/Shanghai`; the ISO keeps the first-screen region choice and bundles the China profile, `--region cn` preselects China
|
||||
|
||||
- [x] T19 Merge the reworked #13690 (timezone-derived region) and align: `mirrorlist-<channel>.prepend` and `timezones` replace `servers.sed`, `mirrorlist.append` and `timezone`; drop #14399's timezone preselection (closed upstream); `install/config/region.sh` reads `/etc/omarchy/region` instead of `OMARCHY_REGION`; drop the dead `china` rename migration
|
||||
- [x] T20 Merge #14433 (input methods) and remove Rime: packages, `install/user/region.sh`, the Rime steps in `install/config/region.sh` and `omarchy-region`
|
||||
- [x] T21 Split language from region: `omarchy-language en|zh` and Setup > Language own `LANG`; `omarchy-region` and `install/config/region.sh` stop touching the locale
|
||||
- [x] T22 omarchy-pkgs: revert the `rime-ice-installer` package
|
||||
- [x] T23 omarchy-iso: merge upstream `quattro` (#237) and #241; remove `--region`, `prepare-region.sh`, the region skel, the region preselected timezone, Rime bundling and its pin
|
||||
- [x] T24 omarchy-iso: the language screen replaces the region screen; Chinese reruns in cage + foot with `noto-fonts-cjk` unpacked from the offline repository (no font packages added to the ISO); the CJK console lists only US-keymap keyboards and preselects Chinese (Simplified, Pinyin); the target gets `zh_CN.UTF-8` through `locale_config`; the harness takes `--language zh`
|
||||
- [x] T25 Docs: `AGENTS.md`, `docs/regions.md`, `manual/52-china.md`, omarchy-iso README
|
||||
- [x] T26 Build the local-source ISO and test in QEMU: Chinese + `Asia/Shanghai`, English + `America/New_York`, runtime region and language switches
|
||||
- [x] T27 Acceptance checklist for T19–T26
|
||||
|
||||
- [x] T28 Drop the Claude Code API setup, the Dim/Kimi roster and migration, and the `omarchy-pkg-add` change (omarchy)
|
||||
- [x] T29 Drop `pin_local_packages` and the pin list (omarchy-iso)
|
||||
- [x] T30 The CJK console skips the second language screen; the summary's keyboard row shows the chosen layout, not its keymap (omarchy-iso)
|
||||
- [x] T31 The CJK console fills the screen and the summary keeps the logo in view (omarchy-iso)
|
||||
- [x] T32 Rebuild omarchy as PR branches on current upstream: CJK fontconfig, language and menus, shell text, cn developer mirrors (on #13690), Setup > Region and China Apps; `omarchycn` becomes their merge
|
||||
- [x] T33 Rebuild omarchy-iso as one PR branch on iso #241 and current upstream; `omarchycn` becomes it; omarchy-pkgs follows upstream master
|
||||
- [x] T34 Build the local-source ISO and test in QEMU: Chinese offline, English
|
||||
- [x] T35 Acceptance checklist for T28–T34
|
||||
- [x] T36 Open the PRs one at a time from Raiden-MeixElysia
|
||||
|
||||
## Open PRs (T36, 2026-10-12, from Raiden-MeixElysia)
|
||||
|
||||
- omacom/omarchy#15116 `cjk-variant-selection`: Noto CJK variant fontconfig. Mergeable.
|
||||
- omacom/omarchy#15117 `chinese-language`: Simplified Chinese as a system language. Mergeable; rebased onto #15091, which removed USB and Thunderbolt approval and with it their Chinese text.
|
||||
- omacom/omarchy-iso#263 `chinese-installer`: language first, CJK console, Chinese installer. Stacked on iso #241, upstream merged in; needs #15117.
|
||||
- omacom/omarchy#15118 `cn-developer-mirrors`: stacked on #13690; shows #13690's conflicts until it rebases.
|
||||
- omacom/omarchy#15119 `cn-region-switch`: stacked on #15118.
|
||||
- Not yet opened: the Chinese wording for Region and China Apps (commit in `omarchycn`), once #15117 and #15119 land.
|
||||
|
||||
## Acceptance checklist for T28–T34 (maintainers' ISO test, 2026-10-11)
|
||||
|
||||
| # | Request | Result | Evidence |
|
||||
|---|---|---|---|
|
||||
| 1 | Drop `pin_local_packages` and its hooks | Pass | omarchy-iso 2a589e9; installed `pacman.conf` has no `IgnorePkg`, `/etc/pacman.d/hooks` holds only upstream hooks (30-state). |
|
||||
| 2 | Drop `omarchy-setup-claude-api` and the Dim/Kimi roster and migration | Pass | omarchy f2af41bb; nothing of them in any PR branch; `omarchy region global` touches no Claude settings. |
|
||||
| 3 | No second language screen in the CJK console | Pass | omarchy-iso 2c523af; cage + foot opens on the keyboard screen (04-keyboard). |
|
||||
| 4 | No black strip; logo whole on the summary; keyboard row shows Pinyin | Pass | omarchy-iso 411acd6 (`[csd] preferred=none`, `line-height=22px`) and 2c523af (`keyboard_label`); 04-keyboard, 14-summary. |
|
||||
| 5 | PRs carry only our changes, stacked on #13690; ISO bundles David's #14433 | Pass | T32/T33 branches; #14433 and iso #237 come from upstream `quattro`; region branches stack on #13690, the ISO branch on iso #241. |
|
||||
| - | Rebuilt ISO, Chinese offline and English | Pass | T34 evidence below. |
|
||||
| - | Automated tests | No new failures | Per-branch shell tests pass on their bases; `fontconfig-cjk-test` needs the installed font set and passes in full on the T34 VM (48-fontconfig-test); the integration suite fails only where upstream fails, plus `pacman-templates-test` until #13690 rebases; ISO unit tests fail only `test_keyboard` (no `localectl`), as upstream. |
|
||||
|
||||
Open: `pacman-templates-test` and the ARM guard wait on #13690's rebase; the Chinese wording for Region and China Apps waits on the language and region PRs; the upstream menu acceptance test still walks Style > Menu Bar.
|
||||
|
||||
## T34 evidence (2026-10-12)
|
||||
|
||||
ISO `release/omarchy-2026.10.12-x86_64-local.iso` from omarchy `omarchycn`, omarchy-iso `omarchycn` (c407101), omarchy-pkgs upstream master (d47199f2). Screenshots in `~/omarchy-vm/shots/`.
|
||||
|
||||
- Chinese, offline (`OFFLINE=on`): the VT language screen, then cage + foot opens straight on the keyboard screen with Pinyin preselected and no strip (04-keyboard); the summary keeps the whole logo and shows `键盘 Chinese (Simplified, Pinyin)` (14-summary); installed in 4m 0s (18-progress). First boot: Chinese desktop and notifications (21-desktop); `LANG=zh_CN.UTF-8`, region `cn`, `mirror.omarchy.cn` first, `[archlinuxcn]`, no `IgnorePkg` or pin hooks, developer mirror links and `NPM_CONFIG_REGISTRY`/`GOPROXY` in the session, Fcitx5 profile with Pinyin (30-state); `Super + I`, `nihao`, Space typed 你好 (31-pinyin); Setup shows 语言 and 地区 (34-setup); `fc-match` gives SC for untagged Han under zh_CN and TC, HK, JP, KR for tagged text, Liberation Sans for tagged Latin (36-fonts).
|
||||
- Runtime, online: `omarchy region global` → Global, no marker, `.org` mirror, no ArchLinuxCN, links gone, `LANG` kept, Chinese logout prompt (42, 43); `omarchy language en` → `en_US.UTF-8`, English logout prompt (44); `omarchy region cn` → China, `.cn` first, ArchLinuxCN, links back, `LANG` kept (45, 46); `omarchy language zh` → `zh_CN.UTF-8` (47). The `.cn` edge mirror lagged `.org` by one sqlite release, which the cn refresh downgraded.
|
||||
- English, harness: install and first boot pass; four acceptance failures, all upstream: Mozc Qt, weather OCR, `omarchy-bore` vs `linux-omarchy`, and the menu test still walking Style > Menu Bar, which upstream moved to Trigger > Appearance.
|
||||
|
||||
## Acceptance checklist
|
||||
|
||||
Evidence comes from the T26 runs of `omarchy-iso/release/omarchy-2026.10.09-x86_64-local.iso` (omarchy fcd681c0, omarchy-iso 30f63b6, omarchy-pkgs a4cc3234): run A in host QEMU with the guest cut off from the network during install, run B in the ISO test harness.
|
||||
|
||||
| # | Requirement (discussion of 2026-10-08) | Result | Evidence |
|
||||
|---|---|---|---|
|
||||
| 1 | Language picker on the first screen; Chinese gets cage + foot | Pass | Run A: the VT screen offers English and Chinese; Chinese reruns in cage + foot on a bilingual language screen, and every later screen and the install dashboard read in Chinese. |
|
||||
| 2 | CJK font from the offline repository, not added to the ISO | Pass | `unpack_cjk_font` extracts `NotoSansCJK-Regular.ttc` from the offline mirror's `noto-fonts-cjk`; the ISO package list drops `ttf-jetbrains-mono` and `wqy-microhei` and keeps only `cage` and `foot`. |
|
||||
| 3 | Translated installer strings and the target locale | Pass | Run A: summary, finish screen and dashboard in Chinese; installed `LANG=zh_CN.UTF-8` through `locale_config`. Run B: English throughout, `LANG=en_US.UTF-8`. |
|
||||
| 4 | Region from the timezone, no extra step (#13690, #241) | Pass | Run A: `Asia/Shanghai` shows 区域 cn on the summary and writes `/etc/omarchy/region` = `cn`. Run B: `America/New_York` installs Global with no marker. |
|
||||
| 5 | Offline install ready without network | Pass | Run A installed in 8m13s with the guest network cut off; ArchLinuxCN keyring trusted, `mirror.omarchy.cn` first, Pinyin ready at first login. |
|
||||
| 6 | `.cn` pacman mirrors from #13690; ours dropped | Pass | China: `mirror.omarchy.cn` before `mirror.omarchy.org`, `[archlinuxcn]` from USTC. Global: mirrorlist equals the channel template; `pacman.conf` differs only by the local-build `IgnorePkg` pin. |
|
||||
| 7 | Developer mirrors on the cn profile | Pass | China links `/etc/environment.d/90-omarchy-china.conf`, `/.cargo/config.toml`, `/etc/gemrc`; Global removes them and `NPM_CONFIG_REGISTRY` is gone after relogin. |
|
||||
| 8 | Input methods from dhh's #14433; Rime removed | Pass | Chinese preselects Chinese (Simplified, Pinyin); `INPUT_METHOD=pinyin`; `Ctrl + Shift + Space` then `nihao` typed 你好. No Rime package, asset or pin remains. |
|
||||
| 9 | Region and language switch separately at runtime | Pass | `omarchy region global` and `cn` leave `LANG` alone; `omarchy language en` and `zh` leave the region alone; each offers logout in the right language; Claude Code API and China Apps follow the region. |
|
||||
| 10 | `omarchy update` keeps region and language | Pass | After `omarchy update -y`: `LANG=zh_CN.UTF-8`, region `cn`, `mirror.omarchy.cn` first, `[archlinuxcn]` present. |
|
||||
| - | Automated tests | Pass, no new regressions | omarchy shell suite in an Arch container: every failure is environmental and predates these tasks; some sit in branch-only tests (`fontconfig-cjk-test` needs the Omarchy font set, `hyprland-jis-bindings-test` needs `lua`, `menu-plugin-test` and `tailscale-receive-test` chmod a symlink on the read-only mount). omarchy-iso `./test/all` passes apart from `test_keyboard` (needs systemd's `localectl`). Harness acceptance: four failures outside this branch (Mozc Qt; weather and reminder prompts on screen but missed by OCR; `linux-omarchy` vs `omarchy-bore` kernel). |
|
||||
|
||||
Open items outside this branch: the shipping prerequisites above; the four acceptance failures listed; `.automated_script.sh` exits 1 when its dashboard returns without rebooting after the mirror warm-up has finished, from upstream's `EXIT` trap under `set -e`; each `omarchy region` switch asks for the sudo password several times through refresh's command-scoped `sudo` calls.
|
||||
@@ -634,6 +634,8 @@ var table = {
|
||||
"Later": "稍后",
|
||||
"Log out now to switch the language?": "立即注销以切换语言?",
|
||||
"Log back in to switch the language.": "重新登录后切换语言。",
|
||||
"Log out now to switch the developer mirrors?": "立即注销以切换开发工具镜像?",
|
||||
"Log back in to switch the developer mirrors.": "重新登录后切换开发工具镜像。",
|
||||
"Superwhisper Dictation Ready": "Superwhisper 语音输入就绪",
|
||||
"Hold Right Alt or F9 to dictate (or toggle with Super + Ctrl + X).": "按住右 Alt 或 F9 说话(或用 Super + Ctrl + X 切换)。",
|
||||
"Background Intros": "背景开场动画",
|
||||
|
||||
@@ -422,7 +422,8 @@ var GUARD_READERS = [
|
||||
"omarchy-hyprland-corners",
|
||||
"omarchy-hyprland-window-transparency",
|
||||
"omarchy-hyprland-workspace-layout-default",
|
||||
"omarchy-language"
|
||||
"omarchy-language",
|
||||
"omarchy-region"
|
||||
]
|
||||
|
||||
// Package and command presence account for most of what the guards ask, and
|
||||
|
||||
@@ -6,6 +6,7 @@ source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
source "$SHELL_TEST_DIR/fixtures/sudo-boundary-test.sh"
|
||||
copy_boundary_file bin/omarchy-channel-set
|
||||
copy_boundary_file bin/omarchy-refresh-pacman
|
||||
copy_boundary_file bin/omarchy-apply-pacman
|
||||
copy_boundary_file bin/omarchy-update
|
||||
export OMARCHY_UPDATE_LOGGED=1
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ run_channel() {
|
||||
: >"$log_file"
|
||||
OMARCHY_CHANNEL_TEST_LOG="$log_file" \
|
||||
OMARCHY_PATH="${OMARCHY_TEST_PATH:-$package_root}" \
|
||||
OMARCHY_REGION_FILE="${OMARCHY_TEST_REGION_FILE:-$test_tmp/no-region}" \
|
||||
HOME="$test_tmp/home" \
|
||||
PATH="$stub_bin:$ROOT/bin:$PATH" \
|
||||
"${OMARCHY_TEST_PATH:-$package_root}/bin/omarchy-channel-set" "$@"
|
||||
@@ -190,6 +191,58 @@ fi
|
||||
assert_log_line $'link\t'"$checkout"$'\t--no-reboot' "switching back to dev links ~/omarchy"
|
||||
pass "switching back to dev reuses the existing ~/omarchy checkout"
|
||||
|
||||
printf 'cn\n' >"$test_tmp/region"
|
||||
if OMARCHY_TEST_REGION_FILE="$test_tmp/region" run_channel dev >"$test_tmp/region.out" 2>"$test_tmp/region.err"; then
|
||||
fail "dev refuses a checkout without this machine's region"
|
||||
fi
|
||||
grep -q "does not support the 'cn' region" "$test_tmp/region.err" || fail "dev explains the missing region" "$(cat "$test_tmp/region.err")"
|
||||
if grep -Eq $'^(link|refresh)\t' "$log_file"; then
|
||||
fail "dev refuses before linking or refreshing pacman" "$(cat "$log_file")"
|
||||
fi
|
||||
pass "dev refuses a checkout without the region before touching pacman"
|
||||
|
||||
# A directory with the region's name, or a helper that cannot run, is not
|
||||
# support: the refresh after linking would fail with dev already linked.
|
||||
mkdir -p "$checkout/default/regions/cn"
|
||||
touch "$checkout/bin/omarchy-apply-pacman"
|
||||
chmod +x "$checkout/bin/omarchy-apply-pacman"
|
||||
if OMARCHY_TEST_REGION_FILE="$test_tmp/region" run_channel dev >"$test_tmp/region.out" 2>"$test_tmp/region.err"; then
|
||||
fail "dev refuses an empty region profile"
|
||||
fi
|
||||
if grep -Eq $'^(link|refresh)\t' "$log_file"; then
|
||||
fail "dev refuses an empty region profile before linking or refreshing pacman" "$(cat "$log_file")"
|
||||
fi
|
||||
mkdir -p "$checkout/default/regions/cn/pacman"
|
||||
cn_fragments=(pacman.conf.append mirrorlist-stable.prepend mirrorlist-rc.prepend mirrorlist-edge.prepend)
|
||||
for missing in "${cn_fragments[@]}"; do
|
||||
for fragment in "${cn_fragments[@]}"; do
|
||||
if [[ $fragment == "$missing" ]]; then
|
||||
rm -f "$checkout/default/regions/cn/pacman/$fragment"
|
||||
else
|
||||
touch "$checkout/default/regions/cn/pacman/$fragment"
|
||||
fi
|
||||
done
|
||||
if OMARCHY_TEST_REGION_FILE="$test_tmp/region" run_channel dev >"$test_tmp/region.out" 2>"$test_tmp/region.err"; then
|
||||
fail "dev refuses a region profile without $missing"
|
||||
fi
|
||||
if grep -Eq $'^(link|refresh)\t' "$log_file"; then
|
||||
fail "dev refuses a profile without $missing before linking or refreshing pacman" "$(cat "$log_file")"
|
||||
fi
|
||||
done
|
||||
touch "${cn_fragments[@]/#/$checkout/default/regions/cn/pacman/}"
|
||||
chmod -x "$checkout/bin/omarchy-apply-pacman"
|
||||
if OMARCHY_TEST_REGION_FILE="$test_tmp/region" run_channel dev >"$test_tmp/region.out" 2>"$test_tmp/region.err"; then
|
||||
fail "dev refuses a checkout whose pacman helper cannot run"
|
||||
fi
|
||||
if grep -Eq $'^(link|refresh)\t' "$log_file"; then
|
||||
fail "dev refuses incomplete region support before linking or refreshing pacman" "$(cat "$log_file")"
|
||||
fi
|
||||
pass "dev refuses a region profile missing any fragment or a helper that cannot run"
|
||||
|
||||
chmod +x "$checkout/bin/omarchy-apply-pacman"
|
||||
OMARCHY_TEST_REGION_FILE="$test_tmp/region" run_channel dev
|
||||
assert_log_line $'refresh\tedge' "dev proceeds with a checkout that supports the region"
|
||||
|
||||
current_channel() {
|
||||
OMARCHY_TEST_VERSION_CHANNEL="$1" \
|
||||
OMARCHY_TEST_PACKAGES="$2" \
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
require_command node
|
||||
|
||||
node - "$ROOT" <<'JS'
|
||||
const fs = require('fs')
|
||||
const root = process.argv[2]
|
||||
const model = require(`${root}/shell/plugins/menu/MenuModel.js`)
|
||||
const items = model.parseMenuJsonc(fs.readFileSync(`${root}/default/omarchy/omarchy-menu.jsonc`, 'utf8'))
|
||||
const china = items.filter(item => item.id === 'install.china' || item.id.startsWith('install.china.'))
|
||||
const guard = '[[ "$(omarchy-region)" == "China" ]]'
|
||||
const fail = message => { console.error(`not ok - ${message}`); process.exit(1) }
|
||||
|
||||
const aur = {
|
||||
wechat: 'wechat-universal-bwrap', qq: 'linuxqq', feishu: 'feishu-bin',
|
||||
dingtalk: 'dingtalk-bin', wemeet: 'wemeet-bin', wps: 'wps-office-cn',
|
||||
}
|
||||
const web = {
|
||||
'tencent-docs': ['腾讯文档', 'https://docs.qq.com'],
|
||||
yuque: ['语雀', 'https://www.yuque.com/dashboard'],
|
||||
shimo: ['石墨文档', 'https://shimo.im/desktop'],
|
||||
}
|
||||
|
||||
if (china.length !== 1 + Object.keys(aur).length + Object.keys(web).length) fail(`China Apps has exactly the expected entries: ${china.map(i => i.id)}`)
|
||||
if (!china.every(item => item.when === guard)) fail('every China Apps entry, including search hits, shows only in China')
|
||||
for (const [id, pkg] of Object.entries(aur)) {
|
||||
const item = china.find(i => i.id === `install.china.${id}`)
|
||||
if (!item || item.action !== `omarchy-launch-floating-terminal-with-presentation 'omarchy-pkg-aur-add ${pkg}'` || item.disabled !== `omarchy-pkg-present ${pkg}`)
|
||||
fail(`${id} installs and checks the AUR package ${pkg}`)
|
||||
}
|
||||
for (const [id, [name, url]] of Object.entries(web)) {
|
||||
const item = china.find(i => i.id === `install.china.${id}`)
|
||||
if (!item || item.action !== `omarchy-launch-floating-terminal-with-presentation 'omarchy-webapp-install ${name} ${url} ""'` || item.disabled !== `[[ -f "$HOME/.local/share/applications/${name}.desktop" ]]`)
|
||||
fail(`${id} installs and checks the ${name} web app`)
|
||||
}
|
||||
console.log('ok - China Apps lists the AUR packages and web apps, shown only in China')
|
||||
JS
|
||||
|
||||
aur_names="wechat-universal-bwrap linuxqq feishu-bin dingtalk-bin wemeet-bin wps-office-cn"
|
||||
if [[ ${OMARCHY_TEST_NETWORK:-} == "1" ]]; then
|
||||
query=$(printf '&arg[]=%s' $aur_names)
|
||||
found=$(curl -fsSL "https://aur.archlinux.org/rpc/v5/info?${query#&}" | jq -r '.results[].Name' | sort | tr '\n' ' ')
|
||||
[[ $found == "$(printf '%s\n' $aur_names | sort | tr '\n' ' ')" ]] || fail "every China Apps package exists in the AUR" "$found"
|
||||
pass "every China Apps package exists in the AUR"
|
||||
else
|
||||
skip "AUR existence check needs OMARCHY_TEST_NETWORK=1"
|
||||
fi
|
||||
|
||||
test_tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$test_tmp"' EXIT
|
||||
mkdir -p "$test_tmp/bin" "$test_tmp/home"
|
||||
cat >"$test_tmp/bin/curl" <<'SH'
|
||||
#!/bin/bash
|
||||
while (($#)); do [[ $1 == "-o" ]] && out=$2; shift; done
|
||||
[[ -n ${out:-} ]] && printf '\x89PNG\r\n\x1a\n\0\0\0\rIHDR\0\0\0\x01\0\0\0\x01\x08\x06\0\0\0\x1f\x15\xc4\x89' >"$out"
|
||||
exit 0
|
||||
SH
|
||||
printf '#!/bin/bash\necho "the web app wizard opened" >&2\nexit 1\n' >"$test_tmp/bin/gum"
|
||||
chmod +x "$test_tmp/bin/curl" "$test_tmp/bin/gum"
|
||||
|
||||
grep -o "omarchy-launch-floating-terminal-with-presentation 'omarchy-webapp-install [^']*'" "$ROOT/default/omarchy/omarchy-menu.jsonc" |
|
||||
sed -e "s/^[^']*'//" -e "s/'$//" -e 's/\\"/"/g' >"$test_tmp/actions"
|
||||
(($(wc -l <"$test_tmp/actions") == 3)) || fail "the menu has three China web app actions"
|
||||
while read -r action; do
|
||||
HOME="$test_tmp/home" PATH="$test_tmp/bin:$ROOT/bin:$PATH" bash -c "$action" >/dev/null ||
|
||||
fail "the menu action installs its web app: $action"
|
||||
done <"$test_tmp/actions"
|
||||
for app in 腾讯文档 语雀 石墨文档; do
|
||||
[[ -f $test_tmp/home/.local/share/applications/$app.desktop ]] || fail "$app gets a launcher"
|
||||
done
|
||||
pass "each China web app action creates its launcher without the wizard"
|
||||
@@ -9,7 +9,11 @@ export SUDO_TEST_LOG="$boundary_tmp/events"
|
||||
export SUDO_TEST_CACHE="$boundary_tmp/cache"
|
||||
export OMARCHY_PATH="$SUDO_TEST_ROOT"
|
||||
export SUDO_TEST_HOME="$boundary_tmp/home"
|
||||
mkdir -p "$SUDO_TEST_HOME"
|
||||
# The machine the render reads its region marker from. It starts global; a test
|
||||
# writes etc/omarchy/region here to model a regional install, never the host's.
|
||||
export SUDO_TEST_TARGET="$boundary_tmp/target"
|
||||
export OMARCHY_REGION_FILE="$SUDO_TEST_TARGET/etc/omarchy/region"
|
||||
mkdir -p "$SUDO_TEST_HOME" "$SUDO_TEST_TARGET/etc"
|
||||
mkdir -p "$SUDO_TEST_ROOT/bin" "$SUDO_TEST_ROOT/mock" "$SUDO_TEST_ROOT/default/omarchy/sudo-no-update"
|
||||
: >"$SUDO_TEST_LOG"
|
||||
|
||||
@@ -24,6 +28,10 @@ s=(source/name).read_text().replace('$HOME', '$SUDO_TEST_HOME')
|
||||
for command in ['sudo','pkexec','pacman','omarchy-pkg-missing','systemd-inhibit','setpriv','snapper']:
|
||||
s=s.replace('/usr/bin/'+command, str(target/'mock'/command))
|
||||
s=s.replace('PATH=/usr/bin:/usr/sbin:/bin:/sbin', 'PATH="'+str(target/'bin')+':/usr/bin:/usr/sbin:/bin:/sbin"')
|
||||
if name.name == 'omarchy-apply-pacman':
|
||||
render_etc='etc="${4:-/}/etc"'
|
||||
assert s.count(render_etc)==1, 'render target lookup moved'
|
||||
s=s.replace(render_etc, 'etc="${4:-$SUDO_TEST_TARGET}/etc"')
|
||||
p.write_text(s)
|
||||
p.chmod((source/name).stat().st_mode & 0o777)
|
||||
PY
|
||||
|
||||
Executable
+133
@@ -0,0 +1,133 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
if ! command -v fc-match >/dev/null || [[ $(fc-list : file) != *NotoSansCJK-Regular.ttc* ]]; then
|
||||
skip "fontconfig CJK variants need fc-match and noto-fonts-cjk"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
test_tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$test_tmp"' EXIT
|
||||
|
||||
candidate="$ROOT/default/fontconfig/conf.avail/50-omarchy.conf"
|
||||
{
|
||||
echo '<?xml version="1.0"?><!DOCTYPE fontconfig SYSTEM "fonts.dtd"><fontconfig>'
|
||||
echo "<dir>/usr/share/fonts</dir><cachedir>$test_tmp/cache</cachedir>"
|
||||
placed=false
|
||||
for conf in /etc/fonts/conf.d/*.conf; do
|
||||
name=${conf##*/}
|
||||
[[ $name == "50-omarchy.conf" ]] && continue
|
||||
if ! $placed && [[ $name > "50-omarchy.conf" ]]; then
|
||||
echo "<include>$candidate</include>"
|
||||
placed=true
|
||||
fi
|
||||
echo "<include>$conf</include>"
|
||||
done
|
||||
$placed || echo "<include>$candidate</include>"
|
||||
echo '</fontconfig>'
|
||||
} >"$test_tmp/fonts.conf"
|
||||
export FONTCONFIG_FILE="$test_tmp/fonts.conf"
|
||||
fc-cache >/dev/null
|
||||
|
||||
# Pango, Qt and Skia add lang as a string, which fc-match never does.
|
||||
cat >"$test_tmp/string-lang.py" <<'PY'
|
||||
import ctypes, sys
|
||||
fc = ctypes.CDLL("libfontconfig.so.1")
|
||||
fc.FcPatternCreate.restype = ctypes.c_void_p
|
||||
fc.FcPatternAddString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
|
||||
fc.FcConfigSubstitute.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int]
|
||||
fc.FcDefaultSubstitute.argtypes = [ctypes.c_void_p]
|
||||
fc.FcPatternFormat.restype = ctypes.c_char_p
|
||||
fc.FcPatternFormat.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
||||
fc.FcCharSetCreate.restype = ctypes.c_void_p
|
||||
fc.FcCharSetAddChar.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
|
||||
fc.FcPatternAddCharSet.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p]
|
||||
fc.FcFontMatch.restype = ctypes.c_void_p
|
||||
fc.FcFontMatch.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int)]
|
||||
for line in sys.stdin:
|
||||
family, lang, char = line.split()
|
||||
pattern = fc.FcPatternCreate()
|
||||
fc.FcPatternAddString(pattern, b"family", family.encode())
|
||||
if lang != "-":
|
||||
fc.FcPatternAddString(pattern, b"lang", lang.encode())
|
||||
if char == "families":
|
||||
fc.FcConfigSubstitute(None, pattern, 0)
|
||||
print(fc.FcPatternFormat(pattern, b"%{family}").decode())
|
||||
continue
|
||||
if char != "-":
|
||||
charset = fc.FcCharSetCreate()
|
||||
fc.FcCharSetAddChar(charset, int(char, 16))
|
||||
fc.FcPatternAddCharSet(pattern, b"charset", charset)
|
||||
fc.FcConfigSubstitute(None, pattern, 0)
|
||||
fc.FcDefaultSubstitute(pattern)
|
||||
result = ctypes.c_int()
|
||||
print(fc.FcPatternFormat(fc.FcFontMatch(None, pattern, ctypes.byref(result)), b"%{family[0]}").decode())
|
||||
PY
|
||||
|
||||
# resolve <locale> <string|langset> reads "family lang char" lines
|
||||
resolve() {
|
||||
if [[ $2 == "string" ]]; then
|
||||
LANG=$1 python3 "$test_tmp/string-lang.py"
|
||||
else
|
||||
local family lang char query
|
||||
while read -r family lang char; do
|
||||
query=$family
|
||||
[[ $lang == "-" ]] || query+=":lang=$lang"
|
||||
[[ $char == "-" ]] || query+=":charset=$char"
|
||||
LANG=$1 fc-match -f '%{family[0]}\n' "$query"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
declare -A latin=([sans-serif]="Liberation Sans" [serif]="Liberation Serif" [monospace]="JetBrainsMono Nerd Font")
|
||||
declare -A cjk=([sans-serif]="Noto Sans CJK" [serif]="Noto Serif CJK" [monospace]="Noto Sans Mono CJK")
|
||||
declare -A variant=(
|
||||
[zh]=SC [zh-cn]=SC [zh-sg]=SC [zh-hans]=SC [zh-hans-cn]=SC [zh-hans-hk]=SC
|
||||
[zh-tw]=TC [zh-hant]=TC [zh-hant-tw]=TC
|
||||
[zh-hk]=HK [zh-mo]=HK [zh-hant-hk]=HK [zh-hant-mo]=HK
|
||||
[yue]=HK [yue-hk]=HK [yue-hant]=HK [yue-hant-hk]=HK [yue-hant-cn]=HK
|
||||
[yue-hans]=SC [yue-hans-cn]=SC [yue-hans-hk]=SC [yue-cn]=SC
|
||||
[ja]=JP [ja-jp]=JP [ko]=KR [ko-kr]=KR
|
||||
)
|
||||
|
||||
queries=() expected=()
|
||||
for tag in "${!variant[@]}"; do
|
||||
for generic in sans-serif serif monospace; do
|
||||
queries+=("$generic $tag -") expected+=("${latin[$generic]}")
|
||||
queries+=("$generic $tag 9aa8") expected+=("${cjk[$generic]} ${variant[$tag]}")
|
||||
done
|
||||
done
|
||||
for generic in sans-serif serif monospace; do
|
||||
queries+=("$generic - -") expected+=("${latin[$generic]}")
|
||||
done
|
||||
|
||||
for locale in C.UTF-8 zh_CN.UTF-8; do
|
||||
for path in string langset; do
|
||||
mapfile -t actual < <(printf '%s\n' "${queries[@]}" | resolve "$locale" "$path")
|
||||
for i in "${!queries[@]}"; do
|
||||
[[ ${actual[i]} == "${expected[i]}" ]] ||
|
||||
fail "$locale $path lang: ${queries[i]} resolves to ${expected[i]}" "${actual[i]}"
|
||||
done
|
||||
done
|
||||
pass "$locale: tagged CJK keeps Omarchy's Latin families and sets Han in the tag's variant"
|
||||
done
|
||||
|
||||
for path in string langset; do
|
||||
for generic in sans-serif serif monospace; do
|
||||
actual=$(echo "$generic - 9aa8" | resolve zh_CN.UTF-8 "$path")
|
||||
[[ $actual == "${cjk[$generic]} SC" ]] || fail "zh_CN $path: untagged Han in $generic is SC" "$actual"
|
||||
done
|
||||
done
|
||||
pass "a Chinese locale sets untagged Han in SC"
|
||||
|
||||
untouched=$(echo "sans-serif en families" | resolve C.UTF-8 string)
|
||||
[[ $(echo "sans-serif ja families" | resolve C.UTF-8 string) != "$untouched" ]] ||
|
||||
fail "lang=ja changes the family list, so the prefix check below can see a match"
|
||||
for tag in jam jav kok koi; do
|
||||
actual=$(echo "sans-serif $tag families" | resolve C.UTF-8 string)
|
||||
[[ $actual == "$untouched" ]] || fail "string lang=$tag is not taken for Japanese or Korean" "$actual"
|
||||
done
|
||||
pass "languages that only share a prefix with ja or ko get no CJK variant"
|
||||
@@ -34,7 +34,7 @@ for (const item of defaults) {
|
||||
if (item.id !== 'system.lock' && zh && zh.declared.includes('label') && result.label !== zh.label) fail(`${item.id} shows its Chinese label`)
|
||||
}
|
||||
if (merged['system.lock'].label !== 'Mine') fail('a user extension still overrides the Chinese label')
|
||||
if (merged['system.reboot'].label !== '重启' || merged['setup.language'].label !== '语言') fail('Chinese labels reach the merged menu')
|
||||
if (merged['system.reboot'].label !== '重启' || merged['setup.language'].label !== '语言' || merged['install.china.wechat'].label !== '微信') fail('Chinese labels reach the merged menu')
|
||||
console.log('ok - Chinese labels replace only the label, and user extensions still win')
|
||||
JS
|
||||
|
||||
|
||||
@@ -304,6 +304,14 @@ assert(
|
||||
&& defaultById['setup.language.zh'].action.includes('omarchy-language zh'),
|
||||
'menu offers English and Simplified Chinese under Setup > Language'
|
||||
)
|
||||
const regionEntries = defaultItems.filter(item => item.parent === 'setup.region')
|
||||
assert(
|
||||
regionEntries.map(item => item.label).join('\0') === 'Global\0China'
|
||||
&& regionEntries.every(item => item.checked.includes(`== \"${item.label}\"`) && !item.when)
|
||||
&& defaultById['setup.region.global'].action.includes('omarchy-region global')
|
||||
&& defaultById['setup.region.china'].action.includes('omarchy-region cn'),
|
||||
'menu offers Global and China under Setup > Region'
|
||||
)
|
||||
// Software you already have keeps its place in Install, dimmed rather than
|
||||
// dropped, so the list reads as a catalog of what Omarchy can install.
|
||||
// Chromium Account is the sole Install row with anything left to hide for. The
|
||||
@@ -314,7 +322,8 @@ const windowsGuard = 'omarchy-hw-x86'
|
||||
const x86OnlyInstalls = ['install.windows', 'install.browser.edge', 'install.service.dropbox', 'install.service.spotify', 'install.gaming.minecraft', 'install.gaming.heroic']
|
||||
assertDeepEqual(
|
||||
defaultItems
|
||||
.filter(item => item.id.startsWith('install.') && item.action && item.when)
|
||||
// China Apps rows hide by region, not because the software is there.
|
||||
.filter(item => item.id.startsWith('install.') && item.action && item.when && item.when !== '[[ "$(omarchy-region)" == "China" ]]')
|
||||
.map(item => item.id)
|
||||
.sort(),
|
||||
[...x86OnlyInstalls, 'install.service.chromium-account'].sort(),
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
scratch=$(mktemp -d)
|
||||
trap 'rm -rf "$scratch"' EXIT
|
||||
export OMARCHY_PATH="$ROOT"
|
||||
export TARGET_ROOT="$scratch/target root"
|
||||
mkdir -p "$TARGET_ROOT/etc/omarchy"
|
||||
|
||||
apply() { "$ROOT/bin/omarchy-apply-pacman" "$1" "$TARGET_ROOT"; }
|
||||
|
||||
for channel in stable rc edge; do
|
||||
apply "$channel"
|
||||
cmp "$ROOT/default/pacman/pacman-$channel.conf" "$TARGET_ROOT/etc/pacman.conf"
|
||||
cmp "$ROOT/default/pacman/mirrorlist-$channel" "$TARGET_ROOT/etc/pacman.d/mirrorlist"
|
||||
done
|
||||
pass "missing region preserves all global channel templates byte for byte"
|
||||
|
||||
chmod 600 "$TARGET_ROOT/etc/pacman.conf"
|
||||
apply stable
|
||||
[[ $(stat -c %a "$TARGET_ROOT/etc/pacman.conf") == 600 ]] || fail "refresh keeps a customized pacman.conf mode"
|
||||
chmod 644 "$TARGET_ROOT/etc/pacman.conf"
|
||||
pass "refresh keeps existing config file modes"
|
||||
|
||||
printf 'cn\n' > "$TARGET_ROOT/etc/omarchy/region"
|
||||
for channel in stable rc edge; do
|
||||
case "$channel" in
|
||||
stable) host=stable-mirror.omarchy ;;
|
||||
rc) host=rc-mirror.omarchy ;;
|
||||
edge) host=mirror.omarchy ;;
|
||||
esac
|
||||
apply "$channel"
|
||||
expected=$(printf 'Server = https://%s.cn/$repo/os/$arch\nServer = https://%s.org/$repo/os/$arch' "$host" "$host")
|
||||
[[ $(<"$TARGET_ROOT/etc/pacman.d/mirrorlist") == "$expected" ]] || fail "$channel mirror order and variables"
|
||||
{
|
||||
cat "$ROOT/default/pacman/pacman-$channel.conf"
|
||||
printf '\n[archlinuxcn]\nServer = https://mirrors.ustc.edu.cn/archlinuxcn/$arch\n'
|
||||
} > "$scratch/expected.conf"
|
||||
cmp "$scratch/expected.conf" "$TARGET_ROOT/etc/pacman.conf"
|
||||
apply "$channel"
|
||||
cmp "$TARGET_ROOT/etc/pacman.conf.bak" "$TARGET_ROOT/etc/pacman.conf"
|
||||
cmp "$TARGET_ROOT/etc/pacman.d/mirrorlist.bak" "$TARGET_ROOT/etc/pacman.d/mirrorlist"
|
||||
done
|
||||
pass "China puts its channel mirror first, keeps the global one as fallback, and is idempotent"
|
||||
|
||||
cp "$TARGET_ROOT/etc/pacman.conf" "$scratch/before.conf"
|
||||
cp "$TARGET_ROOT/etc/pacman.d/mirrorlist" "$scratch/before.mirrors"
|
||||
for region in '' CN chn zz '../cn' '$(touch NEVER_EXECUTE)'; do
|
||||
printf '%s\n' "$region" > "$TARGET_ROOT/etc/omarchy/region"
|
||||
if apply stable > "$scratch/error" 2>&1; then
|
||||
fail "invalid region accepted: $region"
|
||||
fi
|
||||
cmp "$scratch/before.conf" "$TARGET_ROOT/etc/pacman.conf"
|
||||
cmp "$scratch/before.mirrors" "$TARGET_ROOT/etc/pacman.d/mirrorlist"
|
||||
done
|
||||
pass "unsupported and malformed regions fail before replacing either config"
|
||||
|
||||
printf 'cn\n' > "$TARGET_ROOT/etc/omarchy/region"
|
||||
mkdir -p "$scratch/incomplete/default/regions/cn/pacman"
|
||||
cp -a "$ROOT/default/pacman" "$scratch/incomplete/default/"
|
||||
cp "$ROOT/default/regions/cn/pacman/pacman.conf.append" "$scratch/incomplete/default/regions/cn/pacman/"
|
||||
if OMARCHY_PATH="$scratch/incomplete" apply stable > "$scratch/error" 2>&1; then
|
||||
fail "incomplete region profile accepted"
|
||||
fi
|
||||
cmp "$scratch/before.conf" "$TARGET_ROOT/etc/pacman.conf"
|
||||
cmp "$scratch/before.mirrors" "$TARGET_ROOT/etc/pacman.d/mirrorlist"
|
||||
pass "missing fragment cannot leave a half-generated config"
|
||||
|
||||
# Refresh renders these files unprivileged and hands root plain copies; its
|
||||
# privileged path is covered in the sudo-boundary sandbox, never on the host.
|
||||
cp "$TARGET_ROOT/etc/pacman.conf" "$scratch/before.conf"
|
||||
mkdir -p "$scratch/rendered"
|
||||
"$ROOT/bin/omarchy-apply-pacman" --render rc "$scratch/rendered" "$TARGET_ROOT"
|
||||
rg -qxF 'Server = https://pkgs.omarchy.org/rc/$arch' "$scratch/rendered/pacman.conf"
|
||||
rg -qxF '[archlinuxcn]' "$scratch/rendered/pacman.conf"
|
||||
[[ $(<"$scratch/rendered/mirrorlist") == $'Server = https://rc-mirror.omarchy.cn/$repo/os/$arch\nServer = https://rc-mirror.omarchy.org/$repo/os/$arch' ]] ||
|
||||
fail "render puts the regional channel mirror first"
|
||||
cmp "$scratch/before.conf" "$TARGET_ROOT/etc/pacman.conf"
|
||||
pass "render builds the regional channel files without touching the target"
|
||||
|
||||
if "$ROOT/bin/omarchy-apply-pacman" --render dev "$scratch/rendered" "$TARGET_ROOT" > "$scratch/error" 2>&1; then
|
||||
fail "render accepts a nonexistent dev pacman template"
|
||||
fi
|
||||
pass "render rejects channels without pacman templates"
|
||||
|
||||
printf 'global\n' > "$TARGET_ROOT/etc/omarchy/region"
|
||||
apply stable
|
||||
cmp "$ROOT/default/pacman/pacman-stable.conf" "$TARGET_ROOT/etc/pacman.conf"
|
||||
cmp "$ROOT/default/pacman/mirrorlist-stable" "$TARGET_ROOT/etc/pacman.d/mirrorlist"
|
||||
pass "users can opt out of regional repository defaults"
|
||||
|
||||
# The shipped China profile is repository defaults only. Language, locale, and
|
||||
# input methods follow the installer's language choice, not the region.
|
||||
python3 - <<'PY'
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
region = Path(os.environ["ROOT"]) / "default/regions/cn"
|
||||
packages = {line.strip() for line in (region / "packages").read_text().splitlines() if line.strip() and not line.startswith("#")}
|
||||
assert packages == {"archlinuxcn-keyring"}, packages
|
||||
assert not (region / "skel").exists()
|
||||
PY
|
||||
pass "China profile adds only its repository keyring"
|
||||
|
||||
# The installer derives the region from the chosen timezone, so every profile
|
||||
# needs a mapping, and no timezone may select two regions.
|
||||
python3 - <<'PY'
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
zoneinfo = Path("/usr/share/zoneinfo")
|
||||
owners = {}
|
||||
for profile in sorted((Path(os.environ["ROOT"]) / "default/regions").iterdir()):
|
||||
lines = (profile / "timezones").read_text().splitlines()
|
||||
zones = [line.strip() for line in lines if line.strip() and not line.startswith("#")]
|
||||
assert zones, f"{profile.name} maps no timezones"
|
||||
for zone in zones:
|
||||
assert zone not in owners, f"{zone} maps to {owners[zone]} and {profile.name}"
|
||||
owners[zone] = profile.name
|
||||
if zoneinfo.is_dir():
|
||||
assert (zoneinfo / zone).is_file(), f"{profile.name} lists unknown timezone {zone}"
|
||||
|
||||
# The installer offers tzdata's backward-compatible names too, so China
|
||||
# covers every name that links to its two zones and nothing else.
|
||||
# Without tzdata.zi (minimal containers) only the canonical zones can be
|
||||
# checked, so completeness is enforced where the host has the link table.
|
||||
china = {zone for zone, region in owners.items() if region == "cn"}
|
||||
expected = {"Asia/Shanghai", "Asia/Urumqi"}
|
||||
tzdata = zoneinfo / "tzdata.zi"
|
||||
if tzdata.is_file():
|
||||
for line in tzdata.read_text().splitlines():
|
||||
fields = line.split()
|
||||
if len(fields) == 3 and fields[0] == "L" and fields[1] in {"Asia/Shanghai", "Asia/Urumqi"}:
|
||||
expected.add(fields[2])
|
||||
assert china == expected, (china, expected)
|
||||
else:
|
||||
assert expected <= china, (china, expected)
|
||||
PY
|
||||
pass "region profiles map each timezone to at most one region"
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
source "$SHELL_TEST_DIR/fixtures/sudo-boundary-test.sh"
|
||||
copy_boundary_file bin/omarchy-refresh-pacman
|
||||
copy_boundary_file bin/omarchy-apply-pacman
|
||||
for file in default/pacman/pacman-rc.conf default/pacman/mirrorlist-rc \
|
||||
default/regions/cn/pacman/pacman.conf.append default/regions/cn/pacman/mirrorlist-rc.prepend; do
|
||||
copy_boundary_file "$file"
|
||||
done
|
||||
|
||||
# A sandboxed China machine: the render reads this region marker, never the
|
||||
# host's /etc/omarchy/region.
|
||||
mkdir -p "$SUDO_TEST_TARGET/etc/omarchy"
|
||||
printf 'cn\n' >"$SUDO_TEST_TARGET/etc/omarchy/region"
|
||||
|
||||
# cp still logs its step, but now really copies, so the rendered files exist.
|
||||
# Root's copies into /etc land in a capture directory instead of the host.
|
||||
export SUDO_TEST_CAPTURE="$boundary_tmp/capture"
|
||||
rm "$SUDO_TEST_ROOT/bin/cp"
|
||||
cat >"$SUDO_TEST_ROOT/bin/cp" <<'STUB'
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
printf 'step:cp %s\n' "$*" >>"$SUDO_TEST_LOG"
|
||||
destination=${!#}
|
||||
if [[ $destination == /etc/* ]]; then
|
||||
source=${*: -2:1}
|
||||
[[ $source == /etc/* ]] && exit 0
|
||||
/usr/bin/mkdir -p "$SUDO_TEST_CAPTURE${destination%/*}"
|
||||
exec /usr/bin/cp "$source" "$SUDO_TEST_CAPTURE$destination"
|
||||
fi
|
||||
exec /usr/bin/cp "$@"
|
||||
STUB
|
||||
chmod +x "$SUDO_TEST_ROOT/bin/cp"
|
||||
|
||||
# Regional rendering must stay unprivileged: root only copies finished files,
|
||||
# exactly as the channel refresh did before regions existed.
|
||||
reset_boundary
|
||||
"$SUDO_TEST_ROOT/bin/omarchy-refresh-pacman" rc >"$boundary_tmp/output" 2>&1 || fail "refresh failed" "$(<"$boundary_tmp/output")"
|
||||
assert_boundary_cold "regional refresh"
|
||||
python3 - "$SUDO_TEST_LOG" "$SUDO_TEST_ROOT" <<'PY'
|
||||
import re
|
||||
import sys
|
||||
events = open(sys.argv[1]).read().splitlines()
|
||||
root = sys.argv[2]
|
||||
render = [i for i, e in enumerate(events) if e.startswith('step:cp ' + root + '/default/pacman/pacman-rc.conf ')]
|
||||
assert len(render) == 1, events
|
||||
rendered = events[render[0]].split()[-1].rsplit('/', 1)[0]
|
||||
copies = [i for i, e in enumerate(events) if e.startswith('sudo -N cp ')]
|
||||
assert [events[i] for i in copies[2:]] == [
|
||||
f'sudo -N cp -f {rendered}/pacman.conf /etc/pacman.conf',
|
||||
f'sudo -N cp -f {rendered}/mirrorlist /etc/pacman.d/mirrorlist',
|
||||
], events
|
||||
assert not any(e.startswith('sudo ') and 'omarchy-apply-pacman' in e for e in events), events
|
||||
assert render[0] < copies[0], events
|
||||
transaction = next(i for i, e in enumerate(events) if e.startswith('step:pacman '))
|
||||
assert max(copies) < transaction, events
|
||||
PY
|
||||
{
|
||||
cat "$ROOT/default/pacman/pacman-rc.conf"
|
||||
printf '\n'
|
||||
cat "$ROOT/default/regions/cn/pacman/pacman.conf.append"
|
||||
} >"$boundary_tmp/expected-pacman.conf"
|
||||
cat "$ROOT/default/regions/cn/pacman/mirrorlist-rc.prepend" "$ROOT/default/pacman/mirrorlist-rc" >"$boundary_tmp/expected-mirrorlist"
|
||||
cmp -s "$boundary_tmp/expected-pacman.conf" "$SUDO_TEST_CAPTURE/etc/pacman.conf" ||
|
||||
fail "root installs the rendered China pacman.conf" "$(diff "$boundary_tmp/expected-pacman.conf" "$SUDO_TEST_CAPTURE/etc/pacman.conf" 2>&1)"
|
||||
cmp -s "$boundary_tmp/expected-mirrorlist" "$SUDO_TEST_CAPTURE/etc/pacman.d/mirrorlist" ||
|
||||
fail "root installs the rendered China mirrorlist" "$(diff "$boundary_tmp/expected-mirrorlist" "$SUDO_TEST_CAPTURE/etc/pacman.d/mirrorlist" 2>&1)"
|
||||
pass "refresh renders regional config unprivileged and gives root only plain copies"
|
||||
|
||||
reset_boundary
|
||||
if "$SUDO_TEST_ROOT/bin/omarchy-refresh-pacman" dev >"$boundary_tmp/output" 2>&1; then
|
||||
fail "refresh accepts an invalid channel"
|
||||
fi
|
||||
if grep -Eq '^(sudo -N cp |step:pacman )' "$SUDO_TEST_LOG"; then
|
||||
fail "invalid channel reached privileged work" "$(<"$SUDO_TEST_LOG")"
|
||||
fi
|
||||
pass "failed refresh does not proceed to privileged copies or a package update"
|
||||
|
||||
# A failed render still removes its scratch directory and exits cold.
|
||||
reset_boundary
|
||||
mv "$SUDO_TEST_ROOT/bin/omarchy-apply-pacman" "$boundary_tmp/omarchy-apply-pacman"
|
||||
cat >"$SUDO_TEST_ROOT/bin/omarchy-apply-pacman" <<'STUB'
|
||||
#!/bin/bash
|
||||
: >"$3/pacman.conf"
|
||||
exit 1
|
||||
STUB
|
||||
chmod +x "$SUDO_TEST_ROOT/bin/omarchy-apply-pacman"
|
||||
mkdir -p "$boundary_tmp/scratch"
|
||||
if TMPDIR="$boundary_tmp/scratch" "$SUDO_TEST_ROOT/bin/omarchy-refresh-pacman" rc >"$boundary_tmp/output" 2>&1; then
|
||||
fail "refresh succeeds after a failed render"
|
||||
fi
|
||||
mv "$boundary_tmp/omarchy-apply-pacman" "$SUDO_TEST_ROOT/bin/omarchy-apply-pacman"
|
||||
[[ -z $(ls -A "$boundary_tmp/scratch") ]] || fail "failed render leaves its scratch directory" "$(ls -AR "$boundary_tmp/scratch")"
|
||||
if grep -Eq '^(sudo -N cp |step:pacman )' "$SUDO_TEST_LOG"; then
|
||||
fail "failed render reached privileged work" "$(<"$SUDO_TEST_LOG")"
|
||||
fi
|
||||
assert_boundary_cold "failed render"
|
||||
pass "failed render removes its scratch directory and exits cold"
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
test_tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$test_tmp"' EXIT
|
||||
|
||||
root="$test_tmp/root" sandbox="$test_tmp/omarchy" calls="$test_tmp/calls"
|
||||
mkdir -p "$root/etc/pacman.d" "$sandbox/bin" "$sandbox/install/config" "$sandbox/default/regions" "$test_tmp/bin" "$test_tmp/home"
|
||||
cp -r "$ROOT/default/regions/cn" "$sandbox/default/regions/"
|
||||
mkdir -p "$sandbox/shell/Commons" && cp "$ROOT/shell/Commons/I18nZh.js" "$sandbox/shell/Commons/"
|
||||
rewrite() { sed -e "s|/etc/|$root/etc/|g" -e "s| /\\.cargo/| $root/.cargo/|g" "$1" >"$2"; chmod +x "$2"; }
|
||||
rewrite "$ROOT/install/config/region.sh" "$sandbox/install/config/region.sh"
|
||||
rewrite "$ROOT/bin/omarchy-region" "$sandbox/bin/omarchy-region"
|
||||
|
||||
for command in omarchy-refresh-pacman omarchy-system-logout pacman-key pacman omarchy-apply-pacman; do
|
||||
printf '#!/bin/bash\necho "%s $*" >>"%s"\n' "$command" "$calls" >"$test_tmp/bin/$command"
|
||||
done
|
||||
printf '#!/bin/bash\necho "omarchy-update-pacman $*" >>"%s"\n[[ -z ${STUB_UPDATE_FAIL:-} ]]\n' "$calls" >"$test_tmp/bin/omarchy-update-pacman"
|
||||
printf '#!/bin/bash\necho "${STUB_CHANNEL:-stable}"\n' >"$test_tmp/bin/omarchy-version-channel"
|
||||
printf '#!/bin/bash\necho "${STUB_PLATFORM:-x86}"\n' >"$test_tmp/bin/omarchy-hw-platform"
|
||||
printf '#!/bin/bash\n[[ $1 == env ]] && shift\nexec env "$@"\n' >"$test_tmp/bin/sudo"
|
||||
printf '#!/bin/bash\necho "gum $*" >>"%s"\nexit "${GUM_ANSWER:-1}"\n' "$calls" >"$test_tmp/bin/gum"
|
||||
chmod +x "$test_tmp/bin"/*
|
||||
ln -s "$ROOT/bin/omarchy-text" "$test_tmp/bin/omarchy-text"
|
||||
mkdir -p "$test_tmp/tmp"
|
||||
export PATH="$test_tmp/bin:$sandbox/bin:$PATH" OMARCHY_PATH="$sandbox" HOME="$test_tmp/home" TMPDIR="$test_tmp/tmp"
|
||||
switch_calls() { sed "s|$test_tmp/tmp/[^/ ]*|RENDERED|g" "$calls"; }
|
||||
|
||||
omarchy-region cn >/dev/null
|
||||
[[ $(omarchy-region) == "China" && $(<"$root/etc/omarchy/region") == "cn" ]] ||
|
||||
fail "switching to China records cn"
|
||||
[[ $(switch_calls) == $'omarchy-update-pacman -Syu --noconfirm\nomarchy-apply-pacman --render stable RENDERED\npacman --config RENDERED/pacman.conf -Sy --noconfirm --needed archlinuxcn-keyring\npacman-key --populate archlinuxcn\nomarchy-refresh-pacman stable' ]] ||
|
||||
fail "China upgrades, trusts ArchLinuxCN through the rendered cn config, then refreshes" "$(switch_calls)"
|
||||
[[ -z $(ls -A "$test_tmp/tmp") ]] || fail "the rendered config is removed"
|
||||
pass "switching to China trusts ArchLinuxCN before the refresh upgrades through it"
|
||||
|
||||
: >"$calls"
|
||||
STUB_CHANNEL=edge omarchy-region cn >/dev/null
|
||||
grep -qx 'omarchy-refresh-pacman edge' "$calls" || fail "the refresh keeps the machine's channel" "$(<"$calls")"
|
||||
pass "switching to China again keeps the channel"
|
||||
|
||||
: >"$calls"
|
||||
omarchy-region global >/dev/null
|
||||
[[ $(omarchy-region) == "Global" && ! -e $root/etc/omarchy/region ]] ||
|
||||
fail "switching to Global removes the region"
|
||||
[[ $(<"$calls") == 'omarchy-refresh-pacman stable' ]] ||
|
||||
fail "Global refreshes pacman back to the channel defaults" "$(<"$calls")"
|
||||
pass "switching to Global restores the channel's pacman defaults"
|
||||
|
||||
if output=$(omarchy-region mars 2>&1); then
|
||||
fail "an unknown region is refused" "$output"
|
||||
fi
|
||||
if output=$(STUB_CHANNEL="stable / edge" omarchy-region cn 2>&1); then
|
||||
fail "a mixed channel is refused" "$output"
|
||||
fi
|
||||
[[ $(omarchy-region) == "Global" ]] || fail "a refused switch changes nothing"
|
||||
pass "an unknown region or an unclear channel is refused"
|
||||
|
||||
: >"$calls"
|
||||
if output=$(STUB_PLATFORM=aarch64-apple omarchy-region cn 2>&1); then
|
||||
fail "an ARM machine refuses the China region" "$output"
|
||||
fi
|
||||
[[ $(omarchy-region) == "Global" && ! -s $calls ]] || fail "a refused ARM switch changes nothing" "$(<"$calls")"
|
||||
pass "an ARM machine refuses the China region before changing anything"
|
||||
|
||||
: >"$calls"
|
||||
if output=$(STUB_UPDATE_FAIL=1 omarchy-region cn 2>&1); then
|
||||
fail "switching to China fails when the upgrade fails" "$output"
|
||||
fi
|
||||
[[ $(omarchy-region) == "Global" ]] && ! grep -q '^omarchy-refresh-pacman' "$calls" ||
|
||||
fail "a failed switch to China leaves the region Global and pacman untouched" "$(<"$calls")"
|
||||
pass "a failed switch to China leaves the machine Global"
|
||||
|
||||
grep -q '^gum' "$calls" && fail "a switch without a terminal asks nothing" "$(<"$calls")"
|
||||
: >"$calls"
|
||||
output=$(LANG=zh_CN.UTF-8 GUM_ANSWER=1 script -qec "omarchy-region cn" /dev/null)
|
||||
grep -qxF 'gum confirm --affirmative 立即注销 --negative 稍后 立即注销以切换开发工具镜像?' "$calls" ||
|
||||
fail "a Chinese session offers to log out in Chinese" "$(<"$calls")"
|
||||
[[ $output == *"重新登录后切换开发工具镜像。"* ]] && ! grep -q '^omarchy-system-logout' "$calls" ||
|
||||
fail "declining the logout leaves a reminder and the session" "$output"
|
||||
: >"$calls"
|
||||
LANG=en_US.UTF-8 GUM_ANSWER=0 script -qec "omarchy-region global" /dev/null >/dev/null
|
||||
grep -qxF 'gum confirm --affirmative Log out now --negative Later Log out now to switch the developer mirrors?' "$calls" &&
|
||||
grep -qx 'omarchy-system-logout ' "$calls" || fail "a terminal switch offers to log out and logs out on yes" "$(<"$calls")"
|
||||
pass "a switch in a terminal offers to log out in the session's language"
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
test_tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$test_tmp"' EXIT
|
||||
|
||||
region_setup="$test_tmp/region.sh"
|
||||
root="$test_tmp/root"
|
||||
sed -e "s|/etc/|$root/etc/|g" -e "s| /\.cargo/| $root/.cargo/|g" "$ROOT/install/config/region.sh" >"$region_setup"
|
||||
export OMARCHY_PATH="$ROOT"
|
||||
mkdir -p "$root/etc/omarchy"
|
||||
|
||||
bash -e "$region_setup"
|
||||
[[ -z $(find "$root" -type l) ]] || fail "no marker links nothing"
|
||||
pass "no marker installs Global"
|
||||
|
||||
echo cn >"$root/etc/omarchy/region"
|
||||
bash -e "$region_setup"
|
||||
for link in "environment.conf etc/environment.d/90-omarchy-china.conf" "cargo.toml .cargo/config.toml" "gemrc etc/gemrc"; do
|
||||
read -r source target <<<"$link"
|
||||
[[ $(readlink "$root/$target") == "$ROOT/default/regions/cn/$source" ]] || fail "China links /$target to the shipped $source"
|
||||
done
|
||||
bash -e "$region_setup"
|
||||
pass "the cn marker links the developer mirror drop-ins, again without error"
|
||||
|
||||
rm "$root/etc/gemrc"
|
||||
echo ":sources: []" >"$root/etc/gemrc"
|
||||
if output=$(bash -e "$region_setup" 2>&1); then
|
||||
fail "China refuses to replace an admin's /etc/gemrc" "$output"
|
||||
fi
|
||||
[[ $output == "Refusing to replace $root/etc/gemrc, which Omarchy did not create" && $(<"$root/etc/gemrc") == ":sources: []" ]] ||
|
||||
fail "China names the file it refuses to replace and leaves it" "$output"
|
||||
rm "$root/etc/gemrc"
|
||||
pass "region setup never replaces mirror config Omarchy did not create"
|
||||
|
||||
echo mars >"$root/etc/omarchy/region"
|
||||
if output=$(bash -e "$region_setup" 2>&1); then
|
||||
fail "an unknown region fails" "$output"
|
||||
fi
|
||||
[[ $output == "Unknown region: mars" ]] || fail "region setup names the unknown region" "$output"
|
||||
pass "region setup rejects an unknown region"
|
||||
|
||||
echo cn >"$root/etc/omarchy/region"
|
||||
bash -e "$region_setup"
|
||||
OMARCHY_REGION=global bash -e "$region_setup"
|
||||
[[ ! -e $root/etc/omarchy/region && -z $(find "$root" -type l) ]] || fail "Global removes the marker and the developer mirror links"
|
||||
ln -s /usr/share/somewhere/gemrc "$root/etc/gemrc"
|
||||
OMARCHY_REGION=global bash -e "$region_setup"
|
||||
[[ -L $root/etc/gemrc ]] || fail "Global keeps a gemrc link Omarchy did not create"
|
||||
rm "$root/etc/gemrc"
|
||||
pass "switching to Global removes only what Omarchy linked"
|
||||
|
||||
OMARCHY_REGION=cn bash -e "$region_setup"
|
||||
[[ $(<"$root/etc/omarchy/region") == "cn" && $(readlink "$root/etc/gemrc") == "$ROOT/default/regions/cn/gemrc" ]] ||
|
||||
fail "switching to China records cn and links the drop-ins"
|
||||
pass "switching to China records cn and links the drop-ins"
|
||||
|
||||
region_bin="$test_tmp/omarchy-region"
|
||||
sed "s|/etc/omarchy/region|$root/etc/omarchy/region|" "$ROOT/bin/omarchy-region" >"$region_bin"
|
||||
chmod +x "$region_bin"
|
||||
[[ $("$region_bin") == "China" ]] || fail "the cn marker reads as China"
|
||||
rm "$root/etc/omarchy/region"
|
||||
[[ $("$region_bin") == "Global" ]] || fail "no marker reads as Global"
|
||||
echo mars >"$root/etc/omarchy/region"
|
||||
if output=$("$region_bin" 2>&1); then
|
||||
fail "an unknown marker fails" "$output"
|
||||
fi
|
||||
[[ $output == "Unknown region in $root/etc/omarchy/region: mars" ]] || fail "the reader names the file and value" "$output"
|
||||
pass "omarchy region reads Global, China, and fails on anything else"
|
||||
|
||||
grep -qx 'run_logged "$OMARCHY_INSTALL/config/region.sh"' "$ROOT/install/config/all.sh" || fail "the install runs region setup"
|
||||
pass "the install runs region setup"
|
||||
@@ -6,6 +6,7 @@ source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
source "$SHELL_TEST_DIR/fixtures/sudo-boundary-test.sh"
|
||||
copy_boundary_file bin/omarchy-update
|
||||
copy_boundary_file bin/omarchy-refresh-pacman
|
||||
copy_boundary_file bin/omarchy-apply-pacman
|
||||
# Replace the step symlink, preserving the real fixture dispatcher.
|
||||
rm "$SUDO_TEST_ROOT/bin/omarchy-update-aur-pkgs"
|
||||
copy_boundary_file bin/omarchy-update-aur-pkgs
|
||||
|
||||
Reference in new issue
Block a user