chore(deploy): 修复 Docker 启动链 + 补 compose + Gitea Actions
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
name: 构建并部署 VoiceHub
|
||||
run-name: ${{ gitea.actor }} 触发部署 #${{{{ gitea.run_number }}}}
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
# 允许在 Gitea 页面手动点「运行工作流」
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
deploy_target:
|
||||
description: 是否在构建后自动部署到服务器
|
||||
type: boolean
|
||||
default: true
|
||||
required: false
|
||||
|
||||
# 同一时刻只跑一个部署,避免部署竞态
|
||||
concurrency:
|
||||
group: deploy-production
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
# ====== 按需修改(改成你的 Gitea 域名,不要 http:// 前缀) ======
|
||||
GITEA_REGISTRY: gitea.example.com
|
||||
# ====== 一般不改 ======
|
||||
IMAGE_NAME: byw-voicehub
|
||||
DEPLOY_PATH: /opt/byw-voicehub
|
||||
NODE_VERSION: "22"
|
||||
|
||||
jobs:
|
||||
# ============================================================
|
||||
# Job 1: 构建 & 推送 Docker 镜像到 Gitea 自带容器仓库
|
||||
# ============================================================
|
||||
build-image:
|
||||
name: 构建 Docker 镜像
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
image_tag: ${{ steps.meta.outputs.tag }}
|
||||
image_full: ${{ steps.meta.outputs.full }}
|
||||
steps:
|
||||
- name: 📥 拉取代码
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: 🔧 设置 Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
registry-url: https://registry.npmmirror.com # 国内加速,不需要可删
|
||||
|
||||
- name: 🧪 环境校验
|
||||
run: |
|
||||
set -euxo pipefail
|
||||
node -v
|
||||
npm -v
|
||||
npm ci --no-audit --no-fund
|
||||
|
||||
- name: 🔐 登录 Gitea 容器仓库
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.GITEA_REGISTRY }}
|
||||
username: ${{ gitea.repository_owner }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN || gitea.token }}
|
||||
|
||||
- name: 🏷️ 生成镜像标签
|
||||
id: meta
|
||||
run: |
|
||||
set -euo pipefail
|
||||
SHORT_SHA="${GITEA_SHA:0:7}"
|
||||
DATE_TAG="$(date +%Y%m%d)-${SHORT_SHA}"
|
||||
FULL="${GITEA_REGISTRY}/${GITEA_REPO_OWNER}/${IMAGE_NAME}:${DATE_TAG}"
|
||||
LATEST="${GITEA_REGISTRY}/${GITEA_REPO_OWNER}/${IMAGE_NAME}:latest"
|
||||
echo "tag=${DATE_TAG}" >> "$GITEA_OUTPUT"
|
||||
echo "full=${FULL}" >> "$GITEA_OUTPUT"
|
||||
echo "latest=${LATEST}" >> "$GITEA_OUTPUT"
|
||||
echo "✅ 镜像 tag: ${DATE_TAG}"
|
||||
echo "✅ 镜像完整: ${FULL}"
|
||||
|
||||
- name: 🐳 构建并推送镜像(BuildKit 多阶段 + 缓存)
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.meta.outputs.full }}
|
||||
${{ steps.meta.outputs.latest }}
|
||||
cache-from: type=registry,ref=${{ env.GITEA_REGISTRY }}/${{ gitea.repository_owner }}/${{ env.IMAGE_NAME }}:buildcache
|
||||
cache-to: type=registry,ref=${{ env.GITEA_REGISTRY }}/${{ gitea.repository_owner }}/${{ env.IMAGE_NAME }}:buildcache,mode=max
|
||||
build-args: |
|
||||
BUILDKIT_INLINE_CACHE=1
|
||||
labels: |
|
||||
org.opencontainers.image.source=${{ gitea.server_url }}/${{ gitea.repository }}
|
||||
org.opencontainers.image.revision=${{ gitea.sha }}
|
||||
|
||||
# ============================================================
|
||||
# Job 2: SSH 到服务器拉取新镜像并重启
|
||||
# ============================================================
|
||||
deploy:
|
||||
name: 部署到服务器
|
||||
needs: build-image
|
||||
if: ${{ !cancelled() && !failure() && (inputs.deploy_target == true || github.event_name == 'push') }}
|
||||
runs-on: ubuntu-latest
|
||||
environment: production
|
||||
steps:
|
||||
- name: 🌐 SSH 部署到生产服务器
|
||||
uses: https://github.com/appleboy/ssh-action@v1.2.0
|
||||
with:
|
||||
host: ${{ secrets.DEPLOY_HOST }}
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
port: ${{ secrets.DEPLOY_PORT || 22 }}
|
||||
script_stop: true
|
||||
command_timeout: 30m
|
||||
script: |
|
||||
set -euxo pipefail
|
||||
|
||||
# 1) 首次部署时自动创建目录 & 空的 docker-compose.yml/.env
|
||||
mkdir -p "${DEPLOY_PATH}"
|
||||
cd "${DEPLOY_PATH}"
|
||||
|
||||
# 2) 登录 Gitea 镜像仓库(用一个仅 pull 权限的 token 更安全)
|
||||
echo "${{ secrets.REGISTRY_PULL_TOKEN || gitea.token }}" \
|
||||
| docker login "${GITEA_REGISTRY}" -u "${{ secrets.REGISTRY_PULL_USER || gitea.repository_owner }}" --password-stdin 2>/dev/null || true
|
||||
|
||||
# 3) 拉最新镜像
|
||||
IMAGE_LATEST="${GITEA_REGISTRY}/${GITEA_REPO_OWNER}/${IMAGE_NAME}:latest"
|
||||
docker pull "$IMAGE_LATEST"
|
||||
|
||||
# 4) 写一个最小的 docker-compose.override.yml,指向刚拉的镜像
|
||||
# (避免改动服务器上已配置好的 docker-compose.yml 和 .env)
|
||||
cat > docker-compose.override.yml <<'YAML'
|
||||
services:
|
||||
voicehub:
|
||||
image: __REPLACE_WITH_IMAGE__
|
||||
YAML
|
||||
sed -i "s|__REPLACE_WITH_IMAGE__|${IMAGE_LATEST}|g" docker-compose.override.yml
|
||||
|
||||
# 5) 启动(如果没有 docker-compose.yml 就自动从你的仓库默认模板里下载一份)
|
||||
if [ ! -f docker-compose.yml ]; then
|
||||
echo "ℹ️ 首次部署:写入默认 docker-compose.yml 模板(请稍后在服务器上编辑 .env)"
|
||||
curl -fsSL "${{ gitea.server_url }}/${{ gitea.repository }}/raw/branch/main/docker-compose.yml" \
|
||||
-o docker-compose.yml || echo "下载失败,跳过(你需要手动上传 docker-compose.yml)"
|
||||
fi
|
||||
|
||||
# 6) 健康拉取 + 滚动重启(先拉好再 up,downtime 最小)
|
||||
docker compose pull --quiet voicehub 2>/dev/null || true
|
||||
docker compose up -d --remove-orphans
|
||||
|
||||
# 7) 等健康检查通过,最多等 120s
|
||||
for i in $(seq 1 24); do
|
||||
if docker compose ps --format json 2>/dev/null | grep -q '"Health":"healthy"'; then
|
||||
echo "✅ 容器健康"
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
|
||||
# 8) 清理旧镜像(只留最近 2 个 tag,省磁盘)
|
||||
docker image prune -af --filter "until=72h" || true
|
||||
|
||||
echo "🎉 部署完成!镜像=${{ needs.build-image.outputs.image_full }}"
|
||||
Reference in New Issue
Block a user