仅使用 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 在特定分支上运行作业的主要内容,如果未能解决你的问题,请参考以下文章
为除 main 之外的所有分支触发 GitHub Actions,其中唯一的工作流定义文件是