Skip to content
On this page

GitHub Actions工作流语法

GitHub Actions工作流使用YAML语法定义,存储在仓库的.github/workflows/目录下。本章详细介绍工作流文件的语法结构和各项配置选项。

工作流文件基本结构

完整的工作流示例

yaml
# .github/workflows/example.yml
name: Example Workflow

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

env:
  NODE_VERSION: '18.x'
  BUILD_PATH: './dist'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build project
        run: npm run build
        env:
          BUILD_PATH: ${{ env.BUILD_PATH }}

工作流顶级属性

name

工作流的显示名称,在GitHub界面中可见:

yaml
name: CI/CD Pipeline  # 可选,如果不设置则使用文件名

on: [push]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Hello World"

on

定义触发工作流的事件:

yaml
# 单个事件
on: push

# 多个事件
on: [push, pull_request]

# 详细配置事件
on:
  push:
    branches: [main, develop]
    tags: ['v*']
    paths: 
      - 'src/**'
      - 'package.json'
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]
  schedule:
    - cron: '0 2 * * 1'  # 每周一凌晨2点
  workflow_dispatch:  # 手动触发
    inputs:
      logLevel:
        description: 'Log level'
        required: true
        default: 'warning'
        type: choice
        options:
          - info
          - warning
          - debug
      tags:
        description: 'Test scenario tags'
        required: false
        type: boolean
      environment:
        description: 'Environment to run tests against'
        type: environment
        required: true

concurrency

控制并发执行,确保同一时间只有一个工作流实例运行:

yaml
name: Deploy Workflow
on: [push]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true  # 取消进行中的工作流

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying..."

env

定义全局环境变量:

yaml
env:
  NODE_VERSION: '18.x'
  DATABASE_URL: ${{ secrets.DATABASE_URL }}
  DEBUG: true

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo $NODE_VERSION
        # 输出: 18.x

defaults

为所有作业设置默认值:

yaml
defaults:
  run:
    shell: bash
    working-directory: ./subdirectory

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: pwd  # 实际运行在 ./subdirectory

jobs

工作流的核心部分,包含一个或多个作业:

yaml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Build job"
  
  test:
    runs-on: ubuntu-latest
    needs: build  # 依赖build作业
    steps:
      - run: echo "Test job"
  
  deploy:
    runs-on: ubuntu-latest
    needs: [build, test]  # 依赖多个作业
    if: github.ref == 'refs/heads/main'  # 条件执行
    steps:
      - run: echo "Deploy job"

作业配置选项

runs-on

指定作业运行的环境:

yaml
jobs:
  linux-job:
    runs-on: ubuntu-latest
  
  windows-job:
    runs-on: windows-latest
  
  macos-job:
    runs-on: macos-latest
  
  # 使用自定义标签
  custom-job:
    runs-on: [self-hosted, linux, x64]
  
  # 使用表达式
  dynamic-job:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]

strategy

定义作业的执行策略,支持矩阵构建和并行执行:

yaml
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16, 18, 20]
        os: [ubuntu-latest, windows-latest]
        include:
          - node-version: 16
            experimental: true
        exclude:
          - os: windows-latest
            node-version: 16
      fail-fast: false  # 即使一个矩阵作业失败也继续执行其他作业
      max-parallel: 2   # 最大并行作业数
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm test

needs

定义作业依赖关系:

yaml
jobs:
  setup:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Setup completed"
  
  build:
    needs: setup  # 等待setup作业完成
    runs-on: ubuntu-latest
    steps:
      - run: echo "Build completed"
  
  test:
    needs: build  # 等待build作业完成
    runs-on: ubuntu-latest
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
    steps:
      - run: echo "Testing on ${{ matrix.os }}"
  
  deploy:
    needs: [setup, build, test]  # 等待多个作业完成
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - run: echo "Deploying to production"

if

条件执行作业:

yaml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Always runs"
  
  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: |
      github.ref == 'refs/heads/main' &&
      github.event_name == 'push' &&
      !contains(github.event.head_commit.message, '[skip ci]')
    steps:
      - run: echo "Deploying to production"
  
  debug:
    runs-on: ubuntu-latest
    if: ${{ contains(github.event.head_commit.message, 'debug') }}
    steps:
      - run: echo "Debug mode enabled"

environment

为作业指定环境,用于部署保护规则和变量:

yaml
jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - run: echo "Deploying to staging"
  
  deploy-production:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com  # 部署URL
    steps:
      - run: echo "Deploying to production"

步骤配置选项

uses

使用action:

yaml
steps:
  # 使用GitHub Marketplace的action
  - uses: actions/checkout@v4
  
  # 使用特定版本的action
  - uses: actions/setup-node@v4
    with:
      node-version: '18'
  
  # 使用仓库中的action
  - uses: ./path/to/local/action
  
  # 使用Docker Hub的action
  - uses: docker://alpine:latest

run

执行shell命令:

yaml
steps:
  - name: Install dependencies
    run: |
      npm install
      npm run build
  
  - name: Single command
    run: echo "Hello World"
  
  - name: Multi-line command
    run: |
      ls -la
      pwd
      whoami
    shell: bash  # 指定shell类型
  
  - name: Conditional command
    run: npm run deploy
    if: github.ref == 'refs/heads/main'

with

为action传递参数:

yaml
steps:
  - uses: actions/setup-node@v4
    with:
      node-version: '18'
      cache: 'npm'
      cache-dependency-path: '**/package-lock.json'
  
  - uses: actions/upload-artifact@v4
    with:
      name: build-artifacts
      path: dist/
      retention-days: 7
  
  - uses: docker/build-push-action@v5
    with:
      context: .
      push: true
      tags: user/app:latest
      build-args: |
        NODE_VERSION=18
        ENVIRONMENT=production

