Skip to content
On this page

GitHub Actions条件执行

条件执行是GitHub Actions中的重要功能,允许根据各种条件控制工作流、作业和步骤的执行。通过合理使用条件执行,可以创建更加灵活和高效的工作流。

条件执行基础

if关键字

if关键字是最常用的条件执行方式,可以在工作流的不同层级使用:

yaml
# .github/workflows/conditional-execution.yml
name: Conditional Execution Example

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    # 仅在main分支执行
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Build on main branch
        run: echo "Building on main branch"
  
  test:
    runs-on: ubuntu-latest
    # 仅在推送事件时执行
    if: github.event_name == 'push'
    steps:
      - name: Run tests on push
        run: echo "Running tests"
  
  deploy:
    runs-on: ubuntu-latest
    needs: [build, test]
    # 在main分支的推送事件时执行
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    steps:
      - name: Deploy to production
        run: echo "Deploying to production"

基本条件表达式

yaml
jobs:
  basic-conditions:
    runs-on: ubuntu-latest
    steps:
      - name: Equal condition
        if: github.ref == 'refs/heads/main'
        run: echo "On main branch"
      
      - name: Not equal condition
        if: github.ref != 'refs/heads/main'
        run: echo "Not on main branch"
      
      - name: Contains condition
        if: contains(github.event.head_commit.message, 'feat')
        run: echo "Feature commit detected"
      
      - name: Boolean condition
        if: true
        run: echo "This always runs"
      
      - name: Variable condition
        if: env.ENABLE_FEATURE == 'true'
        run: echo "Feature enabled"
        env:
          ENABLE_FEATURE: ${{ vars.ENABLE_FEATURE }}

事件相关的条件

事件类型条件

yaml
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 2 * * 1'

jobs:
  event-specific:
    runs-on: ubuntu-latest
    steps:
      - name: Handle push event
        if: github.event_name == 'push'
        run: |
          echo "Handling push event"
          echo "Ref: ${{ github.ref }}"
          echo "SHA: ${{ github.sha }}"
      
      - name: Handle pull request event
        if: github.event_name == 'pull_request'
        run: |
          echo "Handling pull request event"
          echo "PR number: ${{ github.event.number }}"
          echo "PR title: ${{ github.event.pull_request.title }}"
      
      - name: Handle scheduled event
        if: github.event_name == 'schedule'
        run: |
          echo "Handling scheduled event"
          echo "Scheduled run at: $(date)"

分支和标签条件

yaml
jobs:
  branch-specific:
    runs-on: ubuntu-latest
    steps:
      - name: Main branch specific
        if: github.ref == 'refs/heads/main'
        run: echo "Running on main branch"
      
      - name: Develop branch specific
        if: github.ref == 'refs/heads/develop'
        run: echo "Running on develop branch"
      
      - name: Feature branch specific
        if: startsWith(github.ref, 'refs/heads/feature/')
        run: echo "Running on feature branch: ${{ github.ref_name }}"
      
      - name: Tag specific
        if: startsWith(github.ref, 'refs/tags/')
        run: |
          TAG_NAME="${GITHUB_REF#refs/tags/}"
          echo "Running on tag: $TAG_NAME"

提交信息条件

yaml
jobs:
  commit-message-conditions:
    runs-on: ubuntu-latest
    steps:
      - name: Skip CI
        if: contains(github.event.head_commit.message, '[skip ci]')
        run: echo "Skipping CI due to commit message"
      
      - name: Deploy trigger
        if: contains(github.event.head_commit.message, '[deploy]')
        run: echo "Deploy triggered by commit message"
      
      - name: Release trigger
        if: contains(github.event.head_commit.message, 'release') || contains(github.event.head_commit.message, 'Release')
        run: echo "Release process triggered"
      
      - name: Feature detection
        if: |
          contains(github.event.head_commit.message, 'feat:') ||
          contains(github.event.head_commit.message, 'feature:')
        run: echo "Feature commit detected"

作业依赖条件

基本依赖条件

