-
Notifications
You must be signed in to change notification settings - Fork 16
Description
User story
As a developer I want to be able to cache as much as possible with Nx and leverage affected
Why Commit Info Breaks Caching
To get reliable caching in tools like Nx, task inputs must stay stable across commits — unless the output truly depends on them.
Example ESLint:
ESLint can be cached effectively when its inputs are:
- The ESLint version
- Source files of the project
- Dependency .d.ts files
In this setup, even if you're on a different commit or branch, ESLint won’t re-run unless something relevant changes. This allows maximum cache reuse.
Note
Using Commit SHA as Input
If you include the commit SHA in:
- Task inputs
- Generated files
- Plugin outputs
Then every commit creates a unique input hash, and ESLint (or any task) loses its cache — even when the code hasn’t changed.
Suggestion
Legend:
- 🐳 Owned by Nx — Nx or other external caching solutions
- 🔌 Owned by plugin creator — depends on code changes
- ☑️ Owned by CodePushup — depends on code changes
- 🔧 Code-related — depends on code changes
- ⛓️ Commit-related — depends on commit (includes code changes)
- ⚡ Fast — fast enough to optionally skip caching
- 💾 Cached — slow or expensive, should be cached
- 🚫 No Cache — intentionally avoids caching
New CLI options for command collect
Option | Type | Default | Description |
---|---|---|---|
--persist.report |
boolean |
true |
Will not produce report.json . Normally in combination with --cache |
--cache.write |
boolean |
false |
If true it will generate a {persist.outputDir}/<plugin-slug>/audit-outputs.json file containing the audit output data of a plugin |
--cache.read |
boolean |
false |
If true it will try to read the plugins audit outputs for file system. If not available it will run the plugin to get the audit outputs on the fly. If |
Collect Flow - Generation Phase
Command: npx @code-pushup/cli collect --cache.write --no-report --onlyPlugins eslint
-
For each plugin listed in
--onlyPlugins
:IF
--cache.write
is given it will write the audit outputs data to{persist.outputDir}/<plugin-slug>/audit-outputs.json
(💾☑️).
This is done in executePlugin after the execution. -
After the plugins are run
IF --no-report
is set, no final report persist.outputDir/report.json
is created. (☑️)
Collect Flow - Aggregation Phase
Command: npx @code-pushup/cli collect --cache.read
IF --cache.read
is given the CLI checks for existing plugin audit outputs under persist.outputDir/<plugin-slug>/audit-outputs.json
and if given it will take the data instead of executing the plugin runner.
This is done in executePlugin before the execution.
ELSE:
IF generateArtifactsCommand
is defined
→ Run the command e.g., npx nx run-many ...
to produce the raw artifacts (🐳)
Optional Generate plugin artifacts (💾 🔧 🔌)
Generate the plugin report and persist it to persist.outputDir/<plugin-slug>/audit-outputs.json
(💾 🔧 ☑️)
In executePlugin we save the plugin report as AuditOutputs.
- After all plugins are processed, the CLI will combine reports into the final report
persist.outputDir/report.json
(⛓️ ⚡ 🚫 🔧 ☑️)
Code Pushup Config
{
plugins: [
await coveragePlugin({
generateArtifactsCommand: 'npx nx run-many --project lib-a --targets unit-test int-test',
reports: ['packages/lib-a/coverage/unit-test/lcov.info', 'packages/lib-a/coverage/int-test/lcov.info'],
}),
await nxPerfPlugin({
audits: ['project-graph-time'],
}),
],
}
Targets
{
"name": "lib-a",
"targets": {
"int-test": {
"cache": true,
"outputs": ["{options.coverage.reportsDirectory}"],
"executor": "@nx/vite:test",
"options": {
"configFile": "packages/lib-a/vitest.int.config.ts",
"coverage.reportsDirectory": "{projectRoot}/coverage/int-test"
}
},
"unit-test": {
"cache": true,
"outputs": ["{options.coverage.reportsDirectory}"],
"executor": "@nx/vite:test",
"options": {
"configFile": "packages/lib-a/vitest.unit.config.ts",
"coverage.reportsDirectory": "{projectRoot}/coverage/unit-test"
}
},
"code-pushup:coverage": {
"dependsOn": ["unit-test", "int-test"],
"cache": true,
"inputs": ["{coverage-speciftic-globs}"],
"outputs": ["{options.outputPath}"],
"executor": "@code-pushup/nx-plugin:cli",
"options": {
"report": false,
"cache.write": true,
"onlyPlugins": "coverage",
"persist.outputDir": "{projectRoot}/.code-pushup/coverage",
"outputPath": "{projectRoot}/.code-pushup/coverage/<plugin-slug>/plugin-report.json",
}
},
"//":"This target could be completely skipped as we d not cache anything here",
"code-pushup:nx-perf": {
"cache": false,
"executor": "@code-pushup/nx-plugin:cli",
"options": {
"report": false,
"onlyPlugins": "nx-perf"
"persist.outputDir": "{projectRoot}/.code-pushup/nx-perf"
}
},
"code-pushup": {
"dependsOn": ["code-pushup:coverage", "code-pushup:nx-perf"],
"cache": false,
"outputs": ["{options.outputPath}"],
"executor": "@code-pushup/nx-plugin:cli",
"options": {
"cache.read": true,
"persist.outputDir": "{projectRoot}/.code-pushup",
"outputPath": "{projectRoot}/.code-pushup/report.*",
}
}
}
}
Nx Task Graph
graph TD
A[lib-a:code-pushup ⚡ ⛓️ 🚫] --> B[lib-a:code-pushup:coverage 💾 🔧 ☑️]
A --> E[lib-a:code-pushup:nx-perf 🚫 🔌]
B --> C[lib-a:unit-test 💾 🔧 🐳]
B --> D[lib-a:int-test 💾 🔧 🐳]
Acceptance criteria
- New CLI options
--report
- feat: add report option to cli #1058- New report format
{persist.outputDir}/<plugin-slug>/audit-outputs.json
- New report format
- New CLI options
--cache.write
and--cache.read
feat: add audit output caching for execute plugin #1057- Collect looks for plugin reports first and only runs if they are missing
- CLI respects report and caching options feat: add caching options to cli #1059
Implementation details
No response