Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
17b27ab
Add GitHub Actions workflows and update documentation strategy
Cho-Geer Apr 8, 2026
7d847f9
refactor: reorganize Docker build scripts to booking-deploy/scripts
Cho-Geer Apr 8, 2026
4b563b4
fix(ci): Add pull_request trigger to verify-images workflow
Cho-Geer Apr 8, 2026
982a5b3
ci: inject secrets into verify-images workflow for secure validation
Cho-Geer Apr 10, 2026
5be8c48
fix: make verify-images.sh auto-generate env files from examples in CI
Cho-Geer Apr 10, 2026
876016c
fix: make verify-images.sh auto-generate env files from examples in CI
Cho-Geer Apr 10, 2026
4ef5264
ci: use dynamic DOCKER_HUB_USER variable for image names
Cho-Geer Apr 10, 2026
303ab8d
ci: inject DOCKER_HUB_USER and prepare env files in verify-images wor…
Cho-Geer Apr 10, 2026
68caa7b
Merge branch 'feat/devops-cicd-foundation' of github.com:Cho-Geer/boo…
Cho-Geer Apr 10, 2026
d916376
Add environment specification to verify-images job
Cho-Geer Apr 10, 2026
66ae4b2
Merge branch 'feat/devops-cicd-foundation' of https://github.com/Cho-…
Cho-Geer Apr 10, 2026
0bad331
Remove environment specification from verify-images job
Cho-Geer Apr 10, 2026
3cd131b
fix(ci): use develop tag instead of dev for image verification
Cho-Geer Apr 10, 2026
7aca56d
fix(ci): prevent matrix fail-fast and use develop tag for prod verifi…
Cho-Geer Apr 10, 2026
f666149
fix(ci): enable docker login for pr verification to pull private images
Cho-Geer Apr 10, 2026
d30facf
Merge pull request #2 from Cho-Geer/feat/devops-cicd-foundation
zhaogeyinzuo Apr 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions .github/workflows/verify-deployment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: Verify Deployment Configuration

on:
pull_request:
branches:
- master
- develop
push:
branches:
- master
- develop
paths:
- 'compose/**'
- 'env/**'
- 'scripts/deploy-*.sh'
- '.github/workflows/verify-deployment.yml'

jobs:
verify-deployment-configuration:
runs-on: ubuntu-latest
permissions:
contents: read

strategy:
matrix:
environment: [dev, prod]

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Verify ${{ matrix.environment }} configuration
run: |
echo "🔍 Verifying ${{ matrix.environment }} deployment configuration..."

# Check Compose files
COMPOSE_FILE="compose/docker-compose.${{ matrix.environment }}.yml"
if [ ! -f "$COMPOSE_FILE" ]; then
echo "❌ Error: Compose file $COMPOSE_FILE not found"
exit 1
fi
echo "✅ Compose file exists: $COMPOSE_FILE"

# Check environment variable files
COMPOSE_ENV_FILE="compose/${{ matrix.environment }}.compose.env.example"
if [ ! -f "$COMPOSE_ENV_FILE" ]; then
echo "❌ Error: Compose env file $COMPOSE_ENV_FILE not found"
exit 1
fi
echo "✅ Compose env file exists: $COMPOSE_ENV_FILE"

# Check application environment files
APP_ENV_DIR="env/${{ matrix.environment }}"
if [ ! -d "$APP_ENV_DIR" ]; then
echo "❌ Error: Application env directory $APP_ENV_DIR not found"
exit 1
fi

BACKEND_ENV_FILE="$APP_ENV_DIR/backend.env.example"
if [ ! -f "$BACKEND_ENV_FILE" ]; then
echo "❌ Error: Backend env file $BACKEND_ENV_FILE not found"
exit 1
fi
echo "✅ Backend env file exists: $BACKEND_ENV_FILE"

FRONTEND_ENV_FILE="$APP_ENV_DIR/frontend.env.example"
if [ ! -f "$FRONTEND_ENV_FILE" ]; then
echo "❌ Error: Frontend env file $FRONTEND_ENV_FILE not found"
exit 1
fi
echo "✅ Frontend env file exists: $FRONTEND_ENV_FILE"

# Validate deployment scripts
DEPLOY_SCRIPT="scripts/deploy-${{ matrix.environment }}.sh"
if [ ! -f "$DEPLOY_SCRIPT" ]; then
echo "❌ Error: Deployment script $DEPLOY_SCRIPT not found"
exit 1
fi

if [ ! -x "$DEPLOY_SCRIPT" ]; then
echo "⚠️ Warning: Deployment script $DEPLOY_SCRIPT is not executable"
fi
echo "✅ Deployment script exists: $DEPLOY_SCRIPT"

