feat: 一条 curl 自动装/升(未装安装、已装升级)

This commit is contained in:
Hermes
2026-07-21 14:23:31 +00:00
commit b2a7332f86
20 changed files with 3459 additions and 0 deletions
+256
View File
@@ -0,0 +1,256 @@
#!/usr/bin/env bash
# 仅下载/安装 sing-box(轻量核心)
# 优先级:local → 系统已有 → Gitea Release(整包/分片)→ GitHub 代理 → 直连 GitHub
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# shellcheck source=lib.sh
source "$ROOT_DIR/scripts/lib.sh"
load_config
need_root
ensure_dirs
mkdir -p /var/lib/cellular-proxy
ARCH="$(arch_go)"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
GITHUB_PROXY="${GITHUB_PROXY:-https://git.86482425.xyz}"
GITEA_BASE="${GITEA_BASE:-https://gitea.chickliu.fun}"
GITEA_OWNER="${GITEA_OWNER:-Hermes}"
GITEA_REPO="${GITEA_REPO:-cellular-proxy}"
SING_BOX_RELEASE_TAG="${SING_BOX_RELEASE_TAG:-bin-v${SING_BOX_VERSION}}"
# gitea | github | auto | local | release
SING_BOX_SOURCE="${SING_BOX_SOURCE:-auto}"
github_url() {
local raw="$1"
local proxy="${GITHUB_PROXY:-}"
if [[ -z "$proxy" ]]; then
echo "$raw"
return
fi
proxy="${proxy%/}"
case "$raw" in
"${proxy}"/*) echo "$raw"; return ;;
esac
echo "${proxy}/${raw}"
}
download() {
local url="$1" dest="$2"
info "下载: $url"
if command -v curl >/dev/null 2>&1; then
curl -fL --retry 3 --connect-timeout 20 --max-time 300 -o "$dest" "$url"
elif command -v wget >/dev/null 2>&1; then
wget -O "$dest" "$url"
else
die "需要 curl 或 wget"
fi
}
# 静默探测 URL 是否可下(不写盘)
url_ok() {
local url="$1"
if command -v curl >/dev/null 2>&1; then
curl -fsSIL --connect-timeout 8 --max-time 20 -o /dev/null "$url" 2>/dev/null
else
return 1
fi
}
download_github() {
local raw="$1" dest="$2"
local proxied
proxied="$(github_url "$raw")"
if [[ "$proxied" != "$raw" ]]; then
info "经 GitHub 代理下载: $proxied"
if download "$proxied" "$dest"; then
return 0
fi
warn "代理下载失败,尝试直连 GitHub: $raw"
fi
download "$raw" "$dest"
}
install_from_tarball() {
local tgz="$1" ver="$2" arch="$3"
local name="sing-box-${ver}-linux-${arch}"
tar -xzf "$tgz" -C "$TMP"
if [[ -x "$TMP/${name}/sing-box" ]]; then
install -m 0755 "$TMP/${name}/sing-box" "$INSTALL_DIR/bin/sing-box"
elif [[ -x "$TMP/sing-box" ]]; then
install -m 0755 "$TMP/sing-box" "$INSTALL_DIR/bin/sing-box"
else
local found
found="$(find "$TMP" -type f -name sing-box 2>/dev/null | head -1)"
[[ -n "$found" && -x "$found" ]] || die "压缩包内未找到 sing-box 可执行文件"
install -m 0755 "$found" "$INSTALL_DIR/bin/sing-box"
fi
}
gitea_asset_url() {
local fname="$1"
echo "${GITEA_BASE%/}/${GITEA_OWNER}/${GITEA_REPO}/releases/download/${SING_BOX_RELEASE_TAG}/${fname}"
}
# 从 Gitea 拉整包 tar.gz
try_download_gitea_whole() {
local ver="$1" arch="$2" dest="$3"
local name url
name="sing-box-${ver}-linux-${arch}.tar.gz"
url="$(gitea_asset_url "$name")"
info "尝试 Gitea 预置整包: $url"
if download "$url" "$dest"; then
return 0
fi
return 1
}
# 代理 504 时可能只提供分片:name.tar.gz.part-00.. + name.tar.gz.SHA256SUMS
try_download_gitea_parts() {
local ver="$1" arch="$2" dest="$3"
local base name sums_url part_url part_dir i max
name="sing-box-${ver}-linux-${arch}.tar.gz"
base="${GITEA_BASE%/}/${GITEA_OWNER}/${GITEA_REPO}/releases/download/${SING_BOX_RELEASE_TAG}"
sums_url="${base}/${name}.SHA256SUMS"
# 兼容旧名 SHA256SUMS(仅 armv7 曾用)
part_dir="$TMP/parts_${arch}"
mkdir -p "$part_dir"
if download "$sums_url" "$part_dir/SHA256SUMS" 2>/dev/null; then
info "发现分片清单: $sums_url"
elif [[ "$arch" == "armv7" ]] && download "$(gitea_asset_url SHA256SUMS)" "$part_dir/SHA256SUMS" 2>/dev/null; then
info "使用兼容清单 SHA256SUMS"
else
return 1
fi
# 从清单提取 part 文件名(去掉路径前缀)
mapfile -t parts < <(awk '{print $2}' "$part_dir/SHA256SUMS" | sed 's|^\./||' | grep -E '\.part-[0-9]+$' || true)
if [[ ${#parts[@]} -eq 0 ]]; then
# 无清单文件名时按 part-00..99 探测
parts=()
for i in $(seq -w 0 99); do
part_url="${base}/${name}.part-${i}"
if url_ok "$part_url"; then
parts+=("${name}.part-${i}")
else
[[ ${#parts[@]} -gt 0 ]] && break
fi
done
fi
[[ ${#parts[@]} -gt 0 ]] || return 1
info "Gitea 分片下载 ${#parts[@]} 片 → 合并 $name"
: >"$dest"
for p in "${parts[@]}"; do
p="${p#./}"
# 若清单里只有 basename part-xx,补全前缀
case "$p" in
*.part-*) ;;
*) p="${name}.part-${p##*-}" ;;
esac
# 清单可能是 relative part name only like sing-box-...part-00
if [[ "$p" != *"/"* && "$p" != ${name}.part-* && "$p" == *.part-* ]]; then
:
fi
part_url="${base}/${p}"
if ! download "$part_url" "$part_dir/$p"; then
# 尝试无前缀名
local bn
bn="$(basename "$p")"
part_url="${base}/${bn}"
download "$part_url" "$part_dir/$bn" || return 1
p="$bn"
fi
cat "$part_dir/$p" >>"$dest"
done
# 可选校验
if command -v sha256sum >/dev/null 2>&1; then
(
cd "$part_dir"
# 规范化清单路径
sed 's| \./| |; s|^\([a-f0-9]*\) \./|\1 |' SHA256SUMS > SHA256SUMS.norm || cp SHA256SUMS SHA256SUMS.norm
if sha256sum -c SHA256SUMS.norm --status 2>/dev/null; then
info "分片 SHA256 校验通过"
else
warn "分片 SHA256 校验跳过/未严格通过(已合并,继续)"
fi
) || true
fi
# 快速完整性:tar 列表
tar -tzf "$dest" >/dev/null
return 0
}
try_download_gitea() {
local ver="$1" arch="$2" dest="$3"
if try_download_gitea_whole "$ver" "$arch" "$dest"; then
return 0
fi
warn "Gitea 整包不可用,尝试分片合并(大文件/代理 504 兜底)"
if try_download_gitea_parts "$ver" "$arch" "$dest"; then
return 0
fi
return 1
}
try_download_github_release() {
local ver="$1" arch="$2" dest="$3"
local name raw_url
name="sing-box-${ver}-linux-${arch}"
raw_url="https://github.com/SagerNet/sing-box/releases/download/v${ver}/${name}.tar.gz"
download_github "$raw_url" "$dest"
}
if [[ "$SING_BOX_SOURCE" == "local" ]]; then
[[ -n "$SING_BOX_BIN" && -x "$SING_BOX_BIN" ]] || die "SING_BOX_SOURCE=local 但 SING_BOX_BIN 无效"
install -m 0755 "$SING_BOX_BIN" "$INSTALL_DIR/bin/sing-box"
info "已安装本地 sing-box"
elif command -v sing-box >/dev/null 2>&1 && [[ -z "${FORCE_DOWNLOAD:-}" && "$SING_BOX_SOURCE" != "gitea" && "$SING_BOX_SOURCE" != "github" ]]; then
install -m 0755 "$(command -v sing-box)" "$INSTALL_DIR/bin/sing-box"
info "复用系统 sing-box: $(command -v sing-box)"
else
ver="$SING_BOX_VERSION"
tgz="$TMP/sb.tgz"
ok=0
case "$SING_BOX_SOURCE" in
gitea)
try_download_gitea "$ver" "$ARCH" "$tgz" || die "Gitea 预置包下载失败(tag=$SING_BOX_RELEASE_TAG arch=$ARCH"
ok=1
;;
github|release)
try_download_github_release "$ver" "$ARCH" "$tgz"
ok=1
;;
auto|*)
if try_download_gitea "$ver" "$ARCH" "$tgz"; then
ok=1
info "已从 Gitea Release 获取 sing-box"
else
warn "Gitea 预置包不可用,回退 GitHub(经 ${GITHUB_PROXY:-直连}"
try_download_github_release "$ver" "$ARCH" "$tgz"
ok=1
fi
;;
esac
[[ "$ok" -eq 1 ]] || die "无法下载 sing-box"
install_from_tarball "$tgz" "$ver" "$ARCH"
info "已安装 sing-box v${ver} (${ARCH})"
"$INSTALL_DIR/bin/sing-box" version 2>/dev/null | head -3 || true
fi
# 安装中文静态 UI
mkdir -p "$INSTALL_DIR/ui"
if [[ -f "$ROOT_DIR/ui/index.html" ]]; then
cp -a "$ROOT_DIR/ui/." "$INSTALL_DIR/ui/"
info "已安装内置中文面板 -> $INSTALL_DIR/ui"
else
warn "未找到 ui/index.html"
fi
info "二进制就绪:"
ls -la "$INSTALL_DIR/bin"
du -sh "$INSTALL_DIR/bin" "$INSTALL_DIR/ui" 2>/dev/null || true