GitHub Actions (S2E02) - Reusable workflow

Avoid duplication when creating a workflow by reusing existing workflows. Create a common workflow and refer it in another workflow to reduce duplication.
The workflow referencing the reusable workflow is known as the ‘caller’ workflow
The reusable workflow being referenced is known as the ‘called’ workflow.
gitops-repo/
├── .github/
│ ├── workflows/
│ │ ├── main-workflow.yml # caller workflow
│ │ ├── reusable-workflow.yml # called workflow
├── src/
│ └── main_code.py
├── tests/
│ └── test_code.py
├── requirements.txt
└── README.md
Calling the reusable workflow
Caller workflow
Call a reusable workflow by using the uses
keyword at the job level
jobs:
workflow-in-local-repo: #for reusable workflows in the same repository
uses: ./.github/workflows/reusable-workflow.yml
workflow-in-another-repo: #for reusable workflows in public/private repositories
uses: acme-org/another-repo/.github/workflows/reusable-workflow.yml@v1
Called workflow
For a workflow to be reusable, the values for on
must include workflow_call
:
on:
workflow_call:
jobs:
build:
runs-on: ubuntu-latest # Will run on an Ubuntu VM
steps:
- name: Welcome
run: echo "Called workflow"
Passing secrets
Caller workflow
Use the secrets
keyword to pass named secrets.
jobs:
call-workflow-passing-data:
uses: acme-org/g-repo/.github/workflows/reusable-workflow.yml@172239021f7ba04fe7327647b213799853a9eb89
secrets:
pat_token: ${{ secrets.token }}
Called workflow
Use the secrets
keywords to define secrets that will be passed from a caller workflow.
Reference the secret that you defined in the on
key in the previous step.
on:
workflow_call:
secrets:
pat_token:
required: true
jobs:
build:
runs-on: ubuntu-latest # Will run on an Ubuntu VM
steps:
- uses: actions/labeler@v4
with:
repo-token: ${{ secrets.pat_token }}
Passing inputs
Caller workflow
To pass named inputs to a called workflow, use the with
keyword in a job
jobs:
call-workflow-passing-data:
uses: octo-org/example-repo/.github/workflows/reusable-workflow.yml@main
with:
config-path: .github/labeler.yml
Called workflow
Use the inputs
keywords to define inputs or secrets that will be passed from a caller workflow.
Reference the input that you defined in the on
key in the previous step.
For inputs, the data type of the input value must match the type specified in the called workflow (either boolean, number, or string).
on:
workflow_call:
inputs:
config-path:
required: true
type: string
jobs:
build:
runs-on: ubuntu-latest # Will run on an Ubuntu VM
steps:
- uses: actions/labeler@v4
with:
configuration-path: ${{ inputs.config-path }}
Nesting reusable workflows
You can connect a maximum of four levels of workflows - that is, the top-level caller workflow and up to three levels of reusable workflows. For example: caller-workflow.yml → called-workflow-1.yml → called-workflow-2.yml → called-workflow-3.yml. Loops in the workflow tree are not permitted.
Passing outputs
Called workflow
A reusable workflow may generate data that you want to use in the caller workflow. To use these outputs, you must specify them as the outputs of the reusable workflow.
name: Reusable workflow
on:
workflow_call:
# Map the workflow outputs to job outputs
outputs:
firstword:
description: "The first output string"
value: ${{ jobs.example_job.outputs.output1 }}
secondword:
description: "The second output string"
value: ${{ jobs.example_job.outputs.output2 }}
jobs:
example_job:
name: Generate output
runs-on: ubuntu-latest
# Map the job outputs to step outputs
outputs:
output1: ${{ steps.step1.outputs.firstword }}
output2: ${{ steps.step2.outputs.secondword }}
steps:
- id: step1
run: echo "firstword=hello" >> $GITHUB_OUTPUT
- id: step2
run: echo "secondword=world" >> $GITHUB_OUTPUT
Caller workflow
Reference the outputs using the names defined at the workflow level in the reusable workflow: firstword
and secondword
. In this workflow, job1
calls the reusable workflow and job2
prints the outputs from the reusable workflow ("hello world") to standard output in the workflow log.
jobs:
job1:
uses: octo-org/example-repo/.github/workflows/called-workflow.yml@v1
job2:
runs-on: ubuntu-latest
needs: job1
steps:
- run: echo ${{ needs.job1.outputs.firstword }} ${{ needs.job1.outputs.secondword }}
Complete workflow
Caller workflow
name: Call a reusable workflow and use its outputs, pass secrets & inputs
on:
push:
jobs:
job1:
uses: octo-org/example-repo/.github/workflows/called-workflow.yml@v1
with:
config-path: .github/labeler.yml
secrets:
pat_token: ${{ secrets.token }}
job2:
runs-on: ubuntu-latest
needs: job1
steps:
- run: echo ${{ needs.job1.outputs.firstword }} ${{ needs.job1.outputs.secondword }}
Called workflow
name: Call a reusable workflow and use its outputs
on:
workflow_dispatch:
inputs:
config-path:
required: true
type: string
secrets:
pat_token:
required: true
jobs:
build:
runs-on: ubuntu-latest # Will run on an Ubuntu VM
outputs:
output1: ${{ steps.step1.outputs.firstword }}
output2: ${{ steps.step2.outputs.secondword }}
steps:
- uses: actions/labeler@v4
with:
repo-token: ${{ secrets.pat_token }}
configuration-path: ${{ inputs.config-path }}
- id: step1
run: echo "firstword=hello" >> $GITHUB_OUTPUT
- id: step2
run: echo "secondword=world" >> $GITHUB_OUTPUT
Subscribe to my newsletter
Read articles from Jeason Joseph directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Jeason Joseph
Jeason Joseph
I am an DevOps Engineer working since 2018. Had the opportunity to provide consulting services to many IT service companies. Passionate about learning new technology.