Chapter 13Lesson 05~235 minutes

Checkpoint Lab — Artifacts, Retention, Cross-Job Data, and Workflow Result Management

The checkpoint combines the chapter into one auditable transfer. A tiny build file is uploaded, its identity and digests are carried downstream, an intentional name mismatch is preserved as the first failure, the workflow is repaired to download by artifact ID, and the final evidence packet records run/SHA/retention/provenance before optional cleanup.

Checkpoint labArtifact identityDigest verificationFirst failureCleanup

Learning objectives

  • Predict artifact identity, retention, runner and consumer state before executing the checkpoint.
  • Preserve a deliberate name-selection failure as first-failure evidence rather than overwriting history.
  • Repair the pipeline by transporting artifact ID and verifying artifact/file digests downstream.
  • Record REST metadata tying artifact ID/digest/expiry to the exact producing workflow run and SHA.
  • Perform optional exact-ID deletion only after the final evidence packet is retained.

1. Checkpoint scenario and invariant

You own a disposable repository gha-artifact-checkpoint. One job creates a tiny build manifest and uploads it. A second job must consume exactly that artifact and prove its content came from the current source SHA. The first run is intentionally broken by a name mismatch. You preserve the failure, repair the workflow to pass the artifact ID explicitly, rerun, verify digest/content/retention metadata, then optionally delete only the repaired lab artifact.

Delivery invariant: a downstream job may accept build evidence only when artifact identity, producing run/SHA, integrity and retention metadata all reconcile.

2. Current assumptions and preflight

Item Checkpoint assumption
Platform GitHub.com; no GHES artifact-v7 backend assumption
Runner ubuntu-24.04
Upload action actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a — v7.0.1
Download action actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c — v8.0.1
Credentials only ephemeral GITHUB_TOKEN; Actions read for metadata, optional Actions write for exact-ID cleanup
External systems none
Retention one-day disposable artifact; verify actual expires_at
Sensitive data none; synthetic manifest only

Before running, predict: (1) the producer creates exactly one artifact record with a unique ID/digest tied to the run; (2) the intentionally broken consumer fails despite the artifact existing because it requests the wrong name; (3) after repair, the consumer downloads the exact ID and the extracted file SHA equals the producer's file SHA; (4) optional cleanup deletes the artifact record only after evidence is captured.

3. Attempt 1 — intentionally broken name selection

name: artifact-checkpoint
on:
  workflow_dispatch:
permissions: {}

jobs:
  build:
    runs-on: ubuntu-24.04
    outputs:
      artifact_id: ${{ steps.upload.outputs.artifact-id }}
      artifact_digest: ${{ steps.upload.outputs.artifact-digest }}
      file_sha256: ${{ steps.hash.outputs.sha256 }}
    steps:
      - name: Create build manifest
        shell: bash
        run: |
          mkdir -p dist
          printf 'sha=%s
run=%s
attempt=%s
'             "$GITHUB_SHA" "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" > dist/manifest.txt
      - id: hash
        shell: bash
        run: |
          sha="$(sha256sum dist/manifest.txt | awk '{print $1}')"
          echo "sha256=$sha" >> "$GITHUB_OUTPUT"
      - id: upload
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: manifest-${{ github.run_id }}
          path: dist/manifest.txt
          if-no-files-found: error
          retention-days: 1
          include-hidden-files: false

  consume:
    needs: build
    runs-on: ubuntu-24.04
    steps:
      - name: Broken selection — preserve this failure
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: manifest
          path: ${{ runner.temp }}/incoming

Do not “fix” Attempt 1 by renaming the artifact in the UI or deleting/re-uploading it. Save the run ID, attempt, head SHA, producer logs, upload artifact ID/digest, and the consumer's not-found selection error.

4. Repair — consume the artifact identity, not a naming assumption

Keep the producer unchanged. Replace the consumer with ID-based selection and explicit verification. Add a metadata inspection step with only actions: read.

consume:
    needs: build
    runs-on: ubuntu-24.04
    permissions:
      actions: read
    env:
      GH_TOKEN: ${{ github.token }}
      ARTIFACT_ID: ${{ needs.build.outputs.artifact_id }}
      UPLOAD_DIGEST: ${{ needs.build.outputs.artifact_digest }}
      EXPECTED_FILE_SHA: ${{ needs.build.outputs.file_sha256 }}
    steps:
      - name: Download by exact artifact ID
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          artifact-ids: ${{ needs.build.outputs.artifact_id }}
          path: ${{ runner.temp }}/incoming
          digest-mismatch: error

      - name: Verify source/content
        shell: bash
        run: |
          file="$RUNNER_TEMP/incoming/manifest.txt"
          test -f "$file"
          grep -Fx "sha=$GITHUB_SHA" "$file"
          actual="$(sha256sum "$file" | awk '{print $1}')"
          test "$actual" = "$EXPECTED_FILE_SHA"

      - name: Record GitHub artifact metadata
        shell: bash
        run: |
          gh api             -H 'X-GitHub-Api-Version: 2026-03-10'             "repos/$GITHUB_REPOSITORY/actions/artifacts/$ARTIFACT_ID"             --jq '{id,name,size_in_bytes,digest,expired,created_at,expires_at,workflow_run}'

5. Optional cleanup after evidence retention

After the repaired run is verified and the evidence packet is copied, you may add a separate manual/boolean-gated cleanup job with actions: write. It must delete only the exact artifact ID received from the producer.

cleanup:
  if: ${{ inputs.delete_after && needs.consume.result == 'success' }}
  needs: [build, consume]
  runs-on: ubuntu-24.04
  permissions:
    actions: write
  env:
    GH_TOKEN: ${{ github.token }}
    ARTIFACT_ID: ${{ needs.build.outputs.artifact_id }}
  steps:
    - shell: bash
      run: |
        test -n "$ARTIFACT_ID"
        gh api --method DELETE           -H 'X-GitHub-Api-Version: 2026-03-10'           "repos/$GITHUB_REPOSITORY/actions/artifacts/$ARTIFACT_ID"

If your workflow does not define delete_after, perform cleanup through the Actions UI or an explicitly authorized local gh api command instead. Cleanup is not part of the correctness proof.

6. Prediction and reconciliation table

Prediction Attempt 1 expected Repaired run expected Independent verification
Artifact publication producer succeeds; one artifact exists producer succeeds; one new artifact record exists upload outputs + run UI/REST
Consumer selection fails because manifest name does not exist succeeds by exact artifact ID download log + ID
Integrity artifact digest exists even though consumer fails download digest validation passes upload digest + download action + REST digest
Source binding artifact belongs to Attempt 1 SHA new artifact belongs to repaired run SHA REST workflow_run.head_sha + manifest line
Retention one-day request one-day request REST expires_at
Deletion none before evidence optional exact-ID deletion only DELETE 204 + later metadata no longer available

7. Required evidence packet

Evidence Record
Workflow manifest exact action SHAs, path/name, retention, hidden-file policy and permissions
Attempt 1 identity run ID, attempt, event, ref/head SHA
Attempt 1 first failure download name requested and not-found/error log; producer artifact ID/digest retained
Repaired identity new run ID/attempt and exact source SHA
Artifact identity artifact ID, name, upload digest, URL if useful
Content integrity producer file SHA and consumer recomputed file SHA
Artifact metadata size, REST digest, created_at, expires_at, expired, workflow_run.id/head_sha
Runner/tool evidence ubuntu-24.04, action SHAs, optional gh --version for API inspection
Cleanup whether retained to expiry or exact-ID deletion was performed, with authorization/response
Limitations GitHub.com artifact backend; no cache, release, cloud, deployment or attestation generated in this checkpoint

8. Free/local faithful simulation

If Actions is unavailable, simulate the transport contract locally: create manifest.txt, compute SHA-256, copy it into an ID-named temporary directory, persist a JSON metadata record containing fake artifact ID/run/SHA/expiry, copy the file into a fresh consumer directory, and compare hashes. This proves the architecture of explicit identity + durable file transport, but it does not validate GitHub artifact service IDs, retention enforcement, action digest behavior or API permissions. Record those limitations.

9. Cleanup and rollback

Delete only disposable resources. If the artifact remains, let one-day retention expire or delete its exact ID after saving evidence. Delete the throwaway repository when finished. No external deployment, package, cloud resource, self-hosted runner or real credential exists, so there is no hidden infrastructure rollback.

10. What Chapter 13 adds to the operating model

You can now treat files as governed run outputs: the producer and source revision are recorded, the artifact has a service identity and digest, consumers select an exact record, retention/sensitivity are deliberate, and cleanup happens after evidence rather than instead of evidence. Chapter 14 turns to Dependency Caching, Cache Keys, Restore Strategies, and Performance—a deliberately different persistence mechanism optimized for speed rather than provenance.

Knowledge check

Why must Attempt 1 remain available after the repair?

Why does the repaired consumer use artifact-ids?

What two different digests/hashes are useful in the checkpoint?

Does optional cleanup prove the pipeline is correct?

What concept does Chapter 14 intentionally separate from this checkpoint?

Next chapter concept

Cache for performance without confusing it with evidence

Chapter 14 builds cache keys, restore strategies and performance measurement on top of the artifact/cache boundary established here.

Official references and version notes

Version and compatibility note

Version-sensitive behavior was rechecked on 2026-09-09. Mandatory examples target GitHub.com and ubuntu-24.04. actions/upload-artifact v7.0.1 and actions/download-artifact v8.0.1 are pinned by full commit SHA. Upload v7 excludes dot-prefixed hidden files by default, supports compression levels 0–9, returns artifact ID/URL/SHA-256 digest, and treats overwrite as delete-and-create rather than in-place mutation. Download v8 can select by artifact ID and defaults digest mismatch handling to error. Artifact/log retention is policy bounded: GitHub documents 1–90 days for public repositories and up to 400 days for private repositories when repository/organization/enterprise policy permits. Upload-artifact v4+ is not supported on GitHub Enterprise Server; GHES users must use an appliance-compatible artifact action/backend and must not copy GitHub.com majors blindly. Artifact attestations are introduced conceptually here but generated and verified comprehensively in Chapter 23.

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.