deploy: 一键更新 / 卸载脚本,安装脚本自动选择 IPv4/IPv6 站点与最新版本

- 新增 deploy/update.sh:只替换二进制并重启服务,保留配置;已是最新时跳过、不自动降级;
  重启由独立进程执行(通过 bolt 自身运行也安全),新版本起不来时自动回滚
- 新增 deploy/uninstall.sh:删除服务、二进制与备份,默认同时删除配置,--keep-config 保留
- install.sh:先试 IPv4 站点(git.jocayn.qzz.io)再试 IPv6 站点(git.draws.qzz.io),
  通过 Release API 获取最新版本,支持 SHA256SUMS 校验与 --version;
  已安装时拒绝覆盖配置(--reinstall 强制);macOS 缺少签名时补 ad-hoc 签名
- .gitattributes 强制 *.sh 使用 LF
- README / CLAUDE.md 更新部署说明与发布流程

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jocay
2026-10-18 10:46:21 +08:00
parent 84653bec61
commit 76f6fe32f5
6 changed files with 499 additions and 72 deletions
+94 -31
View File
@@ -2,12 +2,14 @@
# bolt 一键安装脚本(POSIX sh,需 root; Linux 使用 systemd,macOS 使用 launchd)
#
# 用法(在目标设备上):
# 方式 A - 从 Gitea Release 下载二进制并安装:
# curl -fsSL https://git.draws.qzz.io/jocay/bolt/raw/branch/main/deploy/install.sh | sh -s -- --http 0.0.0.0:8080 --token 你的令牌
# (私有仓库下载附件需要认证:先 export BOLT_DL_TOKEN=xxx,与 --token 分开配置)
# 方式 A - 从 Gitea Release 下载最新版二进制并安装:
# IPv4: curl -fsSL https://git.jocayn.qzz.io/jocay/bolt/raw/branch/main/deploy/install.sh | sudo sh -s -- --http 0.0.0.0:8080 --token 你的令牌
# IPv6: curl -fsSL https://git.draws.qzz.io/jocay/bolt/raw/branch/main/deploy/install.sh | sudo sh -s -- --http 0.0.0.0:8080 --token 你的令牌
# 方式 B - 已把二进制拷到目标设备(如 scp):
# ./install.sh --binary ./bolt --http 0.0.0.0:8080 --token xxx --allow "git,docker"
#
# 已安装的设备升级请用 update.sh(保留配置);卸载用 uninstall.sh。
#
# 选项:
# --http <addr> 监听地址,默认 127.0.0.1:8080
# --token <t> HTTP Bearer 认证令牌(监听公网时必填)
@@ -17,8 +19,15 @@
# --file-root <d1,d2> 文件工具与 /files 只能访问这些目录(配置 --allow 时必须设置,否则文件功能禁用)
# --max-upload <n> PUT /files 单次上传上限,支持 K/M/G(默认 2G)
# --binary <src> 二进制来源:本地路径 或 http(s) URL(默认从 Gitea Release 下载)
# --version <tag> 安装指定版本(默认 Release 上的最新版本)
# --reinstall 已安装时按本次参数重写配置(不加则拒绝覆盖,避免丢失令牌等配置)
# --no-start 只安装不启动
# --no-enable 不设置开机自启
#
# 环境变量:
# BOLT_GIT_HOST 指定下载站点(如 https://git.jocayn.qzz.io),默认先试 IPv4 站点再试 IPv6 站点
# BOLT_DL_URL 直接指定二进制下载地址
# BOLT_DL_TOKEN 仓库为私有时的下载令牌(与 --token 无关)
set -eu
HTTP_ADDR="${HTTP_ADDR:-127.0.0.1:8080}"
@@ -29,6 +38,8 @@ TIMEOUT_MS="${BOLT_TIMEOUT_MS:-30000}"
FILE_ROOT=""
MAX_UPLOAD=""
BINARY_SRC=""
VERSION="${BOLT_VERSION:-}"
REINSTALL=0
NO_START=0
NO_ENABLE=0
@@ -42,12 +53,17 @@ while [ $# -gt 0 ]; do
--file-root) FILE_ROOT="${2:-}"; shift 2 ;;
--max-upload) MAX_UPLOAD="${2:-}"; shift 2 ;;
--binary) BINARY_SRC="${2:-}"; shift 2 ;;
--version) VERSION="${2:-}"; shift 2 ;;
--reinstall) REINSTALL=1; shift ;;
--no-start) NO_START=1; shift ;;
--no-enable) NO_ENABLE=1; shift ;;
*) echo "未知参数: $1"; echo "用法见脚本头部注释"; exit 1 ;;
esac
done
REPO=jocay/bolt
HOST4=https://git.jocayn.qzz.io
HOST6=https://git.draws.qzz.io
BIN_PATH=/usr/local/bin/bolt
ENV_FILE=/etc/bolt.env
UNIT_FILE=/etc/systemd/system/bolt.service
@@ -78,50 +94,97 @@ case "$ARCH" in
aarch64|arm64) ARCH=arm64 ;;
*) die "不支持的架构: $ARCH" ;;
esac
DL_URL="${BOLT_DL_URL:-https://git.draws.qzz.io/jocay/bolt/releases/download/${BOLT_VERSION:-v0.1.0}/bolt-$PLATFORM-$ARCH}"
say "目标平台: $PLATFORM/$ARCH"
# 已安装时默认拒绝:重写配置会丢掉原来的令牌、白名单等
if [ "$REINSTALL" -eq 0 ] && [ -x "$BIN_PATH" ] && { [ -e "$ENV_FILE" ] || [ -e "/Library/Application Support/bolt/io.jocay.bolt.plist" ]; }; then
die "检测到已安装的 bolt。升级请用 update.sh(保留配置);确实要按本次参数重写配置请加 --reinstall"
fi
# fetch <url> <输出文件> [-4|-6]
fetch() {
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"
else
curl -fsSL --retry 3 ${3:-} -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"
else
wget -q ${3:-} -O "$2" "$1"
fi
else
die "需要 curl 或 wget"
fi
}
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
mkdir -p /usr/local/bin
# ---------- 1. 获取二进制 ----------
if [ -n "$BINARY_SRC" ]; then
if [ -f "$BINARY_SRC" ]; then
say "使用本地二进制: $BINARY_SRC"
install -m 0755 "$BINARY_SRC" "$BIN_PATH"
cp "$BINARY_SRC" "$TMP/bolt"
elif echo "$BINARY_SRC" | grep -q '^https\?://'; then
say "下载二进制: $BINARY_SRC"
rm -f /tmp/bolt-dl.$$
if command -v curl >/dev/null 2>&1; then
if [ -n "${BOLT_DL_TOKEN:-}" ]; then
curl -fsSL --retry 3 -H "Authorization:token ${BOLT_DL_TOKEN:-}" -o /tmp/bolt-dl.$$ "$BINARY_SRC" || die "下载失败"
else
curl -fsSL --retry 3 -o /tmp/bolt-dl.$$ "$BINARY_SRC" || die "下载失败"
fi
elif command -v wget >/dev/null 2>&1; then
wget -qO /tmp/bolt-dl.$$ "$BINARY_SRC" || die "下载失败"
else
die "需要 curl 或 wget"
fi
install -m 0755 /tmp/bolt-dl.$$ "$BIN_PATH" && rm -f /tmp/bolt-dl.$$
fetch "$BINARY_SRC" "$TMP/bolt" || die "下载失败"
else
die "二进制来源不存在: $BINARY_SRC"
fi
elif [ -n "${BOLT_DL_URL:-}" ]; then
say "下载二进制: $BOLT_DL_URL"
fetch "$BOLT_DL_URL" "$TMP/bolt" || die "下载失败"
else
say "从 Release 下载: $DL_URL"
rm -f /tmp/bolt-dl.$$
if command -v curl >/dev/null 2>&1; then
if [ -n "${BOLT_DL_TOKEN:-}" ]; then
curl -fsSL --retry 3 -H "Authorization:token ${BOLT_DL_TOKEN:-}" -o /tmp/bolt-dl.$$ "$DL_URL" || die "下载失败,请确认 Release 附件存在且 BOLT_DL_TOKEN 有效"
else
curl -fsSL --retry 3 -o /tmp/bolt-dl.$$ "$DL_URL" || die "下载失败,请确认 Release 附件存在;私有仓库需设置 BOLT_DL_TOKEN"
fi
elif command -v wget >/dev/null 2>&1; then
wget -qO /tmp/bolt-dl.$$ "$DL_URL" || die "下载失败"
# 选择站点:BOLT_GIT_HOST 优先,否则先试 IPv4 站点再试 IPv6 站点
HOST=""
IPFLAG=""
if [ -n "${BOLT_GIT_HOST:-}" ]; then
HOST="${BOLT_GIT_HOST%/}"
else
die "需要 curl 或 wget"
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#* }
break
fi
done
[ -n "$HOST" ] || die "无法连接 $HOST4(IPv4)或 $HOST6(IPv6),可用 BOLT_GIT_HOST 指定站点"
fi
say "下载站点: $HOST"
if [ -z "$VERSION" ]; then
fetch "$HOST/api/v1/repos/$REPO/releases/latest" "$TMP/latest.json" "$IPFLAG" || 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 附件"
# 有 SHA256SUMS 附件时校验
if fetch "$BASE/SHA256SUMS" "$TMP/SHA256SUMS" "$IPFLAG" 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}')
else
got=$(shasum -a 256 "$TMP/bolt" | awk '{print $1}')
fi
[ -n "$want" ] && [ "$want" = "$got" ] || die "SHA-256 校验失败(期望 $want,实际 $got)"
say "SHA-256 校验通过"
fi
install -m 0755 /tmp/bolt-dl.$$ "$BIN_PATH" && rm -f /tmp/bolt-dl.$$
fi
"$BIN_PATH" --help >/dev/null 2>&1 || die "二进制不可执行: $BIN_PATH"
chmod 0755 "$TMP/bolt"
if [ "$PLATFORM" = darwin ]; then
# Apple Silicon 要求可执行文件带签名;没有签名时补 ad-hoc 签名
codesign -v "$TMP/bolt" 2>/dev/null || codesign -s - -f "$TMP/bolt" >/dev/null 2>&1 || true
fi
"$TMP/bolt" --help >/dev/null 2>&1 || die "二进制无法在本机运行"
# 先写临时文件再 rename,替换正在运行的旧版本也安全
cp "$TMP/bolt" "$BIN_PATH.new"
chmod 0755 "$BIN_PATH.new"
mv -f "$BIN_PATH.new" "$BIN_PATH"
say "二进制就绪: $BIN_PATH ($("$BIN_PATH" --help | head -1))"
# macOS 将环境配置直接写入 plist,不通过 shell 解释令牌或命令规则。