From a8d6dd4b87a6a31616d38e46aec7172c060faa16 Mon Sep 17 00:00:00 2001 From: jocay Date: Sun, 18 Oct 2026 10:57:56 +0800 Subject: [PATCH] =?UTF-8?q?deploy:=20=E7=AB=99=E7=82=B9=E6=8E=A2=E6=B5=8B?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E5=BC=BA=E5=88=B6=20IPv4/IPv6=20=E5=B9=B6?= =?UTF-8?q?=E5=8A=A0=E8=B6=85=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git.jocayn.qzz.io 在 Cloudflare 后面同时有 IPv4/IPv6 地址,部分网络(如 nas)IPv4 不通、 IPv6 可用;原来用 -4 强制 IPv4 且没有连接超时,会重试到卡死。改为由 curl 自动选择协议族, 探测 8 秒连接超时、15 秒总时长且不重试,下载加 15 秒连接超时。 Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 2 +- README.md | 2 +- deploy/install.sh | 32 ++++++++++++++++++-------------- deploy/update.sh | 33 +++++++++++++++++---------------- 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 923ce11..a83b2a7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -44,7 +44,7 @@ cargo check --release --all-targets --target x86_64-pc-windows-gnu # 在 Linux - `install.sh`:首次安装。自动识别 Linux(systemd:`/usr/local/bin/bolt`、`/etc/bolt.env`、`bolt.service`)或 macOS(launchd 标签 `io.jocay.bolt`,plist 放在 `/Library/Application Support/bolt/`,启用开机自启时复制到 `/Library/LaunchDaemons/`)。已安装时拒绝执行,除非加 `--reinstall`,因为它会按本次参数重写配置。 - `update.sh`:只替换二进制,保留配置。重启和健康检查交给独立进程执行(Linux 用 `systemd-run`,macOS 用 `(trap '' HUP; sh … &)`;macOS 上 `nohup … &` 启动的进程会在调用返回后消失),失败时从 `/usr/local/bin/bolt.bak` 回滚。 - `uninstall.sh`:先删文件,最后才停服务,因为通过 bolt 自身执行时,停服务会结束脚本本身。 -- 下载站点:IPv4 用 `https://git.jocayn.qzz.io`,IPv6 用 `https://git.draws.qzz.io`(同一个 Gitea,仓库 `jocay/bolt` 已公开)。脚本先用 `-4` 试 IPv4 站点,再用 `-6` 试 IPv6 站点;最新版本通过 `/api/v1/repos/jocay/bolt/releases/latest` 获取(这个 Gitea 不支持 `releases/latest/download/` 直链),附件命名为 `bolt--`,可选附带 `SHA256SUMS`。`BOLT_GIT_HOST`、`BOLT_VERSION`、`BOLT_DL_URL`、`--binary` 可以覆盖;`BOLT_DL_TOKEN`(下载认证)与 `--token`(服务器的 Bearer 令牌)相互独立。 +- 下载站点:IPv4 用 `https://git.jocayn.qzz.io`,IPv6 用 `https://git.draws.qzz.io`(同一个 Gitea,仓库 `jocay/bolt` 已公开)。脚本先试 IPv4 站点再试 IPv6 站点(不强制 `-4`/`-6`:`git.jocayn.qzz.io` 在 Cloudflare 后面,同时有 IPv4 和 IPv6 地址,有的网络只有其中一种能通;探测带 8 秒连接超时、不重试);最新版本通过 `/api/v1/repos/jocay/bolt/releases/latest` 获取(这个 Gitea 不支持 `releases/latest/download/` 直链),附件命名为 `bolt--`,可选附带 `SHA256SUMS`。`BOLT_GIT_HOST`、`BOLT_VERSION`、`BOLT_DL_URL`、`--binary` 可以覆盖;`BOLT_DL_TOKEN`(下载认证)与 `--token`(服务器的 Bearer 令牌)相互独立。 - 发布用的二进制用 cargo-zigbuild 编译:`x86_64-unknown-linux-gnu.2.28`(普通 `cargo build` 产物依赖构建机的 glibc 版本,较老的系统上跑不了)和 `aarch64-apple-darwin`。 - `bolt.service` 和 `bolt.env.example` 用于手动 systemd 部署。监听地址写在 unit 文件的 `ExecStart` 里,其余配置来自 `/etc/bolt.env` 中的 `BOLT_*` 变量。 - 修改 CLI 参数时,需同步更新 `parse_args`/`print_help`、`install.sh`(参数解析及其生成的 env/plist)、`bolt.env.example` 和 README 中的参数表。 diff --git a/README.md b/README.md index 47f6daf..94b509a 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ python3 docs/bolt_http_test.py # HTTP 端到端(含 Mcp-Session-Id、/files ## 在其他设备上部署(一键脚本) -脚本支持 Linux(systemd)和 macOS(launchd),自动识别 amd64 / arm64(Apple Silicon 即使在 Rosetta 终端中也优先选择 arm64),需要 root 权限。仓库公开,无需令牌;IPv4 网络用 `git.jocayn.qzz.io`,IPv6 网络用 `git.draws.qzz.io`(脚本下载二进制时也会自动先试 IPv4 站点、再试 IPv6 站点)。 +脚本支持 Linux(systemd)和 macOS(launchd),自动识别 amd64 / arm64(Apple Silicon 即使在 Rosetta 终端中也优先选择 arm64),需要 root 权限。仓库公开,无需令牌;IPv4 网络用 `git.jocayn.qzz.io`,IPv6 网络用 `git.draws.qzz.io`(脚本下载二进制时先试 `git.jocayn.qzz.io`、不通再试 `git.draws.qzz.io`,协议族由 curl 自动选择,探测超时 15 秒)。 | 操作 | IPv4 | IPv6 | |---|---|---| diff --git a/deploy/install.sh b/deploy/install.sh index 9ebfefc..3cbdbdf 100755 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -101,19 +101,24 @@ if [ "$REINSTALL" -eq 0 ] && [ -x "$BIN_PATH" ] && { [ -e "$ENV_FILE" ] || [ -e die "检测到已安装的 bolt。升级请用 update.sh(保留配置);确实要按本次参数重写配置请加 --reinstall" fi -# fetch <输出文件> [-4|-6] +# fetch <输出文件> [probe]:probe 用于探测站点,连接最多等 8 秒、总共 15 秒且不重试 fetch() { + if [ "${3:-}" = probe ]; then + c_opt="--connect-timeout 8 -m 15"; w_opt="-T 15 -t 1" + else + c_opt="--connect-timeout 15 --retry 3"; w_opt="-T 60 -t 3" + fi if command -v curl >/dev/null 2>&1; then if [ -n "${BOLT_DL_TOKEN:-}" ]; then - curl -fsSL --retry 3 ${3:-} -H "Authorization: token $BOLT_DL_TOKEN" -o "$2" "$1" + curl -fsSL $c_opt -H "Authorization: token $BOLT_DL_TOKEN" -o "$2" "$1" else - curl -fsSL --retry 3 ${3:-} -o "$2" "$1" + curl -fsSL $c_opt -o "$2" "$1" fi elif command -v wget >/dev/null 2>&1; then if [ -n "${BOLT_DL_TOKEN:-}" ]; then - wget -q ${3:-} --header="Authorization: token $BOLT_DL_TOKEN" -O "$2" "$1" + wget -q $w_opt --header="Authorization: token $BOLT_DL_TOKEN" -O "$2" "$1" else - wget -q ${3:-} -O "$2" "$1" + wget -q $w_opt -O "$2" "$1" fi else die "需要 curl 或 wget" @@ -141,30 +146,29 @@ elif [ -n "${BOLT_DL_URL:-}" ]; then else # 选择站点:BOLT_GIT_HOST 优先,否则先试 IPv4 站点再试 IPv6 站点 HOST="" - IPFLAG="" if [ -n "${BOLT_GIT_HOST:-}" ]; then HOST="${BOLT_GIT_HOST%/}" else - for cand in "$HOST4 -4" "$HOST6 -6"; do - if fetch "${cand% *}/api/v1/version" "$TMP/probe" "${cand#* }" 2>/dev/null; then - HOST=${cand% *} - IPFLAG=${cand#* } + # 不强制协议族,由 curl 自动选择可用的 IPv4 / IPv6 地址 + for h in "$HOST4" "$HOST6"; do + if fetch "$h/api/v1/version" "$TMP/probe" probe 2>/dev/null; then + HOST=$h break fi done - [ -n "$HOST" ] || die "无法连接 $HOST4(IPv4)或 $HOST6(IPv6),可用 BOLT_GIT_HOST 指定站点" + [ -n "$HOST" ] || die "无法连接 $HOST4 或 $HOST6,可用 BOLT_GIT_HOST 指定站点" fi say "下载站点: $HOST" if [ -z "$VERSION" ]; then - fetch "$HOST/api/v1/repos/$REPO/releases/latest" "$TMP/latest.json" "$IPFLAG" || die "查询最新版本失败" + fetch "$HOST/api/v1/repos/$REPO/releases/latest" "$TMP/latest.json" || die "查询最新版本失败" VERSION=$(sed -n 's/.*"tag_name":"\([^"]*\)".*/\1/p' "$TMP/latest.json") [ -n "$VERSION" ] || die "无法解析最新版本号" fi BASE="$HOST/$REPO/releases/download/$VERSION" say "从 Release 下载: $BASE/bolt-$PLATFORM-$ARCH" - fetch "$BASE/bolt-$PLATFORM-$ARCH" "$TMP/bolt" "$IPFLAG" || die "下载失败,请确认 Release $VERSION 有 bolt-$PLATFORM-$ARCH 附件" + fetch "$BASE/bolt-$PLATFORM-$ARCH" "$TMP/bolt" || die "下载失败,请确认 Release $VERSION 有 bolt-$PLATFORM-$ARCH 附件" # 有 SHA256SUMS 附件时校验 - if fetch "$BASE/SHA256SUMS" "$TMP/SHA256SUMS" "$IPFLAG" 2>/dev/null; then + if fetch "$BASE/SHA256SUMS" "$TMP/SHA256SUMS" 2>/dev/null; then want=$(awk -v f="bolt-$PLATFORM-$ARCH" '$2 == f || $2 == "*" f {print $1}' "$TMP/SHA256SUMS") if command -v sha256sum >/dev/null 2>&1; then got=$(sha256sum "$TMP/bolt" | awk '{print $1}') diff --git a/deploy/update.sh b/deploy/update.sh index 3df4feb..39c692c 100644 --- a/deploy/update.sh +++ b/deploy/update.sh @@ -66,42 +66,43 @@ case "$ARCH" in esac # ---------- 下载工具 ---------- -# fetch <输出文件> [-4|-6] +# fetch <输出文件> [probe]:probe 用于探测站点,连接最多等 8 秒、总共 15 秒且不重试 fetch() { + if [ "${3:-}" = probe ]; then + c_opt="--connect-timeout 8 -m 15"; w_opt="-T 15 -t 1" + else + c_opt="--connect-timeout 15 --retry 3"; w_opt="-T 60 -t 3" + fi if command -v curl >/dev/null 2>&1; then if [ -n "${BOLT_DL_TOKEN:-}" ]; then - curl -fsSL --retry 3 ${3:-} -H "Authorization: token $BOLT_DL_TOKEN" -o "$2" "$1" + curl -fsSL $c_opt -H "Authorization: token $BOLT_DL_TOKEN" -o "$2" "$1" else - curl -fsSL --retry 3 ${3:-} -o "$2" "$1" + curl -fsSL $c_opt -o "$2" "$1" fi elif command -v wget >/dev/null 2>&1; then if [ -n "${BOLT_DL_TOKEN:-}" ]; then - wget -q ${3:-} --header="Authorization: token $BOLT_DL_TOKEN" -O "$2" "$1" + wget -q $w_opt --header="Authorization: token $BOLT_DL_TOKEN" -O "$2" "$1" else - wget -q ${3:-} -O "$2" "$1" + wget -q $w_opt -O "$2" "$1" fi else die "需要 curl 或 wget" fi } -# 探测可用站点:先 IPv4 站点,再 IPv6 站点 -IPFLAG="" +# 探测可用站点:先 IPv4 站点再 IPv6 站点(不强制协议族,由 curl 自动选择可用的 IPv4 / IPv6 地址) pick_host() { if [ -n "${BOLT_GIT_HOST:-}" ]; then HOST="${BOLT_GIT_HOST%/}" return fi - for cand in "$HOST4 -4" "$HOST6 -6"; do - h=${cand% *} - f=${cand#* } - if fetch "$h/api/v1/version" "$TMP/probe" "$f" 2>/dev/null; then + for h in "$HOST4" "$HOST6"; do + if fetch "$h/api/v1/version" "$TMP/probe" probe 2>/dev/null; then HOST=$h - IPFLAG=$f return fi done - die "无法连接 $HOST4(IPv4)或 $HOST6(IPv6),可用 BOLT_GIT_HOST 指定站点" + die "无法连接 $HOST4 或 $HOST6,可用 BOLT_GIT_HOST 指定站点" } # ver_cmp a b:输出 1 / 0 / -1 @@ -140,7 +141,7 @@ else pick_host say "下载站点: $HOST" if [ -z "$VERSION" ]; then - fetch "$HOST/api/v1/repos/$REPO/releases/latest" "$TMP/latest.json" "$IPFLAG" || die "查询最新版本失败" + fetch "$HOST/api/v1/repos/$REPO/releases/latest" "$TMP/latest.json" || die "查询最新版本失败" VERSION=$(sed -n 's/.*"tag_name":"\([^"]*\)".*/\1/p' "$TMP/latest.json") [ -n "$VERSION" ] || die "无法解析最新版本号" EXPLICIT=0 @@ -161,9 +162,9 @@ else fi BASE="$HOST/$REPO/releases/download/$VERSION" say "下载: $BASE/bolt-$PLATFORM-$ARCH" - fetch "$BASE/bolt-$PLATFORM-$ARCH" "$NEW_BIN" "$IPFLAG" || die "下载失败,请确认 Release $VERSION 有 bolt-$PLATFORM-$ARCH 附件" + fetch "$BASE/bolt-$PLATFORM-$ARCH" "$NEW_BIN" || die "下载失败,请确认 Release $VERSION 有 bolt-$PLATFORM-$ARCH 附件" # 有 SHA256SUMS 附件时校验 - if fetch "$BASE/SHA256SUMS" "$TMP/SHA256SUMS" "$IPFLAG" 2>/dev/null; then + if fetch "$BASE/SHA256SUMS" "$TMP/SHA256SUMS" 2>/dev/null; then want=$(awk -v f="bolt-$PLATFORM-$ARCH" '$2 == f || $2 == "*" f {print $1}' "$TMP/SHA256SUMS") if command -v sha256sum >/dev/null 2>&1; then got=$(sha256sum "$NEW_BIN" | awk '{print $1}')