yaml
jobs:
  setup:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Setup completed"
  
  build:
    needs: setup
    runs-on: ubuntu-latest
    steps:
      - run: echo "Build completed"
  
  test:
    needs: build
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
    steps:
      - run: echo "Testing on ${{ matrix.os }}"
  
  deploy-staging:
    needs: [setup, build, test]
    runs-on: ubuntu-latest
    # 仅在所有依赖作业成功时执行
    if: always() && (needs.setup.result == 'success' && needs.build.result == 'success')
    steps:
      - run: echo "Deploying to staging"
  
  deploy-production:
    needs: [setup, build, test]
    runs-on: ubuntu-latest
    # 仅在main分支且所有依赖成功时执行
    if: github.ref == 'refs/heads/main' && needs.setup.result == 'success' && needs.build.result == 'success'
    steps:
      - run: echo "Deploying to production"

作业结果条件

yaml
jobs:
  analysis:
    runs-on: ubuntu-latest
    steps:
      - name: Run analysis
        run: |
          # 模拟分析过程
          if [ $((RANDOM % 2)) -eq 0 ]; then
            echo "Analysis passed"
          else
            exit 1  # 模拟失败
          fi
  
  notify-on-success:
    needs: analysis
    runs-on: ubuntu-latest
    if: needs.analysis.result == 'success'
    steps:
      - name: Notify on success
        run: echo "Analysis completed successfully"
  
  notify-on-failure:
    needs: analysis
    runs-on: ubuntu-latest
    if: needs.analysis.result == 'failure'
    steps:
      - name: Notify on failure
        run: echo "Analysis failed"
  
  notify-always:
    needs: analysis
    runs-on: ubuntu-latest
    if: always()  # 无论结果如何都执行
    steps:
      - name: Always notify
        run: echo "Analysis completed (result: ${{ needs.analysis.result }})"

上下文和表达式条件

GitHub上下文条件

yaml
jobs:
  github-context-conditions:
    runs-on: ubuntu-latest
    steps:
      - name: Actor specific
        if: github.actor == 'dependabot[bot]'
        run: echo "Running for Dependabot"
      
      - name: Repository specific
        if: github.repository == 'owner/repo-name'
        run: echo "Running for specific repository"
      
      - name: Organization specific
        if: github.repository_owner == 'organization-name'
        run: echo "Running for specific organization"
      
      - name: Fork condition
        if: github.event.repository.fork == false
        run: echo "Running on original repository, not fork"

Runner上下文条件

yaml
jobs:
  runner-conditions:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
    steps:
      - name: OS specific step
        if: runner.os == 'Linux'
        run: echo "Running on Linux"
      
      - name: Windows specific step
        if: runner.os == 'Windows'
        run: echo "Running on Windows"
        shell: cmd
      
      - name: macOS specific step
        if: runner.os == 'macOS'
        run: echo "Running on macOS"
      
      - name: Architecture specific
        if: runner.arch == 'X64'
        run: echo "Running on 64-bit architecture"

环境变量条件

yaml
env:
  ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'production' || 'development' }}

jobs:
  environment-conditions:
    runs-on: ubuntu-latest
    steps:
      - name: Production condition
        if: env.ENVIRONMENT == 'production'
        run: |
          echo "Production environment"
          # 生产环境特定的步骤
        env:
          API_URL: ${{ secrets.PROD_API_URL }}
      
      - name: Development condition
        if: env.ENVIRONMENT == 'development'
        run: |
          echo "Development environment"
          # 开发环境特定的步骤
        env:
          API_URL: ${{ secrets.DEV_API_URL }}

复杂条件表达式

逻辑运算符

