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

wea*_*dan 5 github github-actions

我是GitHub Actions的新手,有2个工作-一个运行我的测试,另一个将我的项目部署到服务器上。

显然,我希望测试可以在每个分支上运行,但是部署应该仅在将某些内容推入主程序时进行。

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

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

我将如何实现这一目标?是否可以在特定分支上运行作业

小智 32

2021年更新

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

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

您现在可以使用该事件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
Run Code Online (Sandbox Code Playgroud)

工作流程<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
Run Code Online (Sandbox Code Playgroud)

  • 另外,值得注意的是,只有当您将最新的工作流程推送到默认分支时,使用 **workflow_run** 过滤并在特定分支上运行的整个过程才会起作用。挣扎了几个小时,因为我创建了单独的分支**工作流测试**并在那里进行了测试,但是无论分支如何,都会触发特定的工作流,直到我将其推送到默认的“主” (2认同)

bra*_*adj 23

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

- name: Publish Image
  # Develop branch only
  if: github.ref == 'refs/heads/develop'
  run: |
    ... publish commands ...
Run Code Online (Sandbox Code Playgroud)


goe*_*tzc 22

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

使用该contains功能可以以更少的重复进行存档。

使用contains

contains('
  refs/heads/dev
  refs/heads/staging
  refs/heads/production
', github.ref)
Run Code Online (Sandbox Code Playgroud)

与使用多个相比||

github.ref == 'refs/heads/dev' || 'refs/heads/staging' || github.ref == 'refs/heads/production' || …
Run Code Online (Sandbox Code Playgroud)

完整示例:

---
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: …
Run Code Online (Sandbox Code Playgroud)

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

lon*_*mer 13

虽然这个讨论很老了,但最近我遇到了同样的问题,只是做了一些补充。if条件来检查分支是否main有效,但是如果有人推送他们的分支并更新工作流 yml 文件以删除if条件怎么办?部署作业将在其分支未经审查或合并的情况下被触发main,并且可能会破坏生产环境。这可能是开源项目中的一个问题。

我在任何地方都找不到这个问题的答案,所以想分享我的发现。我希望这是适合它的线程。

为了确保除了特定分支之外不会触发作业,可以使用环境。部署作业很可能有一些 api 密钥来连接到可能存储在机密中的目标服务器。我们不应该将它们存储在存储库内可全局访问的存储库机密中,而应该将它们存储在各自的环境中。

环境的官方文档包含示例脚本的详细说明,但在这里分享一个简单的示例。假设我们只想在main更新时运行生产部署

  1. 从存储库设置中,创建一个production环境
  2. Selected Branches在下拉列表中选择Deployment Branches并添加main图案
  3. 在生产环境密钥中添加 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
Run Code Online (Sandbox Code Playgroud)

环境信息指示必须从何处读取机密。如果当前分支名称与提供的模式不匹配,Selected Branches则作业将立即失败并出现错误。由于我们有一个条件只能在 上运行此操作main,因此通常这不会打扰我们,因为无论如何都会在其他分支上跳过此作业。但是,如果有人错误地或恶意地修改了 yml 文件并在推送分支之前删除了条件,他们就会收到错误。因此,我们的系统至少免受这里的威胁。

希望这可以帮助任何有同样疑问的人。


Mel*_*hia 8

对于步骤或作业,您还可以使用github.ref_name触发工作流运行的分支或标记名称。

name: my workflow
on: push
jobs:
  if: github.ref_name == 'main'
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Execute tests
        run: exit 0
Run Code Online (Sandbox Code Playgroud)

有关 github 上下文的更多信息请查看此处


pet*_*ans 7

在最近的更新中,您现在可以将if条件设置为job水平。请参阅此处的文档。https://help.github.com/cn/articles/workflow-syntax-for-github-actions#jobsjob_idif

我测试了此工作流程,该工作流程可test在每次推送时运行该作业,但仅deploy在master分支上运行。

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
Run Code Online (Sandbox Code Playgroud)

接下来是我的原始答案,如果您希望拥有单独的工作流程,还可以使用其他解决方案。

除以外的每个分支都将运行第一个工作流程master。在此工作流程中,您仅运行测试。

on:
  push:
    branches:
      - '*'
      - '!master'
Run Code Online (Sandbox Code Playgroud)

第二个工作流只master运行一次,如果测试成功通过,则运行您的测试并部署。

on:
  push:
    branches:
      - master
Run Code Online (Sandbox Code Playgroud)

  • 您可能还想仅在之前的作业成功时才运行部署:`if: success() &amp;&amp; github.ref == 'refs/heads/master'` (7认同)
  • @JanDolejsi 检查 `success()` 是没有必要的:“如果你的 `if` 表达式不包含任何状态函数,它将自动生成 `success()`。” [来源](https://help.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#job-status-check-functions) (3认同)
  • 很好的答案。您能解释一下“/refs/heads/...”语法吗? (2认同)
  • @Roly这是git引用的格式。ref 只是一个名称,引用 git 中的特定提交。这就是分支和标签的工作方式。请参阅[此处的文档](https://git-scm.com/book/en/v2/Git-Internals-Git-References)。 (2认同)

Asr*_*rar 5

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

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