- GitHub
- GitHub Actions
When you implement continuous integration and continuous delivery in your projects, if you are using the same technology, you probably noticed that the workflows are similar or identical, and as you know duplicate is not a good thing at all!
Why are we trying to mutualize workflows?
So the question is, how to reuse your workflows and be more efficient between your projects instead of duplicate your workflows?
Let’s suppose you have a workflow to build a dotnet project called DemoReusableTemplates. This project needs a specific version of dotnet to build. So the workflow will looks like this:
name: Main
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build_dotnet:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v2
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
- run: dotnet restore src/DemoReusableTemplates.csproj
- run: dotnet build src/DemoReusableTemplates.csproj
This kind of workflow can be easily reused between your dotnet projects, if you can pass these inputs as parameters:
dotnet version to useThis is where the composite actions come in!
Let’s create a reusable action using the composite actions to build our dotnet application.
In a new GitHub repository let’s create a folder called dotnet-build and let’s add in it a file called action.yml. The name of the file must be action.yml or action.yaml. If your file does not respect this convention, your GitHub Action will not work.
name: "Build dotnet"
description: "Build dotnet..."
inputs:
dotnet-version:
description: 'The dotnet version to use'
default: '2.1.x'
required: false
project-path:
description: 'The path to the project'
required: true
runs:
using: "composite"
steps:
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ inputs.dotnet-version }}
- run: dotnet restore ${{ inputs.project-path }}
shell: bash
- run: dotnet build ${{ inputs.project-path }}
shell: bash
As you can see above we have the project path and the dotnet version defined as input parameters. We pass them using $ and $. Then we use the composite keyword to be able to reuse this action and we add the list of all previous commands afterwards.
Because it’s a composite action we don’t specify a
runnerso we need to add the shell parameter to eachrunaction. If you don’t do it you will have this kind of error message: Required property is missing: shell.
As you have probably already noticed, the dotnet-version has a default value set to 2.1.x so it’s not required.
To reference a composite in your different workflows and projects, it must be save in a specific repository. This common repository will contain different composites with different versions of it:

You can have one project referencing the composite action in the version v0.0.1 and another referencing the version v0.0.2.
With that in mind, let’s commit and push this composite action in a dedicated repository. Then add a tag for instance: v0.0.1.
The idea now is to update our first workflow with our new action:
name: Main
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build_dotnet:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v2
- name: Build dotnet
uses: damienaicheh/demo-action-composite/dotnet-build@v0.0.1
with:
dotnet-version: 5.0.x
project-path: src/DemoReusableTemplates.csproj
As you can see above we reference the dotnet-build action using the name of the repository with the entire path to the folder that contain the action.yml. Also, to specify the version of this composite action we want to use, we add the number of the tag at the end. Here it’s @v0.0.1.
As you saw it’s really easy to reuse your workflows between your projects and avoid duplication. Feel free to create your own repositories of composite action! You will find the two GitHub repository for this article available here:
Happy coding!