GitHub Actions (S1E04) - Matrix, Timeout, Concurrency, Context

Jeason JosephJeason Joseph
4 min read

Matrix

A matrix strategy lets you use variables in a single job definition to automatically create multiple job runs that are based on the combinations of the variables.

jobs:
  example_matrix:
    strategy:
      matrix:
        version: [10, 12]
        os: [ubuntu-22.04, ubuntu-20.04]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.version }}

The above matrix will create four jobs in the following order:

  • {version: 10, os: ubuntu-22.04}

  • {version: 10, os: ubuntu-20.04}

  • {version: 12, os: ubuntu-22.04}

  • {version: 12, os: ubuntu-20.04}

Expanding and exclude the matrix

Use jobs.<job_id>.strategy.matrix.include to expand existing matrix configurations or to add new configurations. The value of include is a list of objects.

To remove specific configurations defined in the matrix, use jobs.<job_id>.strategy.matrix.exclude.

jobs:
  example_matrix:
    strategy:
      max-parallel: 2         # Run 2 jobs in parallel instead of all jobs running in parallel
      matrix:
        os: [ubuntu-22.04, ubuntu-20.04, windows-latest]
        version: [10, 12]
        include:
          - os: ubuntu-22.04   # inclue version 14 for ubuntu-22.04
            version: 14
          - os: ubuntu-latest
            version: 16        # inclue version 16 for ubuntu-latest
        exclude:
          - os: ubuntu-20.04
            version: 12         # skip version 12 for ubuntu-20.04
          - os: windows-latest  # Skip all versions for windows-latest

    runs-on: ${{ matrix.os }}
    steps:
      - run : echo "OS: ${{ matrix.os }} and version ${{ matrix.version }}"

The above workflow will result in

  • example_matrix (ubuntu-22.04, 10)

  • example_matrix (ubuntu-20.04, 10)

  • example_matrix (ubuntu-22.04, 12)

  • example_matrix (ubuntu-22.04, 14)

  • example_matrix (ubuntu-latest, 16)

Handling failures

jobs:
  test:
    continue-on-error: true # continue the job if failed
    strategy:
      fail-fast: false  # continue
      matrix:
        os: [ubuntu-22.04, ubuntu-20.04, windows-latest]
        version: [10, 12]
    runs-on: ${{ matrix.os }}
    steps:
      - run : echo "OS: ${{ matrix.os }} and version ${{ matrix.version }}"

fail-fast : Default is true. GitHub will cancel all in-progress and queued jobs in the matrix if any job in the matrix fails. Set at job level

continue-on-error : applies to a single job. If set to false and that job fails, other jobs in the matrix will continue running. Can set at job/step level

Skip workflow

You can skip workflow runs triggered by the push and pull_request events by including a command in your commit message.

Add the following strings to the commit message in a push, or the HEAD commit of a pull request:

  • [skip ci]

  • [ci skip]

  • [no ci]

  • [skip actions]

  • [actions skip]

Alternatively add a skip-checks trailer to your commit message preceded by two empty lines.

Concurrency

  • By default, multiple workflow runs, or jobs can run at the same time.

  • Control the concurrency of workflow & jobs, so that you can ensure that only one run or one job runs at a time in a specific context.

  • Ex. Multiple commits in a short time can trigger concurrent workflow runs but you may only need the latest workflow/job run to execute or each workflow run to wait for the previous ones to finish.

  • This can be defined at the workflow or job level.

      name:  Workflow-4-concurrency
      on:
        workflow_dispatch
      #Ensure only single instance of the workflow will run at a time.
      concurrency:      # At the workflow level
        group: prod-deployemnt
        cancel-in-progress: true
      jobs:
        build:
          runs-on: ubuntu-latest
       #Ensure only single instance of the job will run at a time.
          concurrency:   # At the job level
            group: prod-deployemnt
            cancel-in-progress: true
          steps: 
          - name: Build and test code
            run: echo "The code build and test is over"
    
  • If we run multiple workflows runs for the above workflow the latest workflow run will cause the already running one to fail.

  • When cancel-in-progress: false the next concurrent workflow run will go into waiting state and if another run is started the waiting workflow will get cancelled and the latest run will enter the waiting state.

  • There can be at most one running and one pending job in a concurrency group at any time.

Context

Contexts are a way to access information about workflow runs, variables, runner environments, jobs, and steps. Each context is an object that contains properties, which can be strings or other objects.

To print the contents of the context.

name: Context testing
on: push

jobs:
  dump_contexts_to_log:
    runs-on: ubuntu-latest
    steps:
      - name: Dump GitHub context
        env:
          GITHUB_CONTEXT: ${{ toJson(github) }}
        run: echo "$GITHUB_CONTEXT"
      - name: Dump job context
        env:
          JOB_CONTEXT: ${{ toJson(job) }}
        run: echo "$JOB_CONTEXT"

The below snippet shows the output generated.

{
  "token": "***",
  "job": "dump_contexts_to_log",
  "ref": "refs/heads/my_branch",
  "sha": "c27d339ee6075c1f744c5d4b200f7901aad2c369",
  "repository": "octocat/hello-world",
#Only partial data is shown

Context Usage - If

We can use the if statement to evaluate the context information with expressions.

name: CI
on: push
jobs:
  prod-check:
    if: ${{ github.ref == 'refs/heads/main' }}
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying to production server on branch $GITHUB_REF"
  • the if statement checks the github.ref context to determine the current branch name; if the name is refs/heads/main, then the subsequent steps are executed.

Timeouts

Set the timeout in minutes at job or step level after which process will be killed.

jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 5   #Ensure the job is killed after 5 min
    steps: 
    - name: Build and test code
      timeout-minutes: 3  #Ensure the step is killed after 3 mins
      run: echo "The code build and test is over"

References

0
Subscribe to my newsletter

Read articles from Jeason Joseph directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jeason Joseph
Jeason Joseph

I am an DevOps Engineer working since 2018. Had the opportunity to provide consulting services to many IT service companies. Passionate about learning new technology.