仅使用 GitHub Actions 在特定分支上运行作业

Posted

技术标签:

【中文标题】仅使用 GitHub Actions 在特定分支上运行作业【英文标题】:Only run job on specific branch with GitHub Actions 【发布时间】:2020-01-28 01:36:18 【问题描述】:

我对 GitHub Actions 比较陌生,我有 2 个工作 - 一个运行我的测试,另一个将我的项目部署到服务器上。

显然我希望测试在每个分支上运行,但只有在将某些内容推送到 master 时才应该进行部署。

我正在努力寻找在特定分支上运行作业的方法。我知道只能在特定分支上运行整个工作流程,但这意味着我将拥有一个“测试”工作流程和一个“部署”工作流程。

这听起来像是一个解决方案,但是它们会并行运行。在理想情况下,测试将首先运行,并且只有当它们成功时,才会开始部署作业。使用 2 个单独的工作流时,情况并非如此。

我怎样才能做到这一点?是否可以在特定分支上运行jobs

【问题讨论】:

我已经用job 级别的新if 条件更新了我的答案。 ***.com/a/58142412/11934042 【参考方案1】:

在最近的更新中,您现在可以将if 条件句置于job 级别。请参阅此处的文档。 https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idif

我测试了这个工作流程,它在每次推送时运行作业 test,但只在主分支上运行 deploy

name: my workflow
on: push
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Execute tests
        run: exit 0
  deploy:
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/master'
    steps:
      - name: Deploy app
        run: exit 0

以下是我的原始答案,如果您希望有单独的工作流程,还有一个替代解决方案。

第一个工作流针对除master 之外的每个分支运行。在此工作流程中,您只运行测试。

on:
  push:
    branches:
      - '*'
      - '!master'

第二个工作流程仅针对 master 运行,如果测试成功通过,则运行您的测试和部署。

on:
  push:
    branches:
      - master

【讨论】:

您可能还希望仅在之前的作业成功的情况下运行部署:if: success() && github.ref == 'refs/heads/master' 不错的答案。你能解释一下/refs/heads/...的语法吗? @Roly 是git引用的格式。 ref 只是一个名称,它指代 git 中的特定提交。这就是分支和标签的工作方式。请参阅documentation here。 @JanDolejsi 检查success() 是不必要的:“如果您的if 表达式不包含任何状态函数,它将自动生成success()。” source 你知道如果github.ref 匹配多个分支名称,是否有比较语法?这个github.ref == 'refs/heads/master' || github.ref == 'refs/heads/staging' || …有点冗长、重复和冗长。【参考方案2】:

这是我为只应在特定分支上运行的步骤所做的。

- name: Publish Image
  # Develop branch only
  if: github.ref == 'refs/heads/develop'
  run: |
    ... publish commands ...

【讨论】:

【参考方案3】:

大多数答案都为 one 单个分支提供了解决方案。要限制作业在 任何 特定分支集上运行,您可以使用带有多个析取 (||) 运算符的if 条件;但这太冗长了,不尊重DRY principle。

使用contains 函数可以减少重复次数。

使用contains

