From 4ff97b266582e2c3ecb2644f6cb45d619e99e52d Mon Sep 17 00:00:00 2001 From: root Date: Tue, 10 Mar 2026 00:43:23 +0800 Subject: [PATCH] ci: switch prod deploy to server-side git pull and go build --- .github/workflows/deploy-prod.yml | 39 ++------------ docs/common/deploy_ci.md | 32 +++++++++--- scripts/ops/deploy_binary.sh | 4 +- scripts/ops/deploy_from_source.sh | 84 +++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 43 deletions(-) create mode 100755 scripts/ops/deploy_from_source.sh diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 07697bc..60ea27b 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -19,21 +19,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version: '1.23.x' - - - name: Download modules - run: go mod download - - - name: Build linux binary - run: | - mkdir -p tmp - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "-s -w" -o tmp/wx_service ./cmd/api - gzip -9 -c tmp/wx_service > tmp/wx_service.gz - ls -lh tmp/wx_service tmp/wx_service.gz - - name: Prepare SSH env: SSH_KEY: ${{ secrets.PROD_SSH_KEY }} @@ -51,36 +36,22 @@ jobs: chmod 600 ~/.ssh/id_ed25519 ssh-keyscan -p "${PORT:-22}" "$HOST" >> ~/.ssh/known_hosts - - name: Upload binary to server + - name: Deploy on server (git pull + go build) env: HOST: ${{ secrets.PROD_HOST }} PORT: ${{ secrets.PROD_PORT }} USER: ${{ secrets.PROD_USER }} run: | set -e - REMOTE_BIN_GZ="/tmp/wx_service-${GITHUB_SHA}.gz" - SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o ServerAliveInterval=10 -o ServerAliveCountMax=3" - scp -O ${SSH_OPTS} -P "${PORT:-22}" tmp/wx_service.gz "${USER:-root}@${HOST}:${REMOTE_BIN_GZ}" - - - name: Deploy on server - env: - HOST: ${{ secrets.PROD_HOST }} - PORT: ${{ secrets.PROD_PORT }} - USER: ${{ secrets.PROD_USER }} - run: | - set -e - REMOTE_BIN_GZ="/tmp/wx_service-${GITHUB_SHA}.gz" - SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o ServerAliveInterval=10 -o ServerAliveCountMax=3" + SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o ServerAliveInterval=10 -o ServerAliveCountMax=6" ssh ${SSH_OPTS} -p "${PORT:-22}" "${USER:-root}@${HOST}" \ "APP_DIR='/www/wwwroot/wx_service' \ DIST_DIR='/www/wwwroot/wx_service/dist' \ - SOURCE_BIN='${REMOTE_BIN_GZ}' \ + DEPLOY_REF='${GITHUB_SHA}' \ RELEASE_ID='${GITHUB_SHA}' \ + GO_VERSION='1.23.6' \ SERVICE_NAME='wx_service' \ RUN_USER='www' \ RUN_GROUP='www' \ PORT='8080' \ - SYNC_CODE='true' \ - DEPLOY_REF='${GITHUB_SHA}' \ - INSTALL_SERVICE='true' \ - bash -s" < scripts/ops/deploy_binary.sh + bash -s" < scripts/ops/deploy_from_source.sh diff --git a/docs/common/deploy_ci.md b/docs/common/deploy_ci.md index 8999e4d..b1c3fa9 100644 --- a/docs/common/deploy_ci.md +++ b/docs/common/deploy_ci.md @@ -2,7 +2,7 @@ 本文档用于 `wx_service` 在宝塔服务器上的自动化发布: - 触发:`main` 分支 push 或手动触发 -- 流程:GitHub Actions 构建二进制 -> SSH 上传到服务器 -> 远程发布脚本重启并健康检查 +- 流程:GitHub Actions SSH 触发服务器发布脚本 -> 服务器 git pull + go build -> 重启并健康检查 - 特点:不依赖 Docker ## 1. 服务器约定 @@ -13,7 +13,7 @@ - 进程端口:`8080` - 反向代理:宝塔 Nginx -> `127.0.0.1:8080` -> 远程脚本:`scripts/ops/deploy_binary.sh` +> 远程脚本:`scripts/ops/deploy_from_source.sh`(内部调用 `scripts/ops/deploy_binary.sh`) ## 2. GitHub Secrets @@ -37,16 +37,18 @@ - `/www/wwwroot/wx_service/.env` - `/www/wwwroot/wx_service/dist/.env` 3. 开放 `8080` 本地监听,并由 Nginx 反代 -4. 第一次执行会自动尝试创建 `systemd` 服务 `wx_service` +4. 服务器无需 Docker;第一次执行会自动安装 Go 1.23.6(若缺失)并尝试创建 `systemd` 服务 `wx_service` ## 5. 发布行为 每次发布会执行: -1. GitHub Actions 构建 Linux 二进制 -2. 上传到服务器 `/tmp/wx_service-` -3. 远程脚本执行: +1. GitHub Actions 通过 SSH 调用服务器发布脚本 +2. 服务器执行: - `git fetch` + `git reset --hard `(同步代码) + - 安装/校验 Go 1.23.6 + - `go mod download` + `go build` +3. 远程发布脚本继续执行: - 备份旧二进制到 `/www/wwwroot/wx_service/backups/` - 原子替换 `dist/wx_service` - 重启服务 @@ -55,7 +57,7 @@ ## 6. 手动发布(应急) -在服务器执行: +在服务器执行(仅二进制手动发布场景): ```bash cd /www/wwwroot/wx_service @@ -86,3 +88,19 @@ systemctl restart wx_service pkill -f /www/wwwroot/wx_service/dist/wx_service su -s /bin/bash - www -c "cd /www/wwwroot/wx_service/dist && nohup ./wx_service >> /www/wwwlogs/wx_service.stdout.log 2>&1 &" ``` + + +## 8. 手动发布(从源码构建) + +```bash +cd /www/wwwroot/wx_service +APP_DIR=/www/wwwroot/wx_service \ +DIST_DIR=/www/wwwroot/wx_service/dist \ +DEPLOY_REF=main \ +RELEASE_ID=manual-$(date +%Y%m%d%H%M%S) \ +GO_VERSION=1.23.6 \ +SERVICE_NAME=wx_service \ +RUN_USER=www RUN_GROUP=www \ +PORT=8080 \ +bash scripts/ops/deploy_from_source.sh +``` diff --git a/scripts/ops/deploy_binary.sh b/scripts/ops/deploy_binary.sh index a5cb195..ede4d38 100755 --- a/scripts/ops/deploy_binary.sh +++ b/scripts/ops/deploy_binary.sh @@ -39,7 +39,7 @@ health_check() { } restart_service() { - if command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files | grep -q "^${SERVICE_NAME}.service"; then + if command -v systemctl >/dev/null 2>&1 && systemctl cat "${SERVICE_NAME}.service" >/dev/null 2>&1; then log "restarting systemd service: ${SERVICE_NAME}" systemctl restart "$SERVICE_NAME" else @@ -58,7 +58,7 @@ create_service_if_needed() { return 0 fi - if systemctl list-unit-files | grep -q "^${SERVICE_NAME}.service"; then + if systemctl cat "${SERVICE_NAME}.service" >/dev/null 2>&1; then return 0 fi diff --git a/scripts/ops/deploy_from_source.sh b/scripts/ops/deploy_from_source.sh new file mode 100755 index 0000000..938fda7 --- /dev/null +++ b/scripts/ops/deploy_from_source.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +APP_DIR="${APP_DIR:-/www/wwwroot/wx_service}" +DEPLOY_REF="${DEPLOY_REF:-main}" +GO_VERSION="${GO_VERSION:-1.23.6}" +GO_ROOT="${GO_ROOT:-/usr/local/go}" +GO_BIN="${GO_BIN:-${GO_ROOT}/bin/go}" +TMP_BUILD_BIN="${TMP_BUILD_BIN:-/tmp/wx_service-${DEPLOY_REF:0:12}}" + +DIST_DIR="${DIST_DIR:-${APP_DIR}/dist}" +SERVICE_NAME="${SERVICE_NAME:-wx_service}" +RUN_USER="${RUN_USER:-www}" +RUN_GROUP="${RUN_GROUP:-www}" +PORT="${PORT:-8080}" +RELEASE_ID="${RELEASE_ID:-${DEPLOY_REF}}" + +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" +} + +install_go_if_needed() { + local need_install="false" + + if [ ! -x "$GO_BIN" ]; then + need_install="true" + else + local current + current="$($GO_BIN version 2>/dev/null | awk '{print $3}' | sed 's/^go//')" + if [ -z "$current" ] || [ "$current" != "$GO_VERSION" ]; then + need_install="true" + fi + fi + + if [ "$need_install" = "false" ]; then + log "go toolchain ok: $($GO_BIN version)" + return 0 + fi + + log "installing go ${GO_VERSION}" + local pkg="/tmp/go${GO_VERSION}.linux-amd64.tar.gz" + curl -fL "https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz" -o "$pkg" + rm -rf "$GO_ROOT" + tar -C /usr/local -xzf "$pkg" + rm -f "$pkg" + log "go installed: $($GO_BIN version)" +} + +if [ ! -d "$APP_DIR/.git" ]; then + echo "invalid app repo: $APP_DIR" >&2 + exit 1 +fi + +install_go_if_needed + +log "syncing repo to ${DEPLOY_REF}" +git -C "$APP_DIR" fetch --all --prune +git -C "$APP_DIR" reset --hard "$DEPLOY_REF" + +export PATH="$GO_ROOT/bin:$PATH" +export GOPROXY="${GOPROXY:-https://goproxy.cn,direct}" + +log "downloading go modules" +cd "$APP_DIR" +"$GO_BIN" mod download + +log "building binary" +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 "$GO_BIN" build -trimpath -ldflags "-s -w" -o "$TMP_BUILD_BIN" ./cmd/api +chmod 755 "$TMP_BUILD_BIN" + +log "publishing binary via deploy_binary.sh" +APP_DIR="$APP_DIR" \ +DIST_DIR="$DIST_DIR" \ +SOURCE_BIN="$TMP_BUILD_BIN" \ +SERVICE_NAME="$SERVICE_NAME" \ +RUN_USER="$RUN_USER" \ +RUN_GROUP="$RUN_GROUP" \ +PORT="$PORT" \ +RELEASE_ID="$RELEASE_ID" \ +SYNC_CODE="false" \ +INSTALL_SERVICE="true" \ +bash "$APP_DIR/scripts/ops/deploy_binary.sh" + +log "source deploy done"