Skip to content

Commit ef2a103

Browse files
Strum355claude
andcommitted
refactor: extract shared resolveVerifiedMavenBinary on JavaMavenProvider
Consolidate duplicate Maven binary resolution logic between ExhortApi and JavaMavenProvider into a single public static method that verifies the binary with --version before returning it. - Add JavaMavenProvider.resolveVerifiedMavenBinary(Path) returning Optional<String> with wrapper-first resolution and verification - Refactor selectMvnRuntime to delegate to the new shared method - Delete duplicate resolveMavenBinary from ExhortApi, update discoverMavenModules to call the shared method via .orElse(null) - Fix @return JavaDoc on discoverMavenModules to match actual behavior - Make 2-arg traverseForMvnw static (required by static caller) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 580d9b3 commit ef2a103

2 files changed

Lines changed: 36 additions & 46 deletions

File tree

src/main/java/io/github/guacsec/trustifyda/impl/ExhortApi.java

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -990,18 +990,18 @@ private List<Path> discoverGoWorkspaceModules(Path workspaceDir, Set<String> ign
990990
* Discovers Maven multi-module workspace manifests by invoking {@code mvn help:evaluate} to list
991991
* declared modules, then recursively checking each module for nested aggregators.
992992
*
993-
* <p>Uses the same Maven binary selection as {@link
994-
* io.github.guacsec.trustifyda.providers.JavaMavenProvider}, including wrapper support via {@code
995-
* selectMvnRuntime}. Always includes the root {@code pom.xml}. Uses a visited set to prevent
996-
* cycles in the module graph.
993+
* <p>Uses {@link JavaMavenProvider#resolveVerifiedMavenBinary} for Maven binary resolution,
994+
* including wrapper support. Always includes the root {@code pom.xml}. Uses a visited set to
995+
* prevent cycles in the module graph.
997996
*
998997
* @param workspaceDir the root directory containing the aggregator pom.xml
999998
* @param ignorePatterns glob patterns for paths to exclude from results
1000-
* @return list of discovered pom.xml paths, or empty list if Maven is unavailable
999+
* @return list of discovered pom.xml paths; always includes the root pom.xml even if Maven is
1000+
* unavailable
10011001
*/
10021002
private List<Path> discoverMavenModules(Path workspaceDir, Set<String> ignorePatterns) {
10031003
Path rootPom = workspaceDir.resolve("pom.xml");
1004-
String mvnBin = resolveMavenBinary(workspaceDir);
1004+
String mvnBin = JavaMavenProvider.resolveVerifiedMavenBinary(workspaceDir).orElse(null);
10051005
if (mvnBin == null) {
10061006
LOG.warning("Maven binary not available; returning root pom.xml only");
10071007
return List.of(rootPom);
@@ -1108,26 +1108,6 @@ static List<String> parseMavenModuleList(String raw) {
11081108
.toList();
11091109
}
11101110

1111-
/**
1112-
* Resolves the Maven binary to use, following the same wrapper preference logic as {@link
1113-
* io.github.guacsec.trustifyda.providers.JavaMavenProvider#selectMvnRuntime}.
1114-
*
1115-
* @param startDir the directory from which to start searching for mvnw
1116-
* @return the resolved Maven binary path, or null if Maven is not available
1117-
*/
1118-
private static String resolveMavenBinary(Path startDir) {
1119-
if (Operations.getWrapperPreference("mvn")) {
1120-
String wrapperName = Operations.isWindows() ? "mvnw.cmd" : "mvnw";
1121-
String wrapper =
1122-
JavaMavenProvider.traverseForMvnw(
1123-
wrapperName, startDir.resolve("pom.xml").toString(), null);
1124-
if (wrapper != null) {
1125-
return wrapper;
1126-
}
1127-
}
1128-
return Operations.getCustomPathOrElse("mvn");
1129-
}
1130-
11311111
/**
11321112
* Checks whether a package.json has "private": true, meaning it should not be analyzed as a
11331113
* publishable package.

src/main/java/io/github/guacsec/trustifyda/providers/JavaMavenProvider.java

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
import java.util.List;
4040
import java.util.Map;
4141
import java.util.Objects;
42-
import java.util.logging.Level;
42+
import java.util.Optional;
4343
import java.util.logging.Logger;
4444
import java.util.stream.Collectors;
4545
import javax.xml.stream.XMLInputFactory;
@@ -431,35 +431,45 @@ private List<String> buildMvnCommandArgs(String... baseArgs) {
431431
}
432432

433433
private String selectMvnRuntime(final Path manifestPath) {
434-
boolean preferWrapper = Operations.getWrapperPreference(MVN);
435-
if (preferWrapper && manifestPath != null) {
436-
String wrapperName = Operations.isWindows() ? "mvnw.cmd" : "mvnw";
437-
String mvnw = traverseForMvnw(wrapperName, manifestPath.toString());
438-
if (mvnw != null) {
439-
try {
440-
// verify maven wrapper is accessible
441-
Operations.runProcess(manifestPath.getParent(), mvnw, ARG_VERSION);
442-
if (debugLoggingIsNeeded()) {
443-
log.info(String.format("using maven wrapper from : %s", mvnw));
444-
}
445-
return mvnw;
446-
} catch (Exception e) {
447-
log.log(
448-
Level.WARNING,
449-
"Failed to check for mvnw due to: {0} Fall back to use mvn",
450-
e.getMessage());
434+
Path dir = (manifestPath != null) ? manifestPath.getParent() : null;
435+
if (dir != null) {
436+
Optional<String> resolved = resolveVerifiedMavenBinary(dir);
437+
if (resolved.isPresent()) {
438+
if (debugLoggingIsNeeded()) {
439+
log.info(String.format("using maven binary: %s", resolved.get()));
451440
}
441+
return resolved.get();
452442
}
453443
}
454-
// If maven wrapper is not requested or not accessible, fall back to use mvn
455444
String mvn = Operations.getExecutable(MVN, ARG_VERSION);
456445
if (debugLoggingIsNeeded()) {
457446
log.info(String.format("using mvn executable from : %s", mvn));
458447
}
459448
return mvn;
460449
}
461450

462-
private String traverseForMvnw(String wrapperName, String startingManifest) {
451+
public static Optional<String> resolveVerifiedMavenBinary(Path workspaceDir) {
452+
if (Operations.getWrapperPreference(MVN)) {
453+
String wrapperName = Operations.isWindows() ? "mvnw.cmd" : "mvnw";
454+
String mvnw = traverseForMvnw(wrapperName, workspaceDir.resolve("pom.xml").toString());
455+
if (mvnw != null) {
456+
try {
457+
Operations.runProcess(workspaceDir, mvnw, ARG_VERSION);
458+
return Optional.of(mvnw);
459+
} catch (Exception e) {
460+
// wrapper found but not functional — fall through to system mvn
461+
}
462+
}
463+
}
464+
try {
465+
String mvn = Operations.getExecutable(MVN, ARG_VERSION);
466+
return Optional.of(mvn);
467+
} catch (RuntimeException e) {
468+
return Optional.empty();
469+
}
470+
}
471+
472+
private static String traverseForMvnw(String wrapperName, String startingManifest) {
463473
return traverseForMvnw(wrapperName, startingManifest, null);
464474
}
465475

0 commit comments

Comments
 (0)