diff --git a/pom.xml b/pom.xml
index 911959bb9..079d68f34 100644
--- a/pom.xml
+++ b/pom.xml
@@ -214,7 +214,8 @@ under the License.
4.0.0
2.0.0-M11
2024-07-17T13:43:56Z
-
+
+ 3.9.1
@@ -294,6 +295,11 @@ under the License.
commons-io
2.20.0
+
+ org.apache.maven.scm
+ maven-scm-api
+ 2.1.0
+
diff --git a/src/it/projects/effective-site/pom.xml b/src/it/projects/effective-site/pom.xml
index d7898fe1f..86083dbc3 100644
--- a/src/it/projects/effective-site/pom.xml
+++ b/src/it/projects/effective-site/pom.xml
@@ -27,7 +27,7 @@ under the License.
org.apache.maven.plugins
maven-plugins
- 43
+ 45
org.apache.maven.plugins.site.its
diff --git a/src/it/projects/full-reporting/pom.xml b/src/it/projects/full-reporting/pom.xml
index 30f726f94..2b6458880 100644
--- a/src/it/projects/full-reporting/pom.xml
+++ b/src/it/projects/full-reporting/pom.xml
@@ -22,7 +22,7 @@ under the License.
org.apache.maven
maven-parent
- 43
+ 45
org.apache.maven.plugins.site.its
diff --git a/src/it/projects/full-reporting/verify.groovy b/src/it/projects/full-reporting/verify.groovy
index deaa154e6..023b4c8db 100644
--- a/src/it/projects/full-reporting/verify.groovy
+++ b/src/it/projects/full-reporting/verify.groovy
@@ -27,7 +27,7 @@ assert content.contains( 'Tests run: 1, Failures: 0, Errors: 0, Skipped: 0' );
sitedir = new File( basedir, 'target/site' );
-assert new File( sitedir, 'surefire-report.html' ).exists();
+assert new File( sitedir, 'surefire.html' ).exists();
assert new File( sitedir, 'index.html' ).exists();
assert new File( sitedir, 'checkstyle.html' ).exists();
assert new File( sitedir, 'cpd.html' ).exists();
diff --git a/src/main/java/org/apache/maven/plugins/site/AbstractSiteMojo.java b/src/main/java/org/apache/maven/plugins/site/AbstractSiteMojo.java
index 44fe1adbf..75f6e9f3f 100644
--- a/src/main/java/org/apache/maven/plugins/site/AbstractSiteMojo.java
+++ b/src/main/java/org/apache/maven/plugins/site/AbstractSiteMojo.java
@@ -18,12 +18,13 @@
*/
package org.apache.maven.plugins.site;
+import javax.inject.Inject;
+
import java.util.List;
import java.util.Locale;
import org.apache.maven.doxia.tools.SiteTool;
import org.apache.maven.plugin.AbstractMojo;
-import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.i18n.I18N;
@@ -54,13 +55,13 @@ public abstract class AbstractSiteMojo extends AbstractMojo {
/**
* SiteTool.
*/
- @Component
+ @Inject
protected SiteTool siteTool;
/**
* Internationalization.
*/
- @Component
+ @Inject
protected I18N i18n;
/**
diff --git a/src/main/java/org/apache/maven/plugins/site/deploy/AbstractDeployMojo.java b/src/main/java/org/apache/maven/plugins/site/deploy/AbstractDeployMojo.java
index 204f2fc61..daeaa040e 100644
--- a/src/main/java/org/apache/maven/plugins/site/deploy/AbstractDeployMojo.java
+++ b/src/main/java/org/apache/maven/plugins/site/deploy/AbstractDeployMojo.java
@@ -18,11 +18,14 @@
*/
package org.apache.maven.plugins.site.deploy;
+import javax.inject.Inject;
+
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import org.apache.maven.doxia.site.inheritance.URIPathDescriptor;
import org.apache.maven.doxia.tools.SiteTool;
@@ -31,10 +34,10 @@
import org.apache.maven.model.DistributionManagement;
import org.apache.maven.model.Site;
import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.site.AbstractSiteMojo;
import org.apache.maven.project.MavenProject;
+import org.apache.maven.scm.provider.ScmUrlUtils;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Server;
import org.apache.maven.settings.Settings;
@@ -53,8 +56,6 @@
import org.apache.maven.wagon.observers.Debug;
import org.apache.maven.wagon.proxy.ProxyInfo;
import org.apache.maven.wagon.repository.Repository;
-import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
/**
* Abstract base class for deploy mojos.
@@ -123,10 +124,10 @@ public abstract class AbstractDeployMojo extends AbstractSiteMojo {
private Site deploySite;
- @Component
- private PlexusContainer container;
+ @Inject
+ private Map wagons;
- @Component
+ @Inject
SettingsDecrypter settingsDecrypter;
/**
@@ -277,16 +278,15 @@ private Wagon getWagon(final Repository repository) throws MojoExecutionExceptio
if (protocol == null) {
throw new MojoExecutionException("Unspecified protocol");
}
- try {
- Wagon wagon = container.lookup(Wagon.class, protocol.toLowerCase(Locale.ROOT));
- if (!wagon.supportsDirectoryCopy()) {
- throw new MojoExecutionException(
- "Wagon protocol '" + repository.getProtocol() + "' doesn't support directory copying");
- }
- return wagon;
- } catch (ComponentLookupException e) {
- throw new MojoExecutionException("Cannot find wagon which supports the requested protocol: " + protocol, e);
+ Wagon wagon = wagons.get(protocol.toLowerCase(Locale.ROOT));
+ if (wagon == null) {
+ throw new MojoExecutionException("Cannot find wagon which supports the requested protocol: " + protocol);
+ }
+ if (!wagon.supportsDirectoryCopy()) {
+ throw new MojoExecutionException(
+ "Wagon protocol '" + repository.getProtocol() + "' doesn't support directory copying");
}
+ return wagon;
}
public AuthenticationInfo getAuthenticationInfo(String id) {
@@ -493,6 +493,66 @@ private static String getFullName(MavenProject project) {
+ project.getVersion() + ')';
}
+ /**
+ * Extracts the provider-specific URL from an SCM URL for comparison purposes.
+ * For non-SCM URLs, returns the original URL.
+ * For SCM URLs with SCP-like syntax (e.g., git@github.com:user/repo.git),
+ * converts them to a comparable format.
+ * For hierarchical SCM systems like SVN, normalizes the scheme to enable
+ * proper comparison of URLs that differ only in http vs https.
+ *
+ * @param url the URL to process
+ * @return the provider-specific URL for SCM URLs, or the original URL otherwise
+ */
+ static String extractComparableUrl(String url) {
+ if (url != null && url.startsWith("scm:")) {
+ // Extract the SCM provider (e.g., "git", "svn")
+ String provider = ScmUrlUtils.getProvider(url);
+
+ // Extract the provider-specific part of the SCM URL
+ // For example: "scm:git:https://github.com/user/repo.git" -> "https://github.com/user/repo.git"
+ String providerSpecificPart = ScmUrlUtils.getProviderSpecificPart(url);
+ if (providerSpecificPart != null && !providerSpecificPart.isEmpty()) {
+ // Handle SCP-like Git syntax (e.g., git@github.com:user/repo.git or user@host:path)
+ // Convert it to a more standard format for comparison
+ // Note: This is a heuristic check - we look for the pattern of user@host:path
+ // where the colon comes after the @ symbol and is followed by a path
+ if (providerSpecificPart.contains("@")
+ && !providerSpecificPart.startsWith("http://")
+ && !providerSpecificPart.startsWith("https://")
+ && !providerSpecificPart.startsWith("ssh://")) {
+ // Find the @ symbol and look for the first : after it that's not part of a URL scheme
+ int atIndex = providerSpecificPart.lastIndexOf('@');
+ int colonIndex = providerSpecificPart.indexOf(':', atIndex);
+
+ // Verify this looks like SCP syntax: user@host:path
+ // The colon should come after @ and before the end
+ if (atIndex >= 0 && colonIndex > atIndex + 1 && colonIndex < providerSpecificPart.length() - 1) {
+ String host = providerSpecificPart.substring(atIndex + 1, colonIndex);
+ String path = providerSpecificPart.substring(colonIndex + 1);
+ // Convert to a pseudo-URL format for comparison
+ // Note: IPv6 addresses in brackets are handled by this approach
+ // as the brackets will be preserved in the host part
+ return "ssh://" + host + "/" + path;
+ }
+ }
+
+ // For hierarchical VCS systems like SVN, normalize the scheme to allow
+ // comparison of URLs that differ only in http vs https
+ // SVN repositories can be accessed via both protocols and should be considered the same
+ if ("svn".equalsIgnoreCase(provider) && providerSpecificPart.startsWith("https://")) {
+ // Normalize https to http for SVN URLs to enable proper comparison
+ return "http" + providerSpecificPart.substring(5);
+ }
+
+ // Return the provider-specific part as-is for standard URLs or
+ // if SCP syntax conversion is not applicable
+ return providerSpecificPart;
+ }
+ }
+ return url;
+ }
+
/**
* Extract the distributionManagement site of the top level parent of the given MavenProject.
* This climbs up the project hierarchy and returns the site of the last project
@@ -524,8 +584,12 @@ protected MavenProject getTopLevelProject(MavenProject project) throws MojoExecu
}
// MSITE-600
- URIPathDescriptor siteURI = new URIPathDescriptor(URIEncoder.encodeURI(site.getUrl()), "");
- URIPathDescriptor oldSiteURI = new URIPathDescriptor(URIEncoder.encodeURI(oldSite.getUrl()), "");
+ // MSITE-1033: For SCM URLs, extract the provider-specific part for comparison
+ String siteUrlToCompare = extractComparableUrl(site.getUrl());
+ String oldSiteUrlToCompare = extractComparableUrl(oldSite.getUrl());
+
+ URIPathDescriptor siteURI = new URIPathDescriptor(URIEncoder.encodeURI(siteUrlToCompare), "");
+ URIPathDescriptor oldSiteURI = new URIPathDescriptor(URIEncoder.encodeURI(oldSiteUrlToCompare), "");
if (!siteURI.sameSite(oldSiteURI.getBaseURI())) {
return oldProject;
diff --git a/src/main/java/org/apache/maven/plugins/site/render/AbstractSiteRenderingMojo.java b/src/main/java/org/apache/maven/plugins/site/render/AbstractSiteRenderingMojo.java
index 9628c6bb9..72270446d 100644
--- a/src/main/java/org/apache/maven/plugins/site/render/AbstractSiteRenderingMojo.java
+++ b/src/main/java/org/apache/maven/plugins/site/render/AbstractSiteRenderingMojo.java
@@ -20,6 +20,7 @@
import java.io.File;
import java.io.IOException;
+import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
@@ -57,7 +58,6 @@
import org.apache.maven.reporting.exec.MavenReportExecution;
import org.apache.maven.reporting.exec.MavenReportExecutor;
import org.apache.maven.reporting.exec.MavenReportExecutorRequest;
-import org.codehaus.plexus.util.ReaderFactory;
import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
@@ -192,7 +192,9 @@ protected AbstractSiteRenderingMojo(
* @return The input files encoding, never null.
*/
protected String getInputEncoding() {
- return (inputEncoding == null || inputEncoding.isEmpty()) ? ReaderFactory.FILE_ENCODING : inputEncoding;
+ return (inputEncoding == null || inputEncoding.isEmpty())
+ ? Charset.defaultCharset().displayName()
+ : inputEncoding;
}
/**
@@ -215,8 +217,8 @@ protected String getOutputEncoding() {
protected void checkInputEncoding() {
if (inputEncoding == null || inputEncoding.isEmpty()) {
- getLog().warn("Input file encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
- + ", i.e. build is platform dependent!");
+ getLog().warn("Input file encoding has not been set, using platform encoding "
+ + Charset.defaultCharset().displayName() + ", i.e. build is platform dependent!");
}
}
diff --git a/src/test/java/org/apache/maven/plugins/site/deploy/AbstractDeployMojoTest.java b/src/test/java/org/apache/maven/plugins/site/deploy/AbstractDeployMojoTest.java
new file mode 100644
index 000000000..bd7a0540d
--- /dev/null
+++ b/src/test/java/org/apache/maven/plugins/site/deploy/AbstractDeployMojoTest.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugins.site.deploy;
+
+import org.apache.maven.model.DistributionManagement;
+import org.apache.maven.model.Site;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for AbstractDeployMojo.
+ */
+public class AbstractDeployMojoTest {
+
+ /**
+ * Test that getTopLevelProject correctly handles SCM URLs with different repositories.
+ * This is the test case for MSITE-1033.
+ */
+ @Test
+ public void testGetTopLevelProjectWithDifferentScmUrls() throws Exception {
+ // Create a mock deploy mojo
+ TestDeployMojo mojo = new TestDeployMojo();
+
+ // Create child project with SCM URL
+ MavenProject childProject =
+ createProjectWithSite("child", "scm:git:git@github.com:codehaus-plexus/plexus-sec-dispatcher.git/");
+
+ // Create parent project with different SCM URL
+ MavenProject parentProject =
+ createProjectWithSite("parent", "scm:git:https://github.com/codehaus-plexus/plexus-pom.git/");
+
+ // Set up the parent-child relationship
+ childProject.setParent(parentProject);
+
+ // Call getTopLevelProject - it should return childProject, not parentProject
+ // because the SCM URLs point to different repositories
+ MavenProject topProject = mojo.getTopLevelProject(childProject);
+
+ // The top project should be the child project itself since the parent has a different site
+ assertEquals("Top project should be child project due to different SCM URLs", childProject, topProject);
+ }
+
+ /**
+ * Test that getTopLevelProject correctly handles SCM URLs with the same repository.
+ */
+ @Test
+ public void testGetTopLevelProjectWithSameScmUrls() throws Exception {
+ // Create a mock deploy mojo
+ TestDeployMojo mojo = new TestDeployMojo();
+
+ // Create child project with SCM URL
+ MavenProject childProject =
+ createProjectWithSite("child", "scm:git:https://github.com/codehaus-plexus/plexus-pom.git/child");
+
+ // Create parent project with same base SCM URL
+ MavenProject parentProject =
+ createProjectWithSite("parent", "scm:git:https://github.com/codehaus-plexus/plexus-pom.git/");
+
+ // Set up the parent-child relationship
+ childProject.setParent(parentProject);
+
+ // Call getTopLevelProject - it should return parentProject
+ // because the SCM URLs point to the same repository
+ MavenProject topProject = mojo.getTopLevelProject(childProject);
+
+ // The top project should be the parent project since they share the same site
+ assertEquals("Top project should be parent project due to same SCM base URL", parentProject, topProject);
+ }
+
+ /**
+ * Test that getTopLevelProject correctly handles non-SCM URLs.
+ */
+ @Test
+ public void testGetTopLevelProjectWithNonScmUrls() throws Exception {
+ // Create a mock deploy mojo
+ TestDeployMojo mojo = new TestDeployMojo();
+
+ // Create child project with regular URL
+ MavenProject childProject = createProjectWithSite("child", "https://example.com/site/child");
+
+ // Create parent project with same base URL
+ MavenProject parentProject = createProjectWithSite("parent", "https://example.com/site/");
+
+ // Set up the parent-child relationship
+ childProject.setParent(parentProject);
+
+ // Call getTopLevelProject - it should return parentProject
+ MavenProject topProject = mojo.getTopLevelProject(childProject);
+
+ // The top project should be the parent project since they share the same site
+ assertEquals("Top project should be parent project for regular URLs", parentProject, topProject);
+ }
+
+ /**
+ * Test that getTopLevelProject correctly handles SCM URLs with standard https format.
+ */
+ @Test
+ public void testGetTopLevelProjectWithHttpsScmUrls() throws Exception {
+ // Create a mock deploy mojo
+ TestDeployMojo mojo = new TestDeployMojo();
+
+ // Create child project with https SCM URL
+ MavenProject childProject = createProjectWithSite("child", "scm:git:https://github.com/user/repo1.git/");
+
+ // Create parent project with different https SCM URL (different domain)
+ MavenProject parentProject = createProjectWithSite("parent", "scm:git:https://gitlab.com/user/repo2.git/");
+
+ // Set up the parent-child relationship
+ childProject.setParent(parentProject);
+
+ // Call getTopLevelProject - it should return childProject
+ // because the SCM URLs point to different repositories
+ MavenProject topProject = mojo.getTopLevelProject(childProject);
+
+ // The top project should be the child project itself since the parent has a different site
+ assertEquals("Top project should be child project due to different https SCM URLs", childProject, topProject);
+ }
+
+ /**
+ * Test that extractComparableUrl properly handles SVN URLs with different schemes but same host.
+ * For SVN (hierarchical VCS), URLs with different schemes (http vs https) should be normalized
+ * to the same scheme to allow URIPathDescriptor.sameSite() to recognize them as the same site.
+ * Note: The paths may differ (one being a subpath of another), but as long as scheme, host, and port
+ * are the same, URIPathDescriptor.sameSite() will correctly identify them as the same site.
+ */
+ @Test
+ public void testExtractComparableUrlForSvnUrls() throws Exception {
+ // Extract and normalize both URLs
+ String url1 = AbstractDeployMojo.extractComparableUrl("scm:svn:http://svn.apache.org/repos/asf/maven/website");
+ String url2 = AbstractDeployMojo.extractComparableUrl(
+ "scm:svn:https://svn.apache.org/repos/asf/maven/website/components");
+
+ // Both should be normalized to http scheme
+ assertEquals("http://svn.apache.org/repos/asf/maven/website", url1);
+ assertEquals("http://svn.apache.org/repos/asf/maven/website/components", url2);
+
+ // Now verify they would be considered the same site by creating projects with these URLs
+ TestDeployMojo mojo = new TestDeployMojo();
+ MavenProject childProject =
+ createProjectWithSite("child", "scm:svn:https://svn.apache.org/repos/asf/maven/website/components");
+ MavenProject parentProject =
+ createProjectWithSite("parent", "scm:svn:http://svn.apache.org/repos/asf/maven/website");
+ childProject.setParent(parentProject);
+
+ // The parent should be returned as the top project since they share the same SVN site
+ MavenProject topProject = mojo.getTopLevelProject(childProject);
+ assertEquals(
+ "Top project should be parent due to normalized SVN URLs pointing to same site",
+ parentProject,
+ topProject);
+ }
+
+ private MavenProject createProjectWithSite(String artifactId, String siteUrl) {
+ MavenProject project = new MavenProject();
+ project.setGroupId("org.apache.maven.test");
+ project.setArtifactId(artifactId);
+ project.setVersion("1.0");
+ project.setName(artifactId);
+
+ DistributionManagement distributionManagement = new DistributionManagement();
+ Site site = new Site();
+ site.setId("test-site");
+ site.setUrl(siteUrl);
+ distributionManagement.setSite(site);
+ project.setDistributionManagement(distributionManagement);
+
+ return project;
+ }
+
+ /**
+ * Test implementation of AbstractDeployMojo for testing.
+ */
+ private static class TestDeployMojo extends AbstractDeployMojo {
+ @Override
+ protected boolean isDeploy() {
+ return true;
+ }
+
+ @Override
+ protected String determineTopDistributionManagementSiteUrl() throws MojoExecutionException {
+ return "test";
+ }
+
+ @Override
+ protected Site determineDeploySite() throws MojoExecutionException {
+ Site site = new Site();
+ site.setId("test");
+ site.setUrl("test");
+ return site;
+ }
+ }
+}