# Validate verify-images.sh
VERIFY_SCRIPT="scripts/verify-images.sh"
if [ ! -f "$VERIFY_SCRIPT" ]; then
echo "❌ Error: Verify images script $VERIFY_SCRIPT not found"
exit 1
fi

if [ ! -x "$VERIFY_SCRIPT" ]; then
echo "⚠️ Warning: Verify script $VERIFY_SCRIPT is not executable"
fi
echo "✅ Verify script exists: $VERIFY_SCRIPT"

echo "🎉 ${{ matrix.environment }} deployment configuration is valid!"
106 changes: 106 additions & 0 deletions .github/workflows/verify-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: Verify Docker Images Deployment Readiness

on:
pull_request:
branches:
- master
- develop
paths:
- 'compose/**'
- 'scripts/verify-images.sh'
- '.github/workflows/verify-images.yml'
push:
branches:
- master
- develop
paths:
- 'compose/**'
- 'scripts/verify-images.sh'
- '.github/workflows/verify-images.yml'
workflow_dispatch:

jobs:
verify-images:
runs-on: ubuntu-latest
permissions:
contents: read
packages: read

strategy:
fail-fast: false # 👈 新增:即使一个环境失败,其他环境继续运行
matrix:
environment: [dev, prod]

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Prepare environment files for ${{ matrix.environment }}
run: |
# Compose 环境文件
COMPOSE_ENV_FILE="compose/${{ matrix.environment }}.compose.env"
COMPOSE_EXAMPLE="${COMPOSE_ENV_FILE}.example"
if [ ! -f "${COMPOSE_ENV_FILE}" ]; then
echo "Creating ${COMPOSE_ENV_FILE} from example..."
cp "${COMPOSE_EXAMPLE}" "${COMPOSE_ENV_FILE}"
fi

# 后端应用环境文件
BACKEND_ENV_FILE="env/${{ matrix.environment }}/backend.env"
BACKEND_EXAMPLE="${BACKEND_ENV_FILE}.example"
if [ ! -f "${BACKEND_ENV_FILE}" ]; then
echo "Creating ${BACKEND_ENV_FILE} from example..."
cp "${BACKEND_EXAMPLE}" "${BACKEND_ENV_FILE}"
fi

# 前端应用环境文件
FRONTEND_ENV_FILE="env/${{ matrix.environment }}/frontend.env"
FRONTEND_EXAMPLE="${FRONTEND_ENV_FILE}.example"
if [ ! -f "${FRONTEND_ENV_FILE}" ]; then
echo "Creating ${FRONTEND_ENV_FILE} from example..."
cp "${FRONTEND_EXAMPLE}" "${FRONTEND_ENV_FILE}"
fi

# 替换 Docker Hub 用户名占位符
sed -i "s/{{DOCKER_HUB_USER}}/${{ vars.DOCKER_HUB_USER }}/g" "${COMPOSE_ENV_FILE}"

# 根据环境注入 Secrets
if [ "${{ matrix.environment }}" = "dev" ]; then
sed -i "s/POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=${{ secrets.DEV_POSTGRES_PASSWORD }}/g" "${COMPOSE_ENV_FILE}"
sed -i "s/5382/${{ secrets.DEV_DB_PASSWORD }}/g" "${BACKEND_ENV_FILE}"
sed -i "s/replace-with-a-real-secret/${{ secrets.DEV_JWT_SECRET }}/g" "${BACKEND_ENV_FILE}"
sed -i "s/replace-with-a-real-refresh-secret/${{ secrets.DEV_JWT_REFRESH_SECRET }}/g" "${BACKEND_ENV_FILE}"
sed -i "s/dev-abc123def/develop/g" "${COMPOSE_ENV_FILE}"
else
sed -i "s/POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=${{ secrets.PROD_POSTGRES_PASSWORD }}/g" "${COMPOSE_ENV_FILE}"
sed -i "s/change-me/${{ secrets.PROD_DB_PASSWORD }}/g" "${BACKEND_ENV_FILE}"
sed -i "s/replace-with-a-real-secret/${{ secrets.PROD_JWT_SECRET }}/g" "${BACKEND_ENV_FILE}"
sed -i "s/replace-with-a-real-refresh-secret/${{ secrets.PROD_JWT_REFRESH_SECRET }}/g" "${BACKEND_ENV_FILE}"
sed -i "s/main-abc123def/develop/g" "${COMPOSE_ENV_FILE}"
fi

echo "Generated ${COMPOSE_ENV_FILE}:"
cat "${COMPOSE_ENV_FILE}"

