我对GitHub Actions相对较新,我有2个工作-一个运行我的测试,一个将我的项目部署到服务器上。
显然,我希望测试在每个分支上运行,但是部署应该只在某些东西被推到master时进行。
我正在努力寻找一种在特定分支上运行工作的方法。我知道在一个特定的分支上只运行整个工作流是可能的,但是这意味着我将有一个“测试”工作流和一个“部署”工作流。
这听起来像是一个解决方案,但它们是并行的。在理想的情况下,测试将首先运行,并且只有当测试成功时,才会启动部署作业。当使用两个单独的工作流时,情况就不是这样了。
我怎样才能做到这一点呢?是否可以在特定的分支上运行作业?
2021年更新
我知道有可能只在特定的工作流上运行整个工作流
分支,然而,这意味着我将有一个“测试”工作流和一个
“部署”的工作流。
这听起来像是一个解决方案,但它们是并行的。在一个
理想的情况是,测试将首先运行,并且只有在他们成功的情况下
deploy作业将启动。当使用两个单独的时候,情况就不是这样了
工作流。
现在,您可以使用事件workflow_run来实现测试首先运行的部分,并且只有当测试成功时,才会启动部署作业(请继续阅读以了解如何执行):
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 /工作流/ tests.yml
name: My tests workflow
on:
push:
branches:
- master
pull_request: {}
jobs:
test:
# ... your implementation to run your tests
工作流< your_repo > / .github /工作流/ 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
虽然这个讨论很老了,但最近我遇到了同样的问题,只是稍微增加了一点。If条件检查分支是否主要工作,但如果有人推动他们的分支和更新工作流yml文件删除If条件呢?deploy作业将在其分支未被检查或合并到main中时被触发,这可能会破坏生产环境。这可能是开源项目中的一个问题。
我在任何地方都找不到答案,所以想分享我的发现。我希望这是正确的线程。
为了确保只有在特定分支中才能触发作业,可以使用环境。部署作业很可能有一些api密钥用于连接到目标服务器,这些密钥可能存储在秘密中。我们应该将它们存储在各自的环境中,而不是存储在可以在存储库中全局访问的存储库秘密中。
环境的官方文档包含了详细的解释和示例脚本,但这里分享了一个简单的示例。假设我们希望只在main更新时运行生产部署
从存储库设置中创建一个生产环境
在“部署分支”下拉菜单中选择“选定的分支”,并在模式中添加main
在生产环境机密中添加api密钥
在工作流yml中,我们只需要添加环境信息environment: production,如下所示(使用来自@peterevans的回答的脚本)
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/main'
environment: production
steps:
- name: Deploy app
run: exit 0
The environment information indicates where the secrets have to be read from. If the current branch name doesn't match the pattern provided in Selected Branches then the job will immediately fail with an error. Since we have a condition to only run this on main, normally that won't bother us because this job will be skipped on other branches anyways. But if someone, mistakenly or with ill intentions, modifies the yml file and removes the condition before pushing their branch, they'll get an error. So, our system remains secure at least from the threat here.
希望这对有同样疑惑的人有所帮助。
大多数答案都为单个分支提供了解决方案。要限制作业在任何特定的分支集上运行,可以使用if条件和多个分离操作符(||);但这太啰嗦了,不尊重DRY原则。
使用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: …
虽然你目前不能在作业级别拥有条件,但你可以在步骤级别拥有条件-请参阅GitHub动作的上下文和表达式语法。
要获得分支名称,目前的解决方案是检查GITHUB_REF环境变量-参见默认环境变量和这个问题的详细信息。
把它们放在一起——如果你决定在最后一个链接中使用公认的答案,你的工作流程可能是这样的:
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。
我希望这只是一个临时的解决方案,工作条件将在测试结束前添加。