Lesson 9 / 25

Outputs and Artifacts

Passing data between steps and jobs.

GITHUB_OUTPUT, job outputs, artifacts

A step writes outputs by appending name=value lines to the file at $GITHUB_OUTPUT; later steps read them as steps.<id>.outputs.<name>. To pass values to other jobs, map them to job outputs and read needs.<job>.outputs.<name>. Files (build results, reports) move between jobs as artifacts with upload-artifact and download-artifact. actionlint type-checks these references, so a misspelled output name is caught before running.

A typo in a job output reference, linted

I checked this workflow with actionlint 1.7.12 (actionlint -oneline .github/workflows/outputs.yml); the output and exit code are copied from that run. actionlint validates syntax, expressions, job dependencies and known actions offline; shellcheck was not installed, so shell scripts inside run: were not linted. The workflow was not executed on GitHub. The version job exposes an output named tag; the publish job reads needs.version.outputs.tagg, and actionlint reports that tagg is not defined.

name: Outputs

on: workflow_dispatch

jobs:
  version:
    runs-on: ubuntu-latest
    outputs:
      tag: ${{ steps.meta.outputs.tag }}
    steps:
      - id: meta
        run: echo "tag=v1.4.${{ github.run_number }}" >> "$GITHUB_OUTPUT"

  publish:
    needs: version
    runs-on: ubuntu-latest
    steps:
      - run: echo "Publishing ${{ needs.version.outputs.tagg }}"

Output:

.github/workflows/outputs.yml:18:35: property "tagg" is not defined in object type {tag: string} [expression]
(exit code 1)

Never use set-output

The old ::set-output command is deprecated; write to $GITHUB_OUTPUT instead.

Quick check: How does one job read a value produced by another job?

  • Through environment variables automatically
  • Through a shared /tmp folder
  • Through job outputs, via needs.<job>.outputs.<name>
  • It cannot
Answer

Through job outputs, via needs.<job>.outputs.<name> — Explicit outputs cross job boundaries.