如何在GitHub Actions步骤中强制退出作业

73
如果满足特定条件,我想要辞职。
jobs:
  foo:
    steps:
      ...
      - name: Early exit
        run: exit_with_success # I want to know what command I should write here
        if: true
      - run: foo
      - run: ...
 ...

怎么做这个?

我认为这是不可能的,但是你可以设置一个条件来跳过每个步骤。https://help.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions - zzarbi
3个回答

64

目前没有任意退出工作的方法,但是可以使用条件语句,如果较早的步骤失败,则可以允许跳过后续步骤:

jobs:
  foo:
    steps:
      ...
      - name: Early exit
        run: exit_with_success # I want to know what command I should write here
      - if: failure()
        run: foo
      - if: failure()
        run: ...
 ...

这个想法是如果第一步失败,那么其余的步骤就会运行,但如果第一步没有失败,其余步骤就不会运行。

然而,需要注意的是,如果接下来的任何步骤失败,其后的步骤仍将运行,这可能是不可取的。


另一个选择是使用步骤输出来指示成功或失败:

jobs:
  foo:
    steps:
      ...
      - id: s1
        name: Early exit
        run: # exit_with_success
      - id: s2
        if: steps.s1.conclusion == 'failure'
        run: foo
      - id: s3
        if: steps.s2.conclusion == 'success'
        run: ...
 ...

这种方法效果不错,可以非常细致地控制允许运行哪些步骤以及何时运行,但是需要添加大量的条件语句,导致代码变得冗长。


另一种选择是创建两个作业:

  • 一个检查你的条件
  • 另一个依赖于它:
jobs:
  check:
    outputs:
      status: ${{ steps.early.conclusion }}
    steps:
      - id: early
        name: Early exit
        run: # exit_with_success
  work:
    needs: check
    if: needs.check.outputs.status == 'success'
    steps:
      - run: foo
      - run: ...
 ...

通过将检查移到单独的作业中并让另一个作业等待并检查状态,可以非常好地解决最后一种方法。但是,如果您有更多的作业,则必须在每个作业中重复相同的检查。与在每个步骤中进行检查相比,这并不太糟糕。


注意:在最后一个示例中,您可以使用对象过滤器语法使check作业依赖于多个步骤的输出,然后在进一步的作业中使用contains函数确保没有任何步骤失败:

jobs:
  check:
    outputs:
      status: ${{ join(steps.*.conclusion) }}
    steps:
      - id: early
        name: Early exit
        run: # exit_with_success
      - id: more_steps
        name: Mooorreee
        run: # exit_maybe_with_success
  work:
    needs: check
    if: !contains(needs.check.outputs.status, 'failure')
    steps:
      - run: foo
      - run: ...

另外,请记住,“失败”和“成功”不是步骤可得出的唯一结论。请参见steps.<step id>.conclusion了解其他可能的原因。


我尝试使用对象过滤器语法。但是,我正在使用策略矩阵,您的带通配符的代码不起作用。我在文档中没有找到任何相关的内容。您是否知道策略矩阵是否有所不同?供参考,我收到了以下错误信息:“错误:未能评估作业输出;错误:模板无效。.github/workflows/build.yml(第54行,第19列):未预期序列。”。 - It's K
1
@It'sK,这有点有道理,因为steps.*.conclusion将计算为一个结论列表,而文档说明输出必须是一个字符串。尝试将其更改为join(steps.*.conclusion),看看是否适用于您。让我知道,以便我可以正确更新答案。 - smac89
我已经进行了测试,但奇怪的是使用 join(steps.*.conclusion) 的多个步骤的输出结果只有 success。无法获取每个步骤的结论。顺便提一下,其中一个步骤成功,一个失败,一个被跳过。 - It's K
@It'sK,那听起来像是一个 bug。如果可以,请报告它。 - smac89
4
这些都是创意十足但不太方便的解决方法,而Github现在应该直接提供这个功能。工单在这里,请点赞支持:https://github.com/actions/runner/issues/662 - Christian

17

可以通过使用 gh run cancelgh run watch 命令来实现 exit 行为:

- name: Early exit
  run: |
    gh run cancel ${{ github.run_id }}
    gh run watch ${{ github.run_id }}
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

由于取消操作不会立即中止,因此需要使用watch

您可能需要添加actions: 'write'权限以便执行该作业,例如:

permissions:
  ...
  actions: 'write'

2
正是我想要的,可以跳过一个步骤/作业,并将其标记为已跳过,而不是成功或失败。+1 - jamesmortensen
我也用过这个,我认为这是目前(2023年初)实现此目标的最佳选择(除了将您的工作流程分成不同的作业)。 供大家参考:整个操作显示为已取消/中性,但工作流运行中的日志记录显示“发生错误”。虽然不完美,但可行。 - Astockwell
4
它取消了整个工作流程。并将整个工作流程标记为已取消。 - undefined
1
感谢您提供的这个解决方法。 虽然不是理想的解决方案,因为它会将工作流标记为已取消并发送通知电子邮件,而我们目前无法抑制这一功能。 - undefined
1
取消后是否有办法将工作流标记为成功? - undefined
显示剩余4条评论

2
我刚刚通过运行exit 1来完成这个。
job:
  runs-on: ubuntu-latest
    steps:
      - name: 'Stage 1'
        run: |
          if [[ "${{ github.event.head_commit.message }}" =~ \[run-stage-2\] ]];
          then
            echo "Valid commit message detected. stage 2 will run."
          else 
            echo "Valid commit message not detected. stage 2 will not run."
            exit 1
          fi
      - name: 'Stage 2'
        run: <whatever>

所以在这里,阶段1会检查提交消息是否包含[run-stage-2]。如果是的话,阶段1将完成并开始阶段2;但如果不是的话,将调用exit 1,工作流将失败。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接