GitHub action it is the GitHub CI/CD solution available out of the box to run custom integration and deployment pipelines called workflows. It is configurable directly from the repository.
CI / CD stands for Continuous Integration and Continuous Deployment, it’s a set of practices usually automated to get the code tested, functional and available in a production environment.
You can create a workflow from an existing template from the Actions tab in your repository.
Or manually by creating a new yaml file in the .github/workflows
folder.
Now the documentation is very well furnished, but I have compiled some most used syntax and how-to in this article to have a quick reference.
Run a workflow …
There are multiple events that can cause a workflow to run, here is how to use some of them in your action.
On a regular basis
By regular basis, it’s like a cron job, and it works the same way:
on:
schedule:
- cron: '0 1 * * 5'
You can find the cron syntax below straight from wikipedia, or use cron expression generator to set it up.
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday;
# │ │ │ │ │ 7 is also Sunday on some systems)
# │ │ │ │ │
# │ │ │ │ │
# * * * * * <command to execute>
In our example the workflow would only run at 01:00 AM, only on Friday.
On a push
To trigger the pipeline each time you push to the repository:
on:
push:
branches: [master]
tags:
- 'v*'
Not mandatory, you can also trigger it the workflow only for certain branches (here master) or even for certain tags
(here a regex to match any tag starting with v
).
On a pull request
You can also have repository events as triggers, such as the pull request:
on:
pull_request:
There’s also a trigger for the issues. You can also fine-tune it, check the documentation for reference.
Manually
This one is to be able to go in the action tab and run the workflow manually. To set it up, you need to use the workflow dispatch event:
on:
workflow_dispatch:
You will see a new button Run workflow showing up in the action tab that will trigger the workflow_dispatch
event.
It’s not incompatible with other events, so you can have a workflow that runs on push and manually.
If you want to parametrise the button with some input, you can use the inputs
keyword:
on:
workflow_dispatch:
inputs:
logLevel:
description: 'Log level'
required: true
default: 'warning'
type: choice
options:
- info
- warning
- debug
tags:
description: 'Test scenario tags'
required: true
type: string
Use the ${{ inputs.logLevel }}
to access the value in your workflow.
After another workflow
Let’s say you have a CI build
workflow, and you wish to have another workflow to be executed after the end of this one.
You can use the workflow_run
event to trigger it.
name: CI notify
on:
workflow_run:
workflows: ["CI build"]
types:
- completed
This workflow needs to be named differently from the one it’s waiting for and will only run when the previous workflow succeeded.
jobs:
on-success:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- run: echo 'The triggering workflow passed'
You have access to the workflow’s conclusion, and can use it in your configuration.
Reuse a workflow …
Prevent duplication with the reusing capabilities of workflows with GitHub actions.
From a local file
You can use a workflow from a local file in your repository:
jobs:
call-workflow-2-in-local-repo:
uses: ./.github/workflows/workflow-2.yml
other-job:
runs-on: ubuntu-latest
steps:
- name: Run a script
run: echo "Hello world"
This will run the workflow-2.yml
file in the .github/workflows
folder.
Then you can have a job that runs after the completion of the workflow.
You can call multiple workflows referencing them in the jobs
section.
From a public repository
It’s also possible to use a workflow from a public repository (but organization can disable it). Any called workflow can have input parameters that can be passed to them:
jobs:
call-workflow-passing-data:
uses: octo-org/example-repo/.github/workflows/reusable-workflow.yml@main
with:
config-path: .github/labeler.yml
secrets:
envPAT: $
Here the reusable-workflow.yml
from the octo-org/example-repo
repository would be used (but it does not exist).
To pass secrets we use the secrets
keyword, and to pass parameters we use the with
keyword.
Run that workflow in …
In a specific folder
You can run a step in a specific folder by using the working-directory
option.
It can be configured for all steps using the default
settings which is at the root like the
jobs
keyword.
defaults:
run:
working-directory: src
Here the build step will use the src
folder as the root folder.
Or directly in the step if you need to execute an action in a different folder:
jobs:
build:
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: npm i
working-directory: src/frontend
The working-directory
is relative to the root of the repository and will override at the step level the one
configured in the defaults
.
In a container image
If you need to run your step in a container, like a docker image (you may want to set up the credential if it’s a private registry).
name: CI
on: [ push ]
jobs:
container-job:
runs-on: ubuntu-latest
container:
image: node:20
env:
NODE_ENV: development
ports:
- 80
volumes:
- my_docker_volume:/volume_mount
steps:
- name: Run in docker
run: echo "Hello from inside the container"
If you don’t need volumes, ports or environment variables, you can just limit yourself to the image name for the container’s configuration.
In a user defined matrix
You can also use a matrix to perform a job with multiple values, here an example with multiple ruby versions:
runs-on: ubuntu-latest
strategy:
matrix:
ruby-version: [ '2.7', '3.0', '3.1' ]
steps:
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
It is user defined, and you define more than one (it’s then called a multi-dimension matrix)
In spite of a failure
If you want the workflow to continue even if a step fails, you can add this to your step:
jobs:
steps:
- name: My first action
run: exit -1
continue-on-error: true
This can be useful when you want to run the following steps that may not be dependent on the failed one.