yaml
jobs:
  complex-conditions:
    runs-on: ubuntu-latest
    steps:
      - name: AND condition
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: echo "Main branch push event"
      
      - name: OR condition
        if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
        run: echo "On main or develop branch"
      
      - name: NOT condition
        if: github.ref != 'refs/heads/hotfix'
        run: echo "Not on hotfix branch"
      
      - name: Complex combination
        if: |
          (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') &&
          github.event_name == 'push' &&
          !contains(github.event.head_commit.message, '[skip ci]')
        run: echo "Complex condition met"

函数条件

yaml
jobs:
  function-conditions:
    runs-on: ubuntu-latest
    steps:
      - name: String function condition
        if: startsWith(github.ref, 'refs/heads/release/')
        run: echo "Release branch detected"
      
      - name: Length condition
        if: length(github.event.head_commit.message) > 10
        run: echo "Commit message is longer than 10 characters"
      
      - name: Contains function
        if: contains(github.event.pull_request.labels.*.name, 'ready-to-merge')
        run: echo "PR has ready-to-merge label"
        # 注意:此条件需要pull_request事件
        
      - name: FromJSON example
        if: ${{ fromJSON('["main", "develop"]') | contains(github.ref_name) }}
        run: echo "Reference is in allowed list"

高级条件模式

多步骤条件

yaml
jobs:
  multi-step-conditions:
    runs-on: ubuntu-latest
    steps:
      - name: Set environment variable
        id: env_setter
        run: |
          if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
            echo "TARGET_ENV=production" >> $GITHUB_OUTPUT
            echo "SHOULD_DEPLOY=true" >> $GITHUB_OUTPUT
          elif [[ "${{ github.ref }}" == "refs/heads/develop" ]]; then
            echo "TARGET_ENV=staging" >> $GITHUB_OUTPUT
            echo "SHOULD_DEPLOY=true" >> $GITHUB_OUTPUT
          else
            echo "TARGET_ENV=testing" >> $GITHUB_OUTPUT
            echo "SHOULD_DEPLOY=false" >> $GITHUB_OUTPUT
          fi
      
      - name: Conditional deployment
        if: steps.env_setter.outputs.SHOULD_DEPLOY == 'true'
        run: |
          echo "Deploying to ${{ steps.env_setter.outputs.TARGET_ENV }}"
          # 部署逻辑
        env:
          TARGET_ENV: ${{ steps.env_setter.outputs.TARGET_ENV }}

矩阵条件

yaml
jobs:
  matrix-conditions:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16, 18, 20]
        experimental: [false, true]
        include:
          - node-version: 20
            experimental: true
            run-experimental: true
        exclude:
          - node-version: 16
            experimental: true
    steps:
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      
      - name: Regular tests
        run: npm test
      
      - name: Experimental tests
        if: matrix.run-experimental == true
        run: npm run test:experimental

环境特定条件

yaml
jobs:
  environment-conditions:
    runs-on: ubuntu-latest
    environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
    steps:
      - name: Production specific
        if: github.ref == 'refs/heads/main'
        run: |
          echo "Running in production environment"
          # 生产环境特定逻辑
        env:
          API_KEY: ${{ secrets.PROD_API_KEY }}
      
      - name: Staging specific
        if: github.ref != 'refs/heads/main'
        run: |
          echo "Running in staging environment"
          # 预发布环境特定逻辑
        env:
          API_KEY: ${{ secrets.STAGING_API_KEY }}

错误处理和条件

错误条件处理

yaml
jobs:
  error-handling:
    runs-on: ubuntu-latest
    steps:
      - name: Step that might fail
        id: risky_step
        run: |
          # 模拟可能失败的步骤
          if [ $((RANDOM % 3)) -eq 0 ]; then
            echo "Success"
            echo "status=success" >> $GITHUB_OUTPUT
          else
            echo "Failure"
            echo "status=failed" >> $GITHUB_OUTPUT
            exit 1
          fi
        continue-on-error: true  # 即使失败也继续执行
      
      - name: Handle success
        if: steps.risky_step.outputs.status == 'success'
        run: echo "Previous step succeeded"
      
      - name: Handle failure
        if: steps.risky_step.outputs.status == 'failed'
        run: echo "Previous step failed, taking alternative action"

Always条件