contains('
  refs/heads/dev
  refs/heads/staging
  refs/heads/production
', github.ref)

与使用多个||相比:

github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/production' || …

完整示例:

---
on: push
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Run tests
      run: …

  deployment:
    name: Deployment
    runs-on: ubuntu-latest
    needs: [test]
    if:
      contains('
        refs/heads/dev
        refs/heads/staging
        refs/heads/production
      ', github.ref)
    steps:
    - uses: actions/checkout@v2
    - name: Deploy
      run: …

【讨论】:

我对这个答案投了反对票,因为有可能出现误报。考虑一个分支“dev”和一个分支“device”。当使用 contains 方法时,“dev”也将匹配“device”并因此运行。即使你不是故意的。解决方法可能是用方括号括起 github.ref 并将分支写为 [refs/heads/dev]。 这是一个很好的观察,根据contains 的文档,使用数组实际上修复了这个子字符串匹配行为。【参考方案4】:

2021 年更新

我知道可以只在特定的 分支,但这意味着我将有一个“测试”工作流程和一个 “部署”工作流程。

这听起来像是一个解决方案,但它们会并行运行。在一个 理想世界,测试将首先运行,并且只有当它们成功时,然后 部署工作将开始。使用 2 个单独的时不是这种情况 工作流程。

您现在可以使用事件workflow_run 来实现the tests would run first, and only if they succeed, then the deploy job would start 部分(继续阅读以了解如何):

workflow_run的文档页面

https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#workflow_run

当请求或完成工作流运行时发生此事件,并且 允许您根据完成的结果执行工作流 另一个工作流程。无论结果如何,都会触发工作流运行 之前的工作流程。

例如,如果您的 pull_request 工作流生成构建工件, 您可以创建一个使用 workflow_run 的新工作流来分析 结果并在原始拉取请求中添加评论。

现在,考虑 OP 的初始问题:

我希望测试在每个分支上运行,但只有在将某些内容推送到 master 时才应该进行部署

现在可以这样解决:

以下设置正在运行,几分钟前我刚刚在我的一个存储库中实现了相同的逻辑

工作流<your_repo>/.github/workflows/tests.yml

name: My tests workflow

on:
  push:
    branches:
      - master
  pull_request: 

jobs:
  test:

    # ... your implementation to run your tests

工作流<your_repo>/.github/workflows/deploy.yml

name: My deploy workflow

on:
  workflow_run:
    workflows: My tests workflow # Reuse the name of your tests workflow
    branches: master
    types: completed

jobs:
  deploy:
    # `if` required because a workflow run is triggered regardless of
    # the result of the previous workflow (see the documentation page)
    if: $ github.event.workflow_run.conclusion == 'success' 

    # ... your implementation to deploy your project

【讨论】:

另外需要注意的是,使用 workflow_run 过滤并在特定分支上运行的整个事情只有在您将最新的工作流推送到默认分支时才有效。挣扎了好几个小时,因为我创建了单独的分支 workflow testing 并在那里进行了测试,但是无论分支如何都会触发特定的工作流,直到我将其推送到默认的“main” 另外,如果我正确阅读了文档,这适用于公共存储库。【参考方案5】:

虽然您目前无法在 job 级别设置条件,但您可以在 step 级别设置条件 - 请参阅Contexts and expression syntax for GitHub Actions。

要获取分支名称,当前的解决方案是检查 GITHUB_REF 环境变量 - 详情请参阅 Default environment variables 和 this question。

将所有内容放在一起 - 如果您决定在最后一个链接中接受已接受的答案,您的工作流程可能如下所示:

jobs:

 test:
  runs-on: ubuntu-latest
  steps:
  - name: Run tests
    run: ./my-tests.sh

 deploy:
  runs-on: ubuntu-latest
  needs: test
  steps:
  - name: Extract branch name
    shell: bash
    run: echo "##[set-output name=branch;]$(echo $GITHUB_REF##*/)"
    id: extract_branch
  - name: Deploy
    run: ./deploy.sh
    if: steps.extract_branch.outputs.branch == 'master'

如果您希望将所有内容保存在工作流文件中而不是单独的脚本中,您可以随时将if 添加到给定作业的每个步骤中。


我希望这只是一个临时解决方案/变通办法,并且工作条件将在 beta 结束之前添加。

【讨论】:

【参考方案6】:

以下内容在工作中对我有用:

PR 目标分支是以下之一:
if: contains(github.base_ref, 'staging')
  || contains(github.base_ref, 'production')
当拉取请求的源分支是以下之一时:
if: contains(github.head_ref, 'feature')
  || contains(github.head_ref, 'release')

【讨论】:

以上是关于仅使用 GitHub Actions 在特定分支上运行作业的主要内容,如果未能解决你的问题,请参考以下文章

Github Actions:在特定操作系统上运行步骤

在 CLI 中观看 GH Actions 工作流输出?

如何使用 Github Actions 运行特定作业

为除 main 之外的所有分支触发 GitHub Actions,其中唯一的工作流定义文件是

如何使用可重用的Github Actions和Heroku构建简单的部署管道

GitHub Actions:某些文件更新的自动 PR?