GitHub Actions Matrix Builds, Caching and Artifacts (with Examples)

Matrices, caches and artifacts make pipelines broader and faster. In this lesson you will run tests across browsers and operating systems in parallel, cache dependencies correctly, and keep and share reports with artifacts.

Advertisement

๐Ÿ“– What You’ll Learn: matrix strategy and job combinations, include and exclude, fail-fast and max-parallel, caching with setup actions and actions/cache, cache keys and restore keys, uploading and downloading artifacts, cache vs artifacts, and a complete cross-browser pipeline.


๐Ÿงฎ Three Tools for Faster, Broader Pipelines

FeatureSolves
MatrixRun the same job for many combinations (browsers, OS, versions) in parallel
CachingReuse downloaded dependencies between runs to save minutes
ArtifactsKeep files produced by a job (reports, screenshots, builds) and share them between jobs

๐Ÿงฎ Matrix Builds

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false                 # keep other combinations running if one fails
      max-parallel: 4
      matrix:
        os: [ ubuntu-latest, windows-latest ]
        browser: [ chromium, firefox, webkit ]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: lts/*
      - run: npm ci
      - run: npx playwright install --with-deps ${{ matrix.browser }}
      - run: npx playwright test --project=${{ matrix.browser }}

This creates 6 jobs (2 operating systems ร— 3 browsers) that run in parallel. Each appears separately in the run, e.g. “test (windows-latest, firefox)”.

include and exclude

strategy:
  matrix:
    os: [ ubuntu-latest, windows-latest ]
    browser: [ chromium, firefox, webkit ]
    exclude:
      - os: windows-latest
        browser: webkit                # skip this combination
    include:
      - os: macos-latest
        browser: webkit                # add one extra combination
      - os: ubuntu-latest
        browser: chromium
        smoke: true                    # add a variable to an existing combination
KeyEffect
fail-fastDefault true cancels all jobs when one fails; set false for test suites so you see every failure
max-parallelLimit simultaneous jobs
excludeRemove combinations
includeAdd combinations or extra values

The matrix is also how you shard a large suite across machines: see Playwright in GitHub Actions.


โšก Caching Dependencies

The easy way: setup actions

Most language setup actions cache package downloads with one line:

- uses: actions/setup-node@v4
  with:
    node-version: lts/*
    cache: npm                 # also: yarn, pnpm

- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    cache: pip

The flexible way: actions/cache

- name: Cache Maven packages
  uses: actions/cache@v4
  with:
    path: ~/.m2/repository
    key: maven-${{ runner.os }}-${{ hashFiles('**/pom.xml') }}
    restore-keys: |
      maven-${{ runner.os }}-
FieldMeaning
pathFolder(s) to save and restore
keyExact cache name; including hashFiles(...) of the lock file creates a new cache when dependencies change
restore-keysFallback prefixes when there is no exact match

๐Ÿ“ Note: Caches are scoped to the branch and its base branch, are evicted when unused for about a week, and have a total size limit per repository. Never cache secrets.

๐Ÿ’ก Tip: For Playwright, caching browsers rarely saves time because install --with-deps still installs system packages. Caching npm downloads (cache: npm) is the reliable win.


๐Ÿ“ฆ Artifacts

Upload

- name: Upload Playwright report
  if: ${{ !cancelled() }}                 # also upload when tests fail
  uses: actions/upload-artifact@v4
  with:
    name: playwright-report-${{ matrix.browser }}   # unique name per matrix job
    path: playwright-report/
    retention-days: 14

โš ๏ธ Common mistake: With upload-artifact@v4, artifact names must be unique within a run. In a matrix, include the matrix values in the name, otherwise uploads fail.

Download in a later job

publish:
  needs: test
  runs-on: ubuntu-latest
  steps:
    - uses: actions/download-artifact@v4
      with:
        pattern: playwright-report-*
        path: all-reports
        merge-multiple: false
    - run: ls -R all-reports

Artifacts also appear at the bottom of the run’s summary page for download by humans.

CacheArtifact
PurposeSpeed up future runsKeep outputs of a run
Typical contentDependenciesReports, screenshots, builds, logs
Shared betweenRunsJobs in the same run, and people
LifetimeEvicted when unusedRetention days (configurable)

๐Ÿงช Putting It Together: Cross-Browser Test Pipeline

name: Cross-browser tests
on: [ pull_request ]

jobs:
  e2e:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        browser: [ chromium, firefox, webkit ]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: lts/*
          cache: npm
      - run: npm ci
      - run: npx playwright install --with-deps ${{ matrix.browser }}
      - run: npx playwright test --project=${{ matrix.browser }}
      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: report-${{ matrix.browser }}
          path: playwright-report/
          retention-days: 7

๐Ÿ“ Assignment

  1. Create a matrix job that prints os and browser for 2 ร— 3 combinations.
  2. Exclude one combination and include one extra; check the job list.
  3. Set fail-fast: false and make one combination fail on purpose.
  4. Add cache: npm to setup-node and compare the install time of the first and second run.
  5. Upload a file as an artifact with a matrix-specific name and download it in a later job.
  6. Bonus: build the cross-browser pipeline above for your Playwright project.

โ“ FAQ

๐Ÿค” What is a matrix in GitHub Actions?

A strategy that runs the same job for every combination of values you list, such as operating systems or browsers, in parallel.

๐Ÿค” How do I cache npm dependencies in GitHub Actions?

Use actions/setup-node with cache: npm, or actions/cache with a key based on hashFiles('**/package-lock.json').

๐Ÿค” What is the difference between cache and artifacts?

Caches speed up future runs by reusing dependencies. Artifacts store outputs of a run, such as reports, for download or for later jobs.

๐Ÿค” Why does upload-artifact fail in my matrix?

Artifact names must be unique per run in v4; include matrix values in the name.

๐Ÿค” How do I upload test reports even when tests fail?

Add if: ${{ !cancelled() }} (or always()) to the upload step.


๐ŸŽฏ Next Steps

๐ŸŽฌ Learn Git and GitHub step by step: watch the series on YouTube!

Subscribe to SqaToolsByDY • Practice on sqatools.in

1 thought on “GitHub Actions Matrix Builds, Caching and Artifacts (with Examples)”

Leave a Comment