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
14 changes: 4 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,14 @@ 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]
## [1.1.136](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.136) - 2026-07-02

### Changed
- `socket manifest gradle --facts` no longer silently skips a Gradle
configuration it can't resolve. Such configurations are now reported and stop
the run unless you pass `--ignore-unresolved`, so an incomplete scan can't slip
by unnoticed. Benign variant-selection ambiguity stays a one-line notice.
- `socket manifest gradle --facts` now reports Gradle configurations it can't resolve instead of silently skipping them, failing the run unless you pass `--ignore-unresolved` so an incomplete scan can't slip by. Benign variant-selection ambiguity stays a one-line notice.
- Config filters (`--include-configs` / `--exclude-configs`) for `socket manifest gradle`, `kotlin`, `scala`, and `maven` are now case-sensitive, as documented, and support `[...]` character classes — e.g. `*[Tt]est*` matches both `testCompileClasspath` and `androidTestCompileClasspath`.

### Fixed
- `socket manifest auto` and `scan create --auto-manifest` now detect a Gradle
project by its build files (`build.gradle`/`.kts` or `settings.gradle`/`.kts`)
instead of requiring a `gradlew` wrapper, so a project that builds with
`gradle` on your PATH — including a settings-only multi-module root — is no
longer skipped.
- `socket manifest auto` and `socket scan create --auto-manifest` now detect a Gradle project by its build files (`build.gradle`/`.kts` or `settings.gradle`/`.kts`) instead of requiring a `gradlew` wrapper, so wrapper-less projects — including settings-only multi-module roots — are no longer skipped.

## [1.1.135](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.135) - 2026-07-01

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "socket",
"version": "1.1.135",
"version": "1.1.136",
"description": "CLI for Socket.dev",
"homepage": "https://github.com/SocketDev/socket-cli",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-gradle.mts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const config: CliCommandConfig = {
includeConfigs: {
type: 'string',
description:
'When generating facts: comma-separated glob patterns matched against Gradle configuration names (case-sensitive, `*` and `?` wildcards). Only configurations matching at least one pattern are resolved. e.g. `*CompileClasspath,*RuntimeClasspath`. Default: every resolvable configuration',
'When generating facts: comma-separated glob patterns matched against Gradle configuration names (case-sensitive; `*`, `?`, and `[...]` wildcards). Only configurations matching at least one pattern are resolved. e.g. `*CompileClasspath,*RuntimeClasspath`. Default: every resolvable configuration',
},
excludeConfigs: {
type: 'string',
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 @@ -28,7 +28,7 @@ describe('socket manifest gradle', async () => {
--facts Emit a Socket facts JSON file (\`.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)
--include-configs When generating facts: comma-separated glob patterns matched against Gradle configuration names (case-sensitive, \`*\` and \`?\` wildcards). Only configurations matching at least one pattern are resolved. e.g. \`*CompileClasspath,*RuntimeClasspath\`. Default: every resolvable configuration
--include-configs When generating facts: comma-separated glob patterns matched against Gradle configuration names (case-sensitive; \`*\`, \`?\`, and \`[...]\` wildcards). Only configurations matching at least one pattern are resolved. e.g. \`*CompileClasspath,*RuntimeClasspath\`. Default: every resolvable configuration
--pom Generate \`pom.xml\` manifest file(s) instead of the default Socket facts file (\`.socket.facts.json\`)
--verbose Print debug messages

Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-kotlin.mts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const config: CliCommandConfig = {
includeConfigs: {
type: 'string',
description:
'When generating facts: comma-separated glob patterns matched against Gradle configuration names (case-sensitive, `*` and `?` wildcards). Only configurations matching at least one pattern are resolved. e.g. `*CompileClasspath,*RuntimeClasspath`. Default: every resolvable configuration',
'When generating facts: comma-separated glob patterns matched against Gradle configuration names (case-sensitive; `*`, `?`, and `[...]` wildcards). Only configurations matching at least one pattern are resolved. e.g. `*CompileClasspath,*RuntimeClasspath`. Default: every resolvable configuration',
},
excludeConfigs: {
type: 'string',
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 @@ -28,7 +28,7 @@ describe('socket manifest kotlin', async () => {
--facts Emit a Socket facts JSON file (\`.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)
--include-configs When generating facts: comma-separated glob patterns matched against Gradle configuration names (case-sensitive, \`*\` and \`?\` wildcards). Only configurations matching at least one pattern are resolved. e.g. \`*CompileClasspath,*RuntimeClasspath\`. Default: every resolvable configuration
--include-configs When generating facts: comma-separated glob patterns matched against Gradle configuration names (case-sensitive; \`*\`, \`?\`, and \`[...]\` wildcards). Only configurations matching at least one pattern are resolved. e.g. \`*CompileClasspath,*RuntimeClasspath\`. Default: every resolvable configuration
--pom Generate \`pom.xml\` manifest file(s) instead of the default Socket facts file (\`.socket.facts.json\`)
--verbose Print debug messages

Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-maven.mts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const config: CliCommandConfig = {
includeConfigs: {
type: 'string',
description:
'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',
'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',
},
excludeConfigs: {
type: 'string',
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 @@ -25,7 +25,7 @@ describe('socket manifest maven', async () => {
--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)
--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
--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>\`
--verbose Print debug messages

Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-scala.mts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const config: CliCommandConfig = {
includeConfigs: {
type: 'string',
description:
'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',
'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',
},
excludeConfigs: {
type: 'string',
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 @@ -27,7 +27,7 @@ describe('socket manifest scala', async () => {
--exclude-configs When generating facts: comma-separated glob patterns; sbt configurations matching any pattern are skipped (applied after --include-configs)
--facts Emit a Socket facts JSON file (\`.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
--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
--out Only with --pom: path of the output \`pom.xml\`, see also --stdout. Does not apply when generating Socket facts (always written to the project root as \`.socket.facts.json\`)
--pom Generate \`pom.xml\` manifest file(s) instead of the default Socket facts file (\`.socket.facts.json\`)
--sbt-opts Additional options to pass on to sbt, as per \`sbt --help\`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,44 @@ public static String bareId(String groupId, String artifactId, String version) {
return coordId(groupId, artifactId, null, null, version);
}

/** Translate a config-name glob ({@code *}/{@code ?}) to a case-insensitive regex. */
/**
* Translate a config-name glob to a case-sensitive regex. Supports {@code *}, {@code ?}, and
* {@code [...]} character classes: enumerations ({@code [cC]}), ranges ({@code [a-z]}), and
* {@code [!..]}/{@code [^..]} negation. A malformed glob falls back to a literal match, never throws.
*/
public static Pattern globToRegex(String glob) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < glob.length(); i++) {
int i = 0;
int n = glob.length();
while (i < n) {
char c = glob.charAt(i);
switch (c) {
case '*': sb.append(".*"); break;
case '?': sb.append('.'); break;
case '\\': case '.': case '^': case '$': case '|':
case '+': case '(': case ')':
case '[': case ']': case '{': case '}':
sb.append('\\').append(c); break;
default: sb.append(c);
}
if (c == '*') { sb.append(".*"); i++; }
else if (c == '?') { sb.append('.'); i++; }
else if (c == '[') {
int j = glob.indexOf(']', i + 1);
// Treat as a class only with a non-empty body; else a literal '['.
if (j <= i + 1) { sb.append("\\["); i++; }
else {
String body = glob.substring(i + 1, j);
boolean neg = body.startsWith("!");
if (neg) body = body.substring(1);
// Only literal chars and '-' ranges are meaningful; neutralize regex-class tricks.
body = body.replace("\\", "\\\\").replace("[", "\\[").replace("&", "\\&");
sb.append('[').append(neg ? "^" : "").append(body).append(']');
i = j + 1;
}
} else if ("\\.^$|+(){}]".indexOf(c) >= 0) {
sb.append('\\').append(c); i++;
} else { sb.append(c); i++; }
}
try {
return Pattern.compile(sb.toString());
} catch (java.util.regex.PatternSyntaxException e) {
return Pattern.compile(Pattern.quote(glob));
}
return Pattern.compile(sb.toString(), Pattern.CASE_INSENSITIVE);
}

/** Parse a comma-separated list of globs into case-insensitive patterns. */
/** Parse a comma-separated list of globs into case-sensitive patterns. */
public static List<Pattern> parsePatterns(String csv) {
List<Pattern> out = new ArrayList<>();
if (csv == null || csv.trim().isEmpty()) return out;
Expand Down
41 changes: 29 additions & 12 deletions src/commands/manifest/scripts/socket-facts.init.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -286,22 +286,39 @@ allprojects { project ->
n.contains('classpath') || n == 'compile' || n == 'runtime'
}

// Config-name glob (`*`/`?` only) to a case-INSENSITIVE regex — Gradle config names are
// camelCase, so a case-sensitive `*Classpath` would miss the base `compileClasspath`.
// Config-name glob to a case-SENSITIVE regex (matching is documented as case-sensitive).
// Supports `*`, `?`, and `[...]` character classes: enumerations (`[cC]`), ranges (`[a-z]`),
// and `[!..]`/`[^..]` negation. A malformed glob falls back to a literal match, never throws.
def globToRegex = { String glob ->
def sb = new StringBuilder()
glob.each { String ch ->
switch (ch) {
case '*': sb << '.*'; break
case '?': sb << '.'; break
case '.': case '\\': case '^': case '$': case '|':
case '+': case '(': case ')':
case '[': case ']': case '{': case '}':
sb << '\\' << ch; break
default: sb << ch
int i = 0
int n = glob.length()
while (i < n) {
def ch = glob[i]
if (ch == '*') { sb << '.*'; i++ }
else if (ch == '?') { sb << '.'; i++ }
else if (ch == '[') {
int j = glob.indexOf(']', i + 1)
// Treat as a class only with a non-empty body; else a literal `[`.
if (j <= i + 1) { sb << '\\['; i++ }
else {
def body = glob.substring(i + 1, j)
boolean neg = body.startsWith('!')
if (neg) { body = body.substring(1) }
// Only literal chars and `-` ranges are meaningful; neutralize regex-class tricks.
body = body.replace('\\', '\\\\').replace('[', '\\[').replace('&', '\\&')
sb << '[' << (neg ? '^' : '') << body << ']'
i = j + 1
}
}
else if ('.\\^$|+(){}]'.contains(ch)) { sb << '\\' << ch; i++ }
else { sb << ch; i++ }
}
try {
java.util.regex.Pattern.compile(sb.toString())
} catch (java.util.regex.PatternSyntaxException e) {
java.util.regex.Pattern.compile(java.util.regex.Pattern.quote(glob))
}
java.util.regex.Pattern.compile(sb.toString(), java.util.regex.Pattern.CASE_INSENSITIVE)
}

// `-Psocket.includeConfigs`/`-Psocket.excludeConfigs`: comma-separated config-name globs. A
Expand Down
37 changes: 30 additions & 7 deletions src/commands/manifest/scripts/socket-facts.plugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -336,16 +336,39 @@ object SocketFactsPlugin extends AutoPlugin {
}
}

// Case-SENSITIVE (matching is documented as case-sensitive). Supports `*`, `?`, and `[...]`
// character classes: enumerations (`[cC]`), ranges (`[a-z]`), and `[!..]`/`[^..]` negation. A
// malformed glob falls back to a literal match, never throws.
private def globToRegex(glob: String): java.util.regex.Pattern = {
val sb = new StringBuilder
glob.foreach {
case '*' => sb.append(".*")
case '?' => sb.append('.')
case c if "\\.^$|+()[]{}".indexOf(c.toInt) >= 0 =>
sb.append('\\').append(c)
case c => sb.append(c)
var i = 0
val n = glob.length
while (i < n) {
val c = glob.charAt(i)
if (c == '*') { sb.append(".*"); i += 1 }
else if (c == '?') { sb.append('.'); i += 1 }
else if (c == '[') {
val j = glob.indexOf(']', i + 1)
// Treat as a class only with a non-empty body; else a literal `[`.
if (j <= i + 1) { sb.append("\\["); i += 1 }
else {
var body = glob.substring(i + 1, j)
val neg = body.startsWith("!")
if (neg) body = body.substring(1)
// Only literal chars and `-` ranges are meaningful; neutralize regex-class tricks.
body = body.replace("\\", "\\\\").replace("[", "\\[").replace("&", "\\&")
sb.append('[').append(if (neg) "^" else "").append(body).append(']')
i = j + 1
}
} else if ("\\.^$|+(){}]".indexOf(c.toInt) >= 0) {
sb.append('\\').append(c); i += 1
} else { sb.append(c); i += 1 }
}
try java.util.regex.Pattern.compile(sb.toString)
catch {
case _: java.util.regex.PatternSyntaxException =>
java.util.regex.Pattern.compile(java.util.regex.Pattern.quote(glob))
}
java.util.regex.Pattern.compile(sb.toString, java.util.regex.Pattern.CASE_INSENSITIVE)
}

// ConfigurationReport.configuration is a String on sbt 0.13, a ConfigRef on 1.x: read `.name` reflectively.
Expand Down
Loading