Create reusable workflows in GitHub Actions
Reusable workflows allow you to embrace the DRY (Don’t Repeat Yourself) principle and usher in standardization and consistently implement best practices. They smooth developer journeys, making processes more streamlined and efficient, without imposing undue restrictions. As we move along, we will be joined by Deutsche Vermögensberatung AG (DVAG) for their advice on getting the most out of reusable workflows.
In this guide, you will learn:
The uses and benefits of reusable workflows
The essential components and syntax required to create reusable workflows
The URL structure and syntax for invoking a reusable workflow
How to incorporate reusable workflows into projects housed in separate repositories
What are reusable workflows and how do they work?
A reusable workflow is a GitHub Actions file that can be executed by other workflows. A single workflow can call multiple reusable workflows, with each reference taking up just one line of YAML. This means that the “caller” workflow may contain just a few lines of YAML, but perform a large number of tasks, since it runs each “called” workflow entirely, as if its jobs and steps were part of its own. Additionally, workflows can be nested up to a maximum of four levels.
What are the benefits of reusable workflows?
Reusable workflows offer several benefits:
Avoid redundancy
Speed up workflow creation through reuse
Reduce maintenance and enhance security by offering a library of reusable workflows that can be centrally maintained
Promote the use of workflows that are well-designed, already tested, and proven effective
With these benefits in mind, and in combination with runner groups, organizations can create paved paths for developers that allow them to spend time focusing on the work at hand, rather than managing infrastructure or recreating existing workflows.
We centralize our workflows in a monorepo where all changes require code-owner approval to ensure consistency and quality over time, while also enabling specialized ownership for certain workflows. We use semantic versioning and avoid breaking changes by sticking to a 1.x.x version scheme. We generally release new workflow versions weekly, using auto-merge functionality to keep updates flowing and testing them on both the main branch and on test repos. We also actively monitor workflow use across repositories, so we can ensure they’re being used effectively.
Components of a reusable workflow
The triggering event
For a workflow to be reusable, the values for on must include workflow_call
:
on:
workflow_call:
Input definition
You can define inputs and secrets in a reusable workflow to receive them from a caller workflow. Use the inputs
and secrets
keywords for this purpose. For more information, see our guide on securing CI/CD with secrets and variables. Below is a commented version of an example reusable workflow:
on: # Specifies the event triggering the workflow
workflow_call: # Indicates that this is a reusable workflow
inputs: # Defines the inputs that can be passed from the caller workflow
config-path: # Name of the input
required: true # Specifies that this input is mandatory
type: string # Specifies the type of the input
secrets: # Defines the secrets that can be passed from the caller workflow
envPAT: # Name of the secret
required: true # Specifies that this secret is mandatory
In the example above, envPAT
is an environment secret that's been added to the production environment. This environment is therefore referenced within the job.
Passing named secrets
To pass named inputs to a called workflow, use the with
keyword in a job. Use the secrets
keyword to pass named secrets. For inputs, the data type of the input value must match the type specified in the called workflow (either boolean, number, or string).
jobs:
call-workflow-passing-data:
uses: octo-org/example-repo/.github/workflows/reusable-workflow.yml@main
with:
config-path: .github/labeler.yml
secrets:
envPAT: ${{ secrets.envPAT }}
Workflows that call reusable workflows in the same organization or enterprise can use the inherit
keyword to implicitly pass the secrets.
jobs:
call-workflow-passing-data:
uses: octo-org/example-repo/.github/workflows/reusable-workflow.yml@main
with:
config-path: .github/labeler.yml
secrets: inherit
We rely on Dependabot to automatically distribute updates of our reusable workflows across teams and projects. Each release automatically triggers a pull request in the user repositories, which we closely track in a dedicated issue. This lets us keep tabs on who has and hasn’t updated their workflows and gives us a streamlined way to ensure everyone is on the same page, using the most current and effective workflows.
Calling reusable Actions
You call a reusable workflow by using the uses
keyword. Unlike when you are using Actions within a workflow, you call reusable workflows directly within a job, and not from within job steps. Let's break down the URL structure for calling a reusable workflow. In the example given above, you’ll see the line:
uses: octo-org/example-repo/.github/workflows/reusable-workflow.yml@main
We can see that the code follows a specific structure:
uses: [OWNER/REPOSITORY PATH]/[.github/workflows/WORKFLOW_FILE]@[REF]
Each segment of this structure has a specific purpose:
1. uses
:
This is a keyword in the Actions syntax that indicates the following string will reference a reusable piece of code, such as an Action or, in this case, a reusable workflow.
2. OWNER
:
This specifies the GitHub user or organization that owns the repository where the reusable workflow is located (ex: octo-org).
3. REPOSITORY PATH
:
This is the path to the repository (ex: example-repo/
).
4. .github/workflows/
:
This is a conventional path where Actions expects to find workflow definitions in a repository. All workflow YAML files should be located within this directory for them to be recognized and executed by Actions.
5. WORKFLOW_FILE
:
This specifies the exact YAML file that contains the workflow definition. (ex: reusable-workflow.yml
).
6. @REF
:
This indicates the branch, tag, or commit SHA from which the workflow should be used. It's essential to specify this so that Actions knows which version of the workflow to use. This is particularly useful if workflows evolve over time, and you want to ensure a specific version is used in your own workflow (ex: @main
).
How to make a workflow reusable
Here’s an example from the build-test-deploy.yml
workflow we created in the Automation Essentials module. Below we updated the file to accommodate reusability. In addition to adding workflow_call
, we also defined parameters allowing us to set the node-version
. Pay attention to the comments outlined in the code:
name: build-test-deploy
# Changed 'on' to enable this workflow to be called from other workflows
on:
workflow_call:
# Introduced 'inputs' to define parameters that can be passed when calling this workflow
inputs:
node-version:
description: "Node version"
required: true
type: string
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: checkout repo
uses: actions/checkout@v3
# Modified to use the node-version from the workflow inputs
- name: use node.js
uses: actions/setup-node@v3
with:
node-version: ${{ inputs.node-version }}
- run: npm install
- run: npm run build
test:
needs: build
runs-on: ubuntu-latest
steps:
- name: checkout repo
uses: actions/checkout@v3
# Modified to use the node-version from the workflow inputs
- name: use node.js
uses: actions/setup-node@v3
with:
node-version: ${{ inputs.node-version }}
- run: npm install
- run: npm run test
Now that we have updated our build-test-deploy
workflow to accommodate reusability and we understand how to call a reusable workflow, let's put it all together. In the example code below, we call the build-test-deploy
workflow from a workflow located in a separate repository:
jobs:
my-job:
# Importing a reusable workflow from another repository and branch
uses: organization/repo/.github/workflows/build-test-deploy.yml@main
# Passing 'node-version' as an input parameter to the reusable workflow
with:
node-version: '18.x'
Up next: Manage and monitor workflows in GitHub Actions
Ready to optimize how you manage and monitor your Actions? Check out our in-depth guide. From analyzing workflow statuses to tracking resource usage, we'll arm you with the tools you need to operate more efficiently.