From dd3b893137c352abac6b4b5720c32d29113acc11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 24 Aug 2026 18:55:08 -0400 Subject: [PATCH] Add cn doctor with mirror auto-failover and restore commands Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp --- bin/omarchy-cn-doctor | 91 +++++++++++++++++++++++++++++++++++ bin/omarchy-cn-restore-config | 33 +++++++++++++ bin/omarchy-cn-restore-list | 31 ++++++++++++ 3 files changed, 155 insertions(+) create mode 100755 bin/omarchy-cn-doctor create mode 100755 bin/omarchy-cn-restore-config create mode 100755 bin/omarchy-cn-restore-list diff --git a/bin/omarchy-cn-doctor b/bin/omarchy-cn-doctor new file mode 100755 index 00000000..b5f8df5f --- /dev/null +++ b/bin/omarchy-cn-doctor @@ -0,0 +1,91 @@ +#!/bin/bash +# omarchy:summary=Check network, pacman mirror, and dev registries; --fix fails over mirrors +# omarchy:args=[network|mirror|dev-mirror] [--fix] +# omarchy:examples=omarchycn doctor | omarchycn doctor mirror --fix + +set -euo pipefail + +source "$OMARCHY_PATH/cn/lib/mirror.sh" + +module="all" +fix="false" +for arg in "$@"; do + case "$arg" in + network | mirror | dev-mirror) module="$arg" ;; + --fix) fix="true" ;; + *) + echo "Unknown argument: $arg" >&2 + exit 2 + ;; + esac +done + +failures=0 + +check_network() { + local host + for host in mirrors.tuna.tsinghua.edu.cn archlinux.org; do + if getent hosts "$host" > /dev/null 2>&1; then + echo "PASS network: DNS resolves $host" + else + echo "FAIL network: cannot resolve $host" + failures=$((failures + 1)) + fi + done + if curl -sSf -o /dev/null -m 8 --connect-timeout 5 https://www.baidu.com 2>/dev/null; then + echo "PASS network: HTTPS reachable (baidu.com)" + else + echo "FAIL network: HTTPS unreachable (baidu.com)" + failures=$((failures + 1)) + fi +} + +check_mirror() { + local primary speed ttfb age + + primary=$(grep -sE '^Server' "$CN_MIRRORLIST" | head -1 | sed -E 's/^Server = //; s|/\$repo/os/\$arch/?$||') + if [[ -z $primary ]]; then + echo "FAIL mirror: no Server entries in $CN_MIRRORLIST" + failures=$((failures + 1)) + return 0 + fi + + echo "INFO mirror: primary $primary" + read -r speed ttfb age < <(cn_mirror_probe "$primary") + if (( speed > 0 )) && [[ $age != "stale" ]]; then + echo "PASS mirror: primary healthy ($((speed / 1024)) KB/s, sync age ${age}s)" + return 0 + fi + + echo "FAIL mirror: primary unhealthy (speed=$speed age=$age)" + failures=$((failures + 1)) + + if [[ $fix == "true" && -f $CN_PROFILE_FILE && $(<"$CN_PROFILE_FILE") == "china" ]]; then + echo "INFO mirror: re-applying china profile (auto-failover)" + if omarchy-cn-mirror-apply china; then + failures=$((failures - 1)) + omarchy-notification-send "OmarchyCN" "pacman 镜像已自动切换" || true + fi + fi +} + +check_dev_mirror() { + omarchy-cn-dev-mirror-doctor || failures=$((failures + 1)) +} + +case "$module" in +network) check_network ;; +mirror) check_mirror ;; +dev-mirror) check_dev_mirror ;; +all) + check_network + check_mirror + check_dev_mirror + ;; +esac + +if (( failures > 0 )); then + echo "Doctor: $failures failure(s)" + exit 1 +fi +echo "Doctor: all checks passed" diff --git a/bin/omarchy-cn-restore-config b/bin/omarchy-cn-restore-config new file mode 100755 index 00000000..d0283f90 --- /dev/null +++ b/bin/omarchy-cn-restore-config @@ -0,0 +1,33 @@ +#!/bin/bash +# omarchy:summary=Restore development registry configs from a backup stamp +# omarchy:args= +# omarchy:examples=omarchycn restore config 20260824-184243 + +set -euo pipefail + +source "$OMARCHY_PATH/cn/lib/dev-mirror.sh" + +stamp="${1:?usage: omarchycn restore config (see: omarchycn restore list)}" +dir="$CN_DM_BACKUP_ROOT/$stamp" + +if [[ ! -d $dir ]]; then + echo "No backup at $dir" >&2 + exit 1 +fi + +restored=0 +for target in "${CN_DM_TARGETS[@]}"; do + dest=$(cn_dm_config_file "$target") + src="$dir/${dest##*/}" + if [[ -f $src && $dest != /etc/* ]]; then + mkdir -p "${dest%/*}" + cp "$src" "$dest" + echo "restored $target: $dest" + restored=$((restored + 1)) + fi +done + +if (( restored == 0 )); then + echo "Nothing restored from $dir (system files like docker are not auto-restored)" >&2 + exit 1 +fi diff --git a/bin/omarchy-cn-restore-list b/bin/omarchy-cn-restore-list new file mode 100755 index 00000000..7fea283f --- /dev/null +++ b/bin/omarchy-cn-restore-list @@ -0,0 +1,31 @@ +#!/bin/bash +# omarchy:summary=List OmarchyCN configuration backups +# omarchy:examples=omarchycn restore list + +set -euo pipefail + +source "$OMARCHY_PATH/cn/lib/dev-mirror.sh" + +echo "Dev-mirror backups ($CN_DM_BACKUP_ROOT):" +if [[ -d $CN_DM_BACKUP_ROOT ]]; then + for dir in "$CN_DM_BACKUP_ROOT"/*/; do + [[ -d $dir ]] || continue + stamp="${dir%/}" + stamp="${stamp##*/}" + echo " $stamp: $(ls "$dir" | tr '\n' ' ')" + done +else + echo " (none)" +fi + +echo "Mirrorlist backups (/etc/pacman.d):" +found="false" +for f in /etc/pacman.d/mirrorlist.omarchycn-bak-*; do + if [[ -e $f ]]; then + echo " ${f##*/}" + found="true" + fi +done +if [[ $found == "false" ]]; then + echo " (none)" +fi