- name: Verify images for ${{ matrix.environment }}
run: |
chmod +x scripts/verify-images.sh
./scripts/verify-images.sh ${{ matrix.environment }}
env:
DEV_POSTGRES_PASSWORD: ${{ secrets.DEV_POSTGRES_PASSWORD }}
DEV_DB_PASSWORD: ${{ secrets.DEV_DB_PASSWORD }}
DEV_JWT_SECRET: ${{ secrets.DEV_JWT_SECRET }}
DEV_JWT_REFRESH_SECRET: ${{ secrets.DEV_JWT_REFRESH_SECRET }}
PROD_POSTGRES_PASSWORD: ${{ secrets.PROD_POSTGRES_PASSWORD }}
PROD_DB_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }}
PROD_JWT_SECRET: ${{ secrets.PROD_JWT_SECRET }}
PROD_JWT_REFRESH_SECRET: ${{ secrets.PROD_JWT_REFRESH_SECRET }}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ uploads/
/.trae-aicc
/.clinerules

# 文档管理策略:默认忽略docs目录,明确列出需要版本管理的文档
/docs/*
# 核心项目文档(必须纳入版本管理)
!/docs/devops-ci-cd-assessment-and-remediation-plan.md
!/docs/devops-runbook.md

# 操作系统文件(不应提交)
.DS_Store
.DS_Store?
Expand Down
18 changes: 9 additions & 9 deletions compose/dev.compose.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
POSTGRES_PASSWORD=5382

# 可变分支标签(仅用于快速开发和测试)
# BACKEND_IMAGE=docker.io/cho-geer/booking-backend:dev
# BACKEND_MIGRATION_IMAGE=docker.io/cho-geer/booking-backend-migration:dev
# FRONTEND_IMAGE=docker.io/cho-geer/booking-frontend:dev
# BACKEND_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-backend:dev
# BACKEND_MIGRATION_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-backend-migration:dev
# FRONTEND_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-frontend:dev

# 不可变提交标签(推荐用于可靠部署)
BACKEND_IMAGE=docker.io/cho-geer/booking-backend:dev-abc123def
BACKEND_MIGRATION_IMAGE=docker.io/cho-geer/booking-backend-migration:dev-abc123def
FRONTEND_IMAGE=docker.io/cho-geer/booking-frontend:dev-abc123def
BACKEND_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-backend:dev-abc123def
BACKEND_MIGRATION_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-backend-migration:dev-abc123def
FRONTEND_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-frontend:dev-abc123def

# 语义版本标签(用于正式发布)
# BACKEND_IMAGE=docker.io/cho-geer/booking-backend:v1.0.0
# BACKEND_MIGRATION_IMAGE=docker.io/cho-geer/booking-backend-migration:v1.0.0
# FRONTEND_IMAGE=docker.io/cho-geer/booking-frontend:v1.0.0
# BACKEND_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-backend:v1.0.0
# BACKEND_MIGRATION_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-backend-migration:v1.0.0
# FRONTEND_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-frontend:v1.0.0
18 changes: 9 additions & 9 deletions compose/prod.compose.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
POSTGRES_PASSWORD=change-me

# 可变分支标签(不推荐用于生产)
# BACKEND_IMAGE=docker.io/cho-geer/booking-backend:main
# BACKEND_MIGRATION_IMAGE=docker.io/cho-geer/booking-backend-migration:main
# FRONTEND_IMAGE=docker.io/cho-geer/booking-frontend:main
# BACKEND_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-backend:main
# BACKEND_MIGRATION_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-backend-migration:main
# FRONTEND_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-frontend:main

# 不可变提交标签(用于生产部署)
BACKEND_IMAGE=docker.io/cho-geer/booking-backend:main-abc123def
BACKEND_MIGRATION_IMAGE=docker.io/cho-geer/booking-backend-migration:main-abc123def
FRONTEND_IMAGE=docker.io/cho-geer/booking-frontend:main-abc123def
BACKEND_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-backend:main-abc123def
BACKEND_MIGRATION_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-backend-migration:main-abc123def
FRONTEND_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-frontend:main-abc123def

# 语义版本标签(生产环境最佳实践)
# BACKEND_IMAGE=docker.io/cho-geer/booking-backend:v1.0.0
# BACKEND_MIGRATION_IMAGE=docker.io/cho-geer/booking-backend-migration:v1.0.0
# FRONTEND_IMAGE=docker.io/cho-geer/booking-frontend:v1.0.0
# BACKEND_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-backend:v1.0.0
# BACKEND_MIGRATION_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-backend-migration:v1.0.0
# FRONTEND_IMAGE=docker.io/{{DOCKER_HUB_USER}}/booking-frontend:v1.0.0
99 changes: 99 additions & 0 deletions scripts/build-all-images.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# 完整的 Docker 镜像构建和推送脚本
# 用于 booking-system 项目

$DOCKER_HUB_USER = "zhaogeyinzuo"

Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Booking System - 完整镜像构建流程" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan

# 1. 检查 Docker 是否运行
Write-Host "`n[1/7] 检查 Docker 状态..." -ForegroundColor Yellow
try {
docker ps | Out-Null
Write-Host "✓ Docker 正在运行" -ForegroundColor Green
} catch {
Write-Host "✗ Docker 未运行,请先启动 Docker Desktop" -ForegroundColor Red
exit 1
}

# 2. 检查 Docker Hub 登录状态
Write-Host "`n[2/7] 检查 Docker Hub 登录状态..." -ForegroundColor Yellow
$loginInfo = docker info 2>&1 | Select-String "Username"
if ($loginInfo) {
Write-Host "✓ 已登录 Docker Hub" -ForegroundColor Green
} else {
Write-Host "⚠ 未检测到登录信息,请执行: docker login" -ForegroundColor Yellow
$login = Read-Host "是否现在登录? (Y/N)"
if ($login -eq "Y" -or $login -eq "y") {
docker login
} else {
Write-Host "跳过登录,稍后请手动执行" -ForegroundColor Gray
}
}

# 3. 构建后端镜像
Write-Host "`n[3/7] 构建后端主镜像..." -ForegroundColor Yellow
Set-Location "../../booking-backend"
docker build -t booking-backend:latest -f Dockerfile .
if ($LASTEXITCODE -ne 0) {
Write-Host "✗ 后端镜像构建失败" -ForegroundColor Red
exit 1
}
Write-Host "✓ 后端镜像构建成功" -ForegroundColor Green

# 4. 构建数据库迁移镜像
Write-Host "`n[4/7] 构建数据库迁移镜像..." -ForegroundColor Yellow
docker build -t booking-migrate:latest -f Dockerfile.migrate .
if ($LASTEXITCODE -ne 0) {
Write-Host "✗ 迁移镜像构建失败" -ForegroundColor Red
exit 1
}
Write-Host "✓ 迁移镜像构建成功" -ForegroundColor Green

# 5. 构建前端镜像
Write-Host "`n[5/7] 构建前端镜像..." -ForegroundColor Yellow
Set-Location "../../booking-frontend"
docker build -t booking-frontend:latest -f Dockerfile .
if ($LASTEXITCODE -ne 0) {
Write-Host "✗ 前端镜像构建失败" -ForegroundColor Red
exit 1
}
Write-Host "✓ 前端镜像构建成功" -ForegroundColor Green

# 6. 打 Docker Hub 标签
Write-Host "`n[6/7] 打 Docker Hub 标签..." -ForegroundColor Yellow
Set-Location "../../booking-deploy/scripts"
docker tag booking-backend:latest ${DOCKER_HUB_USER}/booking-system-backend:latest
docker tag booking-migrate:latest ${DOCKER_HUB_USER}/booking-system-migrate:latest
docker tag booking-frontend:latest ${DOCKER_HUB_USER}/booking-system-frontend:latest
Write-Host "✓ 标签创建成功" -ForegroundColor Green

# 7. 推送镜像到 Docker Hub
Write-Host "`n[7/7] 推送镜像到 Docker Hub..." -ForegroundColor Yellow
$push = Read-Host "是否现在推送镜像到 Docker Hub? (Y/N)"
if ($push -eq "Y" -or $push -eq "y") {
Write-Host "推送后端镜像..." -ForegroundColor Cyan
docker push ${DOCKER_HUB_USER}/booking-system-backend:latest

Write-Host "推送迁移镜像..." -ForegroundColor Cyan
docker push ${DOCKER_HUB_USER}/booking-system-migrate:latest

Write-Host "推送前端镜像..." -ForegroundColor Cyan
docker push ${DOCKER_HUB_USER}/booking-system-frontend:latest

Write-Host "✓ 所有镜像推送成功!" -ForegroundColor Green
} else {
Write-Host "跳过推送,您可以稍后手动执行:" -ForegroundColor Gray
Write-Host " docker push ${DOCKER_HUB_USER}/booking-system-backend:latest" -ForegroundColor Gray
Write-Host " docker push ${DOCKER_HUB_USER}/booking-system-migrate:latest" -ForegroundColor Gray
Write-Host " docker push ${DOCKER_HUB_USER}/booking-system-frontend:latest" -ForegroundColor Gray
}

# 显示构建的镜像
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host " 构建完成的镜像列表" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
docker images --filter "reference=booking-*" --filter "reference=${DOCKER_HUB_USER}/booking-system-*" --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

Write-Host "`n✓ 所有任务完成!" -ForegroundColor Green
Loading
Loading