Chapter 05Lesson 05~190 minutes

Checkpoint Lab — Environment Variables, Configuration Variables, Outputs, and Data Passing

The checkpoint combines the chapter into one controlled experiment. You will build a two-job pipeline with three producer steps, use workflow env, an optional repository vars value, GITHUB_ENV, and GITHUB_OUTPUT, then deliberately misuse the same-job environment channel as a cross-job transport. The first run must fail for the predicted reason. You will preserve it, repair only the interface, and prove the second run succeeds without changing unrelated execution state.

CheckpointTwo jobsThree producer stepsCausal repairEvidence packet

Learning objectives

  • Design and execute a three-step/two-job data pipeline with explicit value origin, scope, and transport predictions.
  • Prove workflow/job/step environment precedence and same-job GITHUB_ENV behavior from logs.
  • Demonstrate a deliberate cross-job misuse and diagnose it without changing runner, permissions, or trigger.
  • Repair the workflow with GITHUB_OUTPUT → job output → needs while preserving the failed run.
  • Produce an evidence packet that distinguishes configuration, environment state, outputs, and file/artifact candidates.

1. Checkpoint charter and safety boundary

Item Checkpoint contract
Repository Learner-owned disposable repository only.
Workflow .github/workflows/ch05-checkpoint.yml
Trigger Manual workflow_dispatch.
Runner ubuntu-24.04.
Permissions {}.
Credentials / external systems None.
Optional configuration variable LAB_TRACK=repo-track; workflow has a safe fallback.
Expected first outcome Consumer job fails because same-job GITHUB_ENV is misused across jobs.
Expected repaired outcome Consumer succeeds through explicit job output.

2. Write predictions before creating the defect

  1. Workflow-level APP_NAME will be visible in producer steps.
  2. Producer Step 1 writes STAGE=assembled; Step 1 itself will not see the new value, but Steps 2/3 will.
  3. Step 2 creates release_label through GITHUB_OUTPUT.
  4. Step 3 confirms both same-job environment and step output.
  5. In broken revision A, consumer will not receive CROSS_JOB_HINT written to producer GITHUB_ENV.
  6. In repaired revision B, consumer receives the mapped release_label through needs.produce.outputs.release_label.

Also predict that the two jobs receive independent runner workspaces. A green producer does not imply the consumer has the producer's environment.

3. Revision A — deliberately broken transport

name: Chapter 05 checkpoint
on:
  workflow_dispatch:
    inputs:
      suffix:
        description: 'Synthetic release suffix'
        type: string
        required: true
        default: 'checkpoint'
permissions: {}

env:
  APP_NAME: chapter05-lab
  TRACK: ${{ vars.LAB_TRACK || 'fallback-track' }}

jobs:
  produce:
    runs-on: ubuntu-24.04
    steps:
      - name: Step 1 - establish same-job environment
        run: |
          echo 'STAGE=assembled' >> "$GITHUB_ENV"
          printf 'writer-sees-stage=%q\n' "${STAGE-}"

      - name: Step 2 - compute explicit step output
        id: meta
        env:
          SUFFIX: ${{ inputs.suffix }}
        run: |
          printf 'stage=%s track=%s\n' "$STAGE" "$TRACK"
          printf 'release_label=%s-%s\n' "$APP_NAME" "$SUFFIX" >> "$GITHUB_OUTPUT"

      - name: Step 3 - verify same-job data and create bad handoff
        env:
          LABEL: ${{ steps.meta.outputs.release_label }}
        run: |
          printf 'producer_label=%s stage=%s\n' "$LABEL" "$STAGE"
          echo "CROSS_JOB_HINT=$LABEL" >> "$GITHUB_ENV"
          printf 'run=%s attempt=%s sha=%s\n' "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" "$GITHUB_SHA"

  consume:
    needs: produce
    runs-on: ubuntu-24.04
    steps:
      - name: Prove the misuse
        run: |
          printf 'consumer_hint=%q\n' "${CROSS_JOB_HINT-}"
          test -n "${CROSS_JOB_HINT-}"
This failure is intentional. Do not “repair” it by putting both jobs on a persistent self-hosted runner, broadening permissions, or moving values into a secret. The defect is the transport contract.

4. Execute Revision A and preserve first-failure evidence

Dispatch with suffix=checkpoint. Record:

  • Workflow/source SHA and run ID/attempt.
  • Optional effective TRACK value (non-sensitive only).
  • Step 1 log proving the writer step does not see newly written STAGE.
  • Step 2/3 logs proving later same-job steps do see STAGE=assembled.
  • Step 3 log proving the step output created the expected release label.
  • Producer job conclusion = success.
  • Consumer log showing empty CROSS_JOB_HINT and consumer conclusion = failure.

Do not edit the workflow until this packet is saved. The failed consumer is evidence, not noise.

5. Diagnose the causal layer

Event creation succeeded; YAML parsed; both jobs entered the graph; the producer runner executed successfully; permissions were irrelevant; no external action failed. The missing value appears only at the job boundary. That isolates the defect to data transport.

The producer already has the correct small value in steps.meta.outputs.release_label. The smallest repair is therefore to expose that value at the job interface and consume it through needs.

6. Revision B — replace ambient handoff with explicit job output

Change only the producer/consumer interface:

