Files
ljwx-stock/.gitea/workflows/cicd.yml
T
brunoandClaude e135a6396f feat: 实现完整的 CICD 流程
- 添加 Gitea Actions 工作流 (.gitea/workflows/)
  - cicd.yml: 完整 CICD 流程(构建、测试、推送、部署)
  - test.yml: 快速测试工作流

- 配置 Kubernetes 部署清单 (k8s/)
  - namespace.yaml: 创建 claude-stock namespace
  - configmap.yaml: 应用配置和 Ollama 连接配置
  - deployment.yaml: 后端和前端部署(支持 Node IP 连接 Ollama)
  - service.yaml: 服务定义、RBAC 和网络策略
  - ollama-bridge.yaml: 四种 K8s 与宿主 Ollama 通信方案
  - secrets.sh: 自动创建 K8s Secrets

- 部署脚本和工具
  - deploy-to-k8s.sh: 一键 K8s 部署脚本
  - verify-cicd.sh: CICD 环境验证脚本

- 文档
  - CICD_DEPLOYMENT_GUIDE.md: 完整部署指南(中文)
  - QUICK_START.md: 5 分钟快速启动指南(中文)

关键功能:
✅ Gitea Runners(Docker + K8s)
✅ Harbor 镜像仓库集成
✅ 自动化测试和安全扫描
✅ K8s 滚动更新和健康检查
✅ Ollama 集成(使用 Node IP 解决通信问题)
✅ 完整的部署后验证

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 11:56:09 +08:00

245 lines
8.2 KiB
YAML

