Files
pangolin/app/kernel/fetch-desktop-bin.sh
T
wangjia 4561ee4cc3
Deploy Client / build-android (push) Successful in 1m58s
Deploy Client / build-windows (push) Successful in 6m51s
Deploy Client / release-deploy (push) Successful in 1m13s
fix(ci): fetch-desktop-bin curl 加固(windows 外网 GFW 抖动)
wintun.net / github 下载偶发超时(curl 28)。retry 3→8、加 --retry-all-errors
+ --retry-connrefused + --connect-timeout 20,拉长重试窗口以命中可达时刻。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 22:56:20 +08:00

237 lines
10 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# fetch-desktop-bin.sh — 从 sing-box GitHub Release 下载桌面平台二进制
#
# 用法: ./fetch-desktop-bin.sh <os> <arch> [--force]
# os : linux | darwin | windows
# arch : amd64 | arm64
# --force 忽略已有产物,强制重新下载
#
# 产物目录: dist/desktop/<os>-<arch>/
# Linux/macOS : sing-box
# Windows : sing-box.exe + wintun.dll
#
# SHA256 校验: 从官方 Release 下载 checksums.txt 后逐项验证。
# 校验通过后,checksums.txt 缓存于产物目录,可离线复核。
#
# Wintun (Windows 专用):
# 版本由 VERSION 中 WINTUN_VERSION 控制。
# 从 https://www.wintun.net/builds/wintun-<ver>.zip 下载,
# 校验 zip 的 SHA256(见下方 WINTUN_SHA256 变量),
# 提取对应 arch 的 wintun.dll 到产物目录。
#
# 幂等: 产物已存在且 SHA256 校验通过则跳过下载(传 --force 强制重下)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ── 加载版本锚点 ─────────────────────────────────────────────────────────────
# shellcheck source=VERSION
. "${SCRIPT_DIR}/VERSION"
# ── Wintun ZIP SHA256(对应 WINTUN_VERSION=0.14.1)─────────────────────────
# 更新 WINTUN_VERSION 时须同步更新此值:
# curl -sL https://www.wintun.net/builds/wintun-<ver>.zip | sha256sum
WINTUN_SHA256="07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51"
# ── 参数解析 ─────────────────────────────────────────────────────────────────
if [[ $# -lt 2 ]]; then
printf 'Usage: %s <os> <arch> [--force]\n' "$0" >&2
printf ' os : linux | darwin | windows\n' >&2
printf ' arch: amd64 | arm64\n' >&2
exit 1
fi
TARGET_OS="$1"
TARGET_ARCH="$2"
FORCE=false
shift 2
for arg in "$@"; do
case "${arg}" in
--force) FORCE=true ;;
*) printf 'Unknown argument: %s\n' "${arg}" >&2; exit 1 ;;
esac
done
# ── 参数验证 ─────────────────────────────────────────────────────────────────
case "${TARGET_OS}" in
linux|darwin|windows) ;;
*) printf '✗ 不支持的 OS: %s(支持: linux | darwin | windows\n' "${TARGET_OS}" >&2; exit 1 ;;
esac
case "${TARGET_ARCH}" in
amd64|arm64) ;;
*) printf '✗ 不支持的 arch: %s(支持: amd64 | arm64\n' "${TARGET_ARCH}" >&2; exit 1 ;;
esac
# ── 路径与文件名配置 ─────────────────────────────────────────────────────────
DIST_DIR="${SCRIPT_DIR}/dist/desktop/${TARGET_OS}-${TARGET_ARCH}"
CACHE_DIR="${SCRIPT_DIR}/.build/cache"
VERSION_BARE="${SINGBOX_VERSION#v}" # 去掉前缀 v(如 v1.13.12 → 1.13.12
if [[ "${TARGET_OS}" == "windows" ]]; then
ARCHIVE_EXT="zip"
BIN_NAME="sing-box.exe"
else
ARCHIVE_EXT="tar.gz"
BIN_NAME="sing-box"
fi
ARCHIVE_STEM="sing-box-${VERSION_BARE}-${TARGET_OS}-${TARGET_ARCH}"
ARCHIVE_FILE="${ARCHIVE_STEM}.${ARCHIVE_EXT}"
GITHUB_BASE="https://github.com/SagerNet/sing-box/releases/download/${SINGBOX_VERSION}"
ARCHIVE_URL="${GITHUB_BASE}/${ARCHIVE_FILE}"
# ── sing-box 压缩包 SHA256(内置 pin)──────────────────────────────────────────
# sing-box 自 v1.13 起的 Release 不再发布 checksums.txt,故改为脚本内置各压缩包
# 的 SHA256。更新 SINGBOX_VERSION 时必须同步更新下表(case 键含版本号,未更新会
# 命中 *) 分支并报错,以防用错版本的哈希):
# curl -fsSL "${GITHUB_BASE}/<archive>" | shasum -a 256
EXPECTED_HASH=""
case "${ARCHIVE_FILE}" in
sing-box-1.13.12-windows-amd64.zip) EXPECTED_HASH="e93fc531134eb1beb4efa3c74990a24e48456098a31c03b60d5ddf17f223cf98" ;;
sing-box-1.13.12-darwin-arm64.tar.gz) EXPECTED_HASH="43eef86f0ea4a79c3696974f397a963c46a457ee46d1ffac9aa913944a5fc986" ;;
sing-box-1.13.12-darwin-amd64.tar.gz) EXPECTED_HASH="f3275316451bf1983bc059599c69c8ed0232d53a619d15cfd535f95cc9a4477a" ;;
sing-box-1.13.12-linux-amd64.tar.gz) EXPECTED_HASH="1540533adb3df24f5ad5f14b5c7ca3dbc2401b10a1c1eb278fcadcada47ec6c4" ;;
esac
OUT_BIN="${DIST_DIR}/${BIN_NAME}"
# ── 工具检查 ─────────────────────────────────────────────────────────────────
_need() {
if ! command -v "$1" >/dev/null 2>&1; then
printf '✗ 依赖未找到: %s\n' "$1" >&2; exit 1
fi
}
_need curl
_need sha256sum || _need shasum # macOS 用 shasumLinux 用 sha256sum
# 兼容 macOS (shasum -a 256) 和 Linux (sha256sum)
_sha256() {
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$1" | awk '{print $1}'
else
shasum -a 256 "$1" | awk '{print $1}'
fi
}
# ── 幂等检查(已有产物则跳过;--force 强制重下)────────────────────────────────
if [[ "${FORCE}" == false && -f "${OUT_BIN}" ]]; then
printf '✓ %s 已存在,跳过下载\n' "${OUT_BIN}"
printf ' 当前 SHA256: %s\n' "$(_sha256 "${OUT_BIN}")"
printf ' (传 --force 强制重新下载并校验压缩包)\n'
exit 0
fi
# ── 创建目录 ─────────────────────────────────────────────────────────────────
mkdir -p "${DIST_DIR}"
mkdir -p "${CACHE_DIR}"
ARCHIVE_CACHE="${CACHE_DIR}/${ARCHIVE_FILE}"
# 未在内置 pin 表中找到该压缩包的 SHA256(多半是 SINGBOX_VERSION 升级后没同步哈希)
if [[ -z "${EXPECTED_HASH}" ]]; then
printf '✗ 脚本内未内置 %s 的 SHA256\n' "${ARCHIVE_FILE}" >&2
printf ' 升级 SINGBOX_VERSION 后需在本脚本 case 表补该压缩包哈希:\n' >&2
printf ' curl -fsSL "%s" | shasum -a 256\n' "${ARCHIVE_URL}" >&2
exit 1
fi
# ── 下载二进制压缩包 ──────────────────────────────────────────────────────────
printf '==> 下载 %s…\n' "${ARCHIVE_FILE}"
curl -fSL --retry 8 --retry-delay 5 --retry-connrefused --retry-all-errors --connect-timeout 20 \
-o "${ARCHIVE_CACHE}" \
"${ARCHIVE_URL}"
# ── SHA256 校验(对压缩包,比对内置 pin)──────────────────────────────────────
printf '==> 校验 SHA256…\n'
ACTUAL_HASH="$(_sha256 "${ARCHIVE_CACHE}")"
if [[ "${ACTUAL_HASH}" != "${EXPECTED_HASH}" ]]; then
printf '✗ SHA256 不匹配!\n' >&2
printf ' 期望: %s\n' "${EXPECTED_HASH}" >&2
printf ' 实际: %s\n' "${ACTUAL_HASH}" >&2
rm -f "${ARCHIVE_CACHE}"
exit 1
fi
printf ' ✓ SHA256 匹配: %s\n' "${ACTUAL_HASH}"
# ── 解压二进制 ────────────────────────────────────────────────────────────────
printf '==> 解压 → %s\n' "${DIST_DIR}"
EXTRACT_TMP="${CACHE_DIR}/extract-${ARCHIVE_STEM}"
rm -rf "${EXTRACT_TMP}"
mkdir -p "${EXTRACT_TMP}"
if [[ "${ARCHIVE_EXT}" == "zip" ]]; then
if ! command -v unzip >/dev/null 2>&1; then
printf '✗ unzip 未找到\n' >&2; exit 1
fi
unzip -q "${ARCHIVE_CACHE}" -d "${EXTRACT_TMP}"
else
tar -xzf "${ARCHIVE_CACHE}" -C "${EXTRACT_TMP}"
fi
# sing-box 压缩包内层目录名与 ARCHIVE_STEM 相同
INNER_DIR="${EXTRACT_TMP}/${ARCHIVE_STEM}"
if [[ ! -f "${INNER_DIR}/${BIN_NAME}" ]]; then
printf '✗ 压缩包中未找到 %s/%s\n' "${ARCHIVE_STEM}" "${BIN_NAME}" >&2
exit 1
fi
cp "${INNER_DIR}/${BIN_NAME}" "${OUT_BIN}"
chmod 755 "${OUT_BIN}"
printf '\n✓ sing-box 二进制: %s\n' "${OUT_BIN}"
printf ' 版本验证: %s version\n' "${OUT_BIN}"
# ── Windows 额外处理: wintun.dll ─────────────────────────────────────────────
if [[ "${TARGET_OS}" == "windows" ]]; then
printf '\n==> Windows: 下载 wintun.dll (v%s)…\n' "${WINTUN_VERSION}"
WINTUN_ZIP_NAME="wintun-${WINTUN_VERSION}.zip"
WINTUN_URL="https://www.wintun.net/builds/${WINTUN_ZIP_NAME}"
WINTUN_ZIP_CACHE="${CACHE_DIR}/${WINTUN_ZIP_NAME}"
WINTUN_OUT="${DIST_DIR}/wintun.dll"
if [[ "${FORCE}" == false && -f "${WINTUN_OUT}" ]]; then
printf '✓ wintun.dll 已存在,跳过(传 --force 重新下载)\n'
else
curl -fSL --retry 8 --retry-delay 5 --retry-connrefused --retry-all-errors --connect-timeout 20 \
-o "${WINTUN_ZIP_CACHE}" \
"${WINTUN_URL}"
printf '==> 校验 wintun.zip SHA256…\n'
WINTUN_ACTUAL="$(_sha256 "${WINTUN_ZIP_CACHE}")"
if [[ "${WINTUN_ACTUAL}" != "${WINTUN_SHA256}" ]]; then
printf '✗ wintun.zip SHA256 不匹配!\n' >&2
printf ' 期望: %s\n' "${WINTUN_SHA256}" >&2
printf ' 实际: %s\n' "${WINTUN_ACTUAL}" >&2
printf ' 如果 WINTUN_VERSION 已更新,请同步更新脚本中的 WINTUN_SHA256\n' >&2
rm -f "${WINTUN_ZIP_CACHE}"
exit 1
fi
printf ' ✓ wintun.zip SHA256 匹配\n'
# 提取对应 arch 的 wintun.dll
# wintun zip 内路径: wintun/bin/<arch>/wintun.dll
WINTUN_EXTRACT="${CACHE_DIR}/extract-wintun"
rm -rf "${WINTUN_EXTRACT}"
mkdir -p "${WINTUN_EXTRACT}"
unzip -q "${WINTUN_ZIP_CACHE}" -d "${WINTUN_EXTRACT}"
WINTUN_DLL_SRC="${WINTUN_EXTRACT}/wintun/bin/${TARGET_ARCH}/wintun.dll"
if [[ ! -f "${WINTUN_DLL_SRC}" ]]; then
printf '✗ wintun zip 中未找到 %s arch 的 wintun.dll\n' "${TARGET_ARCH}" >&2
exit 1
fi
cp "${WINTUN_DLL_SRC}" "${WINTUN_OUT}"
printf '✓ wintun.dll: %s\n' "${WINTUN_OUT}"
fi
fi
# ── 最终输出摘要 ─────────────────────────────────────────────────────────────
printf '\n=== 产物清单: %s ===\n' "${DIST_DIR}"
ls -lh "${DIST_DIR}/"