Skip to content

Commit ac95036

Browse files
committed
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.
1 parent 48c4d3f commit ac95036

4 files changed

Lines changed: 84 additions & 23 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

7+
## [1.1.133](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.133) - 2026-07-01
8+
9+
### Changed
10+
- 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.
11+
712
## [1.1.132](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.132) - 2026-06-30
813

914
### Changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "socket",
3-
"version": "1.1.132",
3+
"version": "1.1.133",
44
"description": "CLI for Socket.dev",
55
"homepage": "https://github.com/SocketDev/socket-cli",
66
"license": "MIT",

src/commands/manifest/run-manifest-facts.mts

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,21 @@ import constants from '../../constants.mts'
1010
import { InputError } from '../../utils/errors.mts'
1111

1212
import type { BuildTool } from './scripts/build-tool.mts'
13+
import type { ManifestRunResult } from './scripts/run.mts'
1314
import type { SidecarAccumulator } from './scripts/sidecar.mts'
1415

16+
const MAX_FAILURE_OUTPUT_LINES = 40
17+
18+
// Last N non-empty lines of the captured build output, for diagnosing a crash
19+
// without forcing a --verbose rebuild.
20+
function tailBuildOutput(stdout: string, stderr: string): string {
21+
const combined = [stdout, stderr]
22+
.map(s => s.trimEnd())
23+
.filter(Boolean)
24+
.join('\n')
25+
return combined.split('\n').slice(-MAX_FAILURE_OUTPUT_LINES).join('\n')
26+
}
27+
1528
// Runs the bundled build-tool resolution script for a JVM project and writes
1629
// `.socket.facts.json`. `withFiles` (reachability only) additionally folds
1730
// resolved artifact paths into `sidecarAcc`. A blocking resolution failure — or
@@ -46,17 +59,32 @@ export async function runManifestFacts({
4659
`Generating Socket facts for the ${ecosystem} project at \`${cwd}\` ...`,
4760
)
4861

49-
const { artifactPaths, code, facts, report } = await runManifestScript(
50-
ecosystem,
51-
{
52-
bin: bin || undefined,
53-
excludeConfigs: excludeConfigs || undefined,
54-
includeConfigs: includeConfigs || undefined,
55-
projectDir: cwd,
56-
toolOpts: buildOpts,
57-
withFiles,
58-
},
59-
)
62+
const scriptOpts = {
63+
bin: bin || undefined,
64+
excludeConfigs: excludeConfigs || undefined,
65+
includeConfigs: includeConfigs || undefined,
66+
projectDir: cwd,
67+
// Stream the build tool's output only when asked; otherwise capture it and
68+
// show a spinner, surfacing the output only if the build crashes.
69+
stdio: verbose ? ('inherit' as const) : ('pipe' as const),
70+
toolOpts: buildOpts,
71+
withFiles,
72+
}
73+
const { spinner } = constants
74+
let result: ManifestRunResult
75+
if (verbose) {
76+
result = await runManifestScript(ecosystem, scriptOpts)
77+
} else {
78+
spinner.start(
79+
`Resolving ${ecosystem} dependencies (pass --verbose to stream build output) ...`,
80+
)
81+
try {
82+
result = await runManifestScript(ecosystem, scriptOpts)
83+
} finally {
84+
spinner.stop()
85+
}
86+
}
87+
const { artifactPaths, code, facts, report, stderr, stdout } = result
6088

6189
const rendered = renderResolutionErrorReport(
6290
report.failures,
@@ -94,7 +122,15 @@ export async function runManifestFacts({
94122
!facts.projects?.length &&
95123
!report.failures.length
96124
) {
97-
const message = `The ${ecosystem} build failed (exit code ${code}) before producing any Socket facts. Re-run with --verbose for the build tool's output.`
125+
if (!verbose) {
126+
const tail = tailBuildOutput(stdout, stderr)
127+
if (tail) {
128+
logger.group('Build output:')
129+
logger.error(tail)
130+
logger.groupEnd()
131+
}
132+
}
133+
const message = `The ${ecosystem} build failed (exit code ${code}) before producing any Socket facts.`
98134
if (!ignoreUnresolved) {
99135
throw new InputError(message)
100136
}

src/commands/manifest/scripts/run.mts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { existsSync, promises as fs } from 'node:fs'
22
import { tmpdir } from 'node:os'
33
import path from 'node:path'
44

5-
import { isSpawnError, spawn } from '@socketsecurity/registry/lib/spawn'
5+
import { spawn } from '@socketsecurity/registry/lib/spawn'
66

77
import { assembleFacts } from './assemble.mts'
88
import { resolveBuildToolBin } from './build-tool.mts'
@@ -34,6 +34,9 @@ export type ManifestRunResult = {
3434
facts: SocketFactsSbom
3535
report: ResolutionReport
3636
artifactPaths: ResolvedArtifactPaths
37+
// Captured build-tool output (empty when stdio is 'inherit').
38+
stderr: string
39+
stdout: string
3740
}
3841

3942
type RunOutput = { code: number; stdout: string; stderr: string }
@@ -67,11 +70,21 @@ async function runNeverThrow(
6770
stderr: typeof result.stderr === 'string' ? result.stderr : '',
6871
}
6972
} catch (e) {
70-
if (isSpawnError(e)) {
73+
// A non-zero exit rejects with the spawn-result shape: a numeric `code` plus
74+
// captured stdout/stderr. Return it so the caller can assemble failure
75+
// records. Anything else (e.g. a missing executable, whose `code` is the
76+
// string 'ENOENT') propagates. Duck-typed on purpose: the registry's
77+
// isSpawnError is unreliable, so the numeric-code check is the real signal.
78+
if (
79+
e !== null &&
80+
typeof e === 'object' &&
81+
typeof (e as { code?: unknown }).code === 'number'
82+
) {
83+
const err = e as { code: number; stdout?: unknown; stderr?: unknown }
7184
return {
72-
code: e.code,
73-
stdout: typeof e.stdout === 'string' ? e.stdout : '',
74-
stderr: typeof e.stderr === 'string' ? e.stderr : '',
85+
code: err.code,
86+
stdout: typeof err.stdout === 'string' ? err.stdout : '',
87+
stderr: typeof err.stderr === 'string' ? err.stderr : '',
7588
}
7689
}
7790
throw e
@@ -101,14 +114,21 @@ async function writeSbtPlugin(globalBase: string): Promise<void> {
101114
}
102115

103116
async function assembleFromRecords(
104-
code: number,
117+
out: RunOutput,
105118
recordsFile: string,
106119
): Promise<ManifestRunResult> {
107120
const text = existsSync(recordsFile)
108121
? await fs.readFile(recordsFile, 'utf8')
109122
: ''
110123
const { artifactPaths, facts, report } = assembleFacts(parseRecords(text))
111-
return { code, facts, report, artifactPaths }
124+
return {
125+
code: out.code,
126+
facts,
127+
report,
128+
artifactPaths,
129+
stderr: out.stderr,
130+
stdout: out.stdout,
131+
}
112132
}
113133

114134
// Missing only in an unbuilt local checkout. Fail loudly: without the extension,
@@ -179,7 +199,7 @@ async function runGradle(
179199
'--console=plain',
180200
]
181201
const out = await runNeverThrow(bin, args, opts)
182-
return await assembleFromRecords(out.code, recordsFile)
202+
return await assembleFromRecords(out, recordsFile)
183203
})
184204
}
185205

@@ -211,7 +231,7 @@ async function runSbt(opts: ManifestScriptOptions): Promise<ManifestRunResult> {
211231
FACTS_TASK,
212232
]
213233
const out = await runNeverThrow(bin, args, opts)
214-
return await assembleFromRecords(out.code, recordsFile)
234+
return await assembleFromRecords(out, recordsFile)
215235
})
216236
}
217237

@@ -241,6 +261,6 @@ async function runMaven(
241261
'validate',
242262
]
243263
const out = await runNeverThrow(bin, args, opts)
244-
return await assembleFromRecords(out.code, recordsFile)
264+
return await assembleFromRecords(out, recordsFile)
245265
})
246266
}

0 commit comments

Comments
 (0)