Skip to content
Closed
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
!/src/commands/manifest/scripts/test/maven-compat/exclude-base/app/
!/src/commands/manifest/scripts/test/maven-compat/exclude-base/app/core/
!/src/commands/manifest/scripts/test/maven-compat/exclude-base/shared/
!/src/commands/manifest/scripts/test/maven-compat/excluded-scope/
!/src/commands/manifest/scripts/test/maven-compat/graph-failure/
!/src/commands/manifest/scripts/test/maven-compat/project/
!/src/commands/manifest/scripts/test/maven-compat/project/app/
Expand Down Expand Up @@ -760,6 +761,7 @@
!/src/commands/manifest/scripts/test/maven-compat/exclude-base/app/core/pom.xml
!/src/commands/manifest/scripts/test/maven-compat/exclude-base/app/pom.xml
!/src/commands/manifest/scripts/test/maven-compat/exclude-base/shared/pom.xml
!/src/commands/manifest/scripts/test/maven-compat/excluded-scope/pom.xml
!/src/commands/manifest/scripts/test/maven-compat/graph-failure/pom.xml
!/src/commands/manifest/scripts/test/maven-compat/project/app/pom.xml
!/src/commands/manifest/scripts/test/maven-compat/project/app/src/main/java/demo/App.java
Expand All @@ -774,6 +776,7 @@
!/src/commands/manifest/scripts/test/maven-compat/smoke-test-descriptor-failure.sh
!/src/commands/manifest/scripts/test/maven-compat/smoke-test-duplicate-failure.sh
!/src/commands/manifest/scripts/test/maven-compat/smoke-test-exclude-base.sh
!/src/commands/manifest/scripts/test/maven-compat/smoke-test-excluded-scope.sh
!/src/commands/manifest/scripts/test/maven-compat/smoke-test-graph-failure.sh
!/src/commands/manifest/scripts/test/maven-compat/smoke-test-repo-inheritance.sh
!/src/commands/manifest/scripts/test/maven-compat/smoke-test-workspaces.sh
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
### Fixed
- 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.
- Maven Socket facts no longer drop dependencies whose scope Maven 3 doesn't define, such as Maven 4's `compile-only` or `none`.
- Maven Socket facts with `--exclude-configs` or `--include-configs` keep dependencies Maven ships even when they sit below a skipped dependency.

## [1.15.0](https://github.com/SocketDev/socket-cli/releases/tag/v1.15.0) - 2026-11-12

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ private void collectModule(
String id = visit(child, edges, scopes, reactorGavs, unresolved, opts, nodes);
if (id != null) directIds.add(id);
}
dropSkipped(nodes, directIds);
for (String id : unfetched) {
Node node = nodes.get(id);
if (node != null) node.unfetched = true;
Expand Down Expand Up @@ -420,7 +421,6 @@ private String visit(
Artifact artifact = dn.getArtifact();
if (dep == null || artifact == null) return null;
String scope = scopeOf(dep);
if (!scopes.passes(scope)) return null;

String gav = gav(artifact);
if (unresolved.contains(gav)) return null;
Expand All @@ -436,6 +436,7 @@ private String visit(
// assembler merges them into its component.
Node node = new Node(id, artifact.getGroupId(), artifact.getArtifactId(),
type == null ? "" : type, classifier == null ? "" : classifier, artifact.getBaseVersion(), internal ? gav : "");
node.skipped = !scopes.passes(scope);
node.prod = scopes.prod(scope);
node.onClasspath = scopes.onClasspath(scope);
scopes.scanned(scope);
Expand All @@ -455,6 +456,51 @@ private String visit(
return id;
}

// A node is kept by its own scope: Maven walks past the nodes its scope filter drops (maven-core
// RepositoryUtils.toArtifacts). A kept node reached only through dropped ones, as a managed scope
// allows (maven-resolver ConflictResolver.deriveScope; ClassicConflictResolver in 2.x), is linked to
// its nearest kept ancestors, the module making it direct.
private static void dropSkipped(Map<String, Node> nodes, Set<String> directIds) {
if (nodes.values().stream().noneMatch(n -> n.skipped)) return;
Map<String, Set<String>> parents = new HashMap<>();
for (Node n : nodes.values()) {
for (String child : n.children) parents.computeIfAbsent(child, k -> new HashSet<>()).add(n.id);
}
Set<String> reached = new HashSet<>();
Deque<String> queue = new ArrayDeque<>();
for (String id : directIds) {
if (!nodes.get(id).skipped && reached.add(id)) queue.add(id);
}
while (!queue.isEmpty()) {
for (String child : nodes.get(queue.poll()).children) {
if (!nodes.get(child).skipped && reached.add(child)) queue.add(child);
}
}
for (Node orphan : nodes.values()) {
if (orphan.skipped || reached.contains(orphan.id)) continue;
Set<String> seen = new HashSet<>();
Deque<String> up = new ArrayDeque<>();
for (String parent : parents.getOrDefault(orphan.id, Collections.emptySet())) {
if (nodes.get(parent).skipped && seen.add(parent)) up.add(parent);
}
while (!up.isEmpty()) {
String skipped = up.poll();
if (directIds.contains(skipped)) directIds.add(orphan.id);
for (String parent : parents.getOrDefault(skipped, Collections.emptySet())) {
Node node = nodes.get(parent);
if (!node.skipped) {
if (!parent.equals(orphan.id)) node.children.add(orphan.id);
} else if (seen.add(parent)) {
up.add(parent);
}
}
}
}
directIds.removeIf(id -> nodes.get(id).skipped);
nodes.values().removeIf(n -> n.skipped);
for (Node n : nodes.values()) n.children.retainAll(nodes.keySet());
}

private static String externalId(Artifact artifact) {
// Maven's `type` rather than aether's file extension, so a test-jar keeps the coordId the
// assembler and the Gradle/SBT scripts already emit. Base version: a resolved remote snapshot's
Expand Down Expand Up @@ -1001,6 +1047,8 @@ private static final class Node {
final String project;
final TreeSet<String> children = new TreeSet<>();
final TreeSet<String> files = new TreeSet<>();
// Its scope is filtered out by --include-configs/--exclude-configs.
boolean skipped;
boolean prod;
boolean onClasspath = true;
boolean unfetched;
Expand Down
2 changes: 2 additions & 0 deletions src/commands/manifest/scripts/test/maven-compat/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ unknown-scopes/localrepo/
unknown-scopes/records.tsv
maven4-scopes/localrepo/
maven4-scopes/records.tsv
excluded-scope/localrepo/
excluded-scope/records.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>demo</groupId>
<artifactId>excluded-scope</artifactId>
<version>1.0</version>
<properties>
<stub.repo.url>file://${maven.multiModuleProjectDirectory}/localrepo</stub.repo.url>
</properties>
<repositories>
<repository>
<id>socket-stubs</id>
<url>${stub.repo.url}</url>
<releases>
<enabled>true</enabled>
<checksumPolicy>ignore</checksumPolicy>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<!-- A managed scope is not derived from the parent's: managed and deep stay compile below test. -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>demo.excl</groupId>
<artifactId>managed</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>demo.excl</groupId>
<artifactId>mgd-test</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>demo.excl</groupId>
<artifactId>deep</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>demo.excl</groupId>
<artifactId>testlib</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>demo.excl</groupId>
<artifactId>lib</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>demo.excl</groupId>
<artifactId>dtest</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>demo.excl</groupId>
<artifactId>k2</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
# Regression guard: with the test scope excluded, a node is kept by its own scope, as Maven keeps it,
# and only a node reached solely through excluded ones (managed, deep) is linked to its nearest kept
# ancestors; shared and e keep just the parents Maven resolved. ranged keeps its edge from k2, though
# the excluded testlib's range picks its version.
#
# Usage: smoke-test-excluded-scope.sh <path-to-mvn> <path-to-extension-jar>.
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
MVN="${1:?usage: smoke-test-excluded-scope.sh <mvn> <extension-jar>}"
JAR="${2:?usage: smoke-test-excluded-scope.sh <mvn> <extension-jar>}"
PROJECT="$HERE/excluded-scope"
RECORDS="$PROJECT/records.tsv"
# shellcheck source=SCRIPTDIR/../compat-cache.sh
. "$HERE/../compat-cache.sh"
M2="$SOCKET_COMPAT_CACHE/m2"

bash "$HERE/../make-stub-repo.sh" "$PROJECT/localrepo" \
'demo.excl:testlib:1.0+demo.excl:managed:1.0,demo.excl:testdep:1.0,demo.excl:shared:1.0,demo.excl:ranged:RANGE' \
'demo.excl:managed:1.0' \
'demo.excl:testdep:1.0' \
'demo.excl:lib:1.0+demo.excl:mgd-test:1.0,demo.excl:shared:1.0,demo.excl:dtest:1.0' \
'demo.excl:mgd-test:1.0+demo.excl:deep:1.0' \
'demo.excl:deep:1.0' \
'demo.excl:shared:1.0' \
'demo.excl:dtest:1.0+demo.excl:e:1.0' \
'demo.excl:k2:1.0+demo.excl:e:1.0,demo.excl:ranged:2.0' \
'demo.excl:e:1.0' \
'demo.excl:ranged:1.5' \
'demo.excl:ranged:2.0' >/dev/null
# A range is a hard constraint whatever the scope declaring it (maven-resolver NearestVersionSelector;
# ConfigurableVersionSelector in 2.x), so Maven resolves ranged 1.5.
sed -i.bak 's|<version>RANGE</version>|<version>[1.0,1.5]</version>|' "$PROJECT/localrepo/demo/excl/testlib/1.0/testlib-1.0.pom"
rm -rf "$M2/demo/excl" "$RECORDS"
echo "+ $("$MVN" -v 2>/dev/null | head -1) (excluded scope)"
( cd "$PROJECT" && "$MVN" --batch-mode -q \
"-Dmaven.ext.class.path=$JAR" \
-Dcoana.task=socket-facts \
"-Dmaven.repo.local=$M2" \
"-Dstub.repo.url=file://$PROJECT/localrepo" \
-Dsocket.excludeConfigs=test \
"-Dsocket.recordsFile=$RECORDS" \
validate )
python3 - "$RECORDS" <<'PY'
import sys
rows = [l.rstrip('\n').split('\t') for l in open(sys.argv[1]) if l.strip()]
prod_roots = {r[1] for r in rows if r[0] == 'root' and r[4] == '1'}
name = lambda coord: coord.split(':')[1]
nodes = {}
for r in rows:
if r[0] == 'node':
nodes.setdefault(name(r[2]), []).append(r)
parents = {}
for r in rows:
if r[0] == 'edge':
parents.setdefault(name(r[3]), set()).add(name(r[2]))
errors = []
for absent in ('testlib', 'testdep', 'mgd-test', 'dtest'):
if absent in nodes:
errors.append(f"{absent} is excluded but emitted")
for kept, direct in (('lib', True), ('k2', True), ('managed', True), ('deep', False), ('shared', False), ('e', False), ('ranged', False)):
rs = nodes.get(kept)
if not rs:
errors.append(f"{kept} missing")
continue
if not any(r[1] in prod_roots for r in rs):
errors.append(f"{kept} should be prod")
if any((r[8] == '1') != direct for r in rs):
errors.append(f"{kept} should {'' if direct else 'not '}be direct")
if any(r[5] != '1.5' for r in nodes.get('ranged', [])):
errors.append(f"ranged should resolve 1.5, got {sorted({r[5] for r in nodes['ranged']})}")
for child, expected in (('deep', {'lib'}), ('shared', {'lib'}), ('e', {'k2'}), ('ranged', {'k2'})):
if parents.get(child, set()) != expected:
errors.append(f"{child} should have parents {sorted(expected)}, has {sorted(parents.get(child, set()))}")
if errors:
print("FAIL:")
for e in errors:
print(" -", e)
sys.exit(1)
print("PASS (excluded scope): managed direct, lib -> deep, shared, e and ranged keep their kept parents")
PY
assemble_records "$RECORDS"
1 change: 1 addition & 0 deletions src/commands/manifest/scripts/test/run-compat.sh
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ run_maven() {
bash "$HERE/maven-compat/smoke-test-unsolvable-conflict.sh" "$dir/bin/mvn" "$jar"
bash "$HERE/maven-compat/smoke-test-extension-type.sh" "$dir/bin/mvn" "$jar"
bash "$HERE/maven-compat/smoke-test-scopes.sh" "$dir/bin/mvn" "$jar"
bash "$HERE/maven-compat/smoke-test-excluded-scope.sh" "$dir/bin/mvn" "$jar"
done
}

Expand Down
Loading