At Gatsby, we have a monorepo that contains all our internal packages. Our CI pipeline will lint, type check, test, and build our entire system. As we add more packages, CI times have taken longer and longer. I’m always on the lookout to shave off a few seconds or minutes of our CI time. One of our issues is that linting the entire monorepo can take about 2 - 2.5 minutes.
I read a post on hacker news about speeding up prettier in CI by using a cache with d-print. Knowing that eslint has a caching option, I wondered if I could do the same thing with our CI lint step.
Eslint has several flags related to caching:
--cache
: enables caching--cache-strategy
: choose a cache strategy from eithermetadata
(last edit time) orcontent
(checksum)--cache-location
: location to write the cache file
Our full command might look something like the following. Which we can then save in our package.json
.
{
...
"scripts": {
"lint": "eslint ./src/ --cache --cache-strategy content --cache-location ~/.cache/eslint/demo"
}
}
Here, we have to use the content
cache strategy.
Git doesn’t save last edit times (metadata
).
If we were to use metadata
strategy, when we checkout our repo in CI, we won’t get a valid cache hit.
Then we can configure the cache action in github actions:
name: CI
on: [push]
jobs:
Explore-GitHub-Actions:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Use Node.js 16.x
id: tests-workflow
uses: actions/setup-node@v2
with:
node-version: 16.14.0
cache: npm
- name: Cache
uses: actions/cache@v3
with:
path: |
~/.cache/eslint/
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json', '**/.eslintrc.js') }}
- name: Install
run: npm ci
- name: Lint
run: npm run lint
We must configure out actions cache path to be the same as our Eslint cache location.
For our cache key, I use the checksum of pacakge-lock.json
and .eslintrc.js
.
That way, the cache will bust if we make any changes to our Eslint config or update our dependencies.
The first run will still take the same amount of time as before since we have not yet built our cache. This run took about 11s.
Subsequent runs will be much faster. This run took about 1s.
You can find all the code used in this demo in this repository.
After implementing an eslint cache for our monorepo at Gatsby, our lint times went from about 2 minutes to 6 seconds with a cache hit.