MonkeyCode写CI/CD流水线:从零配置GitHub Actions自动化部署
CI/CD为什么要用AI辅助
手写CI/CD配置有三大痛点:
- 语法复杂:YAML缩进、GitHub Actions的语法糖、Secret管理,写错一个空格流水线就挂
- 经验依赖:缓存策略、矩阵构建、并发控制这些经验不容易从文档学到
- 平台差异大:GitHub Actions、GitLab CI、Jenkins的写法完全不同
MonkeyCode的优势在于:你描述"我要做什么",它生成对应平台的配置文件。这篇文章用GitHub Actions实战。
给MonkeyCode的Prompt模板
帮我配置GitHub Actions CI/CD流水线,项目信息:
- 语言:Python 3.11 + FastAPI
- 测试框架:pytest
- 需要的功能:
1. push和PR时自动运行测试
2. 代码质量检查(flake8 + black + mypy)
3. 安全扫描(bandit)
4. 构建Docker镜像
5. 自动部署到服务器(SSH部署)
6. 测试报告上传到PR评论
7. Slack通知构建结果
8. 缓存pip依赖加速构建
环境:
- Python 3.11
- PostgreSQL测试数据库(用Docker service)
- Docker Hub自动推送镜像
MonkeyCode生成的完整流水线
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
workflow_dispatch: # 手动触发
env:
PYTHON_VERSION: '3.11'
DOCKER_IMAGE: ${{ secrets.DOCKER_USERNAME }}/myapp
DOCKER_REGISTRY: docker.io
jobs:
# ============================================================
# Job 1: 代码质量检查
# ============================================================
lint:
name: Code Quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-lint-${{ hashFiles('requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-lint-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 black isort mypy bandit
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Check formatting (black + isort)
run: |
black --check --diff .
isort --check-only --diff .
- name: Lint (flake8)
run: |
# 新代码用更严格规则,旧代码宽松
flake8 . --count --statistics \
--max-line-length=120 \
--ignore=E501,W503 \
--exclude=.git,__pycache__,venv,alembic
- name: Type check (mypy)
run: |
mypy app/ --ignore-missing-imports --no-strict-optional
continue-on-error: true
- name: Security scan (bandit)
run: |
bandit -r app/ -f json -o bandit-report.json
continue-on-error: true
- name: Upload bandit report
if: always()
uses: actions/upload-artifact@v4
with:
name: bandit-report
path: bandit-report.json
# ============================================================
# Job 2: 测试(带PostgreSQL)
# ============================================================
test:
name: Run Tests
runs-on: ubuntu-latest
needs: lint
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
ports:
- 5432:5432
options: >-
--health-cmd="pg_isready -U testuser"
--health-interval=10s
--health-timeout=5s
--health-retries=5
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-test-${{ hashFiles('requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-test-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run database migrations
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb
run: alembic upgrade head
- name: Run tests with coverage
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb
TEST_ENV: ci
run: |
pytest tests/ \
--cov=app \
--cov-report=xml:coverage.xml \
--cov-report=html:htmlcov \
--cov-report=term-missing \
--junitxml=test-results.xml \
-v --tb=short
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: coverage.xml
fail_ci_if_error: false
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: |
test-results.xml
htmlcov/
- name: Comment test results on PR
if: github.event_name == 'pull_request' && always()
uses: dorny/test-reporter@v1
with:
name: Test Results
path: test-results.xml
reporter: java-junit
# ============================================================
# Job 3: 构建Docker镜像
# ============================================================
build:
name: Build Docker Image
runs-on: ubuntu-latest
needs: test
permissions:
contents: read
packages: write
outputs:
image_tag: ${{ steps.meta.outputs.tags }}
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.DOCKER_IMAGE }}
tags: |
type=sha,prefix=
type=ref,event=branch
type=semver,pattern={{version}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_DATE=${{ github.event.repository.updated_at }}
VCS_REF=${{ github.sha }}
# ============================================================
# Job 4: 部署到生产环境
# ============================================================
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
environment:
name: production
url: https://myapp.example.com
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_KEY }}
script: |
# 拉取最新镜像
docker pull ${{ env.DOCKER_IMAGE }}:${{ needs.build.outputs.image_tag }}
# 健康检查
echo "Waiting for container to be healthy..."
# 滚动更新
docker-compose -f /opt/myapp/docker-compose.yml up -d --no-deps app
# 清理旧镜像
docker image prune -f
echo "Deploy complete!"
- name: Health check
run: |
for i in {1..30}; do
if curl -sf https://myapp.example.com/health; then
echo "Health check passed!"
exit 0
fi
echo "Waiting... ($i/30)"
sleep 5
done
echo "Health check failed!"
exit 1
- name: Notify Slack on success
if: success()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "✅ Deployment successful!",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "✅ *Deployment to production successful*\n*Branch:* `${{ github.ref_name }}`\n*Commit:* ${{ github.sha }}\n*By:* ${{ github.actor }}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View workflow>"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
- name: Notify Slack on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "❌ Deployment failed!",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "❌ *Deployment to production FAILED*\n*Branch:* `${{ github.ref_name }}`\n*Commit:* ${{ github.sha }}\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View workflow>"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
配套的Dockerfile
# Dockerfile - MonkeyCode生成
FROM python:3.11-slim as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Production stage
FROM python:3.11-slim
WORKDIR /app
# 安装运行时依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*
# 复制依赖
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /app /app
# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
MonkeyCode的CI/CD Prompt优化技巧
技巧1:分阶段描述需求
不要一次把所有需求丢给MonkeyCode,分阶段来:
第一阶段:只跑测试
帮我配置GitHub Actions,push时自动运行pytest测试,Python 3.11项目
第二阶段:加代码质量
在上面流水线基础上,增加flake8、black格式检查
第三阶段:加部署
在上面流水线基础上,增加Docker构建和SSH部署到生产服务器
每阶段确认后再加需求,避免一次生成太多导致出错。
技巧2:明确缓存策略
pip缓存使用 ~/.cache/pip,key用requirements.txt的hash
Docker层缓存使用GitHub Actions的cache-to=type=gha
MonkeyCode会生成对应缓存配置,构建时间从5分钟降到30秒。
技巧3:要求生成矩阵测试
测试job需要矩阵配置:
- Python 3.10, 3.11, 3.12
- PostgreSQL 14, 15
排除组合:Python 3.10 + PG 15
生成的矩阵:
test:
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
postgres-version: ['14', '15']
exclude:
- python-version: '3.10'
postgres-version: '15'
技巧4:要求生成docker-compose
# docker-compose.yml - MonkeyCode生成
version: '3.8'
services:
app:
image: ${DOCKER_IMAGE:-myapp:latest}
ports:
- "8000:8000"
environment:
DATABASE_URL: postgresql://user:pass@db:5432/mydb
REDIS_URL: redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
cpus: '1.0'
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
volumes:
- redisdata:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- app
volumes:
pgdata:
redisdata:
Secret管理最佳实践
GitHub Actions里用的Secret要通过GitHub仓库设置页面添加,不要硬编码:
# 需要配置的Secret列表
DOCKER_USERNAME # Docker Hub用户名
DOCKER_PASSWORD # Docker Hub密码或Access Token
SERVER_HOST # 服务器IP
SERVER_USER # SSH用户名
SSH_KEY # SSH私钥
SLACK_WEBHOOK # Slack Webhook URL
配置方法:GitHub仓库 → Settings → Secrets and variables → Actions → New repository secret
用MonkeyCode生成CI/CD配置后,一定要做两件事:
- 先在测试分支跑一遍,确认没问题再合到main
- 检查生成的Secret引用,确保没有硬编码密码
AI生成的YAML配置通常比手写的更完整,但Secret管理这种安全相关的东西,必须人工确认。

浙公网安备 33010602011771号