From ac95036034d95ca9c5d940fa2d5020a561f8cd8c Mon Sep 17 00:00:00 2001 From: Jeppe Fredsgaard Blaabjerg Date: Wed, 1 Jul 2026 11:48:10 +0200 Subject: [PATCH 1/6] feat(manifest): quiet JVM build-tool output behind a spinner (1.1.133) `socket manifest gradle|sbt|maven` (and `--auto-manifest`) streamed the build tool's full output to the terminal. Capture it by default and show a spinner instead, streaming live only under --verbose. On a build crash the captured output tail is surfaced so failures stay diagnosable without a rebuild. Also fix runNeverThrow: the registry's isSpawnError is unreliable (it never matches), so a non-zero build exit rethrew as an opaque "command failed" instead of returning the exit code. Duck-type the numeric exit code directly. --- CHANGELOG.md | 5 ++ package.json | 2 +- src/commands/manifest/run-manifest-facts.mts | 60 ++++++++++++++++---- src/commands/manifest/scripts/run.mts | 40 +++++++++---- 4 files changed, 84 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55b3e728e..4ef2f22cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [1.1.133](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.133) - 2026-07-01 + +### Changed +- Quieter `socket manifest gradle`, `sbt`, and `maven`: the build tool's output is now hidden behind a progress spinner and shown only if the build fails. Pass `--verbose` to stream it live. + ## [1.1.132](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.132) - 2026-06-30 ### Changed diff --git a/package.json b/package.json index eb07a3472..3922697d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "socket", - "version": "1.1.132", + "version": "1.1.133", "description": "CLI for Socket.dev", "homepage": "https://github.com/SocketDev/socket-cli", "license": "MIT", diff --git a/src/commands/manifest/run-manifest-facts.mts b/src/commands/manifest/run-manifest-facts.mts index 0a3e8039c..77d3a7049 100644 --- a/src/commands/manifest/run-manifest-facts.mts +++ b/src/commands/manifest/run-manifest-facts.mts @@ -10,8 +10,21 @@ import constants from '../../constants.mts' import { InputError } from '../../utils/errors.mts' import type { BuildTool } from './scripts/build-tool.mts' +import type { ManifestRunResult } from './scripts/run.mts' import type { SidecarAccumulator } from './scripts/sidecar.mts' +const MAX_FAILURE_OUTPUT_LINES = 40 + +// Last N non-empty lines of the captured build output, for diagnosing a crash +// without forcing a --verbose rebuild. +function tailBuildOutput(stdout: string, stderr: string): string { + const combined = [stdout, stderr] + .map(s => s.trimEnd()) + .filter(Boolean) + .join('\n') + return combined.split('\n').slice(-MAX_FAILURE_OUTPUT_LINES).join('\n') +} + // Runs the bundled build-tool resolution script for a JVM project and writes // `.socket.facts.json`. `withFiles` (reachability only) additionally folds // resolved artifact paths into `sidecarAcc`. A blocking resolution failure — or @@ -46,17 +59,32 @@ export async function runManifestFacts({ `Generating Socket facts for the ${ecosystem} project at \`${cwd}\` ...`, ) - const { artifactPaths, code, facts, report } = await runManifestScript( - ecosystem, - { - bin: bin || undefined, - excludeConfigs: excludeConfigs || undefined, - includeConfigs: includeConfigs || undefined, - projectDir: cwd, - toolOpts: buildOpts, - withFiles, - }, - ) + const scriptOpts = { + bin: bin || undefined, + excludeConfigs: excludeConfigs || undefined, + includeConfigs: includeConfigs || undefined, + projectDir: cwd, + // Stream the build tool's output only when asked; otherwise capture it and + // show a spinner, surfacing the output only if the build crashes. + stdio: verbose ? ('inherit' as const) : ('pipe' as const), + toolOpts: buildOpts, + withFiles, + } + const { spinner } = constants + let result: ManifestRunResult + if (verbose) { + result = await runManifestScript(ecosystem, scriptOpts) + } else { + spinner.start( + `Resolving ${ecosystem} dependencies (pass --verbose to stream build output) ...`, + ) + try { + result = await runManifestScript(ecosystem, scriptOpts) + } finally { + spinner.stop() + } + } + const { artifactPaths, code, facts, report, stderr, stdout } = result const rendered = renderResolutionErrorReport( report.failures, @@ -94,7 +122,15 @@ export async function runManifestFacts({ !facts.projects?.length && !report.failures.length ) { - const message = `The ${ecosystem} build failed (exit code ${code}) before producing any Socket facts. Re-run with --verbose for the build tool's output.` + if (!verbose) { + const tail = tailBuildOutput(stdout, stderr) + if (tail) { + logger.group('Build output:') + logger.error(tail) + logger.groupEnd() + } + } + const message = `The ${ecosystem} build failed (exit code ${code}) before producing any Socket facts.` if (!ignoreUnresolved) { throw new InputError(message) } diff --git a/src/commands/manifest/scripts/run.mts b/src/commands/manifest/scripts/run.mts index 68343d148..fa0b16c2d 100644 --- a/src/commands/manifest/scripts/run.mts +++ b/src/commands/manifest/scripts/run.mts @@ -2,7 +2,7 @@ import { existsSync, promises as fs } from 'node:fs' import { tmpdir } from 'node:os' import path from 'node:path' -import { isSpawnError, spawn } from '@socketsecurity/registry/lib/spawn' +import { spawn } from '@socketsecurity/registry/lib/spawn' import { assembleFacts } from './assemble.mts' import { resolveBuildToolBin } from './build-tool.mts' @@ -34,6 +34,9 @@ export type ManifestRunResult = { facts: SocketFactsSbom report: ResolutionReport artifactPaths: ResolvedArtifactPaths + // Captured build-tool output (empty when stdio is 'inherit'). + stderr: string + stdout: string } type RunOutput = { code: number; stdout: string; stderr: string } @@ -67,11 +70,21 @@ async function runNeverThrow( stderr: typeof result.stderr === 'string' ? result.stderr : '', } } catch (e) { - if (isSpawnError(e)) { + // A non-zero exit rejects with the spawn-result shape: a numeric `code` plus + // captured stdout/stderr. Return it so the caller can assemble failure + // records. Anything else (e.g. a missing executable, whose `code` is the + // string 'ENOENT') propagates. Duck-typed on purpose: the registry's + // isSpawnError is unreliable, so the numeric-code check is the real signal. + if ( + e !== null && + typeof e === 'object' && + typeof (e as { code?: unknown }).code === 'number' + ) { + const err = e as { code: number; stdout?: unknown; stderr?: unknown } return { - code: e.code, - stdout: typeof e.stdout === 'string' ? e.stdout : '', - stderr: typeof e.stderr === 'string' ? e.stderr : '', + code: err.code, + stdout: typeof err.stdout === 'string' ? err.stdout : '', + stderr: typeof err.stderr === 'string' ? err.stderr : '', } } throw e @@ -101,14 +114,21 @@ async function writeSbtPlugin(globalBase: string): Promise { } async function assembleFromRecords( - code: number, + out: RunOutput, recordsFile: string, ): Promise { const text = existsSync(recordsFile) ? await fs.readFile(recordsFile, 'utf8') : '' const { artifactPaths, facts, report } = assembleFacts(parseRecords(text)) - return { code, facts, report, artifactPaths } + return { + code: out.code, + facts, + report, + artifactPaths, + stderr: out.stderr, + stdout: out.stdout, + } } // Missing only in an unbuilt local checkout. Fail loudly: without the extension, @@ -179,7 +199,7 @@ async function runGradle( '--console=plain', ] const out = await runNeverThrow(bin, args, opts) - return await assembleFromRecords(out.code, recordsFile) + return await assembleFromRecords(out, recordsFile) }) } @@ -211,7 +231,7 @@ async function runSbt(opts: ManifestScriptOptions): Promise { FACTS_TASK, ] const out = await runNeverThrow(bin, args, opts) - return await assembleFromRecords(out.code, recordsFile) + return await assembleFromRecords(out, recordsFile) }) } @@ -241,6 +261,6 @@ async function runMaven( 'validate', ] const out = await runNeverThrow(bin, args, opts) - return await assembleFromRecords(out.code, recordsFile) + return await assembleFromRecords(out, recordsFile) }) } From a3b1f707574a0a55670b9ee87fd4323f58a6e01b Mon Sep 17 00:00:00 2001 From: Jeppe Fredsgaard Blaabjerg Date: Wed, 1 Jul 2026 12:10:23 +0200 Subject: [PATCH 2/6] refactor(manifest): match the --pom path's spawn + error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align the JVM facts path with convertGradleToMaven (the `--pom` generator), which is the direct-spawn sibling of these commands rather than the coana dlx wrapper: - spinner + captured output by default, stream on --verbose (unchanged intent). - On failure use process.exitCode = 1 + logger.fail(...) + return instead of throwing, so the facts path behaves identically to the pom path — including under --auto-manifest, where both now set exit 1 and continue the sequence. - runNeverThrow classifies a non-zero build exit by a numeric `code` (the utils/dlx.mts convention), not the registry's isSpawnError, which is broken upstream and never matches. --- src/commands/manifest/run-manifest-facts.mts | 52 ++++++++++++++------ src/commands/manifest/scripts/run.mts | 12 +++-- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/commands/manifest/run-manifest-facts.mts b/src/commands/manifest/run-manifest-facts.mts index 77d3a7049..e98c59ae2 100644 --- a/src/commands/manifest/run-manifest-facts.mts +++ b/src/commands/manifest/run-manifest-facts.mts @@ -7,7 +7,6 @@ import { renderResolutionErrorReport } from './scripts/resolution-report-render. import { runManifestScript } from './scripts/run.mts' import { accumulateSidecar } from './scripts/sidecar.mts' import constants from '../../constants.mts' -import { InputError } from '../../utils/errors.mts' import type { BuildTool } from './scripts/build-tool.mts' import type { ManifestRunResult } from './scripts/run.mts' @@ -72,17 +71,38 @@ export async function runManifestFacts({ } const { spinner } = constants let result: ManifestRunResult - if (verbose) { - result = await runManifestScript(ecosystem, scriptOpts) - } else { - spinner.start( - `Resolving ${ecosystem} dependencies (pass --verbose to stream build output) ...`, - ) - try { + try { + if (verbose) { + logger.info( + `(Running ${ecosystem} with output streaming; this can take a while.)`, + ) + result = await runManifestScript(ecosystem, scriptOpts) + } else { + logger.info( + `(No live output; pass --verbose to stream the ${ecosystem} build output.)`, + ) + spinner.start(`Resolving ${ecosystem} dependencies ...`) result = await runManifestScript(ecosystem, scriptOpts) - } finally { - spinner.stop() + if (result.code === 0) { + spinner.successAndStop(`Resolved ${ecosystem} dependencies.`) + } else { + spinner.failAndStop( + `${ecosystem} build exited with code ${result.code}.`, + ) + } } + } catch (e) { + // Only a spawn-level failure (e.g. the build tool missing from PATH) reaches + // here; runNeverThrow returns non-zero build exits rather than throwing. + if (!verbose) { + spinner.failAndStop(`Failed to run ${ecosystem}.`) + } + process.exitCode = 1 + logger.fail( + `Could not run the ${ecosystem} build tool` + + (verbose ? `: ${e}` : ' (run with --verbose for details).'), + ) + return } const { artifactPaths, code, facts, report, stderr, stdout } = result @@ -97,10 +117,12 @@ export async function runManifestFacts({ if (ignoreUnresolved) { logger.warn(rendered.summary) } else { + process.exitCode = 1 + logger.fail(rendered.summary) if (verbose && rendered.details) { logger.log(rendered.details) } - throw new InputError(rendered.summary) + return } } if (rendered.nonBlockingNotice) { @@ -131,10 +153,12 @@ export async function runManifestFacts({ } } const message = `The ${ecosystem} build failed (exit code ${code}) before producing any Socket facts.` - if (!ignoreUnresolved) { - throw new InputError(message) + if (ignoreUnresolved) { + logger.warn(message) + return } - logger.warn(message) + process.exitCode = 1 + logger.fail(message) return } diff --git a/src/commands/manifest/scripts/run.mts b/src/commands/manifest/scripts/run.mts index fa0b16c2d..13544c602 100644 --- a/src/commands/manifest/scripts/run.mts +++ b/src/commands/manifest/scripts/run.mts @@ -70,11 +70,13 @@ async function runNeverThrow( stderr: typeof result.stderr === 'string' ? result.stderr : '', } } catch (e) { - // A non-zero exit rejects with the spawn-result shape: a numeric `code` plus - // captured stdout/stderr. Return it so the caller can assemble failure - // records. Anything else (e.g. a missing executable, whose `code` is the - // string 'ENOENT') propagates. Duck-typed on purpose: the registry's - // isSpawnError is unreliable, so the numeric-code check is the real signal. + // A build tool that exits non-zero rejects with the spawn-result shape: a + // numeric exit `code` plus captured stdout/stderr. Return it so the caller + // can assemble failure records / surface the output. Anything else — e.g. a + // missing executable, whose `code` is a string like 'ENOENT' — propagates. + // This mirrors how utils/dlx.mts classifies spawn failures (a numeric `code` + // is a real process exit); the registry's isSpawnError is avoided here + // because it is currently broken and never matches. if ( e !== null && typeof e === 'object' && From a5fc1445462fdea27adf8713084be6926b5ccede Mon Sep 17 00:00:00 2001 From: Jeppe Fredsgaard Blaabjerg Date: Wed, 1 Jul 2026 12:11:24 +0200 Subject: [PATCH 3/6] docs(manifest): correct stale 'throws' in runManifestFacts comment --- src/commands/manifest/run-manifest-facts.mts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/manifest/run-manifest-facts.mts b/src/commands/manifest/run-manifest-facts.mts index e98c59ae2..9d910e753 100644 --- a/src/commands/manifest/run-manifest-facts.mts +++ b/src/commands/manifest/run-manifest-facts.mts @@ -27,8 +27,8 @@ function tailBuildOutput(stdout: string, stderr: string): string { // Runs the bundled build-tool resolution script for a JVM project and writes // `.socket.facts.json`. `withFiles` (reachability only) additionally folds // resolved artifact paths into `sidecarAcc`. A blocking resolution failure — or -// a build that crashes without emitting any facts — throws unless -// `ignoreUnresolved`. +// a build that crashes without emitting any facts — sets a non-zero exit code +// and returns (matching the `--pom` generator) unless `ignoreUnresolved`. export async function runManifestFacts({ bin, buildOpts, From 6ef766e82a2c365a605e7477b4a9cf223983c108 Mon Sep 17 00:00:00 2001 From: Jeppe Fredsgaard Blaabjerg Date: Wed, 1 Jul 2026 12:30:42 +0200 Subject: [PATCH 4/6] feat(manifest): fail closed under --auto-manifest on a JVM build failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under --auto-manifest, a failed manifest generation now aborts the whole run instead of continuing with a partial or empty SBOM (which silently under-reports dependencies). This is enforced uniformly for every JVM path — Gradle, sbt, and Maven, in both Socket-facts and pom mode — in generate_auto_manifest, keying off the exit code each generator already sets. The standalone `socket manifest` commands are unchanged (they exit non-zero, as before). Failures the user opted to tolerate (ignoreUnresolved / --reach-continue-on-install-errors) warn without setting an exit code, so they continue. --- CHANGELOG.md | 1 + .../manifest/generate_auto_manifest.mts | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ef2f22cc..136e1c6c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Changed - Quieter `socket manifest gradle`, `sbt`, and `maven`: the build tool's output is now hidden behind a progress spinner and shown only if the build fails. Pass `--verbose` to stream it live. +- `--auto-manifest` now fails fast: if a Gradle, sbt, or Maven build can't be resolved, the run stops instead of continuing with an incomplete SBOM. Opt into tolerating install errors to keep going. ## [1.1.132](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.132) - 2026-06-30 diff --git a/src/commands/manifest/generate_auto_manifest.mts b/src/commands/manifest/generate_auto_manifest.mts index 1ad3f4cbd..921e7374e 100644 --- a/src/commands/manifest/generate_auto_manifest.mts +++ b/src/commands/manifest/generate_auto_manifest.mts @@ -13,6 +13,7 @@ import { parseBuildToolOpts } from './parse-build-tool-opts.mts' import { resolveBuildToolBin } from './scripts/build-tool.mts' import { serializeSidecar } from './scripts/sidecar.mts' import { REQUIREMENTS_TXT, SOCKET_JSON } from '../../constants.mts' +import { InputError } from '../../utils/errors.mts' import { readOrDefaultSocketJson } from '../../utils/socket-json.mts' import type { GeneratableManifests } from './detect-manifest-actions.mts' @@ -28,6 +29,23 @@ export type GenerateAutoManifestResult = { resolvedPathsSidecar?: ResolvedPathsSidecar | undefined } +// Under --auto-manifest, a manifest generator that failed — raising the exit +// code above the value captured before it ran — aborts the whole run: a partial +// or empty SBOM silently under-reports dependencies. The generator has already +// logged the specifics. Tolerated failures (ignoreUnresolved / +// reachContinueOnInstallErrors) warn without touching the exit code, so they +// pass through here and the run continues. +function abortManifestRunIfFailed( + ecosystem: string, + beforeExitCode: string | number | undefined, +): void { + if (process.exitCode && process.exitCode !== beforeExitCode) { + throw new InputError( + `Auto-manifest generation failed for the ${ecosystem} project; aborting (see the errors above).`, + ) + } +} + export async function generateAutoManifest({ computeArtifactsSidecar, cwd, @@ -77,6 +95,7 @@ export async function generateAutoManifest({ // `defaults.manifest.sbt.facts: false` in socket.json. if (sockJson.defaults?.manifest?.sbt?.facts !== false) { logger.log('Detected a Scala sbt build, generating Socket facts...') + const beforeExitCode = process.exitCode await convertSbtToFacts({ ...sbtArgs, excludeConfigs: sockJson.defaults?.manifest?.sbt?.excludeConfigs ?? '', @@ -87,12 +106,15 @@ export async function generateAutoManifest({ sidecarAcc, withFiles: computeArtifactsSidecar, }) + abortManifestRunIfFailed('sbt', beforeExitCode) } else { logger.log('Detected a Scala sbt build, generating pom files with sbt...') + const beforeExitCode = process.exitCode await convertSbtToMaven({ ...sbtArgs, out: sockJson.defaults?.manifest?.sbt?.outfile ?? './pom.xml', }) + abortManifestRunIfFailed('sbt', beforeExitCode) } } @@ -114,6 +136,7 @@ export async function generateAutoManifest({ logger.log( 'Detected a gradle build (Gradle, Kotlin, Scala), generating Socket facts...', ) + const beforeExitCode = process.exitCode await convertGradleToFacts({ ...gradleArgs, excludeConfigs: @@ -126,16 +149,20 @@ export async function generateAutoManifest({ sidecarAcc, withFiles: computeArtifactsSidecar, }) + abortManifestRunIfFailed('gradle', beforeExitCode) } else { logger.log( 'Detected a gradle build (Gradle, Kotlin, Scala), running default gradle generator...', ) + const beforeExitCode = process.exitCode await convertGradleToMaven(gradleArgs) + abortManifestRunIfFailed('gradle', beforeExitCode) } } if (!sockJson?.defaults?.manifest?.maven?.disabled && detected.maven) { logger.log('Detected a Maven pom.xml build, generating Socket facts...') + const beforeExitCode = process.exitCode await convertMavenToFacts({ // Configured bin wins; else prefer ./mvnw, else mvn on PATH. bin: @@ -154,6 +181,7 @@ export async function generateAutoManifest({ verbose: Boolean(sockJson.defaults?.manifest?.maven?.verbose), withFiles: computeArtifactsSidecar, }) + abortManifestRunIfFailed('maven', beforeExitCode) } if (!sockJson?.defaults?.manifest?.conda?.disabled && detected.conda) { From 75f4bfec90016c4e79c3b6afff1c6f2792e373b1 Mon Sep 17 00:00:00 2001 From: Jeppe Fredsgaard Blaabjerg Date: Wed, 1 Jul 2026 12:35:36 +0200 Subject: [PATCH 5/6] fix(manifest): a crashed JVM build always fails, ignoring ignoreUnresolved ignoreUnresolved (and --ignore-unresolved) means 'the build ran but some dependencies could not be resolved; tolerate those'. It must not swallow the build process itself failing (missing JDK/build tool, unparseable project, OOM, plugin crash). Scope it to the blocking-resolution-failure branch only; a crashed build now fails regardless. --- CHANGELOG.md | 2 +- src/commands/manifest/run-manifest-facts.mts | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 136e1c6c7..fff3ac0c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Changed - Quieter `socket manifest gradle`, `sbt`, and `maven`: the build tool's output is now hidden behind a progress spinner and shown only if the build fails. Pass `--verbose` to stream it live. -- `--auto-manifest` now fails fast: if a Gradle, sbt, or Maven build can't be resolved, the run stops instead of continuing with an incomplete SBOM. Opt into tolerating install errors to keep going. +- `--auto-manifest` now fails fast: if a Gradle, sbt, or Maven build fails, the run stops instead of continuing with an incomplete SBOM. ## [1.1.132](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.132) - 2026-06-30 diff --git a/src/commands/manifest/run-manifest-facts.mts b/src/commands/manifest/run-manifest-facts.mts index 9d910e753..52b631b59 100644 --- a/src/commands/manifest/run-manifest-facts.mts +++ b/src/commands/manifest/run-manifest-facts.mts @@ -26,9 +26,10 @@ function tailBuildOutput(stdout: string, stderr: string): string { // Runs the bundled build-tool resolution script for a JVM project and writes // `.socket.facts.json`. `withFiles` (reachability only) additionally folds -// resolved artifact paths into `sidecarAcc`. A blocking resolution failure — or -// a build that crashes without emitting any facts — sets a non-zero exit code -// and returns (matching the `--pom` generator) unless `ignoreUnresolved`. +// resolved artifact paths into `sidecarAcc`. A blocking resolution failure sets +// a non-zero exit code and returns (matching the `--pom` generator) unless +// `ignoreUnresolved`; a crashed build — a process failure, not an unresolved +// dependency — always fails. export async function runManifestFacts({ bin, buildOpts, @@ -152,13 +153,14 @@ export async function runManifestFacts({ logger.groupEnd() } } - const message = `The ${ecosystem} build failed (exit code ${code}) before producing any Socket facts.` - if (ignoreUnresolved) { - logger.warn(message) - return - } + // A crashed build is a process failure (missing JDK/build tool, unparseable + // project, OOM, plugin error), not an unresolved dependency, so it fails + // regardless of `ignoreUnresolved` — that flag only tolerates dependencies a + // successful run couldn't resolve. process.exitCode = 1 - logger.fail(message) + logger.fail( + `The ${ecosystem} build failed (exit code ${code}) before producing any Socket facts.`, + ) return } From f22e5c47d34c48c47853b4581cb37624093690af Mon Sep 17 00:00:00 2001 From: Jeppe Fredsgaard Blaabjerg Date: Wed, 1 Jul 2026 12:43:00 +0200 Subject: [PATCH 6/6] refactor(scan): decouple --reach-continue-on-install-errors from manifest ignoreUnresolved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --reach-continue-on-install-errors is a Coana concern (it tells Coana to keep going past its own install errors) and is threaded to Coana in perform-reachability-analysis. It should not also decide whether socket-cli's manifest generation tolerates unresolved dependencies — that is a separate concern governed by the manifest's own ignoreUnresolved (socket.json / --ignore-unresolved). Drop the resolveIgnoreUnresolved coupling so the two are independent; the flag still reaches Coana unchanged. --- .../manifest/generate_auto_manifest.mts | 26 +++++++------------ src/commands/scan/handle-create-new-scan.mts | 1 - 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/commands/manifest/generate_auto_manifest.mts b/src/commands/manifest/generate_auto_manifest.mts index 921e7374e..3e2aa40e4 100644 --- a/src/commands/manifest/generate_auto_manifest.mts +++ b/src/commands/manifest/generate_auto_manifest.mts @@ -32,9 +32,9 @@ export type GenerateAutoManifestResult = { // Under --auto-manifest, a manifest generator that failed — raising the exit // code above the value captured before it ran — aborts the whole run: a partial // or empty SBOM silently under-reports dependencies. The generator has already -// logged the specifics. Tolerated failures (ignoreUnresolved / -// reachContinueOnInstallErrors) warn without touching the exit code, so they -// pass through here and the run continues. +// logged the specifics. A tolerated resolution failure (ignoreUnresolved) warns +// without touching the exit code, so it passes through here and the run +// continues. function abortManifestRunIfFailed( ecosystem: string, beforeExitCode: string | number | undefined, @@ -51,7 +51,6 @@ export async function generateAutoManifest({ cwd, detected, outputKind, - reachContinueOnInstallErrors, verbose, }: { // Reachability path: run build tools with files to emit the sidecar. @@ -59,8 +58,6 @@ export async function generateAutoManifest({ detected: GeneratableManifests cwd: string outputKind: OutputKind - // Reachability install-error gate: tolerate a blocking resolution failure. - reachContinueOnInstallErrors?: boolean | undefined verbose: boolean }): Promise { const sockJson = readOrDefaultSocketJson(cwd) @@ -70,11 +67,6 @@ export async function generateAutoManifest({ const sidecarAcc: SidecarAccumulator | undefined = computeArtifactsSidecar ? new Map() : undefined - // Reachability: the install-error gate decides abort; manifest path: socket.json. - const resolveIgnoreUnresolved = (configured: boolean): boolean => - computeArtifactsSidecar - ? configured || Boolean(reachContinueOnInstallErrors) - : configured if (verbose) { logger.info(`Using this ${SOCKET_JSON} for defaults:`, sockJson) @@ -99,8 +91,8 @@ export async function generateAutoManifest({ await convertSbtToFacts({ ...sbtArgs, excludeConfigs: sockJson.defaults?.manifest?.sbt?.excludeConfigs ?? '', - ignoreUnresolved: resolveIgnoreUnresolved( - Boolean(sockJson.defaults?.manifest?.sbt?.ignoreUnresolved), + ignoreUnresolved: Boolean( + sockJson.defaults?.manifest?.sbt?.ignoreUnresolved, ), includeConfigs: sockJson.defaults?.manifest?.sbt?.includeConfigs ?? '', sidecarAcc, @@ -141,8 +133,8 @@ export async function generateAutoManifest({ ...gradleArgs, excludeConfigs: sockJson.defaults?.manifest?.gradle?.excludeConfigs ?? '', - ignoreUnresolved: resolveIgnoreUnresolved( - Boolean(sockJson.defaults?.manifest?.gradle?.ignoreUnresolved), + ignoreUnresolved: Boolean( + sockJson.defaults?.manifest?.gradle?.ignoreUnresolved, ), includeConfigs: sockJson.defaults?.manifest?.gradle?.includeConfigs ?? '', @@ -170,8 +162,8 @@ export async function generateAutoManifest({ resolveBuildToolBin('maven', cwd), cwd, excludeConfigs: sockJson.defaults?.manifest?.maven?.excludeConfigs ?? '', - ignoreUnresolved: resolveIgnoreUnresolved( - Boolean(sockJson.defaults?.manifest?.maven?.ignoreUnresolved), + ignoreUnresolved: Boolean( + sockJson.defaults?.manifest?.maven?.ignoreUnresolved, ), includeConfigs: sockJson.defaults?.manifest?.maven?.includeConfigs ?? '', mavenOpts: parseBuildToolOpts( diff --git a/src/commands/scan/handle-create-new-scan.mts b/src/commands/scan/handle-create-new-scan.mts index 1e2fb100d..bd9bb0faf 100644 --- a/src/commands/scan/handle-create-new-scan.mts +++ b/src/commands/scan/handle-create-new-scan.mts @@ -143,7 +143,6 @@ export async function handleCreateNewScan({ cwd, detected, outputKind, - reachContinueOnInstallErrors: reach.reachContinueOnInstallErrors, verbose: false, }) resolvedPathsSidecar = autoManifestResult.resolvedPathsSidecar