GitHub actions 101
GitHub action is a CI/CD tool integrated within GitHub repositories that can run different kinds of jobs (building, testing, deployment). Store workflow files in .github/workflows
inside the repository, which will be triggered based on specified conditions.
This post covers GitHub actions basics, from specifying the workflow name to configuring different jobs.
Name
Specify the name of the workflow with the name
field.
# .github/workflows/config.ymlname: CI/CD pipeline
Running
on
field specifies when the workflow should be running.
Automatically
The following configuration runs on every push to a specific branch.
# .github/workflows/config.ymlon:push:branches:- main
The following configuration runs on every push to every branch.
# .github/workflows/config.ymlon:push:branches:- '*'
Cron jobs
The following configuration runs at a specified interval (e.g., every hour).
# .github/workflows/config.ymlon:schedule:- cron: '0 * * * *'
Manual triggers
The following configuration enables manual triggering. Trigger it on the Actions tab by selecting the workflow and clicking the Run workflow button.
Use a manual trigger to upload apps to the Google Play console or update the GitHub profile Readme.
# .github/workflows/config.ymlon:workflow_dispatch:
Environment variables
Specify with the env
field. Set repository secrets in Settings → Secrets and variables → Actions page.
# .github/workflows/config.ymlenv:API_KEY: ${{ secrets.API_KEY }}
Jobs
Specify the job name with the name
field. Otherwise, the workflow will use the jobs item as the job name.
Every job should have a runs-on
field specified for the machine, which will be running on (e.g., ubuntu-latest
) or container
field with set Docker image (e.g., node:20.9.0-alpine3.17
)
Every job can have a separate working directory in case you have multiple subdirectories, and you want to run a different job in a different subdirectory so you can specify it within the defaults field
jobs:job-name:defaults:run:working-directory: directory-name
You can specify multiple tasks inside one job. Every task can have the following fields
name
- task nameuses
- GitHub action path from GitHub Marketplacewith
- parameters for the specified GitHub actionrun
- bash commandsenv
- environment variables
# .github/workflows/config.ymljobs:build:name: Custom build jobruns-on: ubuntu-latest# container: node:20.9.0-alpine3.17steps:- name: Checkoutuses: actions/checkout@v4- name: Configure Node.jsuses: actions/setup-node@v4with:node-version: 20- name: Install and buildrun: |npm cinpm run build
Use the needs
field for running jobs sequentially. It specifies a job that has to be finished before starting the next one. Otherwise, jobs will run in parallel.
# .github/workflows/config.ymljobs:build:# ...deploy:name: Custom deploy jobruns-on: ubuntu-latestneeds: buildsteps:- name: Deployrun: |npm run deploy
Every job can run multiple times with different versions using a matrix strategy (e.g., Node versions 18 and 20 or multiple OS versions inside an array of objects).
# .github/workflows/config.ymljobs:build:name: Custom build jobstrategy:matrix:node-version: [18, 20]os:[{ name: 'linux', image: 'ubuntu-latest' },{ name: 'windows', image: 'windows-latest' },{ name: 'macos', image: 'macos-latest' },]runs-on: ${{ matrix.os.image }}steps:- name: Checkoutuses: actions/checkout@v4- name: Configure Node.js ${{ matrix.node-version }}uses: actions/setup-node@v4with:node-version: ${{ matrix.node-version }}- name: Install and buildrun: |npm cinpm run build
Every job can provision databases for e2e tests with a services
field like Postgres in the following example.
# .github/workflows/config.ymljobs:build:name: Custom build jobruns-on: ubuntu-lateststrategy:matrix:database-name:- test-dbdatabase-user:- usernamedatabase-password:- passworddatabase-host:- postgresdatabase-port:- 5432services:postgres:image: postgres:latestenv:POSTGRES_DB: ${{ matrix.database-name }}POSTGRES_USER: ${{ matrix.database-user }}POSTGRES_PASSWORD: ${{ matrix.database-password }}ports:- ${{ matrix.database-port }}:${{ matrix.database-port }}# Set health checks to wait until postgres has startedoptions: --health-cmd pg_isready--health-interval 10s--health-timeout 5s--health-retries 5steps:# ...- run: npm run test:e2eenv:DATABASE_URL: postgres://${{ matrix.database-user }}:${{ matrix.database-password }}@${{ matrix.database-host }}:${{ matrix.database-port }}/${{ matrix.database-name }}
Passing artifacts between jobs can be done with uploading (actions/upload-artifact
) and downloading (actions/download-artifact
) actions.
# .github/workflows/config.ymljobs:build:runs-on: ubuntu-lateststeps:- name: Checkoutuses: actions/checkout@v4- name: Configure Node.jsuses: actions/setup-node@v4with:node-version: 20- name: Install and buildrun: |npm cinpm run build- name: Upload artifactuses: actions/upload-artifact@v3with:name: artifactpath: publicdeploy:needs: buildruns-on: ubuntu-lateststeps:- name: Checkoutuses: actions/checkout@v4- name: Download artifactuses: actions/download-artifact@v3with:name: artifact# ...
Running locally
You can use act to run GitHub actions locally.
Install it and run it with the following commands.
curl -s https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bashcp ./bin/act /usr/local/bin/actact