name: 完整 CICD 流程 (Build, Test, Push to Harbor, Deploy to K8s)
on:
push:
branches:
- main
- develop
paths-ignore:
- '*.md'
- 'docs/**'
- '.gitignore'
pull_request:
branches:
- main
- develop
env:
HARBOR_REGISTRY: localhost:8443
HARBOR_PROJECT: claude-stock
K8S_NAMESPACE: claude-stock
IMAGE_NAME_BACKEND: claude-stock-backend
IMAGE_NAME_FRONTEND: claude-stock-frontend
REGISTRY_USER: admin
REGISTRY_PASSWORD: ${{ secrets.HARBOR_PASSWORD }}
jobs:
# ==================== 构建和推送阶段 ====================
build-and-push:
runs-on: ubuntu-latest
strategy:
matrix:
service:
- backend
- frontend
steps:
- name: 📥 检出代码
uses: actions/checkout@v4
- name: 🏗️ 设置 Docker 构建环境
uses: docker/setup-buildx-action@v3
- name: 🔑 登录 Harbor 镜像仓库
uses: docker/login-action@v3
with:
registry: ${{ env.HARBOR_REGISTRY }}
username: ${{ env.REGISTRY_USER }}
password: ${{ env.REGISTRY_PASSWORD }}
- name: 📦 构建并推送 ${{ matrix.service }} 镜像
uses: docker/build-push-action@v5
with:
context: ./${{ matrix.service }}
file: ./${{ matrix.service }}/Dockerfile
push: true
tags: |
${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/${{ matrix.service }}:latest
${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/${{ matrix.service }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ==================== 测试阶段 ====================
test:
needs: build-and-push
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
POSTGRES_DB: test_db
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: 📥 检出代码
uses: actions/checkout@v4
- name: 🐍 设置 Python 环境
uses: actions/setup-python@v4
with:
python-version: '3.11'
cache: 'pip'
- name: 📚 安装依赖
working-directory: ./backend
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov pytest-asyncio httpx
- name: ✅ 运行单元测试
working-directory: ./backend
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/test_db
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
TUSHARE_TOKEN: ${{ secrets.TUSHARE_TOKEN }}
ALPHA_VANTAGE_API_KEY: ${{ secrets.ALPHA_VANTAGE_API_KEY }}
run: |
pytest --cov=app --cov-report=xml --cov-report=term
- name: 📊 上传覆盖率报告
uses: codecov/codecov-action@v3
if: always()
with:
files: ./backend/coverage.xml
# ==================== 安全扫描 ====================
security-scan:
runs-on: ubuntu-latest
steps:
- name: 📥 检出代码
uses: actions/checkout@v4
- name: 🔐 运行 Trivy 镜像扫描
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/backend:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: 📤 上传扫描结果
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: 'trivy-results.sarif'
# ==================== 部署到 Kubernetes ====================
deploy:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [build-and-push, test]
runs-on: ubuntu-latest
steps:
- name: 📥 检出代码
uses: actions/checkout@v4
- name: 🔐 配置 Kubernetes 凭证
run: |
mkdir -p $HOME/.kube
echo "${{ secrets.KUBECONFIG }}" | base64 -d > $HOME/.kube/config
chmod 600 $HOME/.kube/config
- name: 📋 创建 namespace
run: |
kubectl create namespace ${{ env.K8S_NAMESPACE }} --dry-run=client -o yaml | kubectl apply -f -
- name: 🔑 创建镜像拉取 Secret
run: |
kubectl create secret docker-registry harbor-secret \
--docker-server=${{ env.HARBOR_REGISTRY }} \
--docker-username=${{ env.REGISTRY_USER }} \
--docker-password=${{ env.REGISTRY_PASSWORD }} \
--docker-email=admin@example.com \
-n ${{ env.K8S_NAMESPACE }} \
--dry-run=client -o yaml | kubectl apply -f -
- name: 📝 更新镜像标签
run: |
sed -i 's|IMAGE_TAG|${{ github.sha }}|g' k8s/deployment.yaml
- name: 🚀 部署到 Kubernetes
run: |
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
- name: ⏳ 等待部署完成
run: |
kubectl rollout status deployment/claude-stock-backend \
-n ${{ env.K8S_NAMESPACE }} --timeout=5m
kubectl rollout status deployment/claude-stock-frontend \
-n ${{ env.K8S_NAMESPACE }} --timeout=5m
- name: 📊 获取部署信息
if: always()
run: |
echo "=== Deployment Status ==="
kubectl get deployments -n ${{ env.K8S_NAMESPACE }}
echo "=== Pod Status ==="
kubectl get pods -n ${{ env.K8S_NAMESPACE }}
echo "=== Service Status ==="
kubectl get svc -n ${{ env.K8S_NAMESPACE }}
# ==================== 部署后验证 ====================
smoke-test:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: deploy
runs-on: ubuntu-latest
steps:
- name: 📥 检出代码
uses: actions/checkout@v4
- name: 🔐 配置 Kubernetes 凭证
run: |
mkdir -p $HOME/.kube
echo "${{ secrets.KUBECONFIG }}" | base64 -d > $HOME/.kube/config
- name: 🌐 获取服务端点
run: |
BACKEND_IP=$(kubectl get svc claude-stock-backend -n ${{ env.K8S_NAMESPACE }} \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo 'pending')
echo "Backend Service IP: $BACKEND_IP"
- name: ✅ 健康检查
run: |
for i in {1..30}; do
if kubectl exec -n ${{ env.K8S_NAMESPACE }} \
$(kubectl get pod -n ${{ env.K8S_NAMESPACE }} -l app=backend -o jsonpath='{.items[0].metadata.name}') \
-- curl -f http://localhost:8000/health > /dev/null 2>&1; then
echo "✅ Health check passed"
exit 0
fi
echo "⏳ Waiting for service... ($i/30)"
sleep 10
done
exit 1
# ==================== 通知阶段 ====================
notify:
if: always()
needs: [build-and-push, test, deploy, smoke-test]
runs-on: ubuntu-latest
steps:
- name: 📊 生成部署总结
run: |
echo "## 🚀 CICD 部署总结" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **仓库**: ${{ github.repository }}" >> $GITHUB_STEP_SUMMARY
echo "- **分支**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "- **提交**: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
echo "- **镜像标签**: ${{ env.HARBOR_REGISTRY }}/${{ env.HARBOR_PROJECT }}/backend:${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### 任务状态" >> $GITHUB_STEP_SUMMARY
echo "- Build & Push: ${{ needs.build-and-push.result }}" >> $GITHUB_STEP_SUMMARY
echo "- Test: ${{ needs.test.result }}" >> $GITHUB_STEP_SUMMARY
echo "- Deploy: ${{ needs.deploy.result }}" >> $GITHUB_STEP_SUMMARY
echo "- Smoke Test: ${{ needs.smoke-test.result }}" >> $GITHUB_STEP_SUMMARY