How to cache node_modules in GitHub Actions with Yarn
Matt Pocock
Posted on June 22, 2020
The Problem
I run a small team working on a growing monorepo. Every commit, some CI checks run on the entire codebase, from GitHub actions. The checks were taking ~8 minutes to complete. We wanted them to run faster.
We use yarn workspaces to manage dependencies, so a single yarn install at root is enough to install the dependencies for all clients.
Trouble is, this yarn install was taking ~4.5 minutes on the CI. On my local machine, where the node modules have already been saved, this can take as little as 5 seconds. I wanted to speed up the CI.
The first thing I tried
GitHub actions recommends that you cache yarn’s cache. This means you end up with 2 steps that look like this:
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
The first step grabs the yarn cache directory path, and saves it. The second step looks for anything stored in the cache, and restores it.
This sped things up a little, but it didn’t reach the heights I was hoping for.
The Solution
Instead of caching the yarn cache, you should cache your node_modules.
- uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
This caches all of your node_modules folders throughout your repository, and busts the cache every time a yarn.lock file changes.
This works for our monorepo, and it should also work for single folder projects too.
This took our install step from ~4.5 minutes to ~30 seconds.
The Full Snippet
name: Automated Tests and Linting
on: [push]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
- name: Install packages
run: yarn install
- name: Autogenerate GraphQL
run: yarn codegen
- name: Run Typescript Checks
run: yarn lint
- name: Run Tests
run: yarn test:ci
Posted on June 22, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.