368 lines
17 KiB
Diff
368 lines
17 KiB
Diff
From 10d363ca2b3f6a7cae380cada62c150933b4fef3 Mon Sep 17 00:00:00 2001
|
||
From: HenryDu8133 <813367384@qq.com>
|
||
Date: Sun, 6 Sep 2026 10:35:56 +0800
|
||
Subject: [PATCH 13/58] =?UTF-8?q?style(ci):=20=E7=A7=BB=E9=99=A4=E5=B7=A5?=
|
||
=?UTF-8?q?=E4=BD=9C=E6=B5=81=E6=AD=A5=E9=AA=A4=E4=B8=AD=E7=9A=84=20emoji?=
|
||
=?UTF-8?q?=EF=BC=8C=E6=94=B9=E7=94=A8=E4=B8=93=E4=B8=9A=E9=A3=8E=E6=A0=BC?=
|
||
=?UTF-8?q?=E5=91=BD=E5=90=8D?=
|
||
MIME-Version: 1.0
|
||
Content-Type: text/plain; charset=UTF-8
|
||
Content-Transfer-Encoding: 8bit
|
||
|
||
---
|
||
.gitea/workflows/deploy.yml | 144 ++++++++++++------------------------
|
||
1 file changed, 49 insertions(+), 95 deletions(-)
|
||
|
||
diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml
|
||
index 4e5260c..591afa7 100644
|
||
--- a/.gitea/workflows/deploy.yml
|
||
+++ b/.gitea/workflows/deploy.yml
|
||
@@ -1,4 +1,4 @@
|
||
-name: 构建 & 部署 BYW VoiceHub
|
||
+name: 构建并部署 BYW VoiceHub
|
||
run-name: ${{ github.actor }} 触发部署 · Run ${{ github.run_number }}
|
||
|
||
on:
|
||
@@ -17,45 +17,29 @@ on:
|
||
default: false
|
||
required: false
|
||
|
||
-# 同一时刻只允许一个部署在跑,避免冲突;如果新提交进来,取消前一个未完成的
|
||
concurrency:
|
||
group: deploy-production
|
||
cancel-in-progress: true
|
||
|
||
env:
|
||
- # ============================================================
|
||
- # ⚠️ 用户真实配置(部署 BYW VoiceHub 专用,不要改回 example)
|
||
- # ============================================================
|
||
- # Gitea 实例域名(不含 https://,如果是非标准端口要加 :端口号)
|
||
GITEA_HOST: gitea.fse-media.group
|
||
- # 生产服务器 B 上的实际部署目录(之前 /opt/byw-voicehub 已废弃,以用户实际为准)
|
||
DEPLOY_PATH: /data/opt/byw-voicehub
|
||
- # ============================================================
|
||
IMAGE_NAME: byw-voicehub
|
||
|
||
jobs:
|
||
- # ============================================================
|
||
- # Job 1: 校验构建环境 & 打包产物(npm ci + build,不依赖 Docker)
|
||
- # ============================================================
|
||
preflight:
|
||
- name: Preflight 语法 & 构建检查
|
||
- # ⚠️ Gitea/act_runner 的写法:runs-on 只填注册时的真实标签(我们叫 ubuntu-latest);
|
||
- # 如果要强制用某个 Docker 镜像跑这个 Job,单独写一个 container: 段。
|
||
- # 不能写 "ubuntu-latest:docker://xxx",这是 GitHub Actions 专有语法,Gitea 不认!
|
||
+ name: Preflight 语法与构建检查
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 15
|
||
- # 直接用官方 node:22 镜像当 Job 容器(里面自带 node 22 + npm),
|
||
- # 完全绕过 setup-node 从 GitHub release 下载 node tar.gz 的国内超时老坑
|
||
container:
|
||
image: node:22-bookworm-slim
|
||
steps:
|
||
- - name: 📥 Checkout 代码
|
||
+ - name: 检出代码
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 1
|
||
|
||
- # ── 已经在 container.image=node:22 里跑了,自带 node 22,不需要 actions/setup-node ──
|
||
- - name: 🔎 环境自检(确认容器里有 node/npm,版本对得上)
|
||
+ - name: 运行时环境自检
|
||
shell: bash
|
||
run: |
|
||
set -euxo pipefail
|
||
@@ -64,37 +48,33 @@ jobs:
|
||
which node npm
|
||
cat /etc/os-release 2>/dev/null | head -4 || true
|
||
|
||
- - name: 🧪 安装依赖(npm ci,严格按锁文件)
|
||
+ - name: 安装依赖(npm ci)
|
||
run: |
|
||
set -euxo pipefail
|
||
- # 国内加速镜像(Runner 在国外可以删掉这两行)
|
||
npm config set registry https://registry.npmmirror.com || true
|
||
npm config set fund false
|
||
npm config set audit false
|
||
npm ci
|
||
|
||
- - name: 🔎 检查 package.json 没有 pnpm 残留
|
||
+ - name: 检查 package.json 无 pnpm 残留
|
||
run: |
|
||
set -euo pipefail
|
||
if grep -qE '"packageManager"\s*:\s*"pnpm' package.json; then
|
||
- echo "❌ package.json 里有 pnpm 包管理器声明,和用户要求冲突"
|
||
+ echo "package.json 中发现 pnpm 包管理器声明,与要求冲突,构建中止"
|
||
exit 1
|
||
fi
|
||
- echo "✅ package.json 未发现 pnpm 声明"
|
||
+ echo "package.json 未发现 pnpm 声明"
|
||
|
||
- - name: 🏗️ 本地 nuxt build(独立于 Docker 先验证代码是可构建的)
|
||
+ - name: 执行 nuxt build
|
||
run: |
|
||
set -euxo pipefail
|
||
NODE_OPTIONS="--max-old-space-size=2048" npm run build
|
||
- echo "✅ .output/ 生成成功:"
|
||
+ echo ".output/ 生成结果:"
|
||
ls -la .output/ 2>/dev/null || true
|
||
du -sh .output 2>/dev/null || true
|
||
|
||
- # ============================================================
|
||
- # Job 2: 构建 Docker 镜像 → 推送到 Gitea 自带容器仓库
|
||
- # ============================================================
|
||
build-image:
|
||
- name: 构建并推送镜像
|
||
+ name: 构建并推送容器镜像
|
||
needs: preflight
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 40
|
||
@@ -103,12 +83,12 @@ jobs:
|
||
image_full: ${{ steps.meta.outputs.full }}
|
||
image_latest: ${{ steps.meta.outputs.latest }}
|
||
steps:
|
||
- - name: Checkout 代码
|
||
+ - name: 检出代码
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 1
|
||
|
||
- - name: 计算镜像 tag(日期 + 短 SHA)
|
||
+ - name: 计算镜像标签
|
||
id: meta
|
||
shell: bash
|
||
run: |
|
||
@@ -122,12 +102,12 @@ jobs:
|
||
echo "full=${IMG_FULL}" >> "$GITHUB_OUTPUT"
|
||
echo "latest=${IMG_LATEST}" >> "$GITHUB_OUTPUT"
|
||
echo "======================================"
|
||
- echo "🪪 镜像版本 tag : ${DATE_TAG}"
|
||
- echo "🪪 镜像完整名 : ${IMG_FULL}"
|
||
- echo "🪪 镜像 latest : ${IMG_LATEST}"
|
||
+ echo "Image tag : ${DATE_TAG}"
|
||
+ echo "Full image : ${IMG_FULL}"
|
||
+ echo "Latest image : ${IMG_LATEST}"
|
||
echo "======================================"
|
||
|
||
- - name: 启用 QEMU(跨架构小概率用得到,保险)
|
||
+ - name: 启用 QEMU
|
||
uses: docker/setup-qemu-action@v3
|
||
with:
|
||
platforms: linux/amd64
|
||
@@ -137,16 +117,14 @@ jobs:
|
||
with:
|
||
install: true
|
||
|
||
- - name: 登录 Gitea 容器仓库(Packages)
|
||
+ - name: 登录 Gitea 容器仓库
|
||
uses: docker/login-action@v3
|
||
with:
|
||
registry: ${{ env.GITEA_HOST }}
|
||
- # Secrets 里 REGISTRY_USER 没填就默认用触发构建的用户
|
||
username: ${{ secrets.REGISTRY_USER || github.actor }}
|
||
- # Secrets 里 REGISTRY_TOKEN 优先,没填就用 Gitea Actions 自动注入的仓库级 token
|
||
password: ${{ secrets.REGISTRY_TOKEN || github.token }}
|
||
|
||
- - name: 构建 & 推送(BuildKit 分层缓存,skip_build_cache=true 时禁用)
|
||
+ - name: 构建并推送镜像
|
||
id: build_push
|
||
uses: docker/build-push-action@v6
|
||
env:
|
||
@@ -170,21 +148,17 @@ jobs:
|
||
org.opencontainers.image.revision=${{ github.sha }}
|
||
org.opencontainers.image.created=${{ github.event.head_commit.timestamp }}
|
||
|
||
- - name: 镜像体积 & 层信息(日志里看一下,避免镜像越来越大)
|
||
+ - name: 镜像体积与层信息
|
||
run: |
|
||
set -euxo pipefail
|
||
docker pull "${{ steps.meta.outputs.full }}" || true
|
||
docker image inspect "${{ steps.meta.outputs.full }}" --format='{{.Size}}' 2>/dev/null \
|
||
- | awk '{printf "✅ 镜像体积:%.2f MB\n", $1/1024/1024}' || true
|
||
+ | awk '{printf "Image size: %.2f MB\n", $1/1024/1024}' || true
|
||
docker history --no-trunc "${{ steps.meta.outputs.full }}" 2>/dev/null | head -20 || true
|
||
|
||
- # ============================================================
|
||
- # Job 3: SSH 到服务器 → 拉新镜像 → 滚动重启
|
||
- # ============================================================
|
||
deploy:
|
||
name: 部署到生产服务器
|
||
needs: build-image
|
||
- # push 事件默认自动部署;手动触发时看 checkbox
|
||
if: ${{ !cancelled() && !failure() && (github.event_name == 'push' || inputs.deploy_target == true) }}
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 25
|
||
@@ -192,7 +166,7 @@ jobs:
|
||
name: production
|
||
url: ${{ secrets.NUXT_PUBLIC_SITE_URL }}
|
||
steps:
|
||
- - name: 检查部署所需 Secrets 是否都配置了
|
||
+ - name: 校验部署所需密钥
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
@@ -201,133 +175,113 @@ jobs:
|
||
[ -n "${{ secrets.DEPLOY_USER }}" ] || MISSING+=("DEPLOY_USER")
|
||
[ -n "${{ secrets.DEPLOY_SSH_KEY }}" ] || MISSING+=("DEPLOY_SSH_KEY")
|
||
if [ ${#MISSING[@]} -gt 0 ]; then
|
||
- echo "❌ 缺少以下 Repository Secrets:${MISSING[*]}"
|
||
- echo "👉 去 Gitea 仓库 → Settings → Secrets and variables → Actions 里添加"
|
||
+ echo "缺少以下 Repository Secrets:${MISSING[*]}"
|
||
+ echo "请在 Gitea 仓库 -> 设置 -> 工作流 -> 密钥 页面添加"
|
||
exit 1
|
||
fi
|
||
- echo "✅ 部署 Secrets 配置完整"
|
||
+ echo "部署密钥配置完整"
|
||
|
||
- - name: 一键滚动部署(SSH)
|
||
+ - name: SSH 滚动部署
|
||
uses: appleboy/ssh-action@v1.2.0
|
||
with:
|
||
host: ${{ secrets.DEPLOY_HOST }}
|
||
port: ${{ secrets.DEPLOY_PORT || 22 }}
|
||
username: ${{ secrets.DEPLOY_USER }}
|
||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||
- # 部署可能慢,给足 30 分钟
|
||
command_timeout: 30m
|
||
- # 出错立刻停,不继续往下跑
|
||
script_stop: true
|
||
script: |
|
||
set -euxo pipefail
|
||
|
||
- # ---------- 0. 基本参数 ----------
|
||
IMAGE_NEW="${{ needs.build-image.outputs.image_full }}"
|
||
IMAGE_LATEST="${{ needs.build-image.outputs.image_latest }}"
|
||
DEPLOY_DIR="${DEPLOY_PATH}"
|
||
COMPOSE="docker compose"
|
||
|
||
echo "=============================================="
|
||
- echo "本次部署镜像: ${IMAGE_NEW}"
|
||
- echo "部署目录 : ${DEPLOY_DIR}"
|
||
- echo "执行用户 : $(whoami)"
|
||
+ echo "Deploy image : ${IMAGE_NEW}"
|
||
+ echo "Deploy dir : ${DEPLOY_DIR}"
|
||
+ echo "Executor : $(whoami)"
|
||
echo "=============================================="
|
||
|
||
- # ---------- 1. 确保部署目录存在 ----------
|
||
mkdir -p "${DEPLOY_DIR}"
|
||
cd "${DEPLOY_DIR}"
|
||
|
||
- # ---------- 2. 登录 Gitea 容器仓库(用 pull token,最小权限) ----------
|
||
if [ -n "${{ secrets.REGISTRY_PULL_TOKEN }}" ] && [ -n "${{ secrets.REGISTRY_PULL_USER }}" ]; then
|
||
- echo "🔐 使用专用 pull 凭证登录 Gitea Container Registry"
|
||
+ echo "使用专用 pull 凭证登录 Gitea Container Registry"
|
||
echo "${{ secrets.REGISTRY_PULL_TOKEN }}" \
|
||
| docker login "${GITEA_HOST}" \
|
||
-u "${{ secrets.REGISTRY_PULL_USER }}" \
|
||
--password-stdin 2>/dev/null || true
|
||
fi
|
||
|
||
- # ---------- 3. 先拉镜像(提前下好,后面 up 不会卡) ----------
|
||
- echo "⬇️ 开始拉取新镜像 ${IMAGE_NEW} ..."
|
||
+ echo "Pulling new image ${IMAGE_NEW} ..."
|
||
docker pull "${IMAGE_NEW}"
|
||
docker tag "${IMAGE_NEW}" "${IMAGE_LATEST}"
|
||
- echo "✅ 新镜像拉取完成,并已打 latest tag"
|
||
+ echo "New image pulled and tagged as latest"
|
||
|
||
- # ---------- 4. 生成 / 更新 docker-compose.override.yml ----------
|
||
- # 指向新的精确 tag(不是 latest,出问题能快速回滚)
|
||
cat > docker-compose.override.yml <<YAML
|
||
services:
|
||
voicehub:
|
||
image: ${IMAGE_NEW}
|
||
YAML
|
||
- echo "📝 docker-compose.override.yml 已写入,指定镜像=${IMAGE_NEW}"
|
||
+ echo "docker-compose.override.yml written, image=${IMAGE_NEW}"
|
||
|
||
- # ---------- 5. 没有 docker-compose.yml? 自动从仓库下一份模板 ----------
|
||
if [ ! -f docker-compose.yml ]; then
|
||
- echo "ℹ️ 未发现 docker-compose.yml,自动从仓库模板下载"
|
||
+ echo "docker-compose.yml 不存在,自动从仓库模板下载"
|
||
TEMPLATE_URL="${GITHUB_SERVER_URL:-https://${GITEA_HOST}}/${GITHUB_REPOSITORY:-${GITHUB_REPOSITORY_OWNER}/${IMAGE_NAME}}/raw/branch/${GITHUB_REF_NAME:-main}/docker-compose.yml"
|
||
- echo " ↓ URL=${TEMPLATE_URL}"
|
||
+ echo "URL=${TEMPLATE_URL}"
|
||
curl -fsSL --max-time 30 "${TEMPLATE_URL}" -o docker-compose.yml \
|
||
|| (
|
||
- echo "⚠️ 自动下载模板失败,跳过(你需要手动上传 docker-compose.yml 和 .env)"
|
||
- # 不 exit,因为用户可能已经有一套自定义部署
|
||
+ echo "自动下载模板失败,请手动上传 docker-compose.yml 和 .env"
|
||
)
|
||
fi
|
||
|
||
- # ---------- 6. 没有 .env? 给一份占位(方便首次部署发现问题) ----------
|
||
if [ ! -f .env ]; then
|
||
echo "================================================================="
|
||
- echo "⚠️ WARNING:${DEPLOY_DIR}/.env 不存在!!!"
|
||
- echo "👉 第一次部署请先在服务器上创建 .env(至少填 POSTGRES_PASSWORD / JWT_SECRET / NUXT_PUBLIC_SITE_URL)"
|
||
- echo "👉 参考仓库根的 .env.example 或部署文档"
|
||
+ echo "WARNING:${DEPLOY_DIR}/.env 不存在"
|
||
+ echo "首次部署请在服务器上创建 .env(至少填写 POSTGRES_PASSWORD / JWT_SECRET / NUXT_PUBLIC_SITE_URL)"
|
||
+ echo "参考仓库根目录的 .env.example"
|
||
echo "================================================================="
|
||
- # 不退出,因为 compose 会用 environment 默认值尝试启动(只是大概率连不上 DB)
|
||
fi
|
||
|
||
- # ---------- 7. 滚动启动(如果只有 voicehub 单服务,不会动 postgres/redis) ----------
|
||
- echo "🔄 docker compose up -d(只重建变更过的 voicehub)..."
|
||
+ echo "Executing docker compose up -d (voicehub only)..."
|
||
${COMPOSE} up -d --remove-orphans
|
||
|
||
- # ---------- 8. 等待应用变 healthy,最多 2 分钟 ----------
|
||
- MAX_WAIT=24 # 24 × 5s = 2min
|
||
+ MAX_WAIT=24
|
||
WAITED=0
|
||
- echo "⏳ 等待容器 healthcheck 通过(最多 120s)..."
|
||
+ echo "Waiting for container healthcheck (max 120s)..."
|
||
while [ ${WAITED} -lt ${MAX_WAIT} ]; do
|
||
STATUS="$(${COMPOSE} ps --format json 2>/dev/null || true)"
|
||
if echo "${STATUS}" | grep -q '"Health":"healthy"'; then
|
||
- echo "✅ voicehub 容器健康检查通过"
|
||
+ echo "voicehub healthcheck passed"
|
||
break
|
||
fi
|
||
- # 同时也检查是否已经 running 但健康检查还没跑完(防止 healthcheck 写太严误判)
|
||
if echo "${STATUS}" | grep -q '"State":"running"'; then
|
||
:
|
||
else
|
||
- # 连 running 都不是 → 直接打印日志末尾 60 行帮忙排错
|
||
- echo "⚠️ voicehub 还没 running,当前状态:"
|
||
+ echo "voicehub not running, current status:"
|
||
${COMPOSE} ps || true
|
||
fi
|
||
sleep 5
|
||
WAITED=$((WAITED + 1))
|
||
done
|
||
if [ ${WAITED} -ge ${MAX_WAIT} ]; then
|
||
- echo "⚠️ 等待超时,下面是最近 120 行 voicehub 日志:"
|
||
+ echo "Healthcheck timeout. Last 120 lines of voicehub logs:"
|
||
${COMPOSE} logs --tail=120 voicehub || true
|
||
exit 1
|
||
fi
|
||
|
||
- # ---------- 9. 清理旧镜像(保留最近 2 个 tag + latest) ----------
|
||
- echo "🧹 清理 3 天前的旧镜像 / 悬空层,防止磁盘爆"
|
||
+ echo "Pruning images older than 72 hours..."
|
||
docker image prune -af --filter "until=72h" || true
|
||
docker builder prune -af || true
|
||
|
||
- # ---------- 10. 回滚指引 ----------
|
||
echo ""
|
||
echo "================================================================="
|
||
- echo "🎉 部署成功!访问: ${{ secrets.NUXT_PUBLIC_SITE_URL || '请查看你的站点 URL' }}"
|
||
- echo "🔁 如需紧急回滚到上一个版本,在服务器执行:"
|
||
+ echo "Deploy succeeded. Visit: ${{ secrets.NUXT_PUBLIC_SITE_URL || '请查看你的站点 URL' }}"
|
||
+ echo "Rollback instructions (on server):"
|
||
echo " cd ${DEPLOY_DIR}"
|
||
- echo " # 1) 先看有哪些 tag:"
|
||
echo " docker images | grep ${IMAGE_NAME} | head -5"
|
||
- echo " # 2) 选一个老 tag 填到 override:"
|
||
- echo " echo 'services:\n voicehub:\n image: ${GITEA_HOST}/${GITHUB_REPOSITORY_OWNER}/${IMAGE_NAME}:<旧TAG>' > docker-compose.override.yml"
|
||
+ echo " echo 'services:\n voicehub:\n image: ${GITEA_HOST}/${GITHUB_REPOSITORY_OWNER}/${IMAGE_NAME}:<OLD_TAG>' > docker-compose.override.yml"
|
||
echo " docker compose up -d"
|
||
echo "================================================================="
|
||
--
|
||
2.45.1.windows.1
|
||
|