yaml
jobs:
  always-execute:
    runs-on: ubuntu-latest
    steps:
      - name: Main step
        run: |
          if [ $((RANDOM % 2)) -eq 0 ]; then
            echo "Success"
          else
            echo "Failure"
            exit 1
          fi
      
      - name: Cleanup
        if: always()  # 无论前一步骤成功或失败都会执行
        run: echo "Performing cleanup"
      
      - name: Success notification
        if: success()  # 仅在所有前置步骤成功时执行
        run: echo "All steps succeeded"
      
      - name: Failure notification
        if: failure()  # 仅在至少一个前置步骤失败时执行
        run: echo "Some steps failed"
      
      - name: Cancelled notification
        if: cancelled()  # 仅在工作流被取消时执行
        run: echo "Workflow was cancelled"

实际应用示例

完整的CI/CD条件执行

yaml
name: Advanced CI/CD with Conditions

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

jobs:
  # 仅在非PR事件时运行的作业
  security-scan:
    if: github.event_name != 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Security scan
        run: |
          echo "Running security scan on ${{ github.ref }}"
          # 安全扫描逻辑
  
  # 构建作业
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'
      - name: Install dependencies
        run: npm ci
      - name: Build
        run: npm run build
  
  # 测试作业
  test:
    needs: build
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        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 }}
      - run: npm ci
      - run: npm test
  
  # 部署到预发布环境
  deploy-staging:
    needs: [build, test]
    runs-on: ubuntu-latest
    if: |
      github.ref == 'refs/heads/develop' &&
      github.event_name == 'push' &&
      !contains(github.event.head_commit.message, '[skip deploy]')
    environment: staging
    steps:
      - name: Deploy to staging
        run: echo "Deploying to staging environment"
        env:
          API_KEY: ${{ secrets.STAGING_API_KEY }}
  
  # 部署到生产环境
  deploy-production:
    needs: [build, test, security-scan]
    runs-on: ubuntu-latest
    if: |
      github.ref == 'refs/heads/main' &&
      github.event_name == 'push' &&
      !contains(github.event.head_commit.message, '[skip deploy]') &&
      startsWith(github.ref, 'refs/tags/v')
    environment: production
    permissions:
      id-token: write
    steps:
      - name: Deploy to production
        run: echo "Deploying to production environment"
        env:
          API_KEY: ${{ secrets.PROD_API_KEY }}
  
  # 发布包
  publish-package:
    needs: [build, test, security-scan]
    runs-on: ubuntu-latest
    if: |
      github.ref == 'refs/heads/main' &&
      startsWith(github.ref, 'refs/tags/v')
    steps:
      - name: Publish package
        run: echo "Publishing package to registry"
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

功能标志驱动的条件执行

yaml
# 使用GitHub Variables作为功能标志
env:
  FEATURE_NEW_BUILD_SYSTEM: ${{ vars.FEATURE_NEW_BUILD_SYSTEM || 'false' }}
  FEATURE_BETA_TESTS: ${{ vars.FEATURE_BETA_TESTS || 'false' }}
  ENVIRONMENT: ${{ vars.ENVIRONMENT || 'development' }}

jobs:
  feature-flagged-build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: New build system
        if: env.FEATURE_NEW_BUILD_SYSTEM == 'true'
        run: |
          echo "Using new build system"
          # 新构建系统逻辑
        shell: bash
      
      - name: Legacy build system
        if: env.FEATURE_NEW_BUILD_SYSTEM != 'true'
        run: |
          echo "Using legacy build system"
          # 旧构建系统逻辑
        shell: bash
      
      - name: Beta tests
        if: env.FEATURE_BETA_TESTS == 'true'
        run: |
          echo "Running beta tests"
          npm run test:beta
        shell: bash
      
      - name: Regular tests
        if: env.FEATURE_BETA_TESTS != 'true'
        run: |
          echo "Running regular tests"
          npm run test:regular
        shell: bash

通过合理使用条件执行,可以创建高度灵活和高效的工作流,只在适当的时候执行相应的操作,从而节省资源并提高工作效率。