Chapter 19Lesson 05~240 minutes

Checkpoint Lab — Environments, Required Reviewers, Protection Rules, and Deployment Gates

The checkpoint combines the chapter into one auditable operating model. You will create a free-compatible protected environment, predict its control-chain transitions, deliberately produce one environment-policy failure, repair only that layer, then preserve both GitHub deployment evidence and bounded fake target evidence before cleanup.

CheckpointProtected environmentEvidence packetFailure repairProduction model

Learning objectives

  • Build a staged fake deployment whose protected job cannot start before the environment gate passes.
  • Predict and independently verify credential availability, deployment records, concurrency, and target state.
  • Preserve one deliberately denied deployment attempt before repairing environment policy.
  • Assemble an evidence packet without exposing the synthetic secret value.
  • State the secure production invariant that bridges environments to OIDC federation in Chapter 20.

1. Checkpoint scenario

You maintain a disposable application that builds from the default branch and deploys to lab-production. The environment must wait one minute before the privileged job starts, accept only the disposable default branch, expose one synthetic environment secret and one non-secret environment variable, serialize deployments to that target, create GitHub deployment history, and write only a temporary fake target-state file.

The checkpoint contains two revisions. Revision A intentionally attempts deployment from a disallowed branch and must remain preserved as first-failure evidence. Revision B uses the allowed default branch and should pass the gate and deployment verification.

2. Current assumptions and limits

Assumption Checkpoint baseline
Verification date 2026-09-10
Repository Public disposable repository for free-compatible protection rules
Runner ubuntu-24.04
Checkout action actions/checkout v7.0.1 pinned to full SHA
Environment gate 1-minute wait timer + default-branch-only deployment policy
Credential Synthetic LAB_PROD_TOKEN; never printed
External target None; bounded RUNNER_TEMP state only
API Optional read-only inspection with version 2026-03-10

If your repository cannot use the described protection rule because of plan/visibility, use a public disposable repository. If a public disposable repository is impossible, perform the local gate-state simulation described later and clearly label it as a simulation—it is not evidence that GitHub enforced a reviewer/timer rule on your account.

3. Environment setup

  1. Create a public disposable repository gha-ch19-checkpoint.
  2. Create environment lab-production.
  3. Enable a 1-minute wait timer.
  4. Set deployment branch/tag policy so only the disposable default branch may deploy.
  5. Add environment secret LAB_PROD_TOKEN with a synthetic value.
  6. Add environment variable LAB_SLOT=prod-sim-a.
  7. Keep bypass disabled for the lab if the UI offers that control.

Optional human-review extension: add an eligible second reviewer and enable prevent-self-review. The checkpoint remains complete using the timer plus ref restriction.

4. Exact checkpoint workflow

# .github/workflows/ch19-checkpoint.yml
name: chapter19-checkpoint

on:
  workflow_dispatch:
    inputs:
      release_label:
        description: Synthetic release label
        required: true
        default: checkpoint-v1
        type: string

permissions: {}

jobs:
  build:
    runs-on: ubuntu-24.04
    permissions:
      contents: read
    outputs:
      source_sha: ${{ steps.meta.outputs.source_sha }}
      evidence_sha256: ${{ steps.meta.outputs.evidence_sha256 }}
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - id: meta
        shell: bash
        env:
          RELEASE_LABEL: ${{ inputs.release_label }}
        run: |
          set -euo pipefail
          printf 'release=%s\nsource=%s\nrun=%s\nattempt=%s\n' \
            "$RELEASE_LABEL" "$GITHUB_SHA" "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" > evidence.txt
          digest=$(sha256sum evidence.txt | awk '{print $1}')
          echo "source_sha=$GITHUB_SHA" >> "$GITHUB_OUTPUT"
          echo "evidence_sha256=$digest" >> "$GITHUB_OUTPUT"
          echo "evidence_sha256=$digest"

  deploy:
    needs: build
    runs-on: ubuntu-24.04
    permissions: {}
    environment:
      name: lab-production
      url: https://example.invalid/ch19/checkpoint/${{ github.run_id }}
    concurrency:
      group: deploy-${{ github.repository }}-lab-production
      cancel-in-progress: false
    env:
      LAB_PROD_TOKEN: ${{ secrets.LAB_PROD_TOKEN }}
      LAB_SLOT: ${{ vars.LAB_SLOT }}
      SOURCE_SHA: ${{ needs.build.outputs.source_sha }}
      EVIDENCE_SHA256: ${{ needs.build.outputs.evidence_sha256 }}
    steps:
      - name: Verify authorized context
        shell: bash
        run: |
          set -euo pipefail
          test -n "$LAB_PROD_TOKEN"
          test -n "$LAB_SLOT"
          test "$SOURCE_SHA" = "$GITHUB_SHA"
          echo "credential_state=present"
          echo "slot=$LAB_SLOT"
          echo "source_sha=$SOURCE_SHA"
          echo "evidence_sha256=$EVIDENCE_SHA256"
      - name: Apply bounded fake deployment
        shell: bash
        run: |
          set -euo pipefail
          state="$RUNNER_TEMP/ch19-prod-state.txt"
          printf 'slot=%s\nsource_sha=%s\nevidence_sha256=%s\nrun=%s\n' \
            "$LAB_SLOT" "$SOURCE_SHA" "$EVIDENCE_SHA256" "$GITHUB_RUN_ID" > "$state"
          cat "$state"
      - name: Verify bounded target
        shell: bash
        run: |
          set -euo pipefail
          grep -F "source_sha=$SOURCE_SHA" "$RUNNER_TEMP/ch19-prod-state.txt"
          grep -F "evidence_sha256=$EVIDENCE_SHA256" "$RUNNER_TEMP/ch19-prod-state.txt"

5. Required predictions before execution

Prediction Revision A Revision B
Build Runs and records SHA/digest Runs and records SHA/digest
Environment ref rule Denies disallowed branch Allows default branch
Gate Privileged deploy must not start Waits one minute, then starts
Secret Must not prove presence Proves presence only after gate
Deployment history Record may show denied/requested control state as GitHub exposes it; preserve UI evidence Successful lab-production deployment/status
Fake target state Must not be written Contains exact SHA/digest/run ID

Write the predictions before Revision A. Include what must not happen: a denied deployment must not start a privileged runner step or prove secret presence.

6. Revision A — preserve a real environment-policy denial

Create a temporary branch ch19-checkpoint-denied containing the workflow. Dispatch from that branch while lab-production allows only the default branch. Preserve:

  • run ID and attempt,
  • source ref and SHA,
  • build job conclusion,
  • deployment job waiting/blocked/failure evidence shown by GitHub,
  • environment name and allowed-ref rule,
  • absence of credential_state=present and absence of fake target state.

Do not alter the token, runner, or secret scope. The cause is the environment's allowed-ref policy doing its job.

7. Revision B — repair only the source/ref selection

Return to the default branch and dispatch the same workflow definition from an allowed commit. Preserve the new run ID/attempt/SHA separately from Revision A. The build should complete; the deployment job should wait for the one-minute gate; then it should start, prove only that the synthetic credential exists, write the bounded target state, and succeed.

Do not delete Revision A. A production evidence trail should show both the denied unauthorized attempt and the authorized success.

8. Serialization proof

Temporarily add sleep 45 before the bounded fake deployment, commit on the default branch, and start Run C. After its deploy job starts, dispatch Run D. Record that the second lab-production deployment waits because the concurrency group is occupied. Remove the delay in a later commit.

The expected policy is queue, not cancel. If cancellation is observed, inspect the exact cancel-in-progress expression and group value before doing anything else.

9. Verify GitHub deployment history

Use the repository deployment UI to record the successful lab-production deployment. For an optional read-only API check from your authenticated workstation:

gh api -H "X-GitHub-Api-Version: 2026-03-10" \
  'repos/OWNER/REPO/deployments?environment=lab-production&per_page=10' \
  --jq '.[] | {id,ref,sha,environment,created_at}'

For one returned deployment ID, inspect its statuses:

gh api -H "X-GitHub-Api-Version: 2026-03-10" \
  repos/OWNER/REPO/deployments/DEPLOYMENT_ID/statuses \
  --jq '.[] | {id,state,environment,environment_url,created_at}'

Do not infer the external target state from these API objects. They prove GitHub's deployment control/history layer.

10. Required evidence packet

Evidence Required contents
Workflow manifest path, commit SHA, checkout full SHA, permissions
Run A ID/attempt/ref/SHA, denied environment rule, first-failure screenshot/log
Run B ID/attempt/ref/SHA, wait/gate observation, job conclusions
Environment name/URL, wait timer, branch/tag policy, optional reviewer/self-review state
Credential/config LAB_PROD_TOKEN name + presence result only; LAB_SLOT value
Concurrency exact group and overlapping Run C/D observation
Deployment history deployment/status IDs or UI record, environment URL/status
Target evidence temporary file SHA/digest/run ID + explicit “no real external target” note
Limitations plan/visibility assumptions, no cloud/provider, simulation details if used

Redact nothing that is already non-sensitive, but never include the synthetic secret value just because it is harmless. The training habit is “prove presence and scope, not credential contents.”

11. Faithful local simulation if environment protection is unavailable

If you cannot use a public disposable repository, model the gate state explicitly with files named requested, approved, and deployed. The fake deployment script must refuse to create deployed until approved exists, then write the exact source SHA/digest. This can teach authorization ordering and target verification, but it does not prove GitHub withheld environment secrets or created deployment records. State that limitation in the evidence packet.

12. Cleanup and rollback

  • Remove temporary delay commits/branches only through normal Git history; do not rewrite the preserved run evidence.
  • Delete lab-production only after all waiting jobs finish or are intentionally cancelled and evidence is saved.
  • Delete the disposable repository when complete.
  • If you added a required reviewer, remove only the lab configuration.
  • No provider credential revocation is necessary because no real provider credential was used.

13. What Chapter 19 adds to the production operating model

A secure delivery system now distinguishes: exact build/source evidence; environment authorization; delayed release of environment credentials; target-specific serialization; GitHub deployment/status history; and independently verified external target state. These controls remain effective even when workflows are standardized through templates or reusable workflows because the environment is an independent governance boundary.

Next lesson

Chapter 20 — OpenID Connect, Cloud Federation, Trust Policies, and Secretless Deployment

Chapter 19 protects when deployment credentials may be used. Chapter 20 reduces the need to store long-lived cloud credentials at all by exchanging GitHub's OIDC identity for short-lived provider credentials under explicit trust policy.

Knowledge check

Revision A is blocked by the environment ref rule. What is the smallest repair?

What should be true while the checkpoint deploy job waits on the timer?

Why preserve both Revision A and Revision B?

A GitHub deployment status says success. Is the external target automatically proven healthy?

What does Chapter 20 change about the credential model?

Official references and version notes

Version-sensitive GitHub Actions behavior in this lesson was rechecked on 2026-09-10. Re-verify current plan, repository visibility, API, environment, and protection-rule behavior before production rollout.

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.