GitHub Actions 实用指南
GitHub Actions 是 GitHub 提供的强大持续集成/持续部署(CI/CD)工具,允许您直接在 GitHub 仓库中自动化软件开发工作流程。无需搭建专用的 CI/CD 服务器,您可以轻松实现代码构建、测试和部署等自动化流程。本文将全面介绍 GitHub Actions 的核心概念、配置方法和实际应用场景。
1. GitHub Actions 基础
1.1 核心概念
GitHub Actions 的主要组成部分:
- 工作流(Workflow):自动化流程,由一个或多个作业组成,由事件触发
- 事件(Event):触发工作流的特定活动,如 push、pull request、定时任务等
- 作业(Job):工作流中的一系列步骤,在同一运行器上执行
- 步骤(Step):可以运行命令或 action 的单个任务
- 动作(Action):可重用的工作单元,是构建工作流的基本模块
- 运行器(Runner):执行工作流的服务器,GitHub 提供托管的运行器,也支持自托管
1.2 工作流文件结构
GitHub Actions 使用 YAML 文件定义工作流,存储在仓库的 .github/workflows
目录下。基本结构如下:
name: CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
1.3 事件触发器
GitHub Actions 可以由多种事件触发:
代码相关事件:
on:
push:
branches: [ main, dev ]
paths-ignore:
- '**.md'
pull_request:
types: [opened, synchronize, reopened]
计划事件:
on:
schedule:
- cron: '0 0 * * *' # 每天午夜执行
手动触发:
on:
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
required: true
default: 'staging'
其他事件触发:
on:
release:
types: [published]
issues:
types: [opened]
repository_dispatch:
types: [deploy]
2. 创建基本工作流
2.1 构建与测试
以下是一个基本的 Node.js 项目构建与测试工作流:
name: Node.js CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run build --if-present
- run: npm test
2.2 多平台测试
使用矩阵策略在多个平台上测试:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [14.x, 16.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
2.3 构建与发布 Docker 镜像
name: Docker Build & Push
on:
push:
branches: [ main ]
tags: [ 'v*' ]
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: |
user/app:latest
user/app:${{ github.sha }}
3. 高级配置技巧
3.1 使用环境变量与密钥
在工作流中使用环境变量和密钥:
jobs:
deploy:
runs-on: ubuntu-latest
env:
APP_ENV: production
LOG_LEVEL: info
steps:
- uses: actions/checkout@v3
- name: Deploy to server
env:
NODE_ENV: production
run: |
echo "Using APP_ENV: $APP_ENV"
echo "Using NODE_ENV: $NODE_ENV"
- name: Access secrets
run: |
echo ${{ secrets.API_KEY }} | base64 --decode > api_key.json
3.2 作业依赖与并行执行
控制作业的执行顺序:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run build
test:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm test
deploy:
needs: [build, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run deploy
3.3 条件执行
根据条件决定是否执行步骤:
steps:
- name: Deploy to production
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: npm run deploy:prod
- name: Deploy to staging
if: github.ref == 'refs/heads/dev' && github.event_name == 'push'
run: npm run deploy:staging
- name: Only on pull requests
if: github.event_name == 'pull_request'
run: echo "This is a PR!"
3.4 工作流的复用
使用可重用工作流来减少重复代码:
# .github/workflows/reusable-workflow.yml
name: Reusable workflow
on:
workflow_call:
inputs:
environment:
required: true
type: string
secrets:
deploy_key:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v3
- name: Deploy to ${{ inputs.environment }}
run: ./deploy.sh
env:
DEPLOY_KEY: ${{ secrets.deploy_key }}
调用可重用工作流:
# .github/workflows/caller-workflow.yml
name: Production deployment
on:
push:
branches: [ main ]
jobs:
call-deploy-workflow:
uses: ./.github/workflows/reusable-workflow.yml
with:
environment: production
secrets:
deploy_key: ${{ secrets.PROD_DEPLOY_KEY }}
4. 存储与共享数据
4.1 工件(Artifacts)
在作业之间共享数据:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-files
path: dist/
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-files
path: dist
- name: Deploy
run: ./deploy.sh
4.2 缓存依赖
加速工作流执行:
steps:
- uses: actions/checkout@v3
- name: Cache Node.js modules
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm ci
5. 实用工作流示例
5.1 前端项目工作流
一个完整的前端项目工作流:
name: Frontend CI/CD
on:
push:
branches: [ main, dev ]
pull_request:
branches: [ main, dev ]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run typecheck
test:
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Upload coverage reports
uses: codecov/codecov-action@v3
build:
runs-on: ubuntu-latest
needs: test
if: success() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-files
path: dist/
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v3
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-files
path: dist
- name: Deploy to staging
if: github.ref == 'refs/heads/dev'
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_STAGING }}
projectId: my-app-staging
channelId: live
- name: Deploy to production
if: github.ref == 'refs/heads/main'
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_PROD }}
projectId: my-app-prod
channelId: live
5.2 后端项目工作流
Java Spring Boot项目的CI/CD工作流:
name: Java CI/CD
on:
push:
branches: [ main, dev ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B package --file pom.xml
- name: Run tests
run: mvn test
- name: Build Docker image
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
run: |
docker build -t myapp:${{ github.sha }} .
docker tag myapp:${{ github.sha }} myapp:latest
- name: Login to Docker Registry
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
uses: docker/login-action@v2
with:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Push Docker image
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
run: |
docker push myapp:${{ github.sha }}
docker push myapp:latest
- name: Deploy to Development
if: github.ref == 'refs/heads/dev'
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.DEV_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
docker pull myapp:latest
docker-compose down
docker-compose up -d
- name: Deploy to Production
if: github.ref == 'refs/heads/main'
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.PROD_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
docker pull myapp:latest
docker-compose down
docker-compose up -d
5.3 发布流程
自动创建和发布新版本:
name: Release
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
- name: Publish to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
6. GitHub Actions 的高级特性
6.1 自托管运行器
对于需要特定环境或硬件资源的工作流,可以设置自托管运行器:
jobs:
deploy:
runs-on: self-hosted
steps:
- uses: actions/checkout@v3
- name: Deploy on local server
run: ./deploy.sh
设置自托管运行器的步骤:
- 在 GitHub 仓库中,转到 Settings > Actions > Runners
- 点击 "New self-hosted runner"
- 按照指示在服务器上设置运行器
- 启动运行器服务
6.2 工作流的限制与超时
设置工作流的超时时间:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
- name: Long running task
run: ./long-process.sh
6.3 环境与审批流程
为关键部署设置环境和审批流程:
jobs:
deploy-prod:
runs-on: ubuntu-latest
environment:
name: production
url: https://production.example.com
steps:
- uses: actions/checkout@v3
- name: Deploy to production
run: ./deploy-prod.sh
在 GitHub 中,可以为环境配置保护规则和所需的审批者。
7. 最佳实践
7.1 安全最佳实践
- 使用最小权限原则:为 Actions 分配最小必要的权限
- 谨慎使用第三方 Actions:使用官方或受信任的 Actions
- 不要硬编码密钥:使用 GitHub Secrets 存储敏感信息
- 对环境访问加限制:设置环境保护规则
- 定期审核工作流文件:确保没有引入安全风险
7.2 性能优化
- 减少依赖安装时间:使用缓存
- 并行执行独立任务:使用多个作业
- 减少不必要的步骤:使用条件执行
- 使用矩阵构建高效测试:同时测试多个环境/版本
- 自托管运行器适用场景:需要特定硬件或持久环境时
7.3 维护与调试
- 使用具有描述性的名称:为工作流、作业和步骤提供清晰名称
- 模块化工作流:使用可重用工作流拆分复杂流程
- 适当的日志级别:输出足够的信息以便调试
- 使用注释和文档:记录复杂的工作流配置
- 版本控制工作流文件:将工作流文件视为代码管理
8. 常见问题与解决方案
8.1 故障排除技巧
- 查看详细日志:在 GitHub Actions 页面查看完整日志
- 使用调试环境变量:设置
ACTIONS_RUNNER_DEBUG=true
和ACTIONS_STEP_DEBUG=true
- 本地测试:使用 act 工具在本地测试工作流
- 增量修复:先获取最小工作流,然后逐步添加复杂性
- 审核工作流语法:检查 YAML 格式错误
8.2 常见错误
权限问题:
解决方案:检查 token 权限,使用 GITHUB_TOKEN 或创建具有适当权限的 PAT
环境设置错误:
解决方案:确保所需环境变量已正确设置,检查 secrets 是否已配置
依赖问题:
解决方案:锁定依赖版本,使用缓存加速安装,检查依赖冲突
运行器资源限制:
解决方案:优化资源使用,考虑使用自托管运行器,将工作流拆分为多个作业
总结
GitHub Actions 提供了一个强大且灵活的自动化平台,可以满足从简单的 CI 到复杂的 CD 流程的各种需求。通过精心设计的工作流,您可以减少手动操作,提高代码质量,加快发布速度。
GitHub Actions 的优势在于与 GitHub 仓库的紧密集成,丰富的生态系统,以及易于使用的配置语法。无论是个人项目还是企业级应用,GitHub Actions 都可以帮助您实现高效的自动化工作流程。
随着持续集成和持续部署实践的普及,掌握 GitHub Actions 将成为开发者工具箱中的重要技能。通过应用本文介绍的概念和最佳实践,您可以充分利用 GitHub Actions 提升您的开发效率和产品质量。