Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXLVII · Pipeline DependenciesDataflow

Outputs as inputs — the typed contract between jobs

Intermediate⏱ ~20 mingit

What you'll learn

  • Declare a job output with `outputs:` and set it from a step
  • Consume an output via `needs.<job_id>.outputs.<name>`
  • Distinguish outputs (typed, ephemeral) from artifacts (untyped, durable)
  • Recognise the limits: outputs are strings, scoped to one run

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

Jobs can declare outputs and pass them to dependents. The producer promises a name and a string; the consumer reads via needs.<job_id>.outputs.<name>. One graph, two contracts: ordering and data.

Declaring outputs

The producer declares outputs at job level:

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      version: ${ steps.set-version.outputs.version }
      digest: ${ steps.set-digest.outputs.digest }
    steps:
      - id: set-version
        run: echo "version=1.2.3" >> "$GITHUB_OUTPUT"
      - id: set-digest
        run: echo "digest=sha256:abc..." >> "$GITHUB_OUTPUT"

The outputs: block maps names to expressions that resolve at job-completion time.

Setting outputs from a step

The $GITHUB_OUTPUT file is the runtime:

echo "version=1.2.3" >> "$GITHUB_OUTPUT"
echo "digest=$(sha256sum package.tar | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"

The runner captures each line as a named output.

Consuming outputs

A dependent reads via needs.<job_id>.outputs.<name>:

jobs:
  deploy:
    runs-on: ubuntu-latest
    needs: [build]
    steps:
      - run: |
          echo "Deploying ${ needs.build.outputs.version }"
          echo "Digest ${ needs.build.outputs.digest }"

The dependent must list the producer in needs:.

The typed contract

Outputs are strings. No schema validation; contract by convention.

flowchart LR
    B["build"] -->|outputs.version| D["deploy"]
    B -->|outputs.digest| D

Edges carry ordering (needs:) and data (outputs.<name>).

Outputs versus artifacts

  • Outputs: typed, ephemeral, scoped to one run.
  • Artifacts: untyped, durable, retained for retention-days.
# Outputs
echo "version=1.2.3" >> "$GITHUB_OUTPUT"

# Artifacts
- uses: actions/upload-artifact@v4
  with:
    name: package
    path: dist/package.tar

Outputs for values; artifacts for files.

Production discipline

  1. Declare every output the consumer needs.
  2. Validate shape at consumer if it matters.
  3. Artifacts for files; outputs for values.
  4. Document the output contract.

Cross-course references

  • Ansible for Production Sysadmins - Part XXXVII uses AWX survey fields.
  • Linux for Production Sysadmins - Part XXX uses EnvironmentFile.

Quiz

Knowledge check · 4 questions

  1. Q1. Job A has `outputs: version`. Job B has `needs: [A]` and reads `needs.A.outputs.version`. What type is the value?

  2. Q2. Outputs are visible to any job in the workflow, regardless of whether the consuming job declares the producer in `needs:`.

  3. Q3. Name three limits of GitHub Actions outputs and explain why each matters.

  4. Q4. Diagnose why a deploy job reports version as empty and recommend the fix.

    Team P: build sets `outputs.version` via `echo "version=1.2.3" >> $GITHUB_OUTPUT`. deploy has `needs: [build]` and reads `needs.build.outputs.version`. Deploy reports empty.

Passing score: 75%. Answers are checked in this browser.