Skip to content

Commit 2ea1d77

Browse files
ruromerocursoragentclaude
authored
feat: support JS/TS monorepo workspace batch analysis (#423)
* feat: allow monorepo lock files using workspaceDir Signed-off-by: Ruben Romero Montes <rromerom@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> Implements TC-3862 * refactor: improve workspace discovery robustness and batch analysis maintainability - Replace hand-rolled pnpm-workspace.yaml parser with js-yaml - Fix negation pattern handling in workspace discovery (e.g. !**/test/**) - Refactor stackAnalysisBatch into focused helpers, eliminating duplicated SBOM generation logic between fail-fast and continue-on-error paths - Add integration tests for stackAnalysisBatch with mocked providers and HTTP backend Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Implements TC-3862 * fix: address qodo review findings for batch analysis - Make generateOneSbom async and await provider.provideStack() to support async providers (e.g. python_pip) - Propagate workspaceDir as cwd for package manager commands so npm/pnpm/yarn run from workspace root in monorepos - Fix CLI --html --metadata printing wrapper object instead of HTML string Implements TC-3862 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: use TRUSTIFY_DA_WORKSPACE_DIR convention consistently Remove the opts.workspaceDir fallback pattern and use only the TRUSTIFY_DA_WORKSPACE_DIR key through getCustom(), keeping the existing single-convention pattern for option propagation. TC-3862 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve eslint warnings in workspace.js and batch test Use named import for js-yaml load function and fix import ordering in stack_analysis_batch.test.js. TC-3862 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Signed-off-by: Ruben Romero Montes <rromerom@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9988076 commit 2ea1d77

17 files changed

+1541
-57
lines changed

README.md

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ let stackAnalysis = await client.stackAnalysis('/path/to/pom.xml')
3232
let stackAnalysisHtml = await client.stackAnalysis('/path/to/pom.xml', true)
3333
// Get component analysis in JSON format
3434
let componentAnalysis = await client.componentAnalysis('/path/to/pom.xml')
35+
// For monorepos, pass workspace root so the client finds the lock file
36+
let monorepoOpts = { workspaceDir: '/path/to/workspace-root' }
37+
let stackAnalysisMonorepo = await client.stackAnalysis('/path/to/package.json', false, monorepoOpts)
38+
// Batch analysis for entire workspace (Cargo or JS/TS); optional parallel SBOM generation
39+
let batchReport = await client.stackAnalysisBatch('/path/to/workspace-root', false, { batchConcurrency: 10 })
3540
// Get image analysis in JSON format
3641
let imageAnalysis = await client.imageAnalysis(['docker.io/library/node:18'])
3742
// Get image analysis in HTML format (string)
@@ -101,8 +106,9 @@ $ npx @trustify-da/trustify-da-javascript-client help
101106
Usage: trustify-da-javascript-client {component|stack|image|validate-token|license}
102107

103108
Commands:
104-
trustify-da-javascript-client stack </path/to/manifest> [--html|--summary] produce stack report for manifest path
105-
trustify-da-javascript-client component <path/to/manifest> [--summary] produce component report for a manifest type and content
109+
trustify-da-javascript-client stack </path/to/manifest> [--workspace-dir <path>] [--html|--summary] produce stack report for manifest path
110+
trustify-da-javascript-client stack-batch </path/to/workspace-root> [--html|--summary] produce stack report for all packages/crates in workspace
111+
trustify-da-javascript-client component <path/to/manifest> [--workspace-dir <path>] produce component report for a manifest type and content
106112
trustify-da-javascript-client image <image-refs..> [--html|--summary] produce image analysis report for OCI image references
107113
trustify-da-javascript-client license </path/to/manifest> display project license information from manifest and LICENSE file in JSON format
108114

@@ -121,9 +127,21 @@ $ npx @trustify-da/trustify-da-javascript-client stack /path/to/pom.xml --summar
121127
# get stack analysis in html format format
122128
$ npx @trustify-da/trustify-da-javascript-client stack /path/to/pom.xml --html
123129

130+
# get stack analysis for monorepo (lock file at workspace root)
131+
$ npx @trustify-da/trustify-da-javascript-client stack /path/to/package.json --workspace-dir /path/to/workspace-root
132+
124133
# get component analysis
125134
$ npx @trustify-da/trustify-da-javascript-client component /path/to/pom.xml
126135

136+
# get component analysis for monorepo
137+
$ npx @trustify-da/trustify-da-javascript-client component /path/to/package.json -w /path/to/workspace-root
138+
139+
# batch stack analysis for entire workspace (Cargo or JS/TS)
140+
$ npx @trustify-da/trustify-da-javascript-client stack-batch /path/to/workspace-root
141+
142+
# optional: extra discovery excludes (merged with defaults); repeat --ignore or use TRUSTIFY_DA_WORKSPACE_DISCOVERY_IGNORE
143+
$ npx @trustify-da/trustify-da-javascript-client stack-batch /path/to/workspace-root --ignore '**/fixtures/**'
144+
127145
# get image analysis in json format
128146
$ npx @trustify-da/trustify-da-javascript-client image docker.io/library/node:18
129147

@@ -162,9 +180,21 @@ $ trustify-da-javascript-client stack /path/to/pom.xml --summary
162180
# get stack analysis in html format format
163181
$ trustify-da-javascript-client stack /path/to/pom.xml --html
164182

183+
# get stack analysis for monorepo (lock file at workspace root)
184+
$ trustify-da-javascript-client stack /path/to/package.json --workspace-dir /path/to/workspace-root
185+
165186
# get component analysis
166187
$ trustify-da-javascript-client component /path/to/pom.xml
167188

189+
# get component analysis for monorepo
190+
$ trustify-da-javascript-client component /path/to/package.json -w /path/to/workspace-root
191+
192+
# batch stack analysis for entire workspace
193+
$ trustify-da-javascript-client stack-batch /path/to/workspace-root
194+
195+
# with extra discovery excludes
196+
$ trustify-da-javascript-client stack-batch /path/to/workspace-root -i '**/vendor/**'
197+
168198
# get image analysis in json format
169199
$ trustify-da-javascript-client image docker.io/library/node:18
170200

@@ -382,6 +412,8 @@ let options = {
382412
'TRUSTIFY_DA_PIP_PATH' : '/path/to/pip',
383413
'TRUSTIFY_DA_GRADLE_PATH' : '/path/to/gradle',
384414
'TRUSTIFY_DA_CARGO_PATH' : '/path/to/cargo',
415+
// Workspace root for monorepos (Cargo, npm/pnpm/yarn); lock file expected here
416+
'workspaceDir': '/path/to/workspace-root',
385417
// Configure proxy for all requests
386418
'TRUSTIFY_DA_PROXY_URL': 'http://proxy.example.com:8080'
387419
}
@@ -404,6 +436,21 @@ let imageAnalysisWithArch = await client.imageAnalysis(['httpd:2.4.49^^amd64'],
404436
**_Environment variables takes precedence._**
405437
</p>
406438
439+
<h4>Monorepo / Workspace Support</h4>
440+
<p>
441+
For monorepos (Cargo workspaces, npm/pnpm/yarn workspaces) where the lock file lives at the workspace root rather than next to the manifest, pass the workspace root via <code>workspaceDir</code> or <code>TRUSTIFY_DA_WORKSPACE_DIR</code>:
442+
</p>
443+
<ul>
444+
<li><strong>Cargo:</strong> When set, the client checks only the given directory for <code>Cargo.lock</code> instead of walking up from the manifest.</li>
445+
<li><strong>JavaScript (npm, pnpm, yarn):</strong> When set, the client looks for the lock file (<code>package-lock.json</code>, <code>pnpm-lock.yaml</code>, <code>yarn.lock</code>) at the workspace root.</li>
446+
</ul>
447+
<p>
448+
Use <code>stackAnalysisBatch(workspaceRoot, html, opts)</code> to analyze all packages/crates in a workspace in one request. Supports Cargo workspaces and JS/TS workspaces (pnpm, npm, yarn). Optional <code>batchConcurrency</code> (or <code>TRUSTIFY_DA_BATCH_CONCURRENCY</code>) limits parallel SBOM generation (default 10). For JS/TS, each <code>package.json</code> must have non-empty <code>name</code> and <code>version</code>; invalid manifests are skipped (warnings). Per-manifest SBOM failures are skipped if at least one SBOM succeeds (unless <code>continueOnError: false</code>). Set <code>batchMetadata: true</code> (or <code>TRUSTIFY_DA_BATCH_METADATA</code>) to receive <code>{ analysis, metadata }</code> with <code>errors[]</code>. CLI: <code>stack-batch --metadata</code>, <code>--fail-fast</code>. See <a href="./docs/monorepo-implementation-plan.md">monorepo implementation plan</a> §2.3 and §3.5.
449+
</p>
450+
<p>
451+
See <a href="./docs/vscode-extension-integration-requirements.md">VS Code Extension Integration Requirements</a> for integration details.
452+
</p>
453+
407454
<h4>Proxy Configuration</h4>
408455
<p>
409456
You can configure a proxy for all HTTP/HTTPS requests made by the API. This is useful when your environment requires going through a proxy to access external services.
@@ -508,6 +555,11 @@ following keys for setting custom paths for the said executables.
508555
<td><em>cargo</em></td>
509556
<td>TRUSTIFY_DA_CARGO_PATH</td>
510557
</tr>
558+
<tr>
559+
<td>Workspace root (monorepos)</td>
560+
<td></td>
561+
<td>workspaceDir / TRUSTIFY_DA_WORKSPACE_DIR</td>
562+
</tr>
511563
</table>
512564
513565
#### Match Manifest Versions Feature

package-lock.json

Lines changed: 92 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)