Chapter 18Lesson 02~210 minutes

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.

Hands-onCopy semanticsAnchorsReusable workflowEvidence

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.

Next lesson

Workflow Templates, Organization Standards, YAML Anchors, and Reuse Architecture: Configuration, Design Patterns, and Trade-Offs

Continue with the next lesson to build on the current concepts, evidence, security boundaries, and operational practices.

Knowledge check

After you copy the template into the consumer and edit the template source, what should happen to the consumer file?

Does *baseline_job create a dependency on the anchored job?

Why is $/ useful in the same-repository reusable-workflow example?

Why must a cross-repository uses SHA be pasted literally rather than stored in an environment variable?

Which evidence best proves template copy semantics?

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.

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.