Thanks to the new GitHub Actions feature called "Reusable Workflows" you can now reference an existing workflow with a single line of configuration rather than copying and pasting from one workflow to another.
Basically GitHub Actions Templates on steroids!
What Are Reusable Workflows
So, Reusable Workflows in GitHub Actions. Thanks to this feature you can now reference an entire Actions workflow in another workflow, like if it were a single action.
This new feature builds on top of the Composite Actions introduced a while back. If you don't know what Composite Actions are, check this post or this video, but in short they are one or more steps packaged together which can be then referenced in an Actions workflows by a single line.
Reusable Workflows extend this concept, allowing you to reference an entire workflow in another one. If Composite Actions can be thought of as Templates, Reusable Workflows is on another new level.
Right, let's see how to create a reusable workflow.
Video
As usual, if you are a visual learner, or simply prefer to watch and listen instead of reading, here you have the video with the whole explanation and demo, which to be fair is much more complete than this post.
Link to the video: https://youtu.be/lRypYtmbKMs
If you rather prefer reading, well... let's just continue :)
Create a Reusable Workflow
Reusable workflows are normal Actions YAML files, and as such they have to reside in the .github/workflows
folder in the root of a repo.
The only particular thing they have to have is a special trigger:
on:
workflow_call:
The workflow file can also have different triggers, but to make it reusable one of those must be the workflow_call
.
You can also pass data to a reusable workflow, via the trigger parameters which can be of 2 types:
- inputs
- secrets
The inputs are used to pass normal data (aka not sensitive information):
inputs:
image_name:
required: true
type: string
tag:
type: string
In the example above, where I want to use a reusable workflow as template to build and push a Docker Image to a registry, we can see that we have 2 inputs of type string
, with one required and one not required.
Note: if a required input has not been passed to the reusable workflow, it will fail
Other available types are boolean
and number
.
The secrets, instead, as the name says, are used to pass secret values to the workflow:
secrets:
registry_username:
required: true
registry_password:
required: true
In this case you can see that there is no type
, every secret is treated as string.
Finally, you can use those parameters in your workflow by using {{inputs.NAME_OF_THE_INPUT}}
and {{secrets.NAME_OF_THE_SECRET}}
.
So, in the abovementioned example where I want to use a reusable workflow to build and push a Docker image to a registry, the reusable workflow will look something like this:
name: Create and Publish Docker Image
on:
workflow_call:
inputs:
image_name:
required: true
type: string
tag:
type: string
secrets:
registry_username:
required: true
registry_password:
required: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup BuildX
uses: docker/setup-buildx-action@v1
- name: Login to the Registry
uses: docker/login-action@v1
with:
username: ${{secrets.registry_username}}
password: ${{secrets.registry_password}}
- name: Set the tag
run: |
if [ -z "${{inputs.tag}}" ]
then
echo "final_tag=latest" >> $GITHUB_ENV
else
echo "final_tag=${{inputs.tag}}" >> $GITHUB_ENV
fi
- name: Build and Push the Image
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{secrets.registry_username}}/${{inputs.image_name}}:${{env.final_tag}}
do-something-else:
runs-on: ubuntu-latest
steps:
- run: echo "Hello"
Also note that reusable workflows can have multiple jobs, as you can see in the example (where the do-something-else
does nothing, but it is to show it off)
Easy right? One thing to keep in mind is that if the reusable workflow has other triggers apart from the workflow_call
you may want to make sure it doesn't accidentally run multiple times.
Now that we have our reusable workflow, let's see how to use it in another workflow. And stay with me until the end because I will talk about the limitations of reusable workflows and when they can be useful.
Using a Reusable Workflow
Now that we have our reusable workflow ready, it is time to use it in another workflow.
To do so, just add it directly in a job of your workflow with this syntax:
job_name:
uses: USER_OR_ORG_NAME/REPO_NAME/.github/workflows/REUSABLE_WORKFLOW_FILE.yml@TAG_OR_BRANCH
Let's analyse this:
- You create a job with no steps
- You don't add a
runs-on
clause, because it is contained in the reusable workflow - You reference it as
uses
passing:
- the name of the user or organization that owns the repo where the reusable workflow is stored
- the repo name
- the base folder
- the name of the reusable workflow yaml file
- and the tag or the branch where the file is store (if you haven't created a tag/version for it)
In my real example above, this is how I'd reference it in a job called docker:
docker:
uses: n3wt0n/ReusableWorkflow/.github/workflows/buildAndPublishDockerImage.yml@main
Now of course we have to pass the parameters. Let's start with the inputs:
with:
image_name: my-awesome-app
tag: $GITHUB_RUN_NUMBER
As you can see, we just use the with
clause, and we specify the name of the inputs.
Needless to say, the names have to be the same as the ones in the reusable workflow definition.
For the secrets, instead, we use a new secrets
section:
secrets:
registry_username: ${{secrets.REGISTRY_USERNAME}}
registry_password: ${{secrets.REGISTRY_PASSWORD}}
And this is it. So the complete example would look like this (you can find it here):
# This is a basic workflow to showcase the use of Reusable Workflows
name: Reusable Workflow user
on:
workflow_dispatch:
jobs:
do-it:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run a one-line script
run: echo Hello, world!
docker:
uses: n3wt0n/ReusableWorkflow/.github/workflows/buildAndPublishDockerImage.yml@main
with:
image_name: my-awesome-app
tag: $GITHUB_RUN_NUMBER
secrets:
registry_username: ${{secrets.REGISTRY_USERNAME}}
registry_password: ${{secrets.REGISTRY_PASSWORD}}
Once again, as you can see the caller workflow can have multiple jobs as well.
If we run the workflow, this is what we get:
You can see in the image that we have the logs for the do-it
job that is present in the caller, and then for both the jobs in the reusable workflow.
Since those 2 jobs are run within the docker
job in the caller workflow, they are referenced in the log as docker / build
and docker /do-something-else
.
But apart from that, the logs are complete:
We get the full details of everything that has happened.
Limitations and Caveats
So, let's start with a few notes. First, remember that the Reusable Workflows are currently in beta, so things might change by the time they go GA.
Second, for a workflow to be able to use it, a reusable workflow must be stored in the same repo as the call, or in a public repo, or yet in an internal repo with settings that allow it to be accessed.
Let's talk now about limitations. As direct result of what we have just said, reusable workflows stored in a private repository can be used only by other workflows in the same repo.
Also, Reusable workflows cannot call and consume other Reusable workflows.
Finally, and this is big one you need to remember, environment variables set at workflow level in the caller workflow are not passed to the reusable workflow. So if you need use any of those variables in the reusable workflow, you'll have to pass them to the workflow via the parameters as I've shown above.
Conclusions
Reusing workflows avoids duplication. This makes workflows easier to maintain and allows you to create new workflows more quickly by building on the work of others, just as you do with actions.
Workflow reuse also promotes best practices by helping you to use workflows that are well designed, have already been tested, and have been proved to be effective. Your organization can build up a library of reusable workflows that can be centrally maintained.
Let me know in the comment section below what you think about these new reusable workflows, if and how you plan to use them, and if there is any feature that you think is missing.
You may also want to watch this video where I talk about the Composite Actions as templates.
Like, share and follow me 🚀 for more content:
📽 YouTube
☕ Buy me a coffee
💖 Patreon
📧 Newsletter
🌐 CoderDave.io Website
👕 Merch
👦🏻 Facebook page
🐱💻 GitHub
👲🏻 Twitter
👴🏻 LinkedIn
🔉 Podcast
Top comments (1)
There are also other sometimes more suitable options for reusing code in GitHub Actions