env

设置步骤的环境变量:

yaml
steps:
  - name: Set environment variables
    run: |
      echo "API_URL is $API_URL"
      echo "NODE_ENV is $NODE_ENV"
    env:
      API_URL: ${{ secrets.API_URL }}
      NODE_ENV: production
      TIMESTAMP: ${{ github.run_started_at }}

id

为步骤设置唯一标识符,以便后续步骤引用:

yaml
steps:
  - id: vars
    run: |
      echo "tag=$(git describe --tags --abbrev=0)" >> $GITHUB_OUTPUT
      echo "commit=${GITHUB_SHA:0:7}" >> $GITHUB_OUTPUT
  
  - name: Use outputs
    run: |
      echo "Tag: ${{ steps.vars.outputs.tag }}"
      echo "Commit: ${{ steps.vars.outputs.commit }}"

continue-on-error

即使步骤失败也继续执行后续步骤:

yaml
steps:
  - name: Lint code
    run: npm run lint
    continue-on-error: true  # 即使lint失败也继续
  
  - name: Run tests
    run: npm test

上下文和表达式

上下文对象

yaml
steps:
  - name: Use contexts
    run: |
      echo "Repository: ${{ github.repository }}"
      echo "Actor: ${{ github.actor }}"
      echo "SHA: ${{ github.sha }}"
      echo "Runner OS: ${{ runner.os }}"
      echo "Job Status: ${{ job.status }}"
    env:
      WORKFLOW_NAME: ${{ github.workflow }}
      BRANCH_NAME: ${{ github.ref_name }}

表达式语法

yaml
steps:
  - name: Conditional logic
    run: |
      if [[ "${{ github.event_name }}" == "push" ]]; then
        echo "Push event detected"
      fi
      
      if [[ "${{ contains(github.event.head_commit.message, 'fix') }}" == "true" ]]; then
        echo "Fix commit detected"
      fi
  
  - name: String manipulation
    run: |
      echo "Lowercase: ${{ fromJSON(toJSON(github.repository)) }}"
      echo "Length: ${{ length(github.ref) }}"

输出和依赖

步骤输出

yaml
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      build-number: ${{ steps.version.outputs.build-number }}
      artifact-name: ${{ steps.package.outputs.name }}
    steps:
      - id: version
        run: |
          BUILD_NUM=$(date +%s)
          echo "build-number=$BUILD_NUM" >> $GITHUB_OUTPUT
        shell: bash
      
      - id: package
        run: |
          PKG_NAME="app-${{ steps.version.outputs.build-number }}"
          echo "name=$PKG_NAME" >> $GITHUB_OUTPUT
  
  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: |
          echo "Testing build ${{ needs.build.outputs.build-number }}"
          echo "Artifact name: ${{ needs.build.outputs.artifact-name }}"

矩阵输出

yaml
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16, 18, 20]
    steps:
      - name: Test Node.js version
        run: |
          echo "Testing Node.js ${{ matrix.node-version }}"
          node --version
        id: test
  
  summary:
    needs: test
    runs-on: ubuntu-latest
    if: always()  # 无论前面的作业成功与否都执行
    steps:
      - run: echo "All tests completed"

高级配置示例

复杂工作流

yaml
name: Complex CI/CD Pipeline

on:
  push:
    branches: [main, develop]
    tags: ['v*']
  pull_request:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  NODE_VERSION: '18.x'
  BUILD_PATH: './dist'

defaults:
  run:
    shell: bash

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
  
  test:
    needs: lint
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: ['16.x', '18.x']
      fail-fast: false
      max-parallel: 3
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      - run: npm ci
      - run: npm test
      - name: Upload coverage
        if: matrix.os == 'ubuntu-latest'
        uses: actions/upload-artifact@v4
        with:
          name: coverage-${{ matrix.os }}-${{ matrix.node-version }}
          path: coverage/
  
  build:
    needs: test
    runs-on: ubuntu-latest
    outputs:
      build-number: ${{ steps.version.outputs.number }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      - run: npm ci
      - run: npm run build
      - id: version
        run: |
          echo "number=$(date +%s)" >> $GITHUB_OUTPUT
      - name: Upload build artifacts
        uses: actions/upload-artifact@v4
        with:
          name: build-${{ steps.version.outputs.number }}
          path: ${{ env.BUILD_PATH }}
  
  deploy-dev:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop'
    environment: development
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          name: build-${{ needs.build.outputs.build-number }}
          path: ${{ env.BUILD_PATH }}
      - name: Deploy to dev
        run: ./deploy.sh dev
  
  deploy-prod:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && startsWith(github.ref, 'refs/tags/')
    environment: 
      name: production
      url: https://production.example.com
    permissions:
      id-token: write
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v4
        with:
          name: build-${{ needs.build.outputs.build-number }}
          path: ${{ env.BUILD_PATH }}
      - name: Deploy to production
        run: ./deploy.sh prod
        env:
          AWS_ROLE_TO_ASSUME: ${{ secrets.AWS_ROLE }}

最佳实践

文件组织

yaml
# .github/workflows/ci.yml - 持续集成
# .github/workflows/cd.yml - 持续部署
# .github/workflows/security.yml - 安全扫描
# .github/workflows/release.yml - 发布流程

可维护性

yaml
# 使用描述性的作业和步骤名称
jobs:
  run-unit-tests:
    name: Unit Tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Setup Node.js environment
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Execute unit tests
        run: npm run test:unit

通过掌握这些工作流语法,您可以创建功能强大且灵活的自动化工作流,满足各种CI/CD需求。