在GitHub Action中如何在一个新的作业中使用先前作业的输出结果

110

出于(主要是)教育目的,我正在尝试在GitHub Actions中运行此工作流程:

name: "We  Perl"
on:
  issues:
    types: [opened, edited, milestoned]

jobs:
  seasonal_greetings:
    runs-on: windows-latest
    steps:
      - name: Maybe greet
        id: maybe-greet
        env:
          HEY: "Hey you!"
          GREETING: "Merry Xmas to you too!"
          BODY: ${{ github.event.issue.body }}
        run: |
          $output=(perl -e 'print ($ENV{BODY} =~ /Merry/)?$ENV{GREETING}:$ENV{HEY};')
          Write-Output "::set-output name=GREET::$output"
  produce_comment:
    name: Respond to issue
    runs-on: ubuntu-latest
    steps:
      - name: Dump job context
        env:
          JOB_CONTEXT: ${{ jobs.maybe-greet.steps.id }}
        run: echo "$JOB_CONTEXT"

我需要两个不同的工作,因为它们使用不同的上下文(操作系统),但我需要将第一个工作步骤的输出传递给第二个工作。我正在尝试使用这里找到的几种jobs上下文的组合,但似乎没有任何方法可以做到这一点。显然,jobs只是一个没有实际上下文的YAML变量名称,而上下文job仅包含成功或失败。有什么想法吗?
6个回答

210

查看2020年4月的 "GitHub Actions: New workflow features",这可能有助于您的情况(引用先前作业的步骤输出)

作业输出

您可以指定一组要传递给后续作业的输出,然后从您的需求上下文中访问这些值。

请参阅文档

jobs.<jobs_id>.outputs

作业输出的映射

作业输出可供依赖于该作业的所有下游作业使用。
有关定义作业依赖关系的更多信息,请参阅jobs.<job_id>.needs

作业输出是字符串,包含表达式的作业输出在每个作业结束时在运行器上进行评估。包含机密信息的输出将在运行器上被编辑并且不发送到 GitHub Actions 。

要在依赖作业中使用作业输出,可以使用needs上下文。
有关更多信息,请参见“GitHub Actions 上下文和表达式语法。

要在依赖作业中使用作业输出,可以使用 needs 上下文。

示例

jobs:
  job1:
    runs-on: ubuntu-latest
    # Map a step output to a job output
    outputs:
      output1: ${{ steps.step1.outputs.test }}
      output2: ${{ steps.step2.outputs.test }}
    steps:
    - id: step1
      run: echo "test=hello" >> $GITHUB_OUTPUT
    - id: step2
      run: echo "test=world" >> $GITHUB_OUTPUT
  job2:
    runs-on: ubuntu-latest
    needs: job1
    steps:
    - run: echo ${{needs.job1.outputs.output1}} ${{needs.job1.outputs.output2}}

请注意使用 $GITHUB_OUTPUT,而不是旧的::set-output现在(2022年10月)已弃用

为了避免不受信任的日志数据使用set-stateset-output工作流命令而不是工作流作者的意图,我们引入了一组新的环境文件来管理状态和输出。


Jesse Adelman评论中补充道:

这似乎对于除了静态字符串之外的任何内容都不起作用。
例如,我如何将步骤的多行文本输出(比如说,我正在运行一个pytest或类似的测试)并在另一个作业中使用该输出?


2
@JesseAdelman 很好的问题:我不确定。你应该提出一个单独的问题,并附上指向这个答案的链接,以获得更精确的答案。 - VonC
1
@Jesse Adelman,在这种情况下,您可能需要将多行文本写入文件中。 - jschmitter
2
或者,您可以对输出进行base64编码,然后在下一个作业中解码。 - Nathan Karasch
3
如果有其他人想知道为什么没有生成输出,请检查您的步骤是否使用id而不是name - flymg
1
做得好,@maxisam。我在我的答案中引用了你的答案,以增加其可见性。 - VonC
显示剩余6条评论

19
更新:现在可以设置作业输出,以便将字符串值传输到下游作业。请参见此答案
以下是原始答案。这些技术对某些用例仍然有用。
  1. 将数据写入文件并使用actions/upload-artifactactions/download-artifact。有点笨拙,但它有效。
  2. 创建一个存储库分派事件并将数据发送到第二个工作流。我个人更喜欢这种方法,但缺点是需要一个repo范围的PAT
这是第二种方法如何工作的示例。它使用repository-dispatch操作。
name: "We  Perl"
on:
  issues:
    types: [opened, edited, milestoned]

jobs:
  seasonal_greetings:
    runs-on: windows-latest
    steps:
      - name: Maybe greet
        id: maybe-greet
        env:
          HEY: "Hey you!"
          GREETING: "Merry Xmas to you too!"
          BODY: ${{ github.event.issue.body }}
        run: |
          $output=(perl -e 'print ($ENV{BODY} =~ /Merry/)?$ENV{GREETING}:$ENV{HEY};')
          Write-Output "::set-output name=GREET::$output"
      - name: Repository Dispatch
        uses: peter-evans/repository-dispatch@v1
        with:
          token: ${{ secrets.REPO_ACCESS_TOKEN }}
          event-type: my-event
          client-payload: '{"greet": "${{ steps.maybe-greet.outputs.GREET }}"}'

这将在同一代码库中触发一个仓库分发工作流。

name: Repository Dispatch
on:
  repository_dispatch:
    types: [my-event]
jobs:
  myEvent:
    runs-on: ubuntu-latest
    steps:
      - run: echo ${{ github.event.client_payload.greet }}

1
此外,第二个工作流程将不会出现在原始的推送或拉取请求中。 - jjmerelo

6

可以在run步骤中捕获一个命令的整个输出(以及返回代码),以下是我编写的内容,希望能够帮助别人。但需要提醒,它需要大量的Shell技巧和多行run来确保所有操作在单个Shell实例中完成。

在我的情况下,我需要调用一个脚本并捕获其完整的stdout以供后续步骤使用,同时保留其结果以进行错误检查:

# capture stdout from script 
SCRIPT_OUTPUT=$(./do-something.sh)

# capture exit code as well
SCRIPT_RC=$?

# FYI, this would get stdout AND stderr
SCRIPT_ALL_OUTPUT=$(./do-something.sh 2>&1)

由于 Github 的工作输出似乎只能捕获一行文本,因此我还必须为输出中的任何换行符进行转义:

echo "::set-output name=stdout::${SCRIPT_OUTPUT//$'\n'/\\n}"

此外,我需要最终返回脚本的退出码以正确地指示它是否失败。整个shebang最终看起来像这样:

- name: A run step with stdout as a captured output
  id: myscript
  run: |
    # run in subshell, capturiing stdout to var
    SCRIPT_OUTPUT=$(./do-something.sh)
    # capture exit code too
    SCRIPT_RC=$?
    # print a single line output for github
    echo "::set-output name=stdout::${SCRIPT_OUTPUT//$'\n'/\\n}"
    # exit with the script status
    exit $SCRIPT_RC
  continue-on-error: true
- name: Add above outcome and output as an issue comment
  uses: actions/github-script@v5
  env:
    STEP_OUTPUT: ${{ steps.myscript.outputs.stdout }}
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    script: |
      // indicates whather script succeeded or not
      let comment = `Script finished with \`${{ steps.myscript.outcome }}\`\n`;

      // adds stdout, unescaping newlines again to make it readable
      comment += `<details><summary>Show Output</summary>

      \`\`\`
      ${process.env.STEP_OUTPUT.replace(/\\n/g, '\n')}
      \`\`\`

      </details>`;

      // add the whole damn thing as an issue comment
      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: comment
      })

编辑:还有一个操作可以完成此任务,无需太多引导,我刚刚发现了这个。


5
在我的情况下,我想传递整个构建/工件,而不仅仅是一个字符串:
name: Build something on Ubuntu then use it on MacOS

on:
  workflow_dispatch:
    # Allows for manual build trigger

jobs:
  buildUbuntuProject:
    name: Builds the project on Ubuntu (Put your stuff here)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: some/compile-action@v99
      - uses: actions/upload-artifact@v2
        # Upload the artifact so the MacOS runner do something with it
        with:
          name: CompiledProject
          path: pathToCompiledProject
  doSomethingOnMacOS:
    name: Runs the program on MacOS or something
    runs-on: macos-latest
    needs: buildUbuntuProject # Needed so the job waits for the Ubuntu job to finish
    steps:
      - uses: actions/download-artifact@master
        with:
          name: CompiledProject
          path: somewhereToPutItOnMacOSRunner
      - run: ls somewhereToPutItOnMacOSRunner # See the artifact on the MacOS runner

4

2022年10月更新:GitHub正在弃用set-output,并建议使用GITHUB_OUTPUT。定义输出并在其他步骤和作业中引用它们的语法如下所示。

来自文档的示例:

- name: Set color
  id: random-color-generator
  run: echo "SELECTED_COLOR=green" >> $GITHUB_OUTPUT
- name: Get color
  run: echo "The selected color is ${{ steps.random-color-generator.outputs.SELECTED_COLOR }}"

好观点。我已经相应地编辑了我的答案 - VonC
这种新的方法似乎不能像旧方法那样与 fromJson() 一起使用。它抱怨输出是字符串类型,而不是预期的 JSON。 - Christopher

3

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