ClawSkills logoClawSkills

CI/CD Pipeline

使用 GitHub Actions 创建、调试和管理 CI/CD 流水线。当用户需要设置自动化测试、部署、发布或工作流时使用。涵盖 w

介绍

# CI/CD Pipeline (GitHub Actions)

使用 GitHub Actions 设置和管理 CI/CD 流水线。涵盖工作流创建、测试、部署、发布自动化和调试。

## 适用场景

- 在推送/PR 时设置自动化测试 - 创建部署流水线(staging、production) - 使用变更日志和标签自动化发布 - 调试失败的 CI 工作流 - 设置矩阵构建以进行跨平台测试 - 管理 CI 中的密钥和环境变量 - 通过缓存和并行化优化 CI

## 快速开始:为项目添加 CI

### Node.js 项目

```yaml # .github/workflows/ci.yml name: CI

on: push: branches: [main] pull_request: branches: [main]

jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 cache: npm - run: npm ci - run: npm test - run: npm run lint ```

### Python 项目

```yaml # .github/workflows/ci.yml name: CI

on: push: branches: [main] pull_request: branches: [main]

jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" cache: pip - run: pip install -r requirements.txt - run: pytest - run: ruff check . ```

### Go 项目

```yaml # .github/workflows/ci.yml name: CI

on: push: branches: [main] pull_request: branches: [main]

jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: "1.22" - run: go test ./... - run: go vet ./... ```

### Rust 项目

```yaml # .github/workflows/ci.yml name: CI

on: push: branches: [main] pull_request: branches: [main]

jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: cargo test - run: cargo clippy -- -D warnings ```

## 常见模式

### 矩阵构建(跨版本/操作系统测试)

```yaml jobs: test: strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] node-version: [18, 20, 22] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm ci - run: npm test ```

### 条件作业

```yaml jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm test

deploy: needs: test if: github.ref == 'refs/heads/main' && github.event_name == 'push' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: ./deploy.sh ```

### 缓存依赖项

```yaml # Node.js (automatic with setup-node) - uses: actions/setup-node@v4 with: node-version: 20 cache: npm # or yarn, pnpm

# Generic caching - uses: actions/cache@v4 with: path: | ~/.cache/pip ~/.cargo/registry node_modules key: ${{ runner.os }}-deps-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-deps- ```

### 制品(保存构建输出)

```yaml - uses: actions/upload-artifact@v4 with: name: build-output path: dist/ retention-days: 7

# Download in another job - uses: actions/download-artifact@v4 with: name: build-output path: dist/ ```

### 按计划运行(cron)

```yaml on: schedule: - cron: "0 6 * * 1" # Every Monday at 6 AM UTC workflow_dispatch: # Also allow manual trigger ```

## 部署工作流

### 在打标签时部署到生产环境

```yaml name: Release

on: push: tags: - "v*"

jobs: release: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 cache: npm - run: npm ci - run: npm run build - run: npm test

# Create GitHub release - uses: softprops/action-gh-release@v2 with: generate_release_notes: true files: | dist/*.js dist/*.css ```

### 部署到多个环境

```yaml name: Deploy

on: push: branches: [main, staging]

jobs: deploy: runs-on: ubuntu-latest environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} steps: - uses: actions/checkout@v4 - run: npm ci && npm run build - run: | if [ "${{ github.ref }}" = "refs/heads/main" ]; then ./deploy.sh production else ./deploy.sh staging fi env: DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} ```

### Docker 构建与推送

```yaml name: Docker

on: push: branches: [main] tags: ["v*"]

jobs: build: runs-on: ubuntu-latest permissions: packages: write steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/build-push-action@v6 with: push: true tags: | ghcr.io/${{ github.repository }}:latest ghcr.io/${{ github.repository }}:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max ```

### 在发布时发布到 npm

```yaml name: Publish

on: release: types: [published]

jobs: publish: runs-on: ubuntu-latest permissions: id-token: write steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 registry-url: https://registry.npmjs.org - run: npm ci - run: npm test - run: npm publish --provenance env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} ```

## 密钥管理

### 通过 CLI 设置密钥

```bash # Set a repository secret gh secret set DEPLOY_TOKEN --body "my-secret-value"

# Set from a file gh secret set SSH_KEY < ~/.ssh/deploy_key

# Set for a specific environment gh secret set DB_PASSWORD --env production --body "p@ssw0rd"

# List secrets gh secret list

# Delete a secret gh secret delete OLD_SECRET ```

### 在工作流中使用密钥

```yaml env: # Available to all steps in this job DATABASE_URL: ${{ secrets.DATABASE_URL }}

steps: - run: echo "Deploying..." env: # Available to this step only API_KEY: ${{ secrets.API_KEY }} ```

### 环境保护规则

通过 GitHub UI 或 API 进行设置: - 部署前需要的审核者 - 等待计时器 - 分支限制 - 自定义部署分支策略

```bash # View environments gh api repos/{owner}/{repo}/environments | jq '.environments[].name' ```

## 工作流调试

### 重新运行失败的作业

```bash # List recent workflow runs gh run list --limit 10

# View a specific run gh run view <run-id>

# View failed job logs gh run view <run-id> --log-failed

# Re-run failed jobs only gh run rerun <run-id> --failed

# Re-run entire workflow gh run rerun <run-id> ```

### 使用 SSH 调试(使用 tmate)

```yaml # Add this step before the failing step - uses: mxschmitt/action-tmate@v3 if: failure() with: limit-access-to-actor: true ```

### 常见失败与修复

**脚本出现“Permission denied”(权限被拒绝)** ```yaml - run: chmod +x ./scripts/deploy.sh && ./scripts/deploy.sh ```

**“Node modules not found”(找不到 Node 模块)** ```yaml # Make sure npm ci runs before npm test - run: npm ci # Install exact lockfile versions - run: npm test # Now node_modules exists ```

**“Resource not accessible by integration”(集成无法访问资源)** ```yaml # Add permissions block permissions: contents: write packages: write pull-requests: write ```

**缓存未恢复** ```yaml # Check cache key matches - use hashFiles for lockfile key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} # NOT: key: ${{ runner.os }}-node-${{ hashFiles('package.json') }} ```

**工作流未触发** - 检查:工作流文件是否在默认分支上? - 检查:触发事件是否匹配?(`push` 对比 `pull_request`) - 检查:分支筛选条件是否正确? ```bash # Manually trigger a workflow gh workflow run ci.yml --ref main ```

## 工作流验证

### 推送前在本地验证

```bash # Check YAML syntax python3 -c "import yaml; yaml.safe_load(open('.github/workflows/ci.yml'))" && echo "Valid"

# Use actionlint (if installed) actionlint .github/workflows/ci.yml

# Or via Docker docker run --rm -v "$(pwd):/repo" -w /repo rhysd/actionlint:latest ```

### 以图形方式查看工作流

```bash # List all workflows gh workflow list

# View workflow definition gh workflow view ci.yml

# Watch a running workflow gh run watch ```

## 高级模式

### 可重用工作流

```yaml # .github/workflows/reusable-test.yml name: Reusable Test on: workflow_call: inputs: node-version: required: false type: string default: "20" secrets: npm-token: required: false

jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ inputs.node-version }} - run: npm ci - run: npm test ```

```yaml # .github/workflows/ci.yml - caller name: CI on: [push, pull_request] jobs: test: uses: ./.github/workflows/reusable-test.yml with: node-version: "20" ```

### 并发(防止重复运行)

```yaml concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # Cancel previous runs for same branch ```

### 路径筛选器(仅针对相关更改运行)

```yaml on: push: paths: - "src/**" - "package.json" - "package-lock.json" - ".github/workflows/ci.yml" paths-ignore: - "docs/**" - "*.md" ```

### Monorepo:仅测试发生变更的包

```yaml jobs: changes: runs-on: ubuntu-latest outputs: api: ${{ steps.filter.outputs.api }} web: ${{ steps.filter.outputs.web }} steps: - uses: actions/checkout@v4 - uses: dorny/paths-filter@v3 id: filter with: filters: | api: - 'packages/api/**' web: - 'packages/web/**'

test-api: needs: changes if: needs.changes.outputs.api == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: cd packages/api && npm ci && npm test

test-web: needs: changes if: needs.changes.outputs.web == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: cd packages/web && npm ci && npm test ```

## 提示

- 在每个工作流上使用 `workflow_dispatch`,以便在调试期间手动触发 - 为了供应链安全,将 action 版本固定为 SHA:`uses: actions/checkout@b4ffde...` - 对非关键步骤(如 linting)使用 `continue-on-error: true` - 在作业上设置 `timeout-minutes` 以防止失控的构建(默认为 360 分钟) - 使用作业输出在作业之间传递数据:`outputs: result: ${{ steps.step-id.outputs.value }}` - 对于自托管运行器:使用 `runs-on: self-hosted` 并配合标签以定位特定机器

更多产品