Files
ftb-project-management/.github/workflows/deploy-production.yml
eef09d5af4 feat(ops): 添加发布 smoke test
- 新增只读发布 smoke runner 和 dry-run 测试\n- 将生产部署 workflow 从单点版本检查升级为 smoke test\n- 补充 Docker web runtime 脚本复制、package script 和部署文档\n\nCo-Authored-By: GPT-5 Codex <codex@openai.com>
2026-07-08 16:14:48 +08:00

134 lines
4.5 KiB
YAML

name: Deploy Production
on:
push:
branches:
- master
workflow_dispatch:
concurrency:
group: production
cancel-in-progress: false
permissions:
contents: read
packages: write
env:
REGISTRY: ghcr.io
NEXT_PUBLIC_API_URL: /api/v1
NEXT_API_PROXY_TARGET: http://server:3001
jobs:
build-push-deploy:
name: Build, push, deploy, verify
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Prepare image metadata
id: meta
shell: bash
run: |
repo_lc="${GITHUB_REPOSITORY,,}"
build_time="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
echo "repo_lc=${repo_lc}" >> "$GITHUB_OUTPUT"
echo "build_time=${build_time}" >> "$GITHUB_OUTPUT"
echo "web_image=${REGISTRY}/${repo_lc}/web:${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
echo "server_image=${REGISTRY}/${repo_lc}/server:${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
echo "web_image_master=${REGISTRY}/${repo_lc}/web:master" >> "$GITHUB_OUTPUT"
echo "server_image_master=${REGISTRY}/${repo_lc}/server:master" >> "$GITHUB_OUTPUT"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push web image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile.web
push: true
tags: |
${{ steps.meta.outputs.web_image }}
${{ steps.meta.outputs.web_image_master }}
build-args: |
NEXT_PUBLIC_API_URL=${{ env.NEXT_PUBLIC_API_URL }}
NEXT_API_PROXY_TARGET=${{ env.NEXT_API_PROXY_TARGET }}
APP_VERSION=${{ github.sha }}
APP_BUILD_TIME=${{ steps.meta.outputs.build_time }}
APP_IMAGE_TAG=${{ steps.meta.outputs.web_image }}
- name: Build and push server image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile.server
push: true
tags: |
${{ steps.meta.outputs.server_image }}
${{ steps.meta.outputs.server_image_master }}
build-args: |
APP_VERSION=${{ github.sha }}
APP_BUILD_TIME=${{ steps.meta.outputs.build_time }}
APP_IMAGE_TAG=${{ steps.meta.outputs.server_image }}
- name: Deploy over SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.PROD_HOST }}
username: ${{ secrets.PROD_USER }}
key: ${{ secrets.PROD_SSH_KEY }}
script_stop: true
script: |
set -euo pipefail
cd "${{ secrets.PROD_APP_DIR }}"
test -f .env.production
git fetch origin master
git checkout master
git pull --ff-only origin master
set_env() {
key="$1"
value="$2"
if grep -q "^${key}=" .env.production; then
sed -i "s|^${key}=.*|${key}=${value}|" .env.production
else
printf "\n%s=%s\n" "${key}" "${value}" >> .env.production
fi
}
set_env APP_VERSION "${{ github.sha }}"
set_env APP_BUILD_TIME "${{ steps.meta.outputs.build_time }}"
set_env WEB_IMAGE "${{ steps.meta.outputs.web_image }}"
set_env SERVER_IMAGE "${{ steps.meta.outputs.server_image }}"
if [ -n "${{ secrets.GHCR_READ_TOKEN }}" ]; then
echo "${{ secrets.GHCR_READ_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
fi
docker compose --env-file .env.production -f docker-compose.prod.yml pull web server
docker compose --env-file .env.production -f docker-compose.prod.yml up -d postgres redis
docker compose --env-file .env.production -f docker-compose.prod.yml run --rm -T server pnpm --filter server db:deploy
docker compose --env-file .env.production -f docker-compose.prod.yml up -d --remove-orphans
for attempt in $(seq 1 30); do
if docker compose --env-file .env.production -f docker-compose.prod.yml exec -T web node scripts/smoke-test-release.mjs --base-url http://nginx --expected-version "${{ github.sha }}"; then
exit 0
fi
sleep 2
done
echo "Release smoke check failed after retries"
exit 1