The "Frontend Typecheck + Tests" job spends almost all its wall-clock on environment setup, not the checks. Measured locally: typecheck 1.4s + jest 1.3s ≈ 3s of real work, against installing 452 MB / 460 packages and setting up Node every run.
Changes
Cache node_modules keyed on the lockfile, and skip npm ci on a hit.setup-node's cache: npm only cached the ~/.npm download cache — npm ci still re-linked all 452 MB into node_modules every run. This caches the resolved node_modules itself; on a hit (the common case — deps change rarely) the entire install step is skipped. The key pins runner.os + node major so a restored cache never contains incompatible native binaries. On a miss, npm ci --prefer-offline --no-audit --no-fund (drops the audit/funding network round-trips).
Workflow-level concurrency with cancel-in-progress. A new commit — or one of the empty re-trigger commits — now aborts the previous still-running matrix instead of running a second one alongside it. (Already proven supported on this Gitea: promote.yml uses concurrency.)
Expected effect
On the typical PR push (lockfile unchanged): the frontend job drops from "Node setup + full 452 MB install + checks" to "Node setup + cache restore + ~3s of checks" — the dominant install step disappears. Across the workflow, superseded runs stop wasting a runner.
Further option (not included — needs a runner-support check)
Running the job in container: node:22-bookworm-slim (dropping setup-node entirely, since Node+npm are baked into the image) would remove the Node download/extract too. That's the biggest remaining win for "setting up nodejs", but it depends on the act_runner using the Docker backend for job containers — worth trying on a throwaway PR before committing, since a wrong assumption would red-x the gate. Happy to do that as a follow-up if you want to confirm runner support first.
The "Frontend Typecheck + Tests" job spends almost all its wall-clock on environment setup, not the checks. Measured locally: **typecheck 1.4s + jest 1.3s ≈ 3s of real work**, against installing **452 MB / 460 packages** and setting up Node every run.
## Changes
1. **Cache `node_modules` keyed on the lockfile, and skip `npm ci` on a hit.** `setup-node`'s `cache: npm` only cached the `~/.npm` download cache — `npm ci` still re-linked all 452 MB into `node_modules` every run. This caches the resolved `node_modules` itself; on a hit (the common case — deps change rarely) the entire install step is skipped. The key pins `runner.os` + node major so a restored cache never contains incompatible native binaries. On a miss, `npm ci --prefer-offline --no-audit --no-fund` (drops the audit/funding network round-trips).
2. **Workflow-level `concurrency` with `cancel-in-progress`.** A new commit — or one of the empty re-trigger commits — now aborts the previous still-running matrix instead of running a second one alongside it. (Already proven supported on this Gitea: promote.yml uses `concurrency`.)
## Expected effect
On the typical PR push (lockfile unchanged): the frontend job drops from "Node setup + full 452 MB install + checks" to "Node setup + cache restore + ~3s of checks" — the dominant install step disappears. Across the workflow, superseded runs stop wasting a runner.
## Further option (not included — needs a runner-support check)
Running the job in `container: node:22-bookworm-slim` (dropping `setup-node` entirely, since Node+npm are baked into the image) would remove the Node download/extract too. That's the biggest remaining win for "setting up nodejs", but it depends on the act_runner using the Docker backend for job containers — worth trying on a throwaway PR before committing, since a wrong assumption would red-x the gate. Happy to do that as a follow-up if you want to confirm runner support first.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
The job does ~3s of real work (typecheck 1.4s + jest 1.3s) but installs
452 MB / 460 packages every run. Two changes:
- Cache nextjs-app/node_modules keyed on the lockfile hash (OS + node
major pinned) and skip npm ci entirely on a hit — deps change rarely,
so most PR pushes now do zero install. On miss, npm ci runs with
--prefer-offline --no-audit --no-fund.
- Workflow-level concurrency with cancel-in-progress: a new commit (or an
empty re-trigger) aborts the previous run instead of stacking a second
full matrix.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB
tudor
merged commit 0696518995 into main2026-07-14 21:57:09 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The "Frontend Typecheck + Tests" job spends almost all its wall-clock on environment setup, not the checks. Measured locally: typecheck 1.4s + jest 1.3s ≈ 3s of real work, against installing 452 MB / 460 packages and setting up Node every run.
Changes
node_moduleskeyed on the lockfile, and skipnpm cion a hit.setup-node'scache: npmonly cached the~/.npmdownload cache —npm cistill re-linked all 452 MB intonode_modulesevery run. This caches the resolvednode_modulesitself; on a hit (the common case — deps change rarely) the entire install step is skipped. The key pinsrunner.os+ node major so a restored cache never contains incompatible native binaries. On a miss,npm ci --prefer-offline --no-audit --no-fund(drops the audit/funding network round-trips).concurrencywithcancel-in-progress. A new commit — or one of the empty re-trigger commits — now aborts the previous still-running matrix instead of running a second one alongside it. (Already proven supported on this Gitea: promote.yml usesconcurrency.)Expected effect
On the typical PR push (lockfile unchanged): the frontend job drops from "Node setup + full 452 MB install + checks" to "Node setup + cache restore + ~3s of checks" — the dominant install step disappears. Across the workflow, superseded runs stop wasting a runner.
Further option (not included — needs a runner-support check)
Running the job in
container: node:22-bookworm-slim(droppingsetup-nodeentirely, since Node+npm are baked into the image) would remove the Node download/extract too. That's the biggest remaining win for "setting up nodejs", but it depends on the act_runner using the Docker backend for job containers — worth trying on a throwaway PR before committing, since a wrong assumption would red-x the gate. Happy to do that as a follow-up if you want to confirm runner support first.🤖 Generated with Claude Code
https://claude.ai/code/session_0146VHeLAWjDVE2B5uU67jCB