Hi, my fellow developers!
My last blog post showed how to use GitHub Actions to create simple Continuous Integration workflows for your projects, and today I’d like to share the advanced GitHub Actions features I find most exciting.
Here are my top 3 features you should not miss!
#1 Welcome to the matrix
No revolution here, but the matrix strategy can be explained as “Defined once, run multiple times.”Basically, it allows you to define a job structure to be run in multiple scenarios.
According to the official documentation: “A matrix allows you to create multiple jobs by performing variable substitution in a single job definition. For example, you can use a matrix to create jobs for more than one supported version of a programming language, operating system, or tool. A matrix reuses the job's configuration and creates a job for each matrix you configure.”
So when is the matrix strategy valuable? Of course, it’s hard to define all of the cases where a matrix is helpful, but here are scenarios based on my own experience:
- You want to run your job over several versions of a tool, e.g. Java 11 and 17, or different OS versions
- You want to inject variables to run jobs differently, e.g. change the path to your micro frontends
- You want to combine the two cases above for maximum flexibility
The Entando Standard Banking Demo workflow leverages the matrix strategy to ensure that micro frontend jobs use the same definition for all components. This only changes the final subfolder of a job, and that path value is defined on the fly via variable injection.
Please note, with the include
option we can specify several variable values of a job defined by the matrix strategy.
micro-frontends:
name: ${{ matrix.widget }} micro frontend
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
widget:
- alert-bar-icon-react
- dashboard-card-angular
- dashboard-card-config
- dashboard-card-react
- transaction-table
include:
- widget: dashboard-card-angular
test-script-name: test-ci
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2.1.4
with:
node-version: '14.15.0'
- name: Run tests
run: |
cd $GITHUB_WORKSPACE/$PROJECT_NAME/ui/widgets/banking-widgets/${{ matrix.widget }}
npm install
npm run ${{ matrix.test-script-name || 'test' }}
If you’re interested in other examples of how the matrix strategy can be used, I suggest you check out JHipster pipelines. In fact, JHipster offers many options combinations to generate apps and using the matrix feature addresses most of them.
#2 Compose your jobs using composite actions
My second favorite feature of GitHub Actions is composite actions. Yes, you heard it right. You can create reusable actions and compose them to create a job.
This feature is available as of summer 2021 and lets you not only use scripts or actions from the marketplace, as you could before but also reference actions defined in separate files.
Composite actions allow you to reduce duplication and improve reusability. You can create, update and maintain one action for several jobs and workflows in the same repository.
You can also share these actions across multiple repositories, but that means you need to checkout the project where the actions belong. To do that, you will need to set up a Personal Acces Token and inject it into your repository as an environment variable.
To summarize, using composite actions is great for streamlining your actions inside a repository, or across multiple repositories for more complex cases.
Once again, in the Standard Banking Demo I exploited composite actions to improve my pipelines. For instance, I have created one action to run each time I want to run my Spring Boot backend microservice tests.
name: 'Unit testing Microservice backend application'
description: 'Run unit tests for a backend Spring boot application'
inputs:
path:
description: the project path
required: true
runs:
using: 'composite'
steps:
- name: Run backend tests
shell: bash
run: |
cd ${{inputs.path}}
chmod +x mvnw
./mvnw -ntp clean test
Please note, we need to explicitly set the using
value to 'composite'
and specify the shell
we are using for each step (e.g. bash
).
In the following action, I use one input value (a parameter) to define the path to the project. I have to set it each time I use this action.
name: Banking Plugin CI
on: push
env:
PROJECT_NAME: banking-plugin
jobs:
backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: '11.x'
- uses: ./.github/actions/ms-unit-tests
with:
path: $GITHUB_WORKSPACE/$PROJECT_NAME
- uses: ./.github/actions/ms-build
with:
path: $GITHUB_WORKSPACE/$PROJECT_NAME
Please note, to use a composite action you need to give the path to the action file (e.g. uses: ./.github/actions/ms-unit-tests
), then pass parameters using the keyword with:.
#3 Link your jobs to each other
I believe linking jobs is important for creating a synergy between several jobs when you need to handle complex use cases.
The keyword needs
allows you to define a job need that must be completed before another can run. If backend2
needs backend1
, then backend2
will launch only when backend1
has finished successfully.
name: Banking Plugin CI
on: push
env:
PROJECT_NAME: banking-plugin
jobs:
backend1:
[...]
backend2:
needs: backend1
Or, you can decide to run the second job, even if the first one is failing.
name: Banking Plugin CI
on: push
env:
PROJECT_NAME: banking-plugin
jobs:
backend1:
[...]
backend2:
if: ${{ always() }}
needs: backend1
Pro Tip
The if parameter allows you to run a job only when a condition is met, e.g. always()
means that the job will always run (but still after the previous job has finished, successful or not). It is useful to have flexibility in your pipelines or fallback steps.
Below is an example of dependency between jobs from the project JHipster-lite. The matrix jobs need tests-windows
and tests-linux
to run, while codecov
needs all the matrix jobs.
Conclusion
In this blog post I’ve shared what I like the most about GitHub Actions in an advanced scenario. From my own experience and the different projects I’ve seen on GitHub, these three features were best suited to complete some of my use cases. However, your own experience and needs will determine what works best for you.
What are the best features you’ve used already in your GitHub Actions workflows? Feel free to share it with us on Twitter!
Top comments (0)