Workflow Templates, Organization Standards, YAML Anchors, and Reuse Architecture: Guided Hands-On Workflow
This lab uses one tiny CI contract three ways so the differences are observable rather than theoretical. The mandatory path is free and disposable: one learner-owned repository plus a local directory that faithfully represents an organization template source. No organization administration, paid plan, cloud account, secret, package publication, or production deployment is required.
Learning objectives
- Build one small CI pattern as template copy, anchor reuse, and reusable workflow reference.
- Capture exact source/run evidence before and after each mechanism is changed.
-
Use the current same-repository
$/syntax without checkout where appropriate. - Demonstrate that a template source edit does not mutate an already copied workflow.
- Explain which rollout mechanism is required for each kind of standardization.
1. Preflight and disposable boundary
Create a throwaway repository named gha-ch18-lab. The
workflow examples use ubuntu-24.04,
permissions: {} or contents: read, and no
secrets. The template source is represented locally under
simulated-org-dot-github/; this avoids changing
organization-level repositories while preserving the copy semantics.
If you have an authorized disposable organization, an optional
extension can place the same files in that organization's
.github/workflow-templates/ directory.
gha-ch18-lab/
├── .github/
│ └── workflows/
│ ├── reusable-ci.yml
│ ├── template-copy.yml
│ ├── anchor-ci.yml
│ └── caller.yml
├── simulated-org-dot-github/
│ └── workflow-templates/
│ ├── golden-ci.yml
│ └── golden-ci.properties.json
└── ci/
└── check.sh
Before the first run, record git rev-parse HEAD after
each commit. Do not use a real organization standard repository or
modify company policy for this lab.
2. One synthetic CI behavior
#!/usr/bin/env bash
# ci/check.sh
set -euo pipefail
printf 'contract=chapter18-baseline
'
printf 'sha=%s
' "${GITHUB_SHA:-local}"
printf 'mode=%s
' "${1:-default}"
The script has no network calls and no external side effects. All three reuse mechanisms will ultimately invoke this same bounded behavior, making propagation differences easier to see.
3. Mechanism A — simulate workflow-template onboarding
# simulated-org-dot-github/workflow-templates/golden-ci.yml
name: golden-ci-template
on:
push:
branches: [ $default-branch ]
workflow_dispatch:
permissions: {}
jobs:
baseline:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: bash ci/check.sh template-copy
{
"name": "Chapter 18 Golden CI",
"description": "Disposable template for learning copy semantics.",
"iconName": "octicon workflow",
"categories": ["Continuous integration"]
}
For the local simulation, copy the YAML to
.github/workflows/template-copy.yml and replace
$default-branch with your actual default branch, just
as GitHub does at template creation time. Commit the consumer copy.
Record both file hashes.
cp simulated-org-dot-github/workflow-templates/golden-ci.yml .github/workflows/template-copy.yml
DEFAULT_BRANCH=$(git branch --show-current)
sed -i "s/\$default-branch/${DEFAULT_BRANCH}/g" .github/workflows/template-copy.yml
sha256sum simulated-org-dot-github/workflow-templates/golden-ci.yml .github/workflows/template-copy.yml
Now edit only the simulated template source—for example change the
displayed workflow name to golden-ci-template-v2.
Verify that template-copy.yml remains unchanged. That
is the evidence for copy-time ownership.
4. Mechanism B — same-file anchor reuse
# .github/workflows/anchor-ci.yml
name: chapter18-anchor-ci
on: workflow_dispatch
permissions: {}
jobs:
baseline: &baseline_job
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- run: bash ci/check.sh anchor-a
repeated: *baseline_job
Predict the job graph before running: two jobs, same job configuration, independent GitHub-hosted runners. The alias does not make one job depend on the other, share a workspace, or create a new package. After the run, record the run ID, attempt, source SHA, both job conclusions, and each runner's setup metadata.
Change the anchored job in this same file and commit. Both alias uses change because they are part of one workflow document. There is no external consumer rollout to manage.
5. Mechanism C — referenced reusable workflow
# .github/workflows/reusable-ci.yml
name: chapter18-reusable-contract
on:
workflow_call:
inputs:
profile:
required: true
type: string
outputs:
contract:
description: Contract identifier
value: ${{ jobs.ci.outputs.contract }}
permissions: {}
jobs:
ci:
runs-on: ubuntu-24.04
outputs:
contract: ${{ steps.result.outputs.contract }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- id: result
shell: bash
env:
PROFILE: ${{ inputs.profile }}
run: |
bash ci/check.sh "$PROFILE"
echo "contract=chapter18-reusable-v1" >> "$GITHUB_OUTPUT"
# .github/workflows/caller.yml
name: chapter18-reuse-caller
on: workflow_dispatch
permissions: {}
jobs:
central:
uses: $/.github/workflows/reusable-ci.yml
with:
profile: self-repository
verify:
needs: central
runs-on: ubuntu-24.04
steps:
- shell: bash
env:
CONTRACT: ${{ needs.central.outputs.contract }}
run: test "$CONTRACT" = "chapter18-reusable-v1"
On GitHub.com, $/ resolves to the workflow's own
repository at the exact running commit, with no checkout requirement
for the reference itself. It therefore tracks the caller revision
precisely. If your self-hosted runner is older than 2.336.0, this
syntax is not a safe assumption; update the runner or use the older
supported form.
6. Change the central source and predict propagation
| Change | Template copy | Anchor file | Reusable workflow |
|---|---|---|---|
| Edit simulated template source | Existing copied workflow unchanged | Not applicable | Not applicable |
| Edit anchor definition in consumer commit | Not applicable | Every alias in that workflow sees new node | Not applicable |
| Edit same-repo reusable workflow and commit | Copied template still unchanged | Anchors unaffected |
Caller using $/ resolves the new caller commit
|
| Edit cross-repo reusable after caller pinned SHA | Copied file unchanged | Anchors unaffected | Caller remains on pinned SHA until reference update |
Run the consumer after each relevant commit and record the run source SHA. A changed central file with no changed consumer behavior is not a failure; it may be exactly the versioning property you intended.
7. Optional real cross-repository pinning exercise
If you have two learner-owned disposable public repositories, place
reusable-ci.yml in the central repository, commit it,
and record CENTRAL_SHA=$(git rev-parse HEAD). Then
paste that literal 40-character SHA into the consumer:
jobs:
central:
uses: YOUR-USER/gha-ch18-central/.github/workflows/reusable-ci.yml@YOUR_40_CHARACTER_CENTRAL_SHA
with:
profile: cross-repo
GitHub Actions does not allow an expression in the
uses reference, so the learner must paste the exact
SHA. Do not substitute @main merely to avoid that step.
After the first successful run, make a central v2 commit without
changing the caller. The caller should still execute the pinned v1
revision.
8. Evidence packet
- Consumer repository, branch/ref, source SHA, run ID and attempt.
- Template-source path/hash and copied-workflow path/hash before and after source edit.
- Anchor name and two job IDs generated from the same workflow file.
- Reusable workflow repository/path/reference and resolved source SHA.
-
Evaluated permissions (
{}in this lab), runner OS/image metadata, and job conclusions. - One sentence for each mechanism answering: “what changes automatically when the central source changes?”
Cleanup is simply deleting the disposable repository and local simulation directory after preserving any screenshots/text evidence you want. No caches, artifacts, secrets, releases, deployments, or cloud resources are created by this lab.
9. Challenge: choose the correct layer
Your team wants every new repository to start with an approved CI trigger and also wants a centrally patched security scan implementation. Which mechanism should own each requirement? A strong answer uses a template for onboarding shape and a pinned reusable workflow/action for the central executable contract, with policy controlling what components are allowed.
10. Lesson summary
You now have direct evidence for the three update models: copied, same-file expanded, and referenced. The next lesson turns those observations into architecture choices for autonomy, versioning, auditability, rollback, and organization scale.
Knowledge check
After you copy the template into the consumer and edit the template source, what should happen to the consumer file?
Nothing automatically. The consumer file is repository-owned after onboarding; its hash should remain unchanged until a consumer commit changes it.
Does *baseline_job create a dependency on the
anchored job?
No. It repeats the YAML node. Job dependency still requires
needs; each generated job gets its own runner and
lifecycle.
Why is $/ useful in the same-repository
reusable-workflow example?
It resolves to the exact commit already running and does not require checkout just to resolve the reusable workflow, so internal composition stays revision-consistent.
Why must a cross-repository uses SHA be pasted
literally rather than stored in an environment variable?
The workflow reference is resolved as workflow syntax; expressions/variables are not accepted in that ref position. The explicit literal also makes the dependency visible to review and tooling.
Which evidence best proves template copy semantics?
A source-template change combined with an unchanged hash/commit for the already copied consumer workflow.
Official references and version notes
Platform assumptions in this lesson were rechecked on 2026-09-10. GitHub Actions changes continuously, so re-verify version-sensitive behavior before production rollout.
- GitHub Docs — Reusing workflow configurations
- GitHub Docs — Creating workflow templates for your organization
- GitHub Docs — Reuse workflows
- GitHub Docs — Managing GitHub Actions settings for a repository
- GitHub Changelog — YAML anchors and non-public workflow templates
- GitHub Changelog — self-repository $/ syntax
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.