Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ 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/).

## [Unreleased]

### Added
- `socket manifest dynamic-sbom-inference --ignore-unresolved` keeps going past unresolved JVM dependencies in every discovered build.

### Fixed
- `--dynamic-sbom-inference` honors each build's `verbose` setting in `socket.json`.
- JVM resolution failures name the dependency that failed, the subproject and scope that needed it, and suggest only options the command you ran accepts, pointing at the failing build's own `socket.json`.
- Unresolved JVM dependencies are reported and left out of the Socket facts for Maven, Gradle and sbt alike, as far as each build tool identifies them; a Maven dependency in an excluded scope no longer blocks the run.
- An sbt project with an unresolved dependency keeps the rest of its dependencies instead of silently losing all of them.
- `socket scan create` and `socket scan reach` warn about `--ignore-unresolved`, `--exclude-configs`, `--include-configs` and `--verbose`, which they read from `socket.json` instead.
- When a build tool cannot be started, the error says why, for example a missing `mvn`.

## [1.9.1](https://github.com/SocketDev/socket-cli/releases/tag/v1.9.1) - 2026-10-21

### Changed
Expand Down
8 changes: 8 additions & 0 deletions src/commands/manifest/cmd-manifest-dynamic-sbom-inference.mts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ const config: CliCommandConfig = {
...commonFlags,
...outputFlags,
...excludePathsFlag,
ignoreUnresolved: {
type: 'boolean',
description:
"Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts files); overrides each build's socket.json setting",
},
verbose: {
type: 'boolean',
default: false,
Expand Down Expand Up @@ -76,11 +81,13 @@ async function run(

const {
dryRun,
ignoreUnresolved,
json,
markdown,
verbose: verboseFlag,
} = cli.flags as {
dryRun: boolean
ignoreUnresolved: boolean | undefined
json: boolean
markdown: boolean
verbose: boolean | undefined
Expand Down Expand Up @@ -134,6 +141,7 @@ async function run(
await handleManifestDynamicSbomInference({
cwd,
excludePaths,
ignoreUnresolved,
outputKind,
verbose,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('socket manifest dynamic-sbom-inference', async () => {

Options
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`tests\` matches only \`<cwd>/tests\`; use \`**/tests\` to match at any depth. Negation patterns (\`!path\`) are not supported. Accepts a comma-separated value or multiple flags.
--ignore-unresolved Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts files); overrides each build's socket.json setting
--json Output as JSON
--markdown Output as Markdown
--verbose Print debug messages
Expand Down
4 changes: 4 additions & 0 deletions src/commands/manifest/convert-gradle-to-facts.mts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { runManifestFacts } from './run-manifest-facts.mts'

import type { BuildOptionHints } from './scripts/resolution-report-render.mts'
import type { SidecarAccumulator } from './scripts/sidecar.mts'

// Generates `gradle.socket.facts.json` for a Gradle project via the bundled
Expand All @@ -10,6 +11,7 @@ export async function convertGradleToFacts({
excludeConfigs,
excludePaths,
gradleOpts,
hints,
ignoreUnresolved,
includeConfigs,
javaHome,
Expand All @@ -22,6 +24,7 @@ export async function convertGradleToFacts({
excludeConfigs: string
excludePaths?: string[] | undefined
gradleOpts: string[]
hints?: BuildOptionHints | undefined
ignoreUnresolved: boolean
includeConfigs: string
javaHome?: string | undefined
Expand All @@ -36,6 +39,7 @@ export async function convertGradleToFacts({
ecosystem: 'gradle',
excludeConfigs,
excludePaths,
hints,
ignoreUnresolved,
includeConfigs,
javaHome,
Expand Down
4 changes: 4 additions & 0 deletions src/commands/manifest/convert-maven-to-facts.mts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { runManifestFacts } from './run-manifest-facts.mts'

import type { BuildOptionHints } from './scripts/resolution-report-render.mts'
import type { SidecarAccumulator } from './scripts/sidecar.mts'

// Generates `pom.xml.socket.facts.json` (named after the POM Maven runs on)
Expand All @@ -9,6 +10,7 @@ export async function convertMavenToFacts({
cwd,
excludeConfigs,
excludePaths,
hints,
ignoreUnresolved,
includeConfigs,
javaHome,
Expand All @@ -21,6 +23,7 @@ export async function convertMavenToFacts({
cwd: string
excludeConfigs: string
excludePaths?: string[] | undefined
hints?: BuildOptionHints | undefined
ignoreUnresolved: boolean
includeConfigs: string
javaHome?: string | undefined
Expand All @@ -36,6 +39,7 @@ export async function convertMavenToFacts({
ecosystem: 'maven',
excludeConfigs,
excludePaths,
hints,
ignoreUnresolved,
includeConfigs,
javaHome,
Expand Down
4 changes: 4 additions & 0 deletions src/commands/manifest/convert-sbt-to-facts.mts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { runManifestFacts } from './run-manifest-facts.mts'

import type { BuildOptionHints } from './scripts/resolution-report-render.mts'
import type { SidecarAccumulator } from './scripts/sidecar.mts'

// Generates `sbt.socket.facts.json` for an sbt project via the bundled sbt plugin.
Expand All @@ -10,6 +11,7 @@ export async function convertSbtToFacts({
cwd,
excludeConfigs,
excludePaths,
hints,
ignoreUnresolved,
includeConfigs,
javaHome,
Expand All @@ -23,6 +25,7 @@ export async function convertSbtToFacts({
cwd: string
excludeConfigs: string
excludePaths?: string[] | undefined
hints?: BuildOptionHints | undefined
ignoreUnresolved: boolean
includeConfigs: string
javaHome?: string | undefined
Expand All @@ -40,6 +43,7 @@ export async function convertSbtToFacts({
ecosystem: 'sbt',
excludeConfigs,
excludePaths,
hints,
ignoreUnresolved,
includeConfigs,
javaHome,
Expand Down
5 changes: 1 addition & 4 deletions src/commands/manifest/enumerate-workspaces.mts
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,7 @@ export async function enumerateWorkspaces({
} catch (e) {
process.exitCode = 1
logger.fail(
`Could not run the ${ecosystem} build tool` +
(verbose
? `: ${getErrorMessageOr(e, String(e))}`
: ' (run with --verbose for details).'),
`Could not run the ${ecosystem} build tool: ${getErrorMessageOr(e, String(e))}`,
)
return
}
Expand Down
58 changes: 47 additions & 11 deletions src/commands/manifest/generate-recursive-manifests.mts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
import { parseBuildToolOpts } from './parse-build-tool-opts.mts'
import { runManifestFacts } from './run-manifest-facts.mts'
import { resolveBuildToolBin } from './scripts/build-tool.mts'
import { DSI_SETUP_COMMAND } from './scripts/resolution-report-render.mts'
import { SOCKET_JSON } from '../../constants.mts'
import { withTmpDir } from '../../utils/fs.mts'
import {
readOrDefaultSocketJson,
Expand All @@ -20,6 +22,7 @@ import {
import { projectIgnorePathsToReachExcludePaths } from '../scan/exclude-paths.mts'

import type { BuildTool } from './scripts/build-tool.mts'
import type { BuildOption } from './scripts/resolution-report-render.mts'
import type { SidecarAccumulator } from './scripts/sidecar.mts'
import type { SocketJson } from '../../utils/socket-json.mts'

Expand Down Expand Up @@ -47,21 +50,29 @@ export type EcosystemBuildConfig = {
javaHome: string | undefined
// Set when this build root should be skipped entirely (never invoked).
skipReason: string | undefined
verbose: boolean
}

// Relative to the user's shell when inside it, else absolute.
function displayPath(file: string): string {
const rel = path.relative(process.cwd(), file)
return rel.startsWith('..') || path.isAbsolute(rel) ? file : rel
}

// A cascaded (not just root-level) disabled additionally skips this one build
// root here, on top of its existing root-only ecosystem-wide meaning for
// auto/gradle/etc. facts:false has no pom-mode equivalent here (facts-only),
// so it skips too.
function getSkipReason(
ecosystem: BuildTool,
disabled: boolean | undefined,
facts?: boolean | undefined,
): string | undefined {
if (disabled) {
return 'defaults.manifest.<ecosystem>.disabled is true'
return `defaults.manifest.${ecosystem}.disabled is true`
}
if (facts === false) {
return 'defaults.manifest.<ecosystem>.facts is false (pom mode)'
return `defaults.manifest.${ecosystem}.facts is false (pom mode)`
}
return undefined
}
Expand Down Expand Up @@ -90,6 +101,8 @@ function nearestDisabledRoot(
async function runEcosystemCandidates({
candidatesByTool,
excludePaths,
ignoreUnresolved: ignoreUnresolvedOverride,
optionFlags,
realCwd,
rootSockJson,
sbtTmpDir,
Expand All @@ -99,6 +112,8 @@ async function runEcosystemCandidates({
}: {
candidatesByTool: Map<BuildTool, string[]>
excludePaths: string[] | undefined
ignoreUnresolved: boolean | undefined
optionFlags: ReadonlySet<BuildOption>
realCwd: string
rootSockJson: SocketJson
// sbt only: a shared global base reused across every sbt root in this run,
Expand Down Expand Up @@ -133,6 +148,7 @@ async function runEcosystemCandidates({
includeConfigs,
javaHome,
skipReason,
verbose: configVerbose,
} = resolveEcosystemConfig(ecosystem, dir, sockJson)

if (skipReason) {
Expand Down Expand Up @@ -162,21 +178,29 @@ async function runEcosystemCandidates({
ecosystem,
excludeConfigs,
excludePaths: excludePathsForRoot,
ignoreUnresolved,
hints: {
flags: optionFlags,
setupCommand: DSI_SETUP_COMMAND,
socketJson: displayPath(path.join(dir, SOCKET_JSON)),
},
ignoreUnresolved: ignoreUnresolvedOverride ?? ignoreUnresolved,
includeConfigs,
javaHome,
sidecarAcc,
tmpDir: ecosystem === 'sbt' ? sbtTmpDir : undefined,
verbose,
verbose: verbose || configVerbose,
Comment thread
jfblaa marked this conversation as resolved.
withFiles,
})

if (result === null) {
outcomes.push({ dir, ecosystem, status: 'failed' })
logger.warn(
`Aborting ${ecosystem} discovery: ${dir}'s workspace layout could not be determined, so its remaining build roots cannot be safely classified as covered or independent.`,
)
for (const abortedDir of dirs.slice(dirIndex + 1)) {
const abortedDirs = dirs.slice(dirIndex + 1)
if (abortedDirs.length) {
logger.warn(
`Aborting ${ecosystem} discovery: ${dir} failed to generate Socket facts, and without them the remaining build roots cannot be safely classified as covered or independent.`,
)
}
for (const abortedDir of abortedDirs) {
outcomes.push({ dir: abortedDir, ecosystem, status: 'aborted' })
}
break
Expand Down Expand Up @@ -209,13 +233,20 @@ async function runEcosystemCandidates({
export async function generateRecursiveManifests({
cwd,
excludePaths,
ignoreUnresolved,
optionFlags = new Set(),
sbtTmpDir,
sidecarAcc,
verbose,
withFiles,
}: {
cwd: string
excludePaths?: string[] | undefined
// When set, overrides every build root's own socket.json setting.
ignoreUnresolved?: boolean | undefined
// Options the invoking command takes as flags; hints send the rest to each
// build root's socket.json.
optionFlags?: ReadonlySet<BuildOption> | undefined
// Reachability path only: caller-supplied directory to use as sbt's shared
// global base across every sbt root in this run. sbt provisions the Scala
// toolchain under `<base>/boot`, which withFiles' artifactPaths point into,
Expand Down Expand Up @@ -250,6 +281,8 @@ export async function generateRecursiveManifests({
runEcosystemCandidates({
candidatesByTool,
excludePaths,
ignoreUnresolved,
optionFlags,
realCwd,
rootSockJson,
sbtTmpDir: resolvedSbtTmpDir,
Expand Down Expand Up @@ -296,7 +329,8 @@ export function resolveEcosystemConfig(
ignoreUnresolved: Boolean(config?.ignoreUnresolved),
includeConfigs: config?.includeConfigs ?? '',
javaHome: config?.javaHome ?? undefined,
skipReason: getSkipReason(config?.disabled, config?.facts),
skipReason: getSkipReason(ecosystem, config?.disabled, config?.facts),
verbose: Boolean(config?.verbose),
}
}
if (ecosystem === 'gradle') {
Expand All @@ -309,7 +343,8 @@ export function resolveEcosystemConfig(
ignoreUnresolved: Boolean(config?.ignoreUnresolved),
includeConfigs: config?.includeConfigs ?? '',
javaHome: config?.javaHome ?? undefined,
skipReason: getSkipReason(config?.disabled, config?.facts),
skipReason: getSkipReason(ecosystem, config?.disabled, config?.facts),
verbose: Boolean(config?.verbose),
}
}
const config = sockJson.defaults?.manifest?.maven
Expand All @@ -321,6 +356,7 @@ export function resolveEcosystemConfig(
ignoreUnresolved: Boolean(config?.ignoreUnresolved),
includeConfigs: config?.includeConfigs ?? '',
javaHome: config?.javaHome ?? undefined,
skipReason: getSkipReason(config?.disabled),
skipReason: getSkipReason(ecosystem, config?.disabled),
verbose: Boolean(config?.verbose),
}
}
Loading
Loading