Files
ewoooc/.gitlab-ci.yml
ogt 1b4f3a7bbe
Some checks failed
CD Pipeline / deploy (push) Failing after 59s
feat: EwoooC 初始化 — 完整專案推版至 Gitea
- 建立 Gitea Actions CD pipeline (.gitea/workflows/cd.yaml)
- 部署模式: rsync Python 檔案至 188 → docker restart (volume mount)
- Dockerfile/requirements 變動時自動重建 Docker image
- 部署通知: Telegram (開始/成功/失敗)
- 健康檢查: https://mo.wooo.work/health (最多 5 次重試)
- 同步最新 CLAUDE.md / ADR-008 / memory (2026-04-19)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 01:21:13 +08:00

265 lines
9.3 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# =============================================================================
# WOOO TECH - Momo Pro System
# GitLab CI/CD Pipeline
# =============================================================================
#
# 流程:
# 1. test: 執行 pytest 測試
# 2. security: Bandit 安全掃描
# 3. build: 構建 Docker 映像並推送到 Registry
# 4. deploy-uat: 部署到 UAT 環境
# 5. deploy-gcp: 部署到 GCP 環境 (手動觸發)
#
# Registry:
# - URL: registry.wooo.work
# - 帳號: GitLab CI/CD 變數 REGISTRY_USER
# - 密碼: GitLab CI/CD 變數 REGISTRY_PASSWORD
#
# =============================================================================
stages:
- test
- build
- deploy
variables:
# Docker Registry
REGISTRY_URL: "registry.wooo.work"
IMAGE_NAME: "wooo/momo-pro-system"
IMAGE_PATH: "${REGISTRY_URL}/${IMAGE_NAME}"
# 目標環境
UAT_HOST: "192.168.0.110"
UAT_USER: "wooo"
GCP_HOST: "35.194.233.141"
GCP_USER: "ogt"
# =============================================================================
# Test Stage
# =============================================================================
test:
stage: test
image: python:3.11-slim
before_script:
- pip install --quiet -r requirements.txt
- pip install --quiet pytest pytest-cov
script:
- python -m pytest tests/ -v --tb=short || echo "No tests or tests skipped"
allow_failure: true
tags:
- docker
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
- if: '$CI_PIPELINE_SOURCE == "web"'
# =============================================================================
# Security Scan (Bandit)
# =============================================================================
security-scan:
stage: test
image: python:3.11-slim
before_script:
- pip install --quiet bandit
script:
- bandit -r . -x ./tests,./venv -f json -o bandit-report.json || true
- |
if [ -f bandit-report.json ]; then
HIGH=$(cat bandit-report.json | python3 -c "import sys,json; print(len([r for r in json.load(sys.stdin).get('results',[]) if r.get('issue_severity')=='HIGH']))" 2>/dev/null || echo "0")
echo "High severity issues: $HIGH"
if [ "$HIGH" -gt "0" ]; then
echo "⚠️ 發現高危安全問題"
fi
fi
artifacts:
paths:
- bandit-report.json
expire_in: 1 week
allow_failure: true
tags:
- docker
# =============================================================================
# Build & Push to Registry
# =============================================================================
build:
stage: build
image: docker:24.0.7
services:
- docker:24.0.7-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- echo "$REGISTRY_PASSWORD" | docker login -u "$REGISTRY_USER" --password-stdin $REGISTRY_URL
script:
- echo "Building image..."
- BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
- docker build --build-arg BUILD_DATE=$BUILD_DATE --build-arg GIT_SHA=$CI_COMMIT_SHA -t $IMAGE_PATH:$CI_COMMIT_SHORT_SHA -t $IMAGE_PATH:latest .
- echo "Pushing to registry..."
- docker push $IMAGE_PATH:$CI_COMMIT_SHORT_SHA
- docker push $IMAGE_PATH:latest
- echo "Image pushed - $IMAGE_PATH:latest"
after_script:
- if [ -n "$TELEGRAM_BOT_TOKEN" ] && [ "$CI_JOB_STATUS" = "success" ]; then curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" -d chat_id="${TELEGRAM_CHAT_ID}" -d parse_mode="HTML" -d text="Build Success - ${IMAGE_PATH}:${CI_COMMIT_SHORT_SHA}" > /dev/null 2>&1; fi
tags:
- docker
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
# =============================================================================
# Deploy to UAT
# =============================================================================
deploy-uat:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client curl
- mkdir -p ~/.ssh
- echo "$UAT_SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan -H $UAT_HOST >> ~/.ssh/known_hosts 2>/dev/null
script:
- echo "🚀 部署到 UAT..."
- |
ssh -o StrictHostKeyChecking=no ${UAT_USER}@${UAT_HOST} << ENDSSH
# 設定 kubectl 環境
export KUBECONFIG=/home/wooo/.kube/config
# 同步程式碼
cd /home/wooo/momo_pro_system
git fetch gitlab main
git reset --hard gitlab/main
# 更新 K8s registry secret
kubectl delete secret registry-secret -n momo 2>/dev/null || true
kubectl create secret docker-registry registry-secret \
--docker-server=${REGISTRY_URL} \
--docker-username=${REGISTRY_USER} \
--docker-password=${REGISTRY_PASSWORD} \
-n momo
# 重啟服務 (會自動拉取新映像)
kubectl rollout restart deployment/momo-app deployment/momo-scheduler -n momo
# 等待就緒
kubectl rollout status deployment/momo-app -n momo --timeout=180s
echo "✅ UAT 部署完成"
ENDSSH
- |
# 健康檢查
sleep 10
if curl -s "https://mo.wooo.work/health" | grep -q "healthy"; then
echo "✅ 健康檢查通過"
else
echo "⚠️ 健康檢查失敗"
fi
after_script:
- |
if [ -n "$TELEGRAM_BOT_TOKEN" ]; then
if [ "$CI_JOB_STATUS" == "success" ]; then
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="${TELEGRAM_CHAT_ID}" \
-d parse_mode="HTML" \
-d text="✅ <b>UAT 部署成功</b>%0A%0A🌐 https://mo.wooo.work%0A📌 ${CI_COMMIT_SHORT_SHA}%0A👤 ${GITLAB_USER_NAME}" > /dev/null 2>&1
else
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="${TELEGRAM_CHAT_ID}" \
-d parse_mode="HTML" \
-d text="❌ <b>UAT 部署失敗</b>%0A%0A🔗 ${CI_PIPELINE_URL}" > /dev/null 2>&1
fi
fi
tags:
- docker
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
needs:
- build
# =============================================================================
# Deploy to GCP (手動觸發)
# =============================================================================
deploy-gcp:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client curl docker
- mkdir -p ~/.ssh
- echo "$GCP_SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan -H $GCP_HOST >> ~/.ssh/known_hosts 2>/dev/null
script:
- echo "🚀 部署到 GCP..."
- |
# 從 Registry 拉取映像
echo "$REGISTRY_PASSWORD" | docker login -u "$REGISTRY_USER" --password-stdin $REGISTRY_URL
docker pull $IMAGE_PATH:latest
# 匯出映像
docker save $IMAGE_PATH:latest -o /tmp/momo-image.tar
# 傳輸到 GCP (使用用戶家目錄)
scp -o StrictHostKeyChecking=no /tmp/momo-image.tar ${GCP_USER}@${GCP_HOST}:~/momo-image.tar
# 在 GCP 上匯入並部署
ssh -o StrictHostKeyChecking=no ${GCP_USER}@${GCP_HOST} << ENDSSH
# 匯入映像到 K3s
sudo k3s ctr images import ~/momo-image.tar
rm ~/momo-image.tar
# 更新 Deployment 使用新映像
sudo kubectl set image deployment/momo-app momo-app=${IMAGE_PATH}:latest -n momo
sudo kubectl set image deployment/momo-scheduler scheduler=${IMAGE_PATH}:latest -n momo
# 設定 imagePullPolicy 為 IfNotPresent允許使用本地匯入的映像
sudo kubectl patch deployment momo-app -n momo -p '{"spec":{"template":{"spec":{"containers":[{"name":"momo-app","imagePullPolicy":"IfNotPresent"}]}}}}'
sudo kubectl patch deployment momo-scheduler -n momo -p '{"spec":{"template":{"spec":{"containers":[{"name":"scheduler","imagePullPolicy":"IfNotPresent"}]}}}}'
# 等待部署完成
sudo kubectl rollout status deployment/momo-app -n momo --timeout=180s
echo "✅ GCP 部署完成"
ENDSSH
- |
# 健康檢查
sleep 10
if curl -s "https://momo.wooo.work/health" | grep -q "healthy"; then
echo "✅ GCP 健康檢查通過"
else
echo "⚠️ GCP 健康檢查失敗"
fi
after_script:
- |
if [ -n "$TELEGRAM_BOT_TOKEN" ]; then
if [ "$CI_JOB_STATUS" == "success" ]; then
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="${TELEGRAM_CHAT_ID}" \
-d parse_mode="HTML" \
-d text="✅ <b>GCP 部署成功</b>%0A%0A🌐 https://momo.wooo.work%0A📌 ${CI_COMMIT_SHORT_SHA}" > /dev/null 2>&1
fi
fi
tags:
- docker
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
needs:
- deploy-uat
allow_failure: false
# =============================================================================
# Health Check (手動)
# =============================================================================
health-check:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache curl
script:
- echo "🏥 健康檢查..."
- echo "UAT:" && curl -s "https://mo.wooo.work/health" || echo "無法連線"
- echo "GCP:" && curl -s "https://momo.wooo.work/health" || echo "無法連線"
- echo "Registry:" && curl -s -u "$REGISTRY_USER:$REGISTRY_PASSWORD" "https://$REGISTRY_URL/v2/_catalog" || echo "無法連線"
tags:
- docker
rules:
- if: '$CI_PIPELINE_SOURCE == "web"'
when: manual