GitHub Actions Tutorial — Reusable Workflows Exercises
Practice Exercises — Reusable Workflows
Test your understanding of workflow_call, inputs, outputs, secrets, composition patterns, and org shared workflows with these hands-on exercises.
Exercise 1 — Basic Reusable Workflow
Task: Create:
- A reusable workflow
build.ymlthat takes anode-versionstring input (default'20'), checks out code, installs dependencies, and runsnpm run build - A caller workflow
ci.ymlthat triggers on push tomainand callsbuild.ymlwith Node 20
Show Solution
build.yml:
name: Build
on:
workflow_call:
inputs:
node-version:
type: string
default: '20'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run build
ci.yml:
name: CI
on:
push:
branches: [main]
jobs:
build:
uses: ./.github/workflows/build.yml
with:
node-version: '20'
Exercise 2 — Outputs from Reusable Workflow
Task: Create a reusable workflow version.yml that:
- Generates a version string in format
1.0.<run_number> - Returns it as an output named
version
Then create a caller that prints the version.
Show Solution
version.yml:
name: Generate Version
on:
workflow_call:
outputs:
version:
description: 'Generated version'
value: ${{ jobs.generate.outputs.version }}
jobs:
generate:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.ver.outputs.version }}
steps:
- name: Generate version
id: ver
run: echo "version=1.0.${{ github.run_number }}" >> "$GITHUB_OUTPUT"
caller.yml:
name: Release
on:
push:
branches: [main]
jobs:
version:
uses: ./.github/workflows/version.yml
release:
needs: version
runs-on: ubuntu-latest
steps:
- run: echo "Releasing version ${{ needs.version.outputs.version }}"
The output chain: step → job output → workflow output → caller needs.
Exercise 3 — Secrets Handling
Task: Create a reusable deployment workflow that:
- Accepts inputs:
environment(string) andimage-tag(string) - Requires secrets:
DEPLOY_TOKEN - Runs a deployment step using the secret
Write two caller configurations: one passing the secret explicitly, one using secrets: inherit.
Show Solution
deploy.yml (reusable):
name: Deploy
on:
workflow_call:
inputs:
environment:
type: string
required: true
image-tag:
type: string
required: true
secrets:
DEPLOY_TOKEN:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- run: |
echo "Deploying ${{ inputs.image-tag }} to ${{ inputs.environment }}"
echo "Using token: ***"
env:
TOKEN: ${{ secrets.DEPLOY_TOKEN }}
Explicit secrets:
jobs:
deploy:
uses: ./.github/workflows/deploy.yml
with:
environment: staging
image-tag: 'v2.1.0'
secrets:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
Inherit all secrets:
jobs:
deploy:
uses: ./.github/workflows/deploy.yml
with:
environment: staging
image-tag: 'v2.1.0'
secrets: inherit
Exercise 4 — Linear Pipeline Composition
Task: Create a CI/CD pipeline with four reusable workflows called in sequence:
lint.yml— runs lintingtest.yml— runs tests (takesnode-versioninput)build.yml— builds the projectdeploy.yml— deploys (takesenvironmentinput)
Write the caller workflow that chains them: lint → test → build → deploy (to production).
Show Solution
name: CI/CD Pipeline
on:
push:
branches: [main]
jobs:
lint:
uses: ./.github/workflows/lint.yml
test:
needs: lint
uses: ./.github/workflows/test.yml
with:
node-version: '20'
build:
needs: test
uses: ./.github/workflows/build.yml
secrets: inherit
deploy:
needs: build
uses: ./.github/workflows/deploy.yml
with:
environment: production
secrets: inherit
Each job waits for the previous one via needs:, creating a sequential pipeline.
Exercise 5 — Cross-Repo Shared Workflow
Task: Write a caller workflow that:
- Uses a reusable workflow from
my-org/shared-workflowsrepository - References the
ci-node.ymlworkflow at tagv2 - Passes
node-version: '20'as input - Inherits secrets
Show Solution
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
ci:
uses: my-org/shared-workflows/.github/workflows/ci-node.yml@v2
with:
node-version: '20'
secrets: inherit
Key points:
my-org/shared-workflows— the organization’s central workflow repository@v2— pinned to major version tag for stabilitysecrets: inherit— passes all caller secrets to the reusable workflow- The shared workflow repo must grant access (Settings → Actions → General)
← Back to: Chapter 10: Reusable Workflows
Next Chapter: Chapter 11: Custom Actions →