Skip to content

feat(manifest): case-sensitive config-name globs with character classes (REA-621)#1400

Merged
Jeppe Fredsgaard Blaabjerg (jfblaa) merged 5 commits into
v1.xfrom
jfblaa/rea-621-manifest-config-name-globs-make-matching-case-sensitive-as
Jul 2, 2026
Merged

feat(manifest): case-sensitive config-name globs with character classes (REA-621)#1400
Jeppe Fredsgaard Blaabjerg (jfblaa) merged 5 commits into
v1.xfrom
jfblaa/rea-621-manifest-config-name-globs-make-matching-case-sensitive-as

Conversation

@jfblaa

@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Problem

The --include-configs / --exclude-configs glob matchers in all three JVM facts producers — the Gradle init script (Groovy), the sbt plugin (Scala), and the Maven extension (Java) — compiled their patterns with Pattern.CASE_INSENSITIVE. That contradicts the CLI help ("case-sensitive") and the capitalized examples (*CompileClasspath,*RuntimeClasspath). Confirmed: --include-configs '*classpath' (lowercase) matched compileClasspath etc.

Changes

  1. Case-sensitive matching — dropped CASE_INSENSITIVE in all three globToRegex implementations, matching the documented behavior. (The old code even had a comment justifying the insensitivity on a false premise — *Classpath matches camelCase compileClasspath case-sensitively just fine.)
  2. Character-class support[...] now works: enumerations ([cC]), ranges ([a-z]), and [!..] negation. Under case-sensitive matching this lets a pattern span the camelCase boundary, e.g. *[Tt]est* matches both testCompileClasspath and androidTestCompileClasspath — impossible with */? alone.
  3. Robustness — the class body is hardened (escape \/[/&) so exotic input (POSIX classes, && intersection) degrades to a literal match, and Pattern.compile is wrapped so a malformed glob (e.g. reversed range [z-a]) falls back to a literal match instead of throwing and failing the build.
  4. Help text for gradle/kotlin/sbt/maven mentions [...]; fixed a stale "case-insensitive" javadoc on SocketSupport.parsePatterns.

Why a custom converter (not a library / PathMatcher)

Considered java.nio.file.PathMatcher glob: and TS-side conversion (picomatch). Both were rejected:

  • PathMatcher is case-sensitive on Unix but case-insensitive on Windows (JDK Globs.toWindowsRegexPattern adds CASE_INSENSITIVE), and Path.of is Java 11+ — this code must run on Java 8→newest, Gradle 1→newest, sbt 0.13→newest, Maven 3.3→newest. java.util.regex.Pattern + string ops are stable since Java 1.4 and behave identically across that whole matrix.
  • picomatch/micromatch emit JS-flavored, path-oriented regex (dotfile guards, [^/], trailing /?) and even mangle [Tt] into (?:\[Tt\]|[Tt]) — not portable to Java's regex engine, and passing a regex would change the producers' glob contract and add escaping fragility.

An independent review agent reached the same conclusion (custom converter), flagging the hardening/compile-guard and stale comment fixed here.

Testing

  • End-to-end against a real Gradle project: *classpath (lowercase) now matches nothing; *Classpath matches all four classpath configs; *[Tt]est* matches only the test configs; malformed [z-a] exits 0 (literal fallback, no crash).
  • Maven extension jar rebuilds from source (gitignored, built in CI).
  • Help snapshots updated; pnpm check:tsc clean; 12/12 manifest help tests pass.

Follow-up (noted, not in this PR): a shared cross-language conformance fixture would guard against the three hand-written converters drifting.

Closes REA-621. Part of the same Unreleased version as PRs #1398 (REA-519) and #1399 (REA-622). Note: the gradle/kotlin --include-configs help line also conflicts trivially with #1398 (which removes stale AGP text from the same string) — resolve by keeping both edits.


Note

Medium Risk
Matching semantics change which build configurations/scopes are resolved into facts/SBOMs; users with lowercase or case-loose patterns may see different dependency coverage until patterns are fixed.

Overview
--include-configs / --exclude-configs on JVM manifest commands now match configuration/scope names case-sensitively, aligning runtime behavior with the documented CLI help. Patterns that relied on case folding (e.g. *classpath matching compileClasspath) will stop matching unless updated.

The Gradle init script, sbt facts plugin, and Maven extension globToRegex implementations are updated in parallel: they drop CASE_INSENSITIVE, add [...] glob classes (including [!…] negation), harden class bodies against regex tricks, and fall back to a literal match when compilation fails so bad patterns do not crash the build.

CLI flag descriptions and help snapshots for socket manifest gradle, kotlin, scala, and maven now mention [...] wildcards; CHANGELOG records the behavior change.

Reviewed by Cursor Bugbot for commit 33850b4. Configure here.

The --include-configs/--exclude-configs glob matchers in all three JVM facts
producers (Gradle init script, sbt plugin, Maven extension) compiled with
CASE_INSENSITIVE, contradicting the documented case-sensitive behavior and the
capitalized examples. Make matching case-sensitive, and add `[...]` character
classes (enumerations, ranges, `[!..]` negation) so a pattern can span the
camelCase boundary — e.g. `*[Tt]est*` matches both testCompileClasspath and
androidTestCompileClasspath. A malformed glob falls back to a literal match
instead of throwing (which could otherwise fail the build).

Help text updated to mention `[...]`; the Maven extension jar is rebuilt from
source in CI.

@mtorp Martin Torp (mtorp) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — the three-language globToRegex port (Groovy/Scala/Java) is faithful and consistent, the escape set is complete, and malformed globs degrade safely to a literal match via the PatternSyntaxExceptionPattern.quote fallback. Case-sensitive matching plus [...] classes matches the documented help.

One nit for the CHANGELOG (non-blocking): the Unreleased entry lists "socket manifest gradle, kotlin, sbt, and maven", but there is no socket manifest sbt subcommand — the sbt producer is invoked via socket manifest scala. Suggest scala (or scala (sbt)) so users can find the command by name.

…st-config-name-globs-make-matching-case-sensitive-as

# Conflicts:
#	CHANGELOG.md
#	src/commands/manifest/cmd-manifest-gradle.mts
#	src/commands/manifest/cmd-manifest-gradle.test.mts
#	src/commands/manifest/cmd-manifest-kotlin.mts
#	src/commands/manifest/cmd-manifest-kotlin.test.mts
@jfblaa

Copy link
Copy Markdown
Contributor Author

Good catch on the CHANGELOG — fixed in 28aae8c. Changed the sbt entry to scala (the actual subcommand: socket manifest scala), so the list now reads gradle, kotlin, scala, and maven.

Also merged latest v1.x (now includes #1398 and #1399) and resolved the conflicts: the CHANGELOG Unreleased section keeps all three entries, and the --include-configs help line + snapshots keep both edits — the [...] wildcard phrasing from this PR and the AGP-instrumented-test-default removal from #1398.

Cut 1.1.136 covering the three JVM-manifest changes landing together (REA-519
config-transparency, REA-622 auto-manifest Gradle detection, REA-621
case-sensitive config globs + character classes); tightened the changelog
entries to single-line bullets.
@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) merged commit 1388677 into v1.x Jul 2, 2026
13 checks passed
@jfblaa Jeppe Fredsgaard Blaabjerg (jfblaa) deleted the jfblaa/rea-621-manifest-config-name-globs-make-matching-case-sensitive-as branch July 2, 2026 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants