#!/usr/bin/env bash # # webui4frpc 多平台发布脚本 # # 用法: # ./scripts/release.sh [version] # 默认 version=0.1.2 # # 行为: # 1. 构建前端 (web/dist) 并同步到 internal/httpapi/dist (go:embed 用) # 2. 交叉编译全平台单二进制 (linux/windows/darwin × amd64/arm64) # 3. 打包各平台安装包: # - linux/darwin: tar.gz (二进制 + README + systemd 模板) # - windows: zip (exe + README) # 4. 生成 SHA256SUMS # 5. 输出到 dist/artifacts// # # 环境变量: # GOPROXY_OVERRIDE 覆盖 go module 代理 (默认 goproxy.cn,direct, 走本地 7890 代理) # NO_BUILD_FRONTEND 设为 1 跳过前端构建 (用现有 dist) set -euo pipefail VERSION="${1:-0.1.2}" VERSION="${VERSION#v}" # 去掉可能的前导 v ROOT="$(cd "$(dirname "$0")/.." && pwd)" OUT="$ROOT/dist/artifacts/$VERSION" GOFLAGS_ENV="" # ---- 0. Go module 代理 (内网需走本地代理访问 goproxy.cn) ---- if [ -n "${GOPROXY_OVERRIDE:-}" ]; then GOFLAGS_ENV="GOPROXY=${GOPROXY_OVERRIDE}" else GOFLAGS_ENV="GOPROXY=https://goproxy.cn,direct" fi # 若本地 7890 代理存在则导出 (模块下载走代理) if curl -s -o /dev/null -m 2 -x http://127.0.0.1:7890 https://goproxy.cn >/dev/null 2>&1; then export HTTPS_PROXY=http://127.0.0.1:7890 HTTP_PROXY=http://127.0.0.1:7890 fi echo "==> 版本: $VERSION" mkdir -p "$OUT" # ---- 1. 构建前端 ---- if [ "${NO_BUILD_FRONTEND:-0}" != "1" ]; then echo "==> 构建前端" ( cd "$ROOT/web" && npm run build ) rm -rf "$ROOT/internal/httpapi/dist" cp -r "$ROOT/web/dist" "$ROOT/internal/httpapi/dist" else echo "==> 跳过前端构建 (用现有 dist)" fi # ---- 2. 交叉编译全平台 ---- # target 格式: os arch 扩展名 [tar|zip] TARGETS=( "linux amd64" "linux arm64" "windows amd64" "windows arm64" "darwin amd64" "darwin arm64" ) declare -A PLATFORM_EXT=( [linux-amd64]=tgz [linux-arm64]=tgz [darwin-amd64]=tgz [darwin-arm64]=tgz [windows-amd64]=zip [windows-arm64]=zip ) declare -A PLATFORM_BIN=( [linux-amd64]=webui4frpc [linux-arm64]=webui4frpc [darwin-amd64]=webui4frpc [darwin-arm64]=webui4frpc [windows-amd64]=webui4frpc.exe [windows-arm64]=webui4frpc.exe ) for target in "${TARGETS[@]}"; do os="${target%% *}"; arch="${target##* }" key="$os-$arch" bin="${PLATFORM_BIN[$key]}" # 每个平台编译到独立子目录,避免 darwin/linux 同名二进制互相覆盖 bindir="$OUT/bin/$key" mkdir -p "$bindir" echo "==> 编译 $os/$arch" env $GOFLAGS_ENV CGO_ENABLED=0 GOOS="$os" GOARCH="$arch" \ go build -trimpath \ -ldflags="-s -w -X webui4frpc/internal/version.Version=$VERSION" \ -o "$bindir/$bin" "$ROOT/cmd/webui4frpc" done # ---- 3. 打包各平台 ---- # 整理 systemd 模板 cat > "$OUT/webui4frpc.service.tpl" <<'TPL' [Unit] Description=webui4frpc - visual frpc controller (cluster node) After=network-online.target Wants=network-online.target [Service] Type=simple ExecStart=/opt/webui4frpc/webui4frpc -addr 0.0.0.0:7500 -user admin -password admin -workdir /opt/webui4frpc/data Restart=always RestartSec=5 LimitNOFILE=65535 NoNewPrivileges=true [Install] WantedBy=multi-user.target TPL for target in "${TARGETS[@]}"; do os="${target%% *}"; arch="${target##* }" key="$os-$arch" bin="${PLATFORM_BIN[$key]}" ext="${PLATFORM_EXT[$key]}" base="webui4frpc-${VERSION}-${os}-${arch}" echo "==> 打包 $key -> $base.$ext" cp "$ROOT/README.md" "$OUT/README.md" bindir="$OUT/bin/$key" # staging 目录: 把二进制 + README (+systemd) 放一起再一次性打包 stage="$OUT/.stage/$key" rm -rf "$stage"; mkdir -p "$stage" cp "$bindir/$bin" "$stage/$bin" cp "$OUT/README.md" "$stage/README.md" if [ "$ext" = "zip" ]; then ( cd "$stage" && zip -q "$OUT/$base.zip" "$bin" README.md ) else cp "$OUT/webui4frpc.service.tpl" "$stage/" ( cd "$stage" && tar czf "$OUT/$base.tar.gz" "$bin" README.md webui4frpc.service.tpl ) fi done rm -rf "$OUT/.stage" # ---- 4. 校验和 ---- # 先生成免安装包的校验和;原生安装包构建完后在 4.6 统一重算, # 保证 SHA256SUMS 覆盖 deb/rpm/setup.exe/pkg(否则用户校验会 "no file was verified")。 # ---- 4.5 原生安装包 (deb/rpm/setup.exe/pkg) ---- if [ "${NO_INSTALLERS:-0}" != "1" ]; then echo "" echo "==> 构建原生安装包" "$ROOT/scripts/build_installers.sh" "$VERSION" fi # ---- 4.6 校验和(覆盖全部产物)---- echo "" echo "==> 生成 SHA256SUMS" ( cd "$OUT" && sha256sum *.tar.gz *.zip *.deb *.rpm *-setup.exe *.pkg 2>/dev/null | tee SHA256SUMS ) # ---- 5. 汇总 ---- echo "" echo "==> 发布产物位于: $OUT" ls -lh "$OUT" echo "" echo "==> 完成"