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