Skip to content

Commit 78206c7

Browse files
authored
fix(manifest): resolve Maven modules inside their class realm (#1715)
* fix(manifest): resolve Maven modules inside their class realm Maven resolves a project inside its class realm, where the artifact types its build extensions define are visible (LifecycleDependencyResolver). The facts extension resolved each module after the build, outside that realm, so such a type fell back to Maven's default handler: a type that includes its dependencies was traversed anyway, and its file extension was wrong for --with-files. Each module is now resolved inside its project's class realm. Resolves REA-1163. * test(manifest): keep only the artifact type fields the realm test relies on
1 parent 8ba501d commit 78206c7

6 files changed

Lines changed: 149 additions & 1 deletion

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+
## [Unreleased]
8+
9+
### Fixed
10+
- Maven Socket facts honor artifact types that build extensions define, so they no longer list dependencies Maven doesn't resolve, or fetch files under the wrong extension.
11+
712
## [1.15.0](https://github.com/SocketDev/socket-cli/releases/tag/v1.15.0) - 2026-11-12
813

914
### Added

‎src/commands/manifest/scripts/maven-extension/src/main/java/tech/coana/socket/SocketFactsRecordsEngine.java‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,13 @@ public void run(MavenSession session, List<MavenProject> reactor, File rootDir,
168168
if (SocketSupport.isExcludedDir(excludeBase, module.getBasedir().toPath(), excludes)) continue;
169169
Map<String, Node> nodes = new LinkedHashMap<>();
170170
Set<String> directIds = new HashSet<>();
171-
collectModule(session, module, passingScopes, reactorGavs, populateGavs, opts, nodes, directIds, failures, unscannable);
171+
ClassLoader previous = Thread.currentThread().getContextClassLoader();
172+
if (module.getClassRealm() != null) Thread.currentThread().setContextClassLoader(module.getClassRealm());
173+
try {
174+
collectModule(session, module, passingScopes, reactorGavs, populateGavs, opts, nodes, directIds, failures, unscannable);
175+
} finally {
176+
Thread.currentThread().setContextClassLoader(previous);
177+
}
172178
rootIdx = emitModuleRoots(lines, rootIdx, projectKey(module), nodes, directIds);
173179
}
174180

@@ -186,6 +192,8 @@ private static String projectKey(MavenProject module) {
186192

187193
// ---- resolution ----
188194

195+
// Run inside the module's class realm, as Maven's LifecycleDependencyResolver does: artifact types
196+
// the build extensions contribute are only found from there.
189197
private void collectModule(
190198
MavenSession session,
191199
MavenProject module,

‎src/commands/manifest/scripts/test/maven-compat/.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ descriptor-failure/*/target/
1919
unsolvable-conflict/records.tsv
2020
unsolvable-conflict/target/
2121
unsolvable-conflict/localrepo/
22+
extension-type/localrepo/
23+
extension-type/records.tsv
24+
extension-type/target/
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0">
3+
<modelVersion>4.0.0</modelVersion>
4+
<groupId>demo</groupId>
5+
<artifactId>extension-type</artifactId>
6+
<version>1.0</version>
7+
<properties>
8+
<stub.repo.url>file://${maven.multiModuleProjectDirectory}/localrepo</stub.repo.url>
9+
</properties>
10+
<repositories>
11+
<repository>
12+
<id>socket-stubs</id>
13+
<url>${stub.repo.url}</url>
14+
<releases>
15+
<enabled>true</enabled>
16+
<checksumPolicy>ignore</checksumPolicy>
17+
</releases>
18+
<snapshots>
19+
<enabled>false</enabled>
20+
</snapshots>
21+
</repository>
22+
</repositories>
23+
<pluginRepositories>
24+
<pluginRepository>
25+
<id>socket-stubs</id>
26+
<url>${stub.repo.url}</url>
27+
<releases>
28+
<enabled>true</enabled>
29+
<checksumPolicy>ignore</checksumPolicy>
30+
</releases>
31+
<snapshots>
32+
<enabled>false</enabled>
33+
</snapshots>
34+
</pluginRepository>
35+
</pluginRepositories>
36+
<!-- types-ext defines type fat: a jar that includes its dependencies, so Maven does not resolve
37+
demo.ext:inner below demo.ext:fat. -->
38+
<dependencies>
39+
<dependency>
40+
<groupId>demo.ext</groupId>
41+
<artifactId>fat</artifactId>
42+
<version>1.0</version>
43+
<type>fat</type>
44+
</dependency>
45+
</dependencies>
46+
<build>
47+
<extensions>
48+
<extension>
49+
<groupId>demo.ext</groupId>
50+
<artifactId>types-ext</artifactId>
51+
<version>1.0</version>
52+
</extension>
53+
</extensions>
54+
</build>
55+
</project>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env bash
2+
# Regression guard: an artifact type a build extension defines must apply as it does in Maven's own
3+
# resolution. Type fat, from the types-ext extension, includes its dependencies and is a jar on disk,
4+
# so demo.ext:inner below demo.ext:fat must not be emitted, and --with-files must fetch fat-1.0.jar.
5+
#
6+
# Usage: smoke-test-extension-type.sh <path-to-mvn> <path-to-extension-jar>
7+
set -euo pipefail
8+
HERE="$(cd "$(dirname "$0")" && pwd)"
9+
MVN="${1:?usage: smoke-test-extension-type.sh <mvn> <extension-jar>}"
10+
JAR="${2:?usage: smoke-test-extension-type.sh <mvn> <extension-jar>}"
11+
PROJECT="$HERE/extension-type"
12+
RECORDS="$PROJECT/records.tsv"
13+
STUBS="$PROJECT/localrepo"
14+
# shellcheck source=SCRIPTDIR/../compat-cache.sh
15+
. "$HERE/../compat-cache.sh"
16+
M2="$SOCKET_COMPAT_CACHE/m2"
17+
18+
bash "$HERE/../make-stub-repo.sh" "$STUBS" \
19+
'demo.ext:types-ext:1.0' \
20+
'demo.ext:fat:1.0+demo.ext:inner:1.0' \
21+
'demo.ext:inner:1.0'
22+
# The extension is a components.xml registering Maven's own handler class for type fat.
23+
EXT_DIR="$(mktemp -d)"
24+
trap 'rm -rf "$EXT_DIR"' EXIT
25+
mkdir -p "$EXT_DIR/META-INF/plexus"
26+
cat > "$EXT_DIR/META-INF/plexus/components.xml" <<'XML'
27+
<component-set>
28+
<components>
29+
<component>
30+
<role>org.apache.maven.artifact.handler.ArtifactHandler</role>
31+
<role-hint>fat</role-hint>
32+
<implementation>org.apache.maven.artifact.handler.DefaultArtifactHandler</implementation>
33+
<configuration>
34+
<type>fat</type>
35+
<extension>jar</extension>
36+
<includesDependencies>true</includesDependencies>
37+
</configuration>
38+
</component>
39+
</components>
40+
</component-set>
41+
XML
42+
( cd "$EXT_DIR" && jar cf "$STUBS/demo/ext/types-ext/1.0/types-ext-1.0.jar" META-INF )
43+
rm -rf "$M2/demo/ext/types-ext" "$M2/demo/ext/fat" "$M2/demo/ext/inner" "$RECORDS"
44+
45+
echo "+ $("$MVN" -v 2>/dev/null | head -1) (extension type)"
46+
( cd "$PROJECT" && "$MVN" --batch-mode -q \
47+
"-Dmaven.ext.class.path=$JAR" \
48+
-Dcoana.task=socket-facts \
49+
-Dsocket.withFiles=true \
50+
"-Dmaven.repo.local=$M2" \
51+
"-Dstub.repo.url=file://$STUBS" \
52+
"-Dsocket.recordsFile=$RECORDS" \
53+
validate )
54+
55+
python3 - "$RECORDS" <<'PY'
56+
import sys
57+
rows = [l.rstrip('\n').split('\t') for l in open(sys.argv[1]) if l.strip()]
58+
nodes = {r[2] for r in rows if r[0] == 'node'}
59+
files = {r[3] for r in rows if r[0] == 'file' and r[2].startswith('demo.ext:fat:')}
60+
failures = [r[1:] for r in rows if r[0] in ('failure', 'unscannable')]
61+
errors = []
62+
if not any(n.startswith('demo.ext:fat:') for n in nodes):
63+
errors.append(f"demo.ext:fat missing; nodes={sorted(nodes)}")
64+
if any(n.startswith('demo.ext:inner:') for n in nodes):
65+
errors.append("demo.ext:inner emitted, though type fat includes its dependencies")
66+
if not any(p.endswith('fat-1.0.jar') for p in files):
67+
errors.append(f"fat-1.0.jar not materialized; files={sorted(files)}")
68+
if failures:
69+
errors.append(f"no failures expected; got {failures}")
70+
if errors:
71+
print("FAIL:")
72+
for e in errors:
73+
print(" -", e)
74+
sys.exit(1)
75+
print("PASS (extension type): fat emitted as a jar, inner not resolved below it")
76+
PY

‎src/commands/manifest/scripts/test/run-compat.sh‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ run_maven() {
118118
bash "$HERE/maven-compat/smoke-test-graph-failure.sh" "$dir/bin/mvn" "$jar"
119119
bash "$HERE/maven-compat/smoke-test-descriptor-failure.sh" "$dir/bin/mvn" "$jar"
120120
bash "$HERE/maven-compat/smoke-test-unsolvable-conflict.sh" "$dir/bin/mvn" "$jar"
121+
bash "$HERE/maven-compat/smoke-test-extension-type.sh" "$dir/bin/mvn" "$jar"
121122
done
122123
}
123124

0 commit comments

Comments
 (0)