perf(cd): Optimize CD workflow based on wooo-aiops patterns

Changes:
- Add change detection (only build what changed)
- Add skip_api/skip_web manual inputs for selective builds
- Use native Docker BuildKit (remove buildx-action overhead)
- Add local Next.js cache (/home/wooo/build-cache/awoooi/)
- Split build-images into build-api and build-web jobs

Reference: wooo-aiops ci.yml and fast-deploy-uat.yml

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-03-24 14:13:56 +08:00
parent 580c38de94
commit 515339f2a5

View File

@@ -7,6 +7,15 @@ on:
- 'docs/**'
- '*.md'
workflow_dispatch:
inputs:
skip_api:
description: '跳過 API 建構 (只改前端時)'
type: boolean
default: false
skip_web:
description: '跳過 Web 建構 (只改後端時)'
type: boolean
default: false
# 沿用 AIOPS 設計: 新 commit 自動取消舊 workflow
concurrency:
@@ -16,35 +25,56 @@ concurrency:
env:
REGISTRY: 192.168.0.110:5000
IMAGE_PREFIX: library/awoooi
# 本地快取路徑 (比 Registry cache 更快)
LOCAL_CACHE_DIR: /home/wooo/build-cache/awoooi
jobs:
# ==================== Build & Push Images ====================
build-images:
name: Build & Push Images
# ==================== 變更偵測 ====================
detect-changes:
name: Detect Changes
runs-on: self-hosted
strategy:
matrix:
app: [web, api]
outputs:
api_changed: ${{ steps.changes.outputs.api }}
web_changed: ${{ steps.changes.outputs.web }}
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
# 修復: Harbor 是 HTTP需要設定 insecure registry
driver-opts: |
network=host
buildkitd-config-inline: |
[registry."192.168.0.110:5000"]
http = true
insecure = true
fetch-depth: 2
- name: Login to WOOO Harbor
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.HARBOR_USER }}
password: ${{ secrets.HARBOR_PASSWORD }}
- name: Check changed files
id: changes
run: |
# 取得變更的檔案
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
echo "Changed files: $CHANGED_FILES"
# 偵測 API 變更
if echo "$CHANGED_FILES" | grep -qE "^apps/api/|^packages/"; then
echo "api=true" >> $GITHUB_OUTPUT
else
echo "api=false" >> $GITHUB_OUTPUT
fi
# 偵測 Web 變更
if echo "$CHANGED_FILES" | grep -qE "^apps/web/|^packages/"; then
echo "web=true" >> $GITHUB_OUTPUT
else
echo "web=false" >> $GITHUB_OUTPUT
fi
# ==================== Build API ====================
build-api:
name: Build & Push API
runs-on: self-hosted
needs: detect-changes
if: |
github.event_name == 'workflow_dispatch' && !inputs.skip_api ||
github.event_name == 'push' && needs.detect-changes.outputs.api_changed == 'true' ||
github.event_name == 'push' && needs.detect-changes.outputs.api_changed == 'false' && needs.detect-changes.outputs.web_changed == 'false'
outputs:
image_tag: ${{ steps.tag.outputs.tag }}
steps:
- uses: actions/checkout@v4
- name: Generate image tag
id: tag
@@ -53,26 +83,87 @@ jobs:
RUN_ID=${{ github.run_id }}
echo "tag=${SHA}-${RUN_ID}" >> $GITHUB_OUTPUT
- name: Build & Push to Harbor
uses: docker/build-push-action@v5
with:
context: .
file: apps/${{ matrix.app }}/Dockerfile
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-${{ matrix.app }}:${{ steps.tag.outputs.tag }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Output image tag
- name: Login to Harbor
run: |
echo "::notice::Image pushed: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-${{ matrix.app }}:${{ steps.tag.outputs.tag }}"
echo "${{ secrets.HARBOR_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u ${{ secrets.HARBOR_USER }} --password-stdin
# 🚀 沿用 AIOPS: 原生 BuildKit (無中間層損耗)
- name: Build & Push API (Native BuildKit)
env:
DOCKER_BUILDKIT: 1
run: |
echo "🐳 使用原生 Docker BuildKit 建構 API..."
docker build \
--push \
--tag ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-api:${{ steps.tag.outputs.tag }} \
--file apps/api/Dockerfile \
.
echo "✅ API 映像: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-api:${{ steps.tag.outputs.tag }}"
# ==================== Build Web ====================
build-web:
name: Build & Push Web
runs-on: self-hosted
needs: detect-changes
if: |
github.event_name == 'workflow_dispatch' && !inputs.skip_web ||
github.event_name == 'push' && needs.detect-changes.outputs.web_changed == 'true' ||
github.event_name == 'push' && needs.detect-changes.outputs.api_changed == 'false' && needs.detect-changes.outputs.web_changed == 'false'
outputs:
image_tag: ${{ steps.tag.outputs.tag }}
steps:
- uses: actions/checkout@v4
- name: Generate image tag
id: tag
run: |
SHA=$(git rev-parse --short HEAD)
RUN_ID=${{ github.run_id }}
echo "tag=${SHA}-${RUN_ID}" >> $GITHUB_OUTPUT
- name: Login to Harbor
run: |
echo "${{ secrets.HARBOR_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u ${{ secrets.HARBOR_USER }} --password-stdin
# 🚀 沿用 AIOPS: 恢復本地 Next.js 快取
- name: Restore Next.js cache
run: |
mkdir -p apps/web/.next/cache
if [ -d "${{ env.LOCAL_CACHE_DIR }}/nextjs" ]; then
cp -r ${{ env.LOCAL_CACHE_DIR }}/nextjs/* apps/web/.next/cache/ 2>/dev/null || true
echo "✅ Next.js 快取已恢復"
fi
# 🚀 沿用 AIOPS: 原生 BuildKit
- name: Build & Push Web (Native BuildKit)
env:
DOCKER_BUILDKIT: 1
run: |
echo "🎨 使用原生 Docker BuildKit 建構 Web..."
docker build \
--push \
--tag ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-web:${{ steps.tag.outputs.tag }} \
--file apps/web/Dockerfile \
.
echo "✅ Web 映像: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-web:${{ steps.tag.outputs.tag }}"
# 🚀 沿用 AIOPS: 儲存本地快取
- name: Save Next.js cache
run: |
mkdir -p ${{ env.LOCAL_CACHE_DIR }}/nextjs
if [ -d "apps/web/.next/cache" ]; then
cp -r apps/web/.next/cache/* ${{ env.LOCAL_CACHE_DIR }}/nextjs/ 2>/dev/null || true
echo "✅ Next.js 快取已儲存"
fi
# ==================== Deploy to Production ====================
# Memory 鐵律: 禁止 UAT只有 Dev + Prod
deploy-prod:
name: Deploy to Production
runs-on: self-hosted
needs: build-images
needs: [detect-changes, build-api, build-web]
# 允許部分 build 被跳過
if: always() && (needs.build-api.result == 'success' || needs.build-api.result == 'skipped') && (needs.build-web.result == 'success' || needs.build-web.result == 'skipped')
environment: production
steps:
- uses: actions/checkout@v4
@@ -86,13 +177,13 @@ jobs:
- name: Install kubectl and Kustomize
run: |
mkdir -p $HOME/.local/bin
# Install kubectl
# Install kubectl (110 主機應已預裝)
if ! command -v kubectl &> /dev/null; then
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl $HOME/.local/bin/
fi
# Install kustomize
# Install kustomize (110 主機應已預裝)
if ! command -v kustomize &> /dev/null; then
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
mv kustomize $HOME/.local/bin/