Copilot commented on code in PR #12430:
URL: https://github.com/apache/maven/pull/12430#discussion_r3540261661


##########
impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategy.java:
##########
@@ -874,6 +894,138 @@ private record PluginAnalysis(Set<String> 
needsManagement, Set<String> needsDire
     private record PluginAnalysisResults(
             Map<Path, Set<String>> pluginsNeedingManagement, Map<Path, 
Set<String>> pluginsNeedingDirectOverride) {}
 
+    /**
+     * Checks if the given plugin is a Quarkus Maven plugin.
+     */
+    private boolean isQuarkusPlugin(String groupId, String artifactId) {
+        return "quarkus-maven-plugin".equals(artifactId)
+                && ("io.quarkus".equals(groupId) || 
"io.quarkus.platform".equals(groupId));
+    }
+
+    /**
+     * Checks if a property is used as the version of a Quarkus BOM in 
dependencyManagement.
+     * Quarkus BOMs are identified by having groupId io.quarkus or 
io.quarkus.platform,
+     * type "pom", and scope "import".
+     */
+    private boolean isPropertyUsedByQuarkusBom(Document pomDocument, String 
propertyName) {
+        Element root = pomDocument.root();
+        String propertyRef = "${" + propertyName + "}";
+
+        Element depManagement = 
root.childElement(DEPENDENCY_MANAGEMENT).orElse(null);
+        if (depManagement == null) {
+            return false;
+        }
+        Element dependencies = 
depManagement.childElement(DEPENDENCIES).orElse(null);
+        if (dependencies == null) {
+            return false;
+        }
+
+        return dependencies.childElements(DEPENDENCY).anyMatch(dep -> {
+            String groupId = getChildText(dep, GROUP_ID);
+            String version = getChildText(dep, VERSION);
+            String type = getChildText(dep, "type");
+            String scope = getChildText(dep, "scope");
+            return ("io.quarkus".equals(groupId) || 
"io.quarkus.platform".equals(groupId))
+                    && "pom".equals(type)
+                    && "import".equals(scope)
+                    && propertyRef.equals(version);
+        });
+    }
+
+    /**
+     * Decouples the Quarkus plugin version from a shared BOM property.
+     * Introduces a new property for the plugin version and updates the 
plugin's version element,
+     * leaving the BOM property unchanged.
+     */
+    private boolean decoupleQuarkusPluginVersion(
+            Document pomDocument,
+            Element versionElement,
+            String sharedPropertyName,
+            PluginUpgradeInfo upgrade,
+            String sectionName,
+            UpgradeContext context) {
+
+        // Resolve the current version from the shared property
+        Editor editor = new Editor(pomDocument);
+        Element root = editor.root();
+        Element propertiesElement = root.childElement(PROPERTIES).orElse(null);
+        String currentVersion = null;
+        if (propertiesElement != null) {
+            Element sharedProp =
+                    
propertiesElement.childElement(sharedPropertyName).orElse(null);
+            if (sharedProp != null) {
+                currentVersion = sharedProp.textContentTrimmed();
+            }
+        }
+
+        if (currentVersion != null && !isVersionBelow(currentVersion, 
upgrade.minVersion)) {
+            context.debug("Quarkus plugin version (via shared property " + 
sharedPropertyName + ") " + currentVersion
+                    + " is already >= " + upgrade.minVersion);
+            return false;
+        }

Review Comment:
   If the shared BOM property is inherited (not declared in the current POM), 
currentVersion stays null and this method will still introduce 
quarkus-plugin.version=3.26.0 and switch the plugin to use it. That can 
accidentally downgrade projects whose inherited platform/plugin property is 
already > 3.26.0 (because the actual value isn’t checked).



##########
impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/CommonsCliUpgradeOptions.java:
##########
@@ -144,6 +148,32 @@ protected static class CLIManager extends 
CommonsCliOptions.CLIManager {
         public static final String PLUGINS = "plugins";
         public static final String ALL = "a";
 
+        /**
+         * Overrides the default strict parsing to silently ignore 
unrecognized options.
+         * This is necessary because mvnup inherits the Maven launcher's 
argument handling,
+         * which appends options from {@code .mvn/maven.config}. That file 
often contains
+         * Maven build options like {@code -ntp}, {@code -U}, or {@code -T} 
that mvnup
+         * does not recognize. Without this override, mvnup would abort with a
+         * {@link ParseException} before applying any fixes.
+         */
+        @Override
+        public CommandLine parse(String[] args) throws ParseException {
+            List<String> currentArgs = new ArrayList<>(List.of(args));
+            Set<String> removed = new HashSet<>();
+            while (true) {
+                try {
+                    return super.parse(currentArgs.toArray(new String[0]));
+                } catch (UnrecognizedOptionException e) {
+                    String badOption = e.getOption();
+                    if (removed.contains(badOption) || 
!currentArgs.remove(badOption)) {
+                        // Already tried removing this option or can't find it 
— give up
+                        throw e;
+                    }
+                    removed.add(badOption);
+                }
+            }
+        }

Review Comment:
   This retry loop removes only the unrecognized option token (e.g. "-T") but 
leaves any associated argument behind when the option is provided as a separate 
token (e.g. "-T", "4"). With Commons CLI parsing, that leftover value ends up 
in commandLine.getArgList() and will be treated as an extra goal/phase (e.g. 
"4"), changing mvnup behavior instead of just ignoring the Maven option.



##########
impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/CompatibilityFixStrategy.java:
##########
@@ -816,4 +848,193 @@ private Path findParentPomInMap(
                 .map(Map.Entry::getKey)
                 .orElse(null);
     }
+
+    // ---- Warning-only checks for Maven 4 compatibility issues that cannot 
be auto-fixed ----
+
+    /**
+     * Warns about plugins known to be incompatible with Maven 4 even at their 
latest version.
+     * These plugins call methods on immutable API objects or use removed 
internal APIs
+     * and require upstream fixes before they can work with Maven 4.
+     *
+     * @see <a href="https://github.com/apache/maven/issues/12432";>#12432</a>
+     */
+    private void warnAboutIncompatiblePlugins(Document pomDocument, 
UpgradeContext context) {
+        Element root = pomDocument.root();
+
+        Stream<Element> pluginContainers = Stream.concat(
+                // Root level build
+                root.childElement(BUILD).stream()
+                        .flatMap(build -> Stream.concat(
+                                build.childElement(PLUGINS).stream(),
+                                build.childElement(PLUGIN_MANAGEMENT).stream()
+                                        .flatMap(pm -> 
pm.childElement(PLUGINS).stream()))),
+                // Profile builds
+                root.childElement(PROFILES).stream()
+                        .flatMap(profiles -> profiles.childElements(PROFILE))
+                        .flatMap(profile -> 
profile.childElement(BUILD).stream())
+                        .flatMap(build -> Stream.concat(
+                                build.childElement(PLUGINS).stream(),
+                                build.childElement(PLUGIN_MANAGEMENT).stream()
+                                        .flatMap(pm -> 
pm.childElement(PLUGINS).stream()))));
+
+        pluginContainers.forEach(pluginsElement -> pluginsElement
+                .childElements(PLUGIN)
+                .forEach(pluginElement -> {
+                    String groupId = 
pluginElement.childText(MavenPomElements.Elements.GROUP_ID);
+                    String artifactId = 
pluginElement.childText(MavenPomElements.Elements.ARTIFACT_ID);
+
+                    if (groupId == null && artifactId != null && 
artifactId.startsWith(MAVEN_PLUGIN_PREFIX)) {
+                        groupId = DEFAULT_MAVEN_PLUGIN_GROUP_ID;
+                    }
+
+                    if (groupId != null && artifactId != null) {
+                        String pluginKey = groupId + ":" + artifactId;
+                        String warning = 
KNOWN_INCOMPATIBLE_PLUGINS.get(pluginKey);
+                        if (warning != null) {
+                            context.warning("Known Maven 4 incompatibility: " 
+ pluginKey + " — " + warning);
+                        }
+                    }
+                }));
+    }
+
+    /**
+     * Warns about third-party repositories (non-Maven-Central) that may be 
affected by
+     * Maven 4's prefix-based artifact filtering. When a repository does not 
publish a
+     * {@code prefixes.txt} file, Maven 4 may auto-generate an incomplete 
prefix list
+     * that blocks legitimate artifact resolution.
+     *
+     * @see <a href="https://github.com/apache/maven/issues/12433";>#12433</a>
+     */
+    private void warnAboutThirdPartyRepositoryPrefixFiltering(Document 
pomDocument, UpgradeContext context) {
+        Element root = pomDocument.root();
+
+        Stream<Element> repositoryContainers = Stream.concat(
+                Stream.of(
+                                root.childElement(REPOSITORIES).orElse(null),
+                                
root.childElement(PLUGIN_REPOSITORIES).orElse(null))
+                        .filter(Objects::nonNull),
+                root.childElement(PROFILES).stream()
+                        .flatMap(profiles -> profiles.childElements(PROFILE))
+                        .flatMap(profile -> Stream.of(
+                                        
profile.childElement(REPOSITORIES).orElse(null),
+                                        
profile.childElement(PLUGIN_REPOSITORIES)
+                                                .orElse(null))
+                                .filter(Objects::nonNull)));
+
+        repositoryContainers.forEach(container -> {
+            String elementType = container.name().equals(REPOSITORIES) ? 
REPOSITORY : PLUGIN_REPOSITORY;
+            container.childElements(elementType).forEach(repository -> {
+                String url = repository.childText("url");
+                String id = repository.childText("id");
+
+                if (url != null && !url.contains("${")) {
+                    String normalizedUrl = url.trim().replaceAll("/+$", "");
+                    if (!WELL_KNOWN_REPO_URLS.contains(normalizedUrl)) {
+                        context.warning("Repository '" + (id != null ? id : 
normalizedUrl)
+                                + "' is a third-party repository. Maven 4's 
prefix-based artifact"
+                                + " filtering may block resolution if the 
repository does not"
+                                + " publish a prefixes.txt file. If builds 
fail with"
+                                + " 'ArtifactFilteredOutException', check 
repository"
+                                + " prefix configuration.");
+                    }
+                }

Review Comment:
   This warns for any repository URL that isn’t in WELL_KNOWN_REPO_URLS, 
including non-HTTP(S) repositories such as file: URLs. Maven 4 prefix filtering 
is an HTTP repository concern; warning on local/file repositories will create 
false positives and noise.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to