Initial commit: mihomo 内核管理脚本
bash 脚本,用于在 Linux 服务器上检查、安装、升级、回滚 mihomo 内核, 支持交互菜单与命令行双模式,带安全验证、自动备份与回滚。
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
# mihomo-up
|
||||
|
||||
一个 bash 脚本,用来在 Linux 服务器上**检查、安装、升级、回滚 [mihomo](https://github.com/MetaCubeX/mihomo) 内核**(Clash.Meta 的继任者,MetaCubeX 维护的代理核心)。可以直接跑交互菜单,也可以用子命令接进 cron / CI。
|
||||
|
||||
脚本只管内核二进制本身:它**不会**替你创建 systemd 服务、生成配置文件或设置订阅,这些需要你自己先准备好。
|
||||
|
||||
## 特性
|
||||
|
||||
- **双模式**:终端下无参数运行进入交互菜单;被管道/cron 调用时自动退化为安静的 `check`
|
||||
- **两道安全闸门**:替换前先验证新二进制能运行且版本号对得上,再用新二进制 `-t` 校验你现有的配置,任一失败就中止、不动任何文件
|
||||
- **自动备份与回滚**:替换前把旧版备份到 `/var/backups/mihomo/`(保留最近 3 份);升级后服务起不来会自动回滚并重启旧版
|
||||
- **curl 一键运行友好**:交互输入读 `/dev/tty`,`bash <(curl ...)` 管道方式运行菜单照样能按键
|
||||
- 支持 amd64 / arm64 / armv7,amd64 可指定微架构变体(v1/v2/v3/compatible)
|
||||
|
||||
## 环境要求
|
||||
|
||||
- Linux + bash
|
||||
- 依赖命令:`curl`、`gzip`、`install`、`awk`(一般发行版自带)
|
||||
- 升级操作需要 **root**(要写 `/usr/local/bin` 并重启服务)
|
||||
- 服务的启停依赖 systemd 中名为 `mihomo` 的 service;没有的话脚本只替换二进制、跳过重启
|
||||
|
||||
## 快速开始
|
||||
|
||||
**交互菜单**(把地址换成你自己托管的脚本地址):
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://你的地址/mihomo-up.sh)
|
||||
```
|
||||
|
||||
**命令行**:
|
||||
|
||||
```bash
|
||||
chmod +x mihomo-up.sh
|
||||
|
||||
./mihomo-up.sh check # 只查不改:当前版本 vs 最新版本
|
||||
sudo ./mihomo-up.sh update # 升级到最新版
|
||||
sudo ./mihomo-up.sh update --version v1.19.28 # 装指定版本
|
||||
sudo ./mihomo-up.sh rollback # 回滚到上一个备份
|
||||
./mihomo-up.sh list # 查看本地备份
|
||||
```
|
||||
|
||||
菜单里另有:校验配置文件、服务控制(启停/状态/最近日志)等入口。
|
||||
|
||||
## 在 cron / CI 里用
|
||||
|
||||
`check` 的退出码专为脚本化设计:
|
||||
|
||||
| 退出码 | 含义 |
|
||||
|---|---|
|
||||
| 0 | 已是最新 |
|
||||
| 10 | 有新版(或未安装) |
|
||||
| 1 | 出错 |
|
||||
|
||||
例子——有新版时自动升级:
|
||||
|
||||
```bash
|
||||
mihomo-up.sh check && exit 0
|
||||
[ $? -eq 10 ] && mihomo-up.sh update -y
|
||||
```
|
||||
|
||||
加 `--json` 会额外输出一行机器可读的 JSON,方便接入监控:
|
||||
|
||||
```json
|
||||
{"current":"v1.19.27","latest":"v1.19.28","outdated":true}
|
||||
```
|
||||
|
||||
## 选项一览
|
||||
|
||||
| 选项 | 作用 |
|
||||
|---|---|
|
||||
| `-y, --yes` | 非交互确认,全部默认同意 |
|
||||
| `-n, --dry-run` | 只打印会做什么,不实际执行 |
|
||||
| `--force` | 即使版本相同也重装(`install` 子命令等价于 `update --force`) |
|
||||
| `--version <v>` | 指定版本,如 `v1.19.28` |
|
||||
| `--no-restart` | 装完不启动服务 |
|
||||
| `--json` | `check` 时额外输出一行 JSON |
|
||||
| `--arch <a>` | 覆盖自动识别的架构:`amd64` / `arm64` / `armv7` |
|
||||
| `--variant <v>` | amd64 微架构变体:`v1` / `v2` / `v3` / `compatible` |
|
||||
| `--bin <path>` | 二进制路径,默认 `/usr/local/bin/mihomo` |
|
||||
| `--conf-dir <dir>` | 配置目录,默认 `/etc/mihomo` |
|
||||
|
||||
## 默认值与环境变量
|
||||
|
||||
| 默认值 | 环境变量 |
|
||||
|---|---|
|
||||
| `/usr/local/bin/mihomo` | `MIHOMO_BIN` |
|
||||
| `/etc/mihomo` | `MIHOMO_CONF_DIR` |
|
||||
| `mihomo`(systemd 服务名) | `MIHOMO_SERVICE` |
|
||||
| `/var/backups/mihomo` | `MIHOMO_BACKUP_DIR` |
|
||||
|
||||
备份最多保留 3 份(脚本内常量 `KEEP=3`)。
|
||||
|
||||
## 升级时发生了什么
|
||||
|
||||
1. 从 GitHub Releases 下载对应架构的 `.gz` 包并解压
|
||||
2. **闸门 1**:新二进制必须能执行,且 `-v` 报告的版本号与目标一致
|
||||
3. **闸门 2**:若存在 `config.yaml`,用新二进制 `-t` 校验现有配置,失败即中止
|
||||
4. 备份旧二进制 → 停服务 → 安装新版 → 启动服务
|
||||
5. 启动失败则**自动回滚**到刚才的备份并重启,最后提示你查 `journalctl -u mihomo -n 50`
|
||||
|
||||
任何一步失败都不会留下半个新系统:要么没动,要么已回滚。
|
||||
+388
@@ -0,0 +1,388 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# mihomo-up.sh — mihomo 内核管理脚本 (交互菜单 + 命令行双模式)
|
||||
#
|
||||
# bash <(curl -fsSL https://你的地址/mihomo-up.sh) # 交互菜单
|
||||
# ./mihomo-up.sh check # 非交互, 给 cron/CI 用
|
||||
#
|
||||
set -Eeuo pipefail
|
||||
|
||||
REPO="MetaCubeX/mihomo"
|
||||
BIN="${MIHOMO_BIN:-/usr/local/bin/mihomo}"
|
||||
CONF_DIR="${MIHOMO_CONF_DIR:-/etc/mihomo}"
|
||||
SERVICE="${MIHOMO_SERVICE:-mihomo}"
|
||||
BACKUP_DIR="${MIHOMO_BACKUP_DIR:-/var/backups/mihomo}"
|
||||
KEEP=3
|
||||
|
||||
ARCH_OVERRIDE=""; VARIANT=""; WANT_VERSION=""
|
||||
FORCE=0; DRY_RUN=0; NO_RESTART=0; JSON=0; ASSUME_YES=0
|
||||
TMPDIR_=""
|
||||
|
||||
# ───────────────────────── 输出 ─────────────────────────
|
||||
if [[ -t 1 ]]; then
|
||||
R=$'\033[31m'; G=$'\033[32m'; Y=$'\033[33m'; B=$'\033[34m'
|
||||
C=$'\033[36m'; W=$'\033[1m'; D=$'\033[2m'; N=$'\033[0m'
|
||||
else
|
||||
R=""; G=""; Y=""; B=""; C=""; W=""; D=""; N=""
|
||||
fi
|
||||
|
||||
log() { (( JSON )) || printf '%s\n' "$*" >&2; }
|
||||
info() { log "${D}··${N} $*"; }
|
||||
ok() { log "${G}✓${N} $*"; }
|
||||
warn() { log "${Y}!${N} $*"; }
|
||||
die() { printf '%s✗%s %s\n' "$R" "$N" "$*" >&2; exit 1; }
|
||||
|
||||
cleanup() {
|
||||
local rc=$?
|
||||
trap - ERR
|
||||
[[ -n "${TMPDIR_:-}" && -d "$TMPDIR_" ]] && rm -rf "$TMPDIR_"
|
||||
return $rc
|
||||
}
|
||||
trap cleanup EXIT
|
||||
trap 'die "第 $LINENO 行出错, 已中止"' ERR
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
mihomo-up.sh — 检查 / 安装 / 升级 / 回滚 mihomo 内核
|
||||
|
||||
mihomo-up.sh 进入交互菜单 (终端下默认)
|
||||
mihomo-up.sh check 只查, 不改任何东西
|
||||
mihomo-up.sh update [--force] 升级内核
|
||||
mihomo-up.sh rollback 回滚到上一个备份
|
||||
mihomo-up.sh list 列出本地备份
|
||||
|
||||
选项:
|
||||
-y, --yes 非交互确认, 全部默认同意
|
||||
-n, --dry-run 只打印会做什么
|
||||
--version <v> 指定版本, 如 v1.19.28
|
||||
--no-restart 装完不启动服务
|
||||
--json check 时额外输出一行 JSON
|
||||
--arch <a> amd64 | arm64 | armv7
|
||||
--variant <v> amd64 微架构: v1 | v2 | v3 | compatible
|
||||
--bin <path> 默认 /usr/local/bin/mihomo
|
||||
--conf-dir <dir> 默认 /etc/mihomo
|
||||
|
||||
check 退出码: 0 = 已是最新 10 = 有新版 1 = 出错
|
||||
EOF
|
||||
exit "${1:-0}"
|
||||
}
|
||||
|
||||
# ───────────────────────── 交互输入 ─────────────────────────
|
||||
# 关键: 从 /dev/tty 读, 这样 bash <(curl ...) 和管道场景都不会把 stdin 抢走
|
||||
TTY_IN="/dev/stdin"
|
||||
[[ -r /dev/tty ]] && TTY_IN="/dev/tty"
|
||||
|
||||
ask() { # ask "提示" varname
|
||||
local prompt="$1" __var="$2" __ans=""
|
||||
printf '%s' "$prompt" >&2
|
||||
read -r __ans < "$TTY_IN" || __ans=""
|
||||
printf -v "$__var" '%s' "$__ans"
|
||||
}
|
||||
|
||||
confirm() { # confirm "问题?" -> 0/1
|
||||
(( ASSUME_YES )) && return 0
|
||||
local a; ask "$1 ${D}[y/N]${N} " a
|
||||
[[ "$a" =~ ^[Yy]$ ]]
|
||||
}
|
||||
|
||||
pause() { local _x; ask "$(printf '\n%s按回车继续…%s' "$D" "$N")" _x; }
|
||||
|
||||
# ───────────────────────── 基础能力 ─────────────────────────
|
||||
need() { command -v "$1" >/dev/null 2>&1 || die "缺少命令: $1"; }
|
||||
|
||||
detect_arch() {
|
||||
[[ -n "$ARCH_OVERRIDE" ]] && { echo "$ARCH_OVERRIDE"; return; }
|
||||
case "$(uname -m)" in
|
||||
x86_64|amd64) echo "amd64" ;;
|
||||
aarch64|arm64) echo "arm64" ;;
|
||||
armv7l|armv7) echo "armv7" ;;
|
||||
*) die "未适配的架构: $(uname -m) (可用 --arch 指定)" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
asset_name() {
|
||||
local arch suffix=""; arch="$(detect_arch)"
|
||||
[[ "$arch" == "amd64" && -n "$VARIANT" ]] && suffix="-${VARIANT}"
|
||||
echo "mihomo-linux-${arch}${suffix}-${1}.gz"
|
||||
}
|
||||
|
||||
version_line() { # 不用 head, 避免 SIGPIPE 触发 ERR trap
|
||||
local out; out="$("$1" -v 2>/dev/null || true)"
|
||||
printf '%s' "${out%%$'\n'*}"
|
||||
}
|
||||
|
||||
current_version() {
|
||||
[[ -x "$BIN" ]] || { printf ''; return; }
|
||||
local line; line="$(version_line "$BIN")"
|
||||
[[ -n "$line" ]] && awk '{print $3}' <<<"$line" || printf ''
|
||||
}
|
||||
|
||||
latest_version() {
|
||||
local v
|
||||
v="$(curl -fsSL --retry 3 --connect-timeout 10 \
|
||||
"https://github.com/${REPO}/releases/latest/download/version.txt" | tr -d '[:space:]')" \
|
||||
|| die "拉取版本号失败 (GitHub 不通?)"
|
||||
[[ "$v" =~ ^v[0-9]+\.[0-9]+ ]] || die "版本号格式异常: $v"
|
||||
echo "$v"
|
||||
}
|
||||
|
||||
svc_state() {
|
||||
command -v systemctl >/dev/null 2>&1 || { echo "无 systemd"; return; }
|
||||
if systemctl is-active --quiet "$SERVICE" 2>/dev/null; then echo "running"
|
||||
elif systemctl list-unit-files 2>/dev/null | grep -q "^${SERVICE}\.service"; then echo "stopped"
|
||||
else echo "未安装"; fi
|
||||
}
|
||||
|
||||
# ───────────────────────── 动作 ─────────────────────────
|
||||
do_update() { # $1 = 目标版本
|
||||
local target="$1" cur; cur="$(current_version)"
|
||||
|
||||
if [[ "$cur" == "$target" && $FORCE -eq 0 ]]; then
|
||||
ok "已是最新 ($cur), 无需操作"; return 0
|
||||
fi
|
||||
[[ ${EUID:-$(id -u)} -eq 0 ]] || die "需要 root (要写 $BIN 并重启服务)"
|
||||
|
||||
TMPDIR_="$(mktemp -d)"
|
||||
local asset url; asset="$(asset_name "$target")"
|
||||
url="https://github.com/${REPO}/releases/download/${target}/${asset}"
|
||||
|
||||
info "下载 $asset"
|
||||
(( DRY_RUN )) && { ok "[dry-run] 会下载 $url"; return 0; }
|
||||
curl -fL -# --retry 3 --connect-timeout 15 -o "$TMPDIR_/m.gz" "$url" || die "下载失败: $url"
|
||||
gzip -dc "$TMPDIR_/m.gz" > "$TMPDIR_/mihomo" || die "解压失败"
|
||||
chmod +x "$TMPDIR_/mihomo"
|
||||
|
||||
# 闸门 1: 新二进制能跑, 版本号对得上
|
||||
local newline newver
|
||||
newline="$(version_line "$TMPDIR_/mihomo")"
|
||||
[[ -n "$newline" ]] || die "新二进制无法执行 (架构选错?)"
|
||||
newver="$(awk '{print $3}' <<<"$newline")"
|
||||
[[ "$newver" == "$target" ]] || die "版本对不上: 期望 $target, 实际 $newver"
|
||||
ok "校验通过: $newline"
|
||||
|
||||
# 闸门 2: 用新二进制验现有配置
|
||||
if [[ -f "$CONF_DIR/config.yaml" ]]; then
|
||||
if "$TMPDIR_/mihomo" -t -d "$CONF_DIR" >"$TMPDIR_/test.log" 2>&1; then
|
||||
ok "配置在新版本下校验通过"
|
||||
else
|
||||
warn "新版本校验现有配置失败:"; sed 's/^/ /' "$TMPDIR_/test.log" >&2
|
||||
die "已中止, 未替换任何文件"
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
if [[ -x "$BIN" && -n "$cur" ]]; then
|
||||
cp -a "$BIN" "$BACKUP_DIR/mihomo-$cur"; info "已备份旧版 → $BACKUP_DIR/mihomo-$cur"
|
||||
fi
|
||||
|
||||
local was_active=0
|
||||
[[ "$(svc_state)" == "running" ]] && was_active=1
|
||||
|
||||
trap - ERR # 下面自行处理失败并回滚
|
||||
(( was_active )) && { info "停止 $SERVICE"; systemctl stop "$SERVICE"; }
|
||||
install -m 0755 "$TMPDIR_/mihomo" "$BIN"
|
||||
ok "已安装 $target → $BIN"
|
||||
|
||||
if (( NO_RESTART )); then
|
||||
warn "--no-restart: 记得手动 systemctl start $SERVICE"
|
||||
elif (( was_active )); then
|
||||
systemctl start "$SERVICE" || true
|
||||
sleep 2
|
||||
if [[ "$(svc_state)" == "running" ]]; then
|
||||
ok "$SERVICE 运行中"
|
||||
else
|
||||
warn "$SERVICE 启动失败, 自动回滚"
|
||||
[[ -f "$BACKUP_DIR/mihomo-$cur" ]] && {
|
||||
install -m 0755 "$BACKUP_DIR/mihomo-$cur" "$BIN"
|
||||
systemctl start "$SERVICE" || true
|
||||
warn "已回滚到 $cur; 查日志: journalctl -u $SERVICE -n 50"
|
||||
}
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
compgen -G "$BACKUP_DIR/mihomo-*" >/dev/null && \
|
||||
ls -1t "$BACKUP_DIR"/mihomo-* | tail -n +$((KEEP + 1)) | xargs -r rm -f
|
||||
ok "完成: ${cur:-无} → $target"
|
||||
trap 'die "第 $LINENO 行出错, 已中止"' ERR
|
||||
}
|
||||
|
||||
do_rollback() {
|
||||
local b; b="$(ls -1t "$BACKUP_DIR"/mihomo-* 2>/dev/null | head -n1 || true)"
|
||||
[[ -n "$b" ]] || { warn "没有可回滚的备份"; return 1; }
|
||||
info "回滚到 $(basename "$b")"
|
||||
(( DRY_RUN )) && { ok "[dry-run] 到此为止"; return 0; }
|
||||
systemctl stop "$SERVICE" 2>/dev/null || true
|
||||
install -m 0755 "$b" "$BIN"
|
||||
systemctl start "$SERVICE" 2>/dev/null || true
|
||||
ok "已回滚: $(version_line "$BIN")"
|
||||
}
|
||||
|
||||
do_list() {
|
||||
if compgen -G "$BACKUP_DIR/mihomo-*" >/dev/null 2>&1; then
|
||||
ls -1t "$BACKUP_DIR"/mihomo-* | while read -r f; do
|
||||
printf ' %s %s%s%s\n' "$(basename "$f")" "$D" "$(date -r "$f" '+%F %T')" "$N"
|
||||
done
|
||||
else
|
||||
log " ${D}没有备份 ($BACKUP_DIR)${N}"
|
||||
fi
|
||||
}
|
||||
|
||||
do_test_conf() {
|
||||
[[ -x "$BIN" ]] || { warn "mihomo 还没装"; return 1; }
|
||||
[[ -f "$CONF_DIR/config.yaml" ]] || { warn "找不到 $CONF_DIR/config.yaml"; return 1; }
|
||||
if "$BIN" -t -d "$CONF_DIR"; then ok "配置有效"; else warn "配置有问题, 见上面报错"; fi
|
||||
}
|
||||
|
||||
# ───────────────────────── 菜单 ─────────────────────────
|
||||
banner() {
|
||||
printf '%s\n' "" >&2
|
||||
cat >&2 <<EOF
|
||||
${C} ╭──────────────────────────────────────────╮
|
||||
│ ${W}mihomo 内核管理${N}${C} ╭( ・ㅂ・)و ✧ │
|
||||
╰──────────────────────────────────────────╯${N}
|
||||
EOF
|
||||
}
|
||||
|
||||
status_panel() {
|
||||
local cur latest svc conf
|
||||
cur="$(current_version)"; svc="$(svc_state)"
|
||||
latest="$(latest_version 2>/dev/null || echo '查询失败')"
|
||||
|
||||
if [[ -x "$BIN" && -f "$CONF_DIR/config.yaml" ]]; then
|
||||
"$BIN" -t -d "$CONF_DIR" >/dev/null 2>&1 && conf="${G}有效${N}" || conf="${R}有错${N}"
|
||||
else
|
||||
conf="${D}无配置${N}"
|
||||
fi
|
||||
|
||||
local cur_s="${cur:-${Y}未安装${N}}" latest_s="$latest"
|
||||
[[ -n "$cur" && "$cur" != "$latest" && "$latest" != "查询失败" ]] && latest_s="${Y}${latest} ← 有新版${N}"
|
||||
[[ -n "$cur" && "$cur" == "$latest" ]] && latest_s="${G}${latest}${N}"
|
||||
|
||||
case "$svc" in
|
||||
running) svc="${G}● 运行中${N}" ;;
|
||||
stopped) svc="${Y}○ 已停止${N}" ;;
|
||||
*) svc="${D}${svc}${N}" ;;
|
||||
esac
|
||||
|
||||
printf ' %s当前版本%s %s\n' "$D" "$N" "$cur_s" >&2
|
||||
printf ' %s最新版本%s %s\n' "$D" "$N" "$latest_s" >&2
|
||||
printf ' %s服务状态%s %s\n' "$D" "$N" "$svc" >&2
|
||||
printf ' %s配置校验%s %s\n' "$D" "$N" "$conf" >&2
|
||||
printf ' %s架构 %s %s\n' "$D" "$N" "$(detect_arch)" >&2
|
||||
}
|
||||
|
||||
menu() {
|
||||
while true; do
|
||||
clear 2>/dev/null || true
|
||||
banner
|
||||
status_panel
|
||||
cat >&2 <<EOF
|
||||
|
||||
${W} 选个操作吧:${N}
|
||||
${C}1.${N} 🔍 检查更新
|
||||
${C}2.${N} ⬆️ 升级到最新版
|
||||
${C}3.${N} 📌 安装指定版本
|
||||
${C}4.${N} ⏪ 回滚到上一版
|
||||
${C}5.${N} 📦 查看本地备份
|
||||
${C}6.${N} 🧪 校验配置文件
|
||||
${C}7.${N} 🎛️ 服务控制 (启停/日志)
|
||||
${C}0.${N} 👋 退出
|
||||
EOF
|
||||
local c; ask "$(printf '\n %s>%s ' "$W" "$N")" c
|
||||
echo >&2
|
||||
case "$c" in
|
||||
1) local cur latest; cur="$(current_version)"; latest="$(latest_version)"
|
||||
if [[ -z "$cur" ]]; then warn "还没安装, 选 2 装上"
|
||||
elif [[ "$cur" == "$latest" ]]; then ok "已是最新 $cur"
|
||||
else warn "有新版: $cur → $latest"
|
||||
confirm " 现在升级?" && do_update "$latest"; fi
|
||||
pause ;;
|
||||
2) local latest; latest="$(latest_version)"
|
||||
confirm " 升级/重装到 $latest ?" && { FORCE=1; do_update "$latest"; FORCE=0; }
|
||||
pause ;;
|
||||
3) local v; ask " 输入版本号 (如 v1.19.28): " v
|
||||
[[ "$v" =~ ^v[0-9] ]] && { FORCE=1; do_update "$v"; FORCE=0; } || warn "版本号格式不对"
|
||||
pause ;;
|
||||
4) confirm " 确认回滚?" && do_rollback; pause ;;
|
||||
5) echo >&2; do_list; pause ;;
|
||||
6) do_test_conf || true; pause ;;
|
||||
7) svc_menu ;;
|
||||
0|q|"") log " ${D}bye~${N}"; exit 0 ;;
|
||||
*) warn "没这个选项啦"; sleep 1 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
svc_menu() {
|
||||
cat >&2 <<EOF
|
||||
|
||||
${C}1.${N} ▶️ 启动 ${C}2.${N} ⏹️ 停止 ${C}3.${N} 🔄 重启
|
||||
${C}4.${N} 📊 状态 ${C}5.${N} 📜 最近日志 ${C}0.${N} ↩️ 返回
|
||||
EOF
|
||||
local c; ask "$(printf '\n %s>%s ' "$W" "$N")" c
|
||||
case "$c" in
|
||||
1) systemctl start "$SERVICE" && ok "已启动" ;;
|
||||
2) systemctl stop "$SERVICE" && ok "已停止" ;;
|
||||
3) systemctl restart "$SERVICE" && ok "已重启" ;;
|
||||
4) systemctl status "$SERVICE" --no-pager || true ;;
|
||||
5) journalctl -u "$SERVICE" -n 40 --no-pager || true ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
pause
|
||||
}
|
||||
|
||||
# ───────────────────────── 参数解析 ─────────────────────────
|
||||
CMD=""
|
||||
if [[ $# -eq 0 ]]; then
|
||||
# stdin 是终端就进菜单; 被重定向/管道 (cron、CI) 就安静地跑 check
|
||||
if [[ -t 0 ]] || { [[ -t 1 ]] && [[ -r /dev/tty ]]; }; then CMD="menu"; else CMD="check"; fi
|
||||
else
|
||||
case "$1" in
|
||||
check|update|install|rollback|list|menu) CMD="$1"; shift ;;
|
||||
-h|--help|help) usage 0 ;;
|
||||
-*) CMD="check" ;;
|
||||
*) echo "未知子命令: $1" >&2; usage 1 ;;
|
||||
esac
|
||||
fi
|
||||
[[ "$CMD" == install ]] && { CMD="update"; FORCE=1; }
|
||||
|
||||
while (( $# )); do
|
||||
case "$1" in
|
||||
--force) FORCE=1 ;;
|
||||
-y|--yes) ASSUME_YES=1 ;;
|
||||
-n|--dry-run) DRY_RUN=1 ;;
|
||||
--no-restart) NO_RESTART=1 ;;
|
||||
--json) JSON=1 ;;
|
||||
--version) WANT_VERSION="${2:?}"; shift ;;
|
||||
--arch) ARCH_OVERRIDE="${2:?}"; shift ;;
|
||||
--variant) VARIANT="${2:?}"; shift ;;
|
||||
--bin) BIN="${2:?}"; shift ;;
|
||||
--conf-dir) CONF_DIR="${2:?}"; shift ;;
|
||||
-h|--help) usage 0 ;;
|
||||
*) die "未知参数: $1" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
need curl; need gzip; need install
|
||||
|
||||
# ───────────────────────── 分发 ─────────────────────────
|
||||
case "$CMD" in
|
||||
menu) menu ;;
|
||||
list) do_list ;;
|
||||
rollback) do_rollback ;;
|
||||
check)
|
||||
CUR="$(current_version)"; TARGET="${WANT_VERSION:-$(latest_version)}"
|
||||
(( JSON )) && printf '{"current":"%s","latest":"%s","outdated":%s}\n' \
|
||||
"${CUR:-null}" "$TARGET" "$([[ "$CUR" != "$TARGET" ]] && echo true || echo false)"
|
||||
log "当前: ${CUR:-${Y}未安装${N}} 最新: $TARGET"
|
||||
[[ -z "$CUR" ]] && { log "→ 未安装"; exit 10; }
|
||||
[[ "$CUR" == "$TARGET" ]] && { ok "已是最新"; exit 0; }
|
||||
warn "有新版本可升级"; exit 10 ;;
|
||||
update)
|
||||
TARGET="${WANT_VERSION:-$(latest_version)}"
|
||||
CUR="$(current_version)"
|
||||
log "当前: ${CUR:-${Y}未安装${N}} 目标: $TARGET"
|
||||
do_update "$TARGET" ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user