Appearance
GitHub Actions最佳实践
GitHub Actions最佳实践涵盖了工作流设计、安全、性能优化和维护等方面的最佳做法,帮助您构建高效、安全、可维护的CI/CD管道。
安全最佳实践
1. 最小权限原则
始终为工作流提供最小必需的权限:
yaml
# .github/workflows/secure-workflow.yml
name: Secure Workflow
on: [push, pull_request]
# 明确声明所需权限
permissions:
contents: read # 只读访问仓库内容
packages: write # 写入包权限(仅在需要时)
id-token: write # OIDC令牌权限(仅在需要时)
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false # 防止凭据泄露
- name: Build application
run: npm ci && npm run build
2. 保护敏感信息
yaml
jobs:
secure-deployment:
runs-on: ubuntu-latest
steps:
- name: Use secrets safely
run: |
# 密钥不会在日志中显示
curl -X POST \
-H "Authorization: Bearer ${{ secrets.API_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"message": "Deploy successful"}' \
${{ secrets.API_ENDPOINT }}
env:
# 敏感信息通过环境变量传递
API_TOKEN: ${{ secrets.API_TOKEN }}
API_ENDPOINT: ${{ secrets.API_ENDPOINT }}
- name: Avoid logging sensitive data
run: |
# 避免在日志中直接打印敏感信息
echo "Processing deployment..." # 好的做法
# echo "Token: $TOKEN" # 避免这样做
3. 验证外部Action
yaml
jobs:
safe-actions:
runs-on: ubuntu-latest
steps:
# 使用固定版本的Action(推荐)
- name: Checkout with fixed version
uses: actions/checkout@a5ac7e51c2d5a6a4e9c19e32d7e8ea8dd9d5e459 # v4.1.7的完整SHA
# 或者使用主要版本标签(次选)
- name: Setup Node.js
uses: actions/setup-node@v4
# 避免使用main分支(不安全)
# - uses: some-user/some-action@main # 不推荐
性能优化最佳实践
1. 合理使用缓存
yaml
jobs:
optimized-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 使用actions/setup-*内置缓存
- name: Setup Node.js with cache
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: package-lock.json
# 或使用actions/cache手动缓存
- name: Cache dependencies
uses: actions/cache@v4
with:
path: |
~/.npm
~/.cache/Cypress
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm ci
- name: Build with cache
run: npm run build
2. 并行执行优化
yaml
jobs:
parallel-tests:
runs-on: ubuntu-latest
strategy:
matrix:
# 控制矩阵大小
node-version: [16, 18, 20]
os: [ubuntu-latest]
# 限制并行度以节省资源
max-parallel: 2
fail-fast: false # 收集所有测试结果
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
3. 资源优化
yaml
jobs:
resource-efficient:
runs-on: ubuntu-latest
timeout-minutes: 30 # 设置合理的超时时间
steps:
- name: Limit resource usage
run: |
# 在资源受限的环境中运行
npm run build -- --max-old-space-size=4096 # 限制内存使用
timeout-minutes: 10 # 设置步骤超时
- name: Clean up resources
if: always() # 无论成功或失败都执行清理
run: |
# 清理临时文件
rm -rf /tmp/build-cache
docker system prune -f # 清理Docker资源
工作流设计最佳实践
1. 作业依赖管理
yaml
jobs:
setup:
runs-on: ubuntu-latest
outputs:
build-id: ${{ steps.generate-id.outputs.id }}
steps:
- name: Generate build ID
id: generate-id
run: |
BUILD_ID=$(date +%s)-${{ github.run_number }}
echo "id=$BUILD_ID" >> $GITHUB_OUTPUT
build:
needs: setup
runs-on: ubuntu-latest
outputs:
artifact-name: ${{ steps.package.outputs.name }}
steps:
- uses: actions/checkout@v4
- name: Build with ID
run: npm run build -- --build-id ${{ needs.setup.outputs.build-id }}
- name: Package artifact
id: package
run: |
ARTIFACT_NAME="app-${{ needs.setup.outputs.build-id }}"
echo "name=$ARTIFACT_NAME" >> $GITHUB_OUTPUT
test:
needs: [setup, build]
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- name: Download artifact
run: echo "Using artifact from build job"
deploy:
needs: [setup, build, test]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: production
steps:
- name: Deploy with build ID
run: echo "Deploying build ${{ needs.setup.outputs.build-id }}"
2. 条件执行
yaml
jobs:
conditional-execution:
runs-on: ubuntu-latest
steps:
- name: Skip on certain conditions
if: |
github.event_name == 'push' &&
github.ref == 'refs/heads/main' &&
!contains(github.event.head_commit.message, '[skip ci]')
run: echo "Running on main branch push without skip ci"
- name: Environment-specific execution
if: github.ref == 'refs/heads/main'
run: echo "Running on production environment"
env:
NODE_ENV: production
API_URL: ${{ secrets.PROD_API_URL }}
- name: Pull request specific
if: github.event_name == 'pull_request'
run: echo "Running on pull request"
env:
NODE_ENV: development
API_URL: ${{ secrets.DEV_API_URL }}
错误处理和监控
1. 容错设计
yaml
jobs:
fault-tolerant:
runs-on: ubuntu-latest
strategy:
fail-fast: false # 继续运行其他矩阵作业
steps:
- name: Retry on failure
run: |
# 重试机制
for i in {1..3}; do
if npm run test; then
echo "Tests passed on attempt $i"
break
elif [ $i -eq 3 ]; then
echo "Tests failed after 3 attempts"
exit 1
else
echo "Attempt $i failed, retrying in 10 seconds..."
sleep 10
fi
done
- name: Continue on error
continue-on-error: true # 即使失败也继续执行后续步骤
run: |
# 可能失败但不影响整体流程的步骤
npm run lint || echo "Lint failed but continuing..."
- name: Cleanup always runs
if: always() # 无论前面步骤成功或失败都执行
run: |
echo "Performing cleanup..."
# 清理资源
2. 监控和通知
yaml
jobs:
monitored-job:
runs-on: ubuntu-latest
outputs:
status: ${{ steps.monitor.outputs.status }}
steps:
- name: Start monitoring
id: start-time
run: echo "start_time=$(date -u +%s)" >> $GITHUB_OUTPUT
- name: Main operation
id: main-op
run: |
# 主要操作
npm run deploy
echo "exit_code=$?" >> $GITHUB_OUTPUT
- name: Monitor and log
id: monitor
run: |
END_TIME=$(date -u +%s)
DURATION=$((END_TIME - ${{ steps.start-time.outputs.start_time }}))
EXIT_CODE=${{ steps.main-op.outputs.exit_code }}
if [ $EXIT_CODE -eq 0 ]; then
STATUS="success"
else
STATUS="failure"
fi
echo "duration=$DURATION" >> $GITHUB_OUTPUT
echo "status=$STATUS" >> $GITHUB_OUTPUT
# 记录到外部监控系统
curl -X POST https://monitoring.example.com/api/logs \
-H "Content-Type: application/json" \
-d "{
\"job\": \"${{ github.job }}\",
\"run_id\": \"${{ github.run_id }}\",
\"status\": \"$STATUS\",
\"duration\": $DURATION,
\"repository\": \"${{ github.repository }}\"
}"
notifications:
needs: monitored-job
runs-on: ubuntu-latest
if: always() # 总是运行以发送通知
steps:
- name: Send notification on failure
if: needs.monitored-job.outputs.status == 'failure'
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
-H "Content-Type: application/json" \
-d '{
"text": "❌ Deployment failed in ${{ github.repository }}",
"channel": "#deployments"
}'
- name: Send notification on success
if: needs.monitored-job.outputs.status == 'success'
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
-H "Content-Type: application/json" \
-d '{
"text": "✅ Deployment succeeded in ${{ github.repository }}",
"channel": "#deployments"
}'
代码组织最佳实践
1. 工作流分离
yaml
# .github/workflows/ci.yml
name: Continuous Integration
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm test
- run: npm run lint
---
# .github/workflows/cd.yml
name: Continuous Deployment
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy application
run: echo "Deploying to production"
2. 可重用组件
yaml
# 使用复合Action创建可重用组件
# reusable-build/action.yml
name: 'Reusable Build Action'
description: 'Common build steps'
inputs:
node-version:
description: 'Node.js version'
required: true
default: '18'
build-target:
description: 'Build target'
required: false
default: 'dist'
runs:
using: 'composite'
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
- name: Install dependencies
shell: bash
run: npm ci
- name: Build project
shell: bash
run: npm run build -- --target ${{ inputs.build-target }}
- name: Run tests
shell: bash
run: npm test
3. 环境变量管理
yaml
# 在工作流级别定义公共环境变量
env:
NODE_VERSION: '18'
BUILD_PATH: './dist'
CI: 'true'
jobs:
build:
runs-on: ubuntu-latest
# 作业级别的环境变量
env:
NODE_ENV: 'production'
steps:
- name: Use environment variables
run: |
echo "Node version: $NODE_VERSION" # 来自工作流级别
echo "Build path: $BUILD_PATH" # 来自工作流级别
echo "Node environment: $NODE_ENV" # 来自作业级别
# 步骤级别的环境变量
env:
STEP_VAR: 'step-specific-value'
审计和合规
1. 审计日志
yaml
jobs:
audited-workflow:
runs-on: ubuntu-latest
steps:
- name: Log workflow execution
run: |
echo "Workflow: ${{ github.workflow }}"
echo "Run ID: ${{ github.run_id }}"
echo "Actor: ${{ github.actor }}"
echo "Repository: ${{ github.repository }}"
echo "Ref: ${{ github.ref }}"
echo "SHA: ${{ github.sha }}"
echo "Timestamp: $(date -u)"
# 发送到审计系统
curl -X POST ${{ secrets.AUDIT_LOG_URL }} \
-H "Authorization: Bearer ${{ secrets.AUDIT_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{
\"event\": \"workflow_execution\",
\"workflow\": \"${{ github.workflow }}\",
\"run_id\": \"${{ github.run_id }}\",
\"actor\": \"${{ github.actor }}\",
\"repository\": \"${{ github.repository }}\",
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"
}"
2. 合规检查
yaml
jobs:
compliance-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check for secrets in code
run: |
# 扫描代码中是否包含密钥字面量
if git grep -l "password\|secret\|token\|key" -- "*.yml" "*.yaml" "*.json" "*.js" "*.py" "*.ts"; then
echo "Potential secrets found in code. Please review."
exit 1
fi
- name: License compliance check
run: |
# 检查依赖项许可证合规性
npm run license-check
维护和文档
1. 工作流文档
yaml
# .github/workflows/documentation.yml
name: Documentation and Maintenance
on:
schedule:
- cron: '0 2 * * 1' # 每周一凌晨2点
workflow_dispatch:
jobs:
maintain-workflows:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Validate workflow files
run: |
# 验证工作流文件语法
# 可以使用第三方工具或自定义脚本
echo "Validating workflow files..."
- name: Update dependencies
run: |
# 更新Action依赖到最新安全版本
echo "Updating Action dependencies..."
- name: Generate workflow documentation
run: |
# 生成工作流文档
echo "# Workflow Documentation" > WORKFLOW_DOCS.md
echo "Generated on $(date)" >> WORKFLOW_DOCS.md
2. 版本管理
yaml
# 在README.md中记录工作流信息
# README.md
"""
## CI/CD Pipelines
### Workflows
- **Continuous Integration** (`.github/workflows/ci.yml`): Runs on every push and PR
- Tests: Unit, integration, and end-to-end tests
- Linting: Code style validation
- Security: Dependency vulnerability scanning
- **Continuous Deployment** (`.github/workflows/cd.yml`): Runs on main branch pushes
- Builds production-ready artifacts
- Deploys to staging environment
- Runs post-deployment tests
"""
实际应用示例
完整的生产级工作流
yaml
name: Production CI/CD Pipeline
on:
push:
branches: [main, develop]
tags: ['v*']
pull_request:
branches: [main, develop]
# 限制并发执行
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
NODE_VERSION: '18'
APP_NAME: 'my-app'
jobs:
# 安全扫描(仅在非PR时运行)
security-scan:
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- name: Run security scan
uses: github/super-linter@v4
env:
VALIDATE_ALL_CODEBASE: false
DEFAULT_BRANCH: main
# 构建和测试
build-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test:ci
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.node-version }}
path: coverage/
if: matrix.node-version == '18' # 只上传一个版本的覆盖率
# 构建生产版本
build-production:
needs: [security-scan, build-and-test]
runs-on: ubuntu-latest
outputs:
build-id: ${{ steps.build-info.outputs.id }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Build production
run: npm run build:prod
- name: Generate build info
id: build-info
run: |
BUILD_ID="${{ github.sha }}-${{ github.run_number }}"
echo "id=$BUILD_ID" >> $GITHUB_OUTPUT
- name: Upload production build
uses: actions/upload-artifact@v4
with:
name: production-build
path: dist/
# 部署到预发布环境
deploy-staging:
needs: build-production
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
environment: staging
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: production-build
path: dist/
- name: Deploy to staging
run: echo "Deploying build ${{ needs.build-production.outputs.build-id }} to staging"
env:
DEPLOY_TOKEN: ${{ secrets.STAGING_DEPLOY_TOKEN }}
# 部署到生产环境
deploy-production:
needs: [build-production, security-scan]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && startsWith(github.ref, 'refs/tags/')
environment: production
permissions:
id-token: write
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: production-build
path: dist/
- name: Deploy to production
run: echo "Deploying build ${{ needs.build-production.outputs.build-id }} to production"
env:
DEPLOY_TOKEN: ${{ secrets.PROD_DEPLOY_TOKEN }}
通过遵循这些最佳实践,您可以构建安全、高效、可维护的GitHub Actions工作流,提高软件交付的质量和效率。