Skip to content

Commit 8d2b39b

Browse files
committed
Tooling: Skip pnpm install when lockfile unchanged
- Store `pnpm-lock.yaml` mtime in `node_modules/.pnpm-install-marker` after successful install - On next run, skip install if mtime matches (saves ~20s and heavy CPU) - Always runs in CI mode (`--ci`) - Marker is auto-invalidated when `node_modules` is deleted
1 parent 7997fcb commit 8d2b39b

File tree

3 files changed

+41
-6
lines changed

3 files changed

+41
-6
lines changed

scripts/check/CLAUDE.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,12 @@ Use `CommandExists()` to check if a tool is installed, and auto-install if possi
202202
**Decision**: Auto-fix locally, check-only in CI.
203203
**Why**: Developers get instant fixes locally (less friction), CI ensures code is properly formatted before merge. Controlled by the `--ci` flag.
204204

205+
**Decision**: Skip `pnpm install` when lockfile is unchanged.
206+
**Why**: `pnpm install` takes ~20s and pegs all CPUs even when deps haven't changed. A marker file
207+
(`node_modules/.pnpm-install-marker`) stores `pnpm-lock.yaml`'s mtime after each successful install.
208+
On the next run, if the mtime matches, install is skipped. The marker lives inside `node_modules/` so
209+
it's automatically invalidated if `node_modules` is deleted. Always runs in CI (`--ci`).
210+
205211
## Dependencies
206212

207213
`golang.org/x/term`, `golang.org/x/sys` (transitive). Go 1.25.

scripts/check/checks/common.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,25 @@ func indentOutput(output string) string {
222222
}
223223

224224
// EnsurePnpmDependencies runs pnpm install to ensure all dependencies are installed.
225-
// In CI mode, uses --frozen-lockfile to fail if lockfile is out of sync.
226-
func EnsurePnpmDependencies(ctx *CheckContext) error {
225+
// Skips the install if pnpm-lock.yaml hasn't changed since the last successful run.
226+
// In CI mode, uses --frozen-lockfile and always runs (never skips).
227+
// Returns true if the install was skipped.
228+
func EnsurePnpmDependencies(ctx *CheckContext) (skipped bool, err error) {
229+
lockfilePath := filepath.Join(ctx.RootDir, "pnpm-lock.yaml")
230+
markerPath := filepath.Join(ctx.RootDir, "node_modules", ".pnpm-install-marker")
231+
232+
if !ctx.CI {
233+
if lockInfo, lockErr := os.Stat(lockfilePath); lockErr == nil {
234+
if markerContent, markerErr := os.ReadFile(markerPath); markerErr == nil {
235+
recorded := string(markerContent)
236+
current := lockInfo.ModTime().UTC().Format("2006-01-02T15:04:05.000000000Z")
237+
if recorded == current {
238+
return true, nil
239+
}
240+
}
241+
}
242+
}
243+
227244
args := []string{"install"}
228245
if ctx.CI {
229246
args = append(args, "--frozen-lockfile")
@@ -233,9 +250,16 @@ func EnsurePnpmDependencies(ctx *CheckContext) error {
233250
cmd.Dir = ctx.RootDir
234251
output, err := RunCommand(cmd, true)
235252
if err != nil {
236-
return fmt.Errorf("pnpm install failed:\n%s", indentOutput(output))
253+
return false, fmt.Errorf("pnpm install failed:\n%s", indentOutput(output))
237254
}
238-
return nil
255+
256+
// Write marker with lockfile's current mtime
257+
if lockInfo, lockErr := os.Stat(lockfilePath); lockErr == nil {
258+
mtime := lockInfo.ModTime().UTC().Format("2006-01-02T15:04:05.000000000Z")
259+
_ = os.WriteFile(markerPath, []byte(mtime), 0644)
260+
}
261+
262+
return false, nil
239263
}
240264

241265
// Pluralize returns singular if count is 1, plural otherwise.

scripts/check/main.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,13 +280,18 @@ func ensurePnpmDependencies(ctx *checks.CheckContext) error {
280280
fmt.Print("📦 Ensuring pnpm dependencies are installed... ")
281281
startTime := time.Now()
282282

283-
if err := checks.EnsurePnpmDependencies(ctx); err != nil {
283+
skipped, err := checks.EnsurePnpmDependencies(ctx)
284+
if err != nil {
284285
fmt.Printf("%sFAILED%s\n", colorRed, colorReset)
285286
return err
286287
}
287288

288289
duration := time.Since(startTime)
289-
fmt.Printf("%sOK%s (%s)\n\n", colorGreen, colorReset, formatDuration(duration))
290+
if skipped {
291+
fmt.Printf("%sOK%s (skipped, lockfile unchanged)\n\n", colorGreen, colorReset)
292+
} else {
293+
fmt.Printf("%sOK%s (%s)\n\n", colorGreen, colorReset, formatDuration(duration))
294+
}
290295
return nil
291296
}
292297

0 commit comments

Comments
 (0)