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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ 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]

### Changed
- Modules outside the scan root can no longer be excluded with `--exclude-paths`; `**/x` used to match a `../x` module by accident. Single-root runs whose Maven or Gradle options relocate the build root (`-f`, `-p`) now match patterns relative to the current directory, as documented.

### Fixed
- `--exclude-paths` patterns are now matched relative to the scan root in every build root. They now also exclude modules a build reaches outside its own directory (Maven `../` modules, relocated Gradle projects) and modules matched by wildcards that span a build-root boundary, such as `*/core`.
- `--exclude-paths` patterns written with Windows `\` separators now exclude JVM build modules in single-root runs too.

## [1.12.2](https://github.com/SocketDev/socket-cli/releases/tag/v1.12.2) - 2026-10-30

### Changed
Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-auto.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('socket manifest auto', async () => {
$ socket manifest auto [options] [CWD=.]

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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.
--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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project. A build module outside CWD cannot be excluded.
--verbose Enable debug output (only for auto itself; sub-steps need to have it pre-configured), may help when running into errors

Tries to figure out what language your target repo uses. If it finds a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('socket manifest dynamic-sbom-inference', async () => {
CWD itself.

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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.
--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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project. A build module outside CWD cannot be excluded.
--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
Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-gradle.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('socket manifest gradle', async () => {
Options
--bin Location of the gradle binary to use, default: ./gradlew if present, else gradle on PATH
--exclude-configs When generating facts: comma-separated glob patterns; Gradle configurations matching any pattern are skipped (applied after --include-configs)
--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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.
--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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project. A build module outside CWD cannot be excluded.
--facts Emit a Socket facts JSON file (\`gradle.socket.facts.json\`) describing the resolved dependency graph. This is the default; pass \`--pom\` to generate \`pom.xml\` files instead
--gradle-opts Additional options to pass on to ./gradlew, see \`./gradlew --help\`
--ignore-unresolved When generating facts: warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)
Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-kotlin.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('socket manifest kotlin', async () => {
Options
--bin Location of the gradle binary to use, default: ./gradlew if present, else gradle on PATH
--exclude-configs When generating facts: comma-separated glob patterns; Gradle configurations matching any pattern are skipped (applied after --include-configs)
--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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.
--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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project. A build module outside CWD cannot be excluded.
--facts Emit a Socket facts JSON file (\`gradle.socket.facts.json\`) describing the resolved dependency graph. This is the default; pass \`--pom\` to generate \`pom.xml\` files instead
--gradle-opts Additional options to pass on to ./gradlew, see \`./gradlew --help\`
--ignore-unresolved When generating facts: warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)
Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-maven.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('socket manifest maven', async () => {
Options
--bin Location of the maven binary to use, default: ./mvnw if present, else mvn on PATH
--exclude-configs Comma-separated glob patterns; Maven scopes matching any pattern are skipped (applied after --include-configs)
--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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.
--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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project. A build module outside CWD cannot be excluded.
--ignore-unresolved Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)
--include-configs Comma-separated glob patterns matched against Maven dependency scopes (case-sensitive; \`*\`, \`?\`, and \`[...]\` wildcards). Only scopes matching at least one pattern are resolved. e.g. \`compile,runtime\`. Default: every scope
--maven-opts Additional options to pass on to maven, e.g. \`-P <profile> -s <settings.xml>\`
Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-scala.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('socket manifest scala', async () => {
Options
--bin Location of sbt binary to use
--exclude-configs When generating facts: comma-separated glob patterns; sbt configurations matching any pattern are skipped (applied after --include-configs)
--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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.
--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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project. A build module outside CWD cannot be excluded.
--facts Emit a Socket facts JSON file (\`sbt.socket.facts.json\`) describing the resolved dependency graph. This is the default; pass \`--pom\` to generate \`pom.xml\` files instead
--ignore-unresolved When generating facts: warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)
--include-configs When generating facts: comma-separated glob patterns matched against sbt configuration names (case-sensitive; \`*\`, \`?\`, and \`[...]\` wildcards). Only configurations matching at least one pattern are resolved. e.g. \`compile,test\`. Default: compile,optional,provided,runtime,test
Expand Down
4 changes: 4 additions & 0 deletions src/commands/manifest/enumerate-workspaces.mts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export async function enumerateWorkspaces({
cwd,
ecosystem,
entryFile,
excludeBase,
excludePaths,
javaHome,
verbose,
Expand All @@ -35,6 +36,8 @@ export async function enumerateWorkspaces({
ecosystem: BuildTool
// See ManifestScriptOptions.entryFile.
entryFile?: string | undefined
// See ManifestScriptOptions.excludeBase.
excludeBase?: string | undefined
excludePaths?: string[] | undefined
javaHome?: string | undefined
verbose: boolean
Expand All @@ -55,6 +58,7 @@ export async function enumerateWorkspaces({
const scriptOpts = {
bin: bin || undefined,
entryFile,
excludeBase,
excludePaths: excludePaths?.length ? excludePaths : undefined,
// `env` replaces the spawned process's whole environment, not just JAVA_HOME.
env: resolvedJavaHome
Expand Down
20 changes: 6 additions & 14 deletions src/commands/manifest/generate-recursive-manifests.mts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
readOrDefaultSocketJson,
readSocketJsonCascade,
} from '../../utils/socket-json.mts'
import { projectIgnorePathsToReachExcludePaths } from '../scan/exclude-paths.mts'

import type { BuildRoot } from './discover-manifest-roots.mts'
import type { BuildTool } from './scripts/build-tool.mts'
Expand Down Expand Up @@ -171,10 +170,7 @@ async function runEcosystemCandidates({
const covered = new Set<string>()
for (const [ecosystem, roots] of candidatesByTool) {
const disabledRoots: DisabledRoot[] = []
const configByDir = new Map<
string,
EcosystemBuildConfig & { excludePaths: string[] }
>()
const configByDir = new Map<string, EcosystemBuildConfig>()
for (const { 0: rootIndex, 1: root } of roots.entries()) {
const { dir, entryFile } = root
if (isBuildRootCovered(covered, ecosystem, root)) {
Expand All @@ -188,13 +184,7 @@ async function runEcosystemCandidates({
const sockJson = nearestRoot
? readSocketJsonCascade(dir, nearestRoot.dir, nearestRoot.sockJson)
: readSocketJsonCascade(dir, realCwd, rootSockJson)
dirConfig = {
...resolveEcosystemConfig(ecosystem, dir, sockJson),
excludePaths: projectIgnorePathsToReachExcludePaths(excludePaths, {
cwd: realCwd,
target: dir,
}),
}
dirConfig = resolveEcosystemConfig(ecosystem, dir, sockJson)
configByDir.set(dir, dirConfig)
if (dirConfig.skipReason) {
// Only warn for a genuinely new disabled root, not one already
Expand All @@ -214,7 +204,6 @@ async function runEcosystemCandidates({
bin,
buildOpts,
excludeConfigs,
excludePaths: excludePathsForRoot,
ignoreUnresolved,
includeConfigs,
javaHome,
Expand All @@ -234,7 +223,10 @@ async function runEcosystemCandidates({
ecosystem,
entryFile,
excludeConfigs,
excludePaths: excludePathsForRoot,
// Relative to the scan root: a build can reach modules outside its
// own directory (Maven `../x` modules, relocated Gradle projects).
excludeBase: realCwd,
excludePaths,
hints: {
continueOnInstallErrorsFlag: withFiles
? REACH_CONTINUE_ON_INSTALL_ERRORS_FLAG
Expand Down
22 changes: 22 additions & 0 deletions src/commands/manifest/generate-recursive-manifests.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,28 @@ describe('generateRecursiveManifests', () => {
expect(byKey.get('maven:dual-marker-dir')).toBe('empty')
})

it('passes every build root the scan-root-relative --exclude-paths and the scan root', async () => {
vi.mocked(runManifestFacts).mockImplementation(async ({ cwd }) => ({
factsPath: path.join(cwd, '.socket.facts.json'),
projects: [],
}))

await generateRecursiveManifests({
cwd: monorepo,
excludePaths: ['reactor/moduleB'],
verbose: false,
})

const reactorCall = vi
.mocked(runManifestFacts)
.mock.calls.map(([opts]) => opts)
.find(opts => opts.cwd === reactor)
expect(reactorCall).toMatchObject({
excludeBase: await fs.realpath(monorepo),
excludePaths: ['reactor/moduleB'],
})
})

it('resolves each build root its own nearest socket.json instead of only the root config', async () => {
vi.mocked(readSocketJsonCascade).mockImplementation(
(dir, _boundaryDir, fallback) =>
Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/manifest-flags.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ export const excludePathsFlag: MeowFlags = {
type: 'string',
isMultiple: true,
description:
'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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.',
'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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project. A build module outside CWD cannot be excluded.',
},
}
4 changes: 4 additions & 0 deletions src/commands/manifest/run-manifest-facts.mts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export async function runManifestFacts({
cwd,
ecosystem,
entryFile,
excludeBase,
excludeConfigs,
excludePaths,
hints = FLAG_HINTS,
Expand All @@ -79,6 +80,8 @@ export async function runManifestFacts({
ecosystem: BuildTool
// See ManifestScriptOptions.entryFile.
entryFile?: string | undefined
// See ManifestScriptOptions.excludeBase.
excludeBase?: string | undefined
excludeConfigs: string
excludePaths?: string[] | undefined
// Where the user sets build options, for remedy hints.
Expand Down Expand Up @@ -122,6 +125,7 @@ export async function runManifestFacts({
bin: bin || undefined,
entryFile,
excludeConfigs: excludeConfigs || undefined,
excludeBase,
excludePaths: excludePaths?.length ? excludePaths : undefined,
// `env` replaces the spawned process's whole environment, not just JAVA_HOME.
env: resolvedJavaHome
Expand Down
5 changes: 4 additions & 1 deletion src/commands/manifest/scripts/exclude-paths-glob.mts
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,13 @@ export function excludePathGlobToRegexSource(glob: string): string {
}
}

// A Windows-style `foo\bar` means the path `foo/bar`.
export function compileExcludePathPatterns(
paths: readonly string[] | undefined,
): string[] {
return (paths ?? []).map(excludePathGlobToRegexSource)
return (paths ?? []).map(p =>
excludePathGlobToRegexSource(p.replaceAll('\\', '/')),
)
}

// Transport form handed to the build-tool scripts: comma-joined pattern
Expand Down
6 changes: 6 additions & 0 deletions src/commands/manifest/scripts/exclude-paths-glob.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ const MATCH_VECTORS: Array<{
matches: ['ax', '&x'],
rejects: ['bx'],
},
// A Windows path separator.
{
glob: 'app\\core',
matches: ['app/core', 'app/core/x'],
rejects: ['app', 'core'],
},
]

describe('exclude-paths-glob vectors (cross-language contract)', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public void afterSessionEnd(MavenSession session) throws MavenExecutionException
opts.includeConfigs = opt(session, "socket.includeConfigs");
opts.excludeConfigs = opt(session, "socket.excludeConfigs");
opts.excludePaths = opt(session, "socket.excludePaths");
opts.excludeBase = opt(session, "socket.excludeBase");
File rootDir = new File(session.getExecutionRootDirectory());
CollectionObserver observer = new CollectionObserver();
CoanaFactsEventSpy.observe(observer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public void afterProjectsRead(MavenSession session) throws MavenExecutionExcepti
SocketWorkspacesRecordsEngine.Options opts = new SocketWorkspacesRecordsEngine.Options();
opts.recordsFile = recordsFile;
opts.excludePaths = opt(session, "socket.excludePaths");
opts.excludeBase = opt(session, "socket.excludeBase");
File rootDir = new File(session.getExecutionRootDirectory());
try {
SocketWorkspacesRecordsEngine.run(session.getProjects(), rootDir, opts, runtimeInformation.getMavenVersion());
Expand Down
Loading
Loading