jobs:
  produce:
    runs-on: ubuntu-24.04
    outputs:
      release_label: ${{ steps.meta.outputs.release_label }}
    steps:
      # Keep the same three producer steps.
      # Remove CROSS_JOB_HINT as a supposed handoff.

  consume:
    needs: produce
    runs-on: ubuntu-24.04
    steps:
      - name: Consume explicit handoff
        env:
          LABEL: ${{ needs.produce.outputs.release_label }}
        run: |
          printf 'consumer_label=%s\n' "$LABEL"
          test "$LABEL" = 'chapter05-lab-checkpoint'

Keep the same trigger, suffix, runner label, permissions, optional repository variable, and synthetic values so the interface change is the only material difference.

7. Execute Revision B and compare causally

Record the new workflow SHA, run ID/attempt, producer/consumer conclusions, and output value. Verify the consumer succeeds. The difference between A and B should be explainable without invoking runner drift, token changes, or external service behavior.

Evidence Revision A Revision B
Producer same-job STAGE Works Works
Step output release label Produced Produced
Cross-job mechanism Incorrect GITHUB_ENV assumption Mapped job output
Consumer value Empty Expected label
Consumer conclusion Failure Success

8. Classify one file-shaped value

Add a thought experiment: producer generates evidence/report.json containing 5 MB of test evidence. Do not add its contents to the job output. Record that the correct production transport is an artifact/file mechanism, optionally paired with a small digest/path identifier output.

This checkpoint does not need an external artifact action to prove the design boundary; later chapters cover artifact mechanics in depth.

9. Required evidence packet

  • Both workflow revisions with hashes.
  • Prediction list completed before execution.
  • Run IDs/attempts for A and B.
  • Event name, source SHA, workflow SHA, runner OS/arch.
  • Value ledger for APP_NAME, TRACK, STAGE, release_label, and the hypothetical report file.
  • Producer Step 1/2/3 conclusions and bounded logs.
  • Consumer failure evidence from Revision A.
  • Exact job-output mapping and consumer success evidence from Revision B.
  • Assumptions/limitations: no secrets, artifacts uploaded, packages, deployments, self-hosted runners, or cloud resources.

10. Verification checklist

  • Exactly two jobs; producer has the three required progressive steps.
  • permissions: {} remains unchanged.
  • All values are synthetic/non-sensitive.
  • GITHUB_ENV is used only as same-job state.
  • GITHUB_OUTPUT creates the named step output.
  • Revision B maps the output at job scope and consumes it through needs.
  • Broken run remains preserved before repair.
  • No whole-context dump or credential logging exists.
  • 5 MB report is classified as artifact/file data, not an output.

11. Cleanup / rollback

  1. Keep both runs until checkpoint review is complete.
  2. Leave Revision B as the repaired state or revert the entire disposable branch after review.
  3. Remove LAB_TRACK if it was created only for the lab.
  4. Delete only learner-owned disposable repository resources after evidence retention needs are met.
  5. No cloud, registry, environment, package, self-hosted runner, or credential cleanup is required.

12. What Chapter 05 adds to the production operating model

Chapter 05 adds the value-transport contract: every important value has a documented origin, sensitivity class, effective scope, precedence/owner, lifetime, transport mechanism, consumer, size/format expectation, and evidence trail. Same-job environment state stays same-job; cross-job control data uses explicit outputs; file-shaped evidence uses file/artifact transport; secrets remain outside ordinary configuration.

Chapter 06 can now focus on GITHUB_TOKEN, permissions, least privilege, and API access without conflating authorization with ordinary configuration/dataflow.

Next lesson

GITHUB_TOKEN, Workflow Permissions, Least Privilege, and API Access

Move from transporting ordinary data safely to controlling what a workflow identity is authorized to read or mutate.

Knowledge check

Why must Revision A fail even though the producer job succeeds?

What is the smallest causal repair?

Why keep runner, permissions, trigger, and inputs unchanged between A and B?

Where should a 5 MB report go?

What does Chapter 05 add before token/permission work begins?

Official references and version notes

Version and compatibility note

Version-sensitive variable/output behavior was rechecked against current primary GitHub documentation on 2026-09-09. Executable labs use a disposable repository, ubuntu-24.04, built-in Bash steps only, and explicit permissions: {}; no external action, secret, package, deployment, self-hosted runner, or cloud account is required. At verification time, workflow/job/step env uses the most specific scope; default GITHUB_*/RUNNER_* environment variables cannot be overwritten; the step that writes GITHUB_ENV does not see the new value but later steps in the same job do; GITHUB_ENV cannot set NODE_OPTIONS; GITHUB_OUTPUT is the current step-output channel; multiline environment/output values use delimiter syntax whose delimiter must not occur on a line by itself; and arbitrary data is safer as a file rather than delimiter-encoded text. Configuration variables are non-secret, have organization/repository/environment scopes, and are subject to current limits including 48 KB per variable, 500 repository variables, 1,000 organization variables, 100 environment variables, and a 256 KB combined repository+organization payload per workflow run. Job outputs are limited to 1 MB per job and 50 MB total per workflow run (size approximated using UTF-16), so large/binary evidence belongs in files/artifacts rather than outputs. GitHub Actions is continuously delivered, so regenerate only after rechecking these limits and commands. The checkpoint intentionally uses no external action so the transport boundary is isolated from action runtime/version behavior.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.