Add Arch mirror manager: benchmark, apply, pin, restore, status
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp
This commit is contained in:
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
# omarchy:summary=Apply a mirror profile or pin one mirror for pacman
|
||||
# omarchy:args=<china|official|mirror-id>
|
||||
# omarchy:examples=omarchycn mirror apply china | omarchycn mirror apply official | omarchycn mirror apply tuna
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$OMARCHY_PATH/cn/lib/mirror.sh"
|
||||
|
||||
target="${1:?usage: omarchycn mirror apply <china|official|mirror-id>}"
|
||||
|
||||
case "$target" in
|
||||
china)
|
||||
echo "Benchmarking China mirrors..."
|
||||
mapfile -t ranked < <(cn_mirror_rank $(cn_mirror_ids_by_region cn))
|
||||
if (( ${#ranked[@]} == 0 )); then
|
||||
echo "No healthy China mirror reachable; keeping current mirrorlist" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Primary + up to two backups
|
||||
cn_mirror_write_list china "${ranked[@]:0:3}"
|
||||
;;
|
||||
official)
|
||||
cn_mirror_write_list official geo worldwide
|
||||
;;
|
||||
*)
|
||||
url=$(cn_mirror_url "$target")
|
||||
if [[ -z $url ]]; then
|
||||
echo "Unknown mirror id: $target (see: omarchycn mirror benchmark)" >&2
|
||||
exit 1
|
||||
fi
|
||||
cn_mirror_write_list "pin:$target" "$target"
|
||||
;;
|
||||
esac
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
# omarchy:summary=Benchmark Arch mirrors for speed, latency, and sync freshness
|
||||
# omarchy:examples=omarchycn mirror benchmark
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$OMARCHY_PATH/cn/lib/mirror.sh"
|
||||
|
||||
printf '%-11s %-22s %12s %8s %10s\n' "ID" "NAME" "SPEED" "TTFB" "SYNC"
|
||||
|
||||
for id in $(cn_mirror_ids); do
|
||||
url=$(cn_mirror_url "$id")
|
||||
read -r speed ttfb age < <(cn_mirror_probe "$url")
|
||||
|
||||
if (( speed > 0 )); then
|
||||
speed_h="$((speed / 1024)) KB/s"
|
||||
else
|
||||
speed_h="FAIL"
|
||||
fi
|
||||
case "$age" in
|
||||
stale) sync_h="STALE" ;;
|
||||
unknown) sync_h="?" ;;
|
||||
*) sync_h="$((age / 60))m ago" ;;
|
||||
esac
|
||||
|
||||
printf '%-11s %-22s %12s %7ss %10s\n' "$id" "$(cn_mirror_name "$id")" "$speed_h" "${ttfb:0:5}" "$sync_h"
|
||||
done
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
# omarchy:summary=Pin pacman to a single mirror
|
||||
# omarchy:args=<mirror-id>
|
||||
# omarchy:examples=omarchycn mirror pin tuna
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
exec omarchy-cn-mirror-apply "${1:?usage: omarchycn mirror pin <mirror-id>}"
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
# omarchy:summary=Restore the most recent mirrorlist backup
|
||||
# omarchy:examples=omarchycn mirror restore
|
||||
# omarchy:requires-sudo=true
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$OMARCHY_PATH/cn/lib/mirror.sh"
|
||||
|
||||
backups=("$CN_MIRRORLIST".omarchycn-bak-*)
|
||||
if [[ ! -e ${backups[0]} ]]; then
|
||||
echo "No OmarchyCN mirrorlist backups found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
latest="${backups[-1]}"
|
||||
sudo cp "$latest" "$CN_MIRRORLIST"
|
||||
rm -f "$CN_PROFILE_FILE"
|
||||
echo "Restored $CN_MIRRORLIST from ${latest##*/}"
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
# omarchy:summary=Show current pacman mirror profile and servers
|
||||
# omarchy:examples=omarchycn mirror status
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$OMARCHY_PATH/cn/lib/mirror.sh"
|
||||
|
||||
if [[ -f $CN_PROFILE_FILE ]]; then
|
||||
echo "Profile: $(<"$CN_PROFILE_FILE")"
|
||||
else
|
||||
echo "Profile: not managed by OmarchyCN"
|
||||
fi
|
||||
|
||||
echo "Mirrorlist: $CN_MIRRORLIST"
|
||||
echo "Servers:"
|
||||
grep -E '^Server' "$CN_MIRRORLIST" | sed 's/^/ /'
|
||||
|
||||
backups=("$CN_MIRRORLIST".omarchycn-bak-*)
|
||||
if [[ -e ${backups[0]} ]]; then
|
||||
echo "Backups: ${#backups[@]} (latest: ${backups[-1]##*/})"
|
||||
fi
|
||||
@@ -0,0 +1,77 @@
|
||||
# Sourced helpers for omarchy-cn-mirror-* commands
|
||||
|
||||
CN_MIRRORS_JSON="$OMARCHY_PATH/cn/mirrors.json"
|
||||
CN_MIRRORLIST="/etc/pacman.d/mirrorlist"
|
||||
CN_PROFILE_FILE="$HOME/.config/omarchycn/mirror-profile"
|
||||
|
||||
cn_mirror_ids() {
|
||||
jq -r '.mirrors[].id' "$CN_MIRRORS_JSON"
|
||||
}
|
||||
|
||||
cn_mirror_url() {
|
||||
jq -r --arg id "$1" '.mirrors[] | select(.id == $id) | .url' "$CN_MIRRORS_JSON"
|
||||
}
|
||||
|
||||
cn_mirror_name() {
|
||||
jq -r --arg id "$1" '.mirrors[] | select(.id == $id) | .name' "$CN_MIRRORS_JSON"
|
||||
}
|
||||
|
||||
cn_mirror_ids_by_region() {
|
||||
jq -r --arg r "$1" '.mirrors[] | select(.region == $r) | .id' "$CN_MIRRORS_JSON"
|
||||
}
|
||||
|
||||
# Probe one mirror: prints "<speed_bytes_s> <ttfb_s> <sync_age_s|stale|unknown>"
|
||||
cn_mirror_probe() {
|
||||
local url="$1"
|
||||
local speed ttfb lastsync age now
|
||||
|
||||
read -r speed ttfb < <(curl -sS -o /dev/null -m 12 --connect-timeout 5 \
|
||||
-w '%{speed_download} %{time_starttransfer}\n' \
|
||||
"$url/core/os/x86_64/core.db" 2>/dev/null) || true
|
||||
speed="${speed%%.*}"
|
||||
[[ -z $speed ]] && speed=0
|
||||
|
||||
age="unknown"
|
||||
lastsync=$(curl -sS -m 5 --connect-timeout 5 "$url/lastsync" 2>/dev/null | tr -cd '0-9')
|
||||
if [[ -n $lastsync ]]; then
|
||||
now=$(date +%s)
|
||||
age=$((now - lastsync))
|
||||
(( age > 86400 )) && age="stale"
|
||||
fi
|
||||
|
||||
echo "$speed ${ttfb:-0} $age"
|
||||
}
|
||||
|
||||
# Rank ids by probe speed, excluding dead (speed 0) and stale mirrors
|
||||
cn_mirror_rank() {
|
||||
local id url speed ttfb age
|
||||
|
||||
for id in "$@"; do
|
||||
url=$(cn_mirror_url "$id")
|
||||
read -r speed ttfb age < <(cn_mirror_probe "$url")
|
||||
(( speed > 0 )) || continue
|
||||
[[ $age == "stale" ]] && continue
|
||||
echo "$speed $id"
|
||||
done | sort -rn | awk '{print $2}'
|
||||
}
|
||||
|
||||
# Write mirrorlist from mirror ids (first = primary), with timestamped backup
|
||||
cn_mirror_write_list() {
|
||||
local profile="$1"
|
||||
shift
|
||||
local stamp id url content=""
|
||||
|
||||
stamp=$(date +%Y%m%d-%H%M%S)
|
||||
content="## OmarchyCN mirrorlist (profile: $profile, generated $stamp)\n"
|
||||
for id in "$@"; do
|
||||
url=$(cn_mirror_url "$id")
|
||||
content+="## $id: $(cn_mirror_name "$id")\nServer = $url/\$repo/os/\$arch\n"
|
||||
done
|
||||
|
||||
sudo cp "$CN_MIRRORLIST" "$CN_MIRRORLIST.omarchycn-bak-$stamp"
|
||||
printf '%b' "$content" | sudo tee "$CN_MIRRORLIST" > /dev/null
|
||||
|
||||
mkdir -p "${CN_PROFILE_FILE%/*}"
|
||||
echo "$profile" > "$CN_PROFILE_FILE"
|
||||
echo "Mirrorlist written ($CN_MIRRORLIST), backup: $CN_MIRRORLIST.omarchycn-bak-$stamp"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"mirrors": [
|
||||
{ "id": "tuna", "name": "清华大学 TUNA", "region": "cn", "url": "https://mirrors.tuna.tsinghua.edu.cn/archlinux" },
|
||||
{ "id": "ustc", "name": "中国科学技术大学", "region": "cn", "url": "https://mirrors.ustc.edu.cn/archlinux" },
|
||||
{ "id": "aliyun", "name": "阿里云", "region": "cn", "url": "https://mirrors.aliyun.com/archlinux" },
|
||||
{ "id": "tencent", "name": "腾讯云", "region": "cn", "url": "https://mirrors.cloud.tencent.com/archlinux" },
|
||||
{ "id": "netease", "name": "网易", "region": "cn", "url": "https://mirrors.163.com/archlinux" },
|
||||
{ "id": "sjtu", "name": "上海交通大学", "region": "cn", "url": "https://mirror.sjtu.edu.cn/archlinux" },
|
||||
{ "id": "geo", "name": "Arch 官方 Geo Mirror", "region": "global", "url": "https://geo.mirror.pkgbuild.com" },
|
||||
{ "id": "worldwide", "name": "Arch 官方 Worldwide", "region": "global", "url": "https://mirror.rackspace.com/archlinux" }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user