Skip to content

Commit fe2dc4d

Browse files
authored
feat: enhance caching options in Node setup action (#188)
Adds flexible caching strategies to the centralized workflows, allowing teams to optimize CI performance based on their project structure. Tested on Chief with **49% faster CI times** (4m 17s → 2m 10s). ## 🚀 What's New ### New Workflow Inputs Two new optional inputs for `frontend-pr-workflow.yml`: ```yaml cache-mode: 'full' | 'node_modules-only' | 'yarn-cache-only' # Default: 'full' (backwards compatible) disable-restore-keys: true | false # Default: false (backwards compatible) ``` ### Cache Modes Explained | Mode | What's Cached | Best For | Cache Size | Install on Hit | |------|---------------|----------|------------|----------------| | **`full`** (default) | `node_modules` + `~/.cache/yarn` | Workspace/monorepos, general use | Large | Sometimes* | | **`node_modules-only`** | `node_modules` only | Large (>1GB) non-workspace repos | Medium | Rarely | | **`yarn-cache-only`** | `~/.cache/yarn` only | Frequent dependency changes | Small | Always | \* Workspace repos always run install to recreate symlinks ## 📊 Performance Results (Chief) **Before (full mode):** - Cache size: ~1.25 GB - CI time: 4m 17s **After (node_modules-only mode):** - Cache size: ~440 MB (65% smaller) - CI time: 2m 10s (49% faster) - Setup time: 2m 17s → 1m 13s (47% faster) ## 📖 How to Use ### For Workspace/Monorepo Projects ```yaml uses: Typeform/.github/.github/workflows/frontend-pr-workflow.yml@v1 with: app-name: 'my-monorepo' # cache-mode: 'full' # Default, no need to specify ``` ### For Large Non-Workspace Projects (like Chief) ```yaml uses: Typeform/.github/.github/workflows/frontend-pr-workflow.yml@v1 with: app-name: 'my-app' cache-mode: 'node_modules-only' # Faster restore, smaller cache ``` ### For Projects with Frequent Dependency Changes ```yaml uses: Typeform/.github/.github/workflows/frontend-pr-workflow.yml@v1 with: app-name: 'my-app' cache-mode: 'yarn-cache-only' # Smallest cache, fast restore ``` ## 🔍 How to Choose **Quick decision tree:** 1. **Workspace/monorepo?** (has `"workspaces"` in package.json) → Use `'full'` (default) 2. **Large cache (>1GB)?** → Use `'node_modules-only'` 3. **Frequent dependency changes?** → Use `'yarn-cache-only'` 4. **Not sure?** → Use `'full'` (default) See the [Cache Strategy Guide](shared-actions/setup-node-with-cache/CACHE-STRATEGY-GUIDE.md) for detailed recommendations.
1 parent 70d4142 commit fe2dc4d

4 files changed

Lines changed: 364 additions & 8 deletions

File tree

.github/workflows/frontend-pr-workflow.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,27 @@ on:
5050
type: boolean
5151
default: false
5252

53+
# Cache configuration
54+
# Choose the right strategy for your project:
55+
# - 'full' (default): Cache node_modules + ~/.cache/yarn
56+
# → Best for: Workspace/monorepos, repos with "workspaces" in package.json
57+
# → Why: Workspace installs benefit from yarn cache even when node_modules is cached
58+
# - 'node_modules-only': Cache only node_modules
59+
# → Best for: Large non-workspace repos (>1GB cache), simple single-package projects
60+
# → Why: Faster cache restore, acceptable slower install on miss
61+
# → Example: Chief, standalone apps
62+
# - 'yarn-cache-only': Cache only ~/.cache/yarn
63+
# → Best for: Repos with frequent dependency changes, small projects
64+
# → Why: Smallest cache, fast restore, yarn install runs every time but is fast
65+
cache-mode:
66+
description: 'Cache strategy: full, node_modules-only, or yarn-cache-only'
67+
type: string
68+
default: 'full'
69+
disable-restore-keys:
70+
description: 'Disable restore-keys to avoid restoring stale caches (forces exact key match)'
71+
type: boolean
72+
default: false
73+
5374
# Runner configuration
5475
runner:
5576
description: 'Runner for build/deploy jobs'
@@ -285,6 +306,8 @@ jobs:
285306
with:
286307
node-version: ${{ inputs.node-version }}
287308
use-asdf: ${{ inputs.use-asdf }}
309+
cache-mode: ${{ inputs.cache-mode }}
310+
disable-restore-keys: ${{ inputs.disable-restore-keys }}
288311
GH_TOKEN: ${{ secrets.GH_TOKEN }}
289312

290313
- name: Setup Jarvis
@@ -344,6 +367,8 @@ jobs:
344367
with:
345368
node-version: ${{ inputs.node-version }}
346369
use-asdf: ${{ inputs.use-asdf }}
370+
cache-mode: ${{ inputs.cache-mode }}
371+
disable-restore-keys: ${{ inputs.disable-restore-keys }}
347372
GH_TOKEN: ${{ secrets.GH_TOKEN }}
348373

349374
- name: Setup Jarvis
@@ -388,6 +413,8 @@ jobs:
388413
with:
389414
node-version: ${{ inputs.node-version }}
390415
use-asdf: ${{ inputs.use-asdf }}
416+
cache-mode: ${{ inputs.cache-mode }}
417+
disable-restore-keys: ${{ inputs.disable-restore-keys }}
391418
GH_TOKEN: ${{ secrets.GH_TOKEN }}
392419

393420
- name: Setup Jarvis
@@ -445,6 +472,8 @@ jobs:
445472
with:
446473
node-version: ${{ inputs.node-version }}
447474
use-asdf: ${{ inputs.use-asdf }}
475+
cache-mode: ${{ inputs.cache-mode }}
476+
disable-restore-keys: ${{ inputs.disable-restore-keys }}
448477
GH_TOKEN: ${{ secrets.GH_TOKEN }}
449478

450479
- name: Download Build Artifacts
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
# Cache Strategy Guide
2+
3+
This guide helps you choose the right `cache-mode` for your project.
4+
5+
## Quick Decision Tree
6+
7+
```
8+
Is your project a workspace/monorepo?
9+
(Check for "workspaces" field in package.json)
10+
11+
├─ YES → Use 'full' (default)
12+
│ ✅ Workspace installs benefit from yarn cache
13+
│ ✅ Already runs yarn install for symlinks anyway
14+
15+
└─ NO → Is your cache large (>1GB)?
16+
17+
├─ YES → Use 'node_modules-only'
18+
│ ✅ Faster cache restore
19+
│ ✅ Acceptable slower install on miss
20+
│ 📦 Example: Chief
21+
22+
└─ NO → Do you change dependencies frequently?
23+
24+
├─ YES → Use 'yarn-cache-only'
25+
│ ✅ Smallest cache
26+
│ ✅ Fast restore
27+
│ ⚠️ Runs install every time (but fast)
28+
29+
└─ NO → Use 'full' (default)
30+
✅ Best overall performance
31+
```
32+
33+
## Cache Mode Comparison
34+
35+
| Mode | What's Cached | Cache Size | Restore Time | Install on Hit | Install on Miss | Best For |
36+
|------|---------------|------------|--------------|----------------|-----------------|----------|
37+
| **full** (default) | `node_modules`<br>`~/.cache/yarn` | Large | Slow | Sometimes* | Fast | Workspaces, general use |
38+
| **node_modules-only** | `node_modules` only | Medium | Fast | Rarely | Slow | Large non-workspace repos |
39+
| **yarn-cache-only** | `~/.cache/yarn` only | Small | Very Fast | Always | Fast | Frequent dep changes |
40+
41+
\* Workspaces always run install to recreate symlinks
42+
43+
## Detailed Recommendations
44+
45+
### 1. Workspace/Monorepo Projects
46+
47+
**Use: `full` (default)**
48+
49+
```yaml
50+
uses: Typeform/.github/.github/workflows/frontend-pr-workflow.yml@v1
51+
with:
52+
app-name: 'my-monorepo'
53+
# cache-mode: 'full' # Default, no need to specify
54+
```
55+
56+
**Why:**
57+
- Workspace repos have `"workspaces"` in root `package.json`
58+
- Multiple `node_modules/` directories (root + packages)
59+
- `yarn install` runs anyway to create inter-package symlinks
60+
- `~/.cache/yarn` makes that install faster
61+
62+
**Examples:**
63+
- Monorepos with `packages/*` or `apps/*` structure
64+
- Projects using Lerna, Turborepo, Nx with Yarn workspaces
65+
66+
---
67+
68+
### 2. Large Non-Workspace Projects
69+
70+
**Use: `node_modules-only`**
71+
72+
```yaml
73+
uses: Typeform/.github/.github/workflows/frontend-pr-workflow.yml@v1
74+
with:
75+
app-name: 'my-app'
76+
cache-mode: 'node_modules-only'
77+
```
78+
79+
**Why:**
80+
- Single `node_modules/` at root (no workspaces)
81+
- Current cache is >1GB (slow to restore)
82+
- Faster cache restore outweighs slower install on miss
83+
- No inter-package symlinks to recreate
84+
85+
**Examples:**
86+
- Chief (~1.25 GB cache)
87+
- Large standalone applications
88+
- Single-package projects with many dependencies
89+
90+
**Tradeoffs:**
91+
- ✅ Faster cache restore (smaller payload)
92+
- ⚠️ Slower `yarn install` on cache miss (must download from registry)
93+
94+
---
95+
96+
### 3. Projects with Frequent Dependency Changes
97+
98+
**Use: `yarn-cache-only`**
99+
100+
```yaml
101+
uses: Typeform/.github/.github/workflows/frontend-pr-workflow.yml@v1
102+
with:
103+
app-name: 'my-app'
104+
cache-mode: 'yarn-cache-only'
105+
```
106+
107+
**Why:**
108+
- Dependencies change often (frequent cache misses)
109+
- Smallest cache size = fastest restore
110+
- `yarn install` runs every time but is fast (packages cached)
111+
112+
**Examples:**
113+
- Active development with frequent dependency updates
114+
- Experimental projects
115+
- Small projects where install is quick anyway
116+
117+
**Tradeoffs:**
118+
- ✅ Smallest cache, fastest restore
119+
- ⚠️ Always runs `yarn install` (even on cache hit)
120+
- ✅ Install is fast because packages are cached
121+
122+
---
123+
124+
## How to Check Your Cache Size
125+
126+
1. Go to your repo's **Settings → Actions → Caches**
127+
2. Look for caches with key pattern: `linux-x64-yarn-*`
128+
3. Check the size column
129+
130+
**Guidelines:**
131+
- **< 500 MB**: Use `full` (default)
132+
- **500 MB - 1 GB**: Consider `node_modules-only` if not a workspace
133+
- **> 1 GB**: Use `node_modules-only` if not a workspace
134+
135+
---
136+
137+
## How to Check if You're a Workspace Repo
138+
139+
Look in your root `package.json`:
140+
141+
```json
142+
{
143+
"name": "my-project",
144+
"workspaces": [ ← If this field exists, you're a workspace repo
145+
"packages/*",
146+
"apps/*"
147+
]
148+
}
149+
```
150+
151+
**If you have `"workspaces"`**: Use `full` mode (default)
152+
**If you don't have `"workspaces"`**: Consider `node_modules-only` if cache is large
153+
154+
---
155+
156+
## Advanced: Disable Restore Keys
157+
158+
For very large caches, you can disable restore-keys to avoid downloading huge stale caches:
159+
160+
```yaml
161+
uses: Typeform/.github/.github/workflows/frontend-pr-workflow.yml@v1
162+
with:
163+
app-name: 'my-app'
164+
cache-mode: 'node_modules-only'
165+
disable-restore-keys: true # Forces exact key match or fresh install
166+
```
167+
168+
**When to use:**
169+
- Cache is >2GB and restore-keys often pull stale caches
170+
- You prefer fresh installs over restoring old caches
171+
172+
---
173+
174+
## Migration Guide
175+
176+
### Switching from `full` to `node_modules-only`
177+
178+
1. Update your workflow:
179+
```yaml
180+
cache-mode: 'node_modules-only'
181+
```
182+
183+
2. **First run will be a cache miss** (different cache key)
184+
- This is expected and intentional
185+
- New cache will be created with `-node_modules-only` suffix
186+
187+
3. Monitor the impact:
188+
- Check cache restore time (should be faster)
189+
- Check install time on miss (will be slower)
190+
- Overall CI time should improve if cache was >1GB
191+
192+
### Switching back to `full`
193+
194+
Simply remove the `cache-mode` line (defaults to `full`):
195+
196+
```yaml
197+
# cache-mode: 'node_modules-only' # Remove this line
198+
```
199+
200+
First run will be a cache miss, then back to normal.
201+
202+
---
203+
204+
## Examples from Real Projects
205+
206+
### Chief (Non-Workspace, Large Cache)
207+
```yaml
208+
uses: Typeform/.github/.github/workflows/frontend-pr-workflow.yml@v1
209+
with:
210+
app-name: 'chief'
211+
use-asdf: true
212+
cache-mode: 'node_modules-only' # ~1.25 GB cache → faster restore
213+
```
214+
215+
### Paprikations (Non-Workspace, node_modules-only)
216+
```yaml
217+
# Uses custom caching in .github/workflows/pull-request.yaml
218+
# Caches only node_modules (no yarn cache)
219+
# Works well for large non-workspace projects
220+
```
221+
222+
### Typical Monorepo (Workspace)
223+
```yaml
224+
uses: Typeform/.github/.github/workflows/frontend-pr-workflow.yml@v1
225+
with:
226+
app-name: 'my-monorepo'
227+
# cache-mode: 'full' # Default, best for workspaces
228+
```
229+
230+
---
231+
232+
## Troubleshooting
233+
234+
### Cache restore is slow (>1 minute)
235+
→ Check cache size, consider `node_modules-only` if >1GB and not a workspace
236+
237+
### Install always runs even with cache hit
238+
→ Expected for workspace repos (need to recreate symlinks)
239+
→ For non-workspace repos, check cache integrity in logs
240+
241+
### Cache miss on every run
242+
→ Check if you recently changed `cache-mode` (creates new cache key)
243+
→ Verify `yarn.lock` is committed and not changing
244+
245+
### Want to force fresh install
246+
→ Use `disable-restore-keys: true` to prevent stale cache restores
247+
→ Or manually delete caches in Settings → Actions → Caches
248+
249+
---
250+
251+
## Summary
252+
253+
**Default (`full`)**: Good for most projects, especially workspaces
254+
**`node_modules-only`**: Best for large (>1GB) non-workspace repos
255+
**`yarn-cache-only`**: Best for frequent dependency changes or small projects
256+
257+
When in doubt, start with the default and optimize if cache restore becomes a bottleneck.

shared-actions/setup-node-with-cache/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ Standardized Node.js setup with enhanced yarn caching and GitHub packages regist
3333
| `use-asdf` | Use asdf-vm for version management | No | `'false'` |
3434
| `GH_TOKEN` | GitHub token for private packages | Yes | - |
3535
| `enable-yarn-cache` | Enable yarn global cache (~/.cache/yarn) | No | `'true'` |
36+
| `cache-mode` | Cache strategy: `full`, `node_modules-only`, or `yarn-cache-only` | No | `'full'` |
37+
| `disable-restore-keys` | Disable restore-keys to avoid restoring stale caches | No | `'false'` |
38+
39+
**📖 Need help choosing the right cache mode?** See the [Cache Strategy Guide](./CACHE-STRATEGY-GUIDE.md) for detailed recommendations.
3640

3741
## Outputs
3842

@@ -160,6 +164,33 @@ act pull_request -j build
160164
GH_TOKEN: ${{ secrets.GH_TOKEN }}
161165
```
162166
167+
### Cache node_modules Only (Faster for Non-Workspace Repos)
168+
```yaml
169+
- uses: Typeform/.github/shared-actions/setup-node-with-cache@main
170+
with:
171+
GH_TOKEN: ${{ secrets.GH_TOKEN }}
172+
cache-mode: 'node_modules-only'
173+
```
174+
**Use when**: Your `node_modules` cache is small and you want to skip yarn install entirely on cache hit. Best for non-workspace repos like Chief where the full cache (node_modules + yarn cache) is large.
175+
176+
### Cache Yarn Cache Only (Faster Install on Miss)
177+
```yaml
178+
- uses: Typeform/.github/shared-actions/setup-node-with-cache@main
179+
with:
180+
GH_TOKEN: ${{ secrets.GH_TOKEN }}
181+
cache-mode: 'yarn-cache-only'
182+
```
183+
**Use when**: You expect frequent cache misses or want to reduce cache restore time. Yarn install will run every time but will be faster because tarballs are cached.
184+
185+
### Disable Restore Keys (Exact Match Only)
186+
```yaml
187+
- uses: Typeform/.github/shared-actions/setup-node-with-cache@main
188+
with:
189+
GH_TOKEN: ${{ secrets.GH_TOKEN }}
190+
disable-restore-keys: 'true'
191+
```
192+
**Use when**: You want to avoid restoring large stale caches. Only exact cache key matches will be restored.
193+
163194
### Disable Yarn Global Cache
164195
```yaml
165196
- uses: Typeform/.github/shared-actions/setup-node-with-cache@main

0 commit comments

Comments
 (0)