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.
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
-
Create a public disposable repository
gha-ch19-checkpoint. - Create environment
lab-production. - Enable a 1-minute wait timer.
- Set deployment branch/tag policy so only the disposable default branch may deploy.
-
Add environment secret
LAB_PROD_TOKENwith a synthetic value. - Add environment variable
LAB_SLOT=prod-sim-a. - 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=presentand 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-productiononly 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.
Knowledge check
Revision A is blocked by the environment ref rule. What is the smallest repair?
Dispatch the same approved workflow from an allowed ref/commit or deliberately change the environment policy through review. Do not widen token permissions or move the environment secret.
What should be true while the checkpoint deploy job waits on the timer?
The protected steps have not started, the environment secret is not available to them, and no bounded fake target state has been written.
Why preserve both Revision A and Revision B?
Together they prove the environment denied an unauthorized ref and later allowed the corrected authorized path without hiding the first failure.
A GitHub deployment status says success. Is the external target automatically proven healthy?
No. Deployment/status history is GitHub control-plane evidence. External target state/health must be verified separately.
What does Chapter 20 change about the credential model?
It introduces OIDC-based federation so a protected job can often obtain short-lived provider credentials instead of storing long-lived cloud secrets in GitHub.
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.
- GitHub Docs — Deployments and environments
- GitHub Docs — Managing environments for deployment
- GitHub Docs — Reviewing deployments
- GitHub Docs — Deploying with GitHub Actions
- GitHub Docs — Deploying to a specific environment
-
GitHub Docs — Workflow syntax: jobs.
.environment - GitHub Docs — REST API for deployment environments
- GitHub Docs — REST API for deployments
- GitHub Docs — REST API for deployment statuses
- GitHub Docs — Secrets reference
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.