diff --git a/src/it/it-parent-updates-report-001/invoker.properties b/src/it/it-parent-updates-report-001/invoker.properties new file mode 100644 index 0000000000..c01f7db0e4 --- /dev/null +++ b/src/it/it-parent-updates-report-001/invoker.properties @@ -0,0 +1 @@ +invoker.goals=site diff --git a/src/it/it-parent-updates-report-001/module/pom.xml b/src/it/it-parent-updates-report-001/module/pom.xml new file mode 100644 index 0000000000..80c8c58638 --- /dev/null +++ b/src/it/it-parent-updates-report-001/module/pom.xml @@ -0,0 +1,64 @@ + + 4.0.0 + + + localhost + dummy-parent3 + 1.0.0 + + + module + 1.0.0 + + + + + + maven-clean-plugin + 2.2 + + + maven-deploy-plugin + 2.3 + + + maven-install-plugin + 2.2 + + + maven-site-plugin + @sitePluginVersion@ + + + maven-project-info-reports-plugin + 2.1 + + + + + + + + + maven-project-info-reports-plugin + 2.1 + + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + + parent-updates-report + + + + + + + + \ No newline at end of file diff --git a/src/it/it-parent-updates-report-001/pom.xml b/src/it/it-parent-updates-report-001/pom.xml new file mode 100644 index 0000000000..ef9bad54d2 --- /dev/null +++ b/src/it/it-parent-updates-report-001/pom.xml @@ -0,0 +1,70 @@ + + 4.0.0 + + + localhost + dummy-parent2 + 1.0 + + + it-parent-updates-report-001 + 1.0.0 + pom + + + module + + + + + + + maven-clean-plugin + 2.2 + + + maven-deploy-plugin + 2.3 + + + maven-install-plugin + 2.2 + + + maven-site-plugin + @sitePluginVersion@ + + + maven-project-info-reports-plugin + 2.1 + + + + + + + + + maven-project-info-reports-plugin + 2.1 + + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + + parent-updates-report + + + + + + + + diff --git a/src/it/it-parent-updates-report-001/src/site/site.xml b/src/it/it-parent-updates-report-001/src/site/site.xml new file mode 100644 index 0000000000..e955148484 --- /dev/null +++ b/src/it/it-parent-updates-report-001/src/site/site.xml @@ -0,0 +1,7 @@ + + + org.apache.maven.skins + maven-default-skin + 1.0 + + diff --git a/src/it/it-parent-updates-report-001/verify.groovy b/src/it/it-parent-updates-report-001/verify.groovy new file mode 100644 index 0000000000..130ccd01f0 --- /dev/null +++ b/src/it/it-parent-updates-report-001/verify.groovy @@ -0,0 +1,13 @@ +report = new File( basedir, "target/site/parent-updates-report.html" ).text + .replaceAll( "<[^>]+>", " " ) + .replaceAll( "&[^;]+;", " " ) + .replaceAll( "\\s+", " " ) + +assert report =~ /dummy-parent2 +1.0 +pom +3.1/ + +report = new File( basedir, "module/target/site/parent-updates-report.html" ).text + .replaceAll( "<[^>]+>", " " ) + .replaceAll( "&[^;]+;", " " ) + .replaceAll( "\\s+", " " ) + +assert report =~ /dummy-parent3 +1.0.0 +pom/ \ No newline at end of file diff --git a/src/main/java/org/codehaus/mojo/versions/DependencyUpdatesRenderer.java b/src/main/java/org/codehaus/mojo/versions/DependencyUpdatesRenderer.java index 647b865873..d1d6b807af 100644 --- a/src/main/java/org/codehaus/mojo/versions/DependencyUpdatesRenderer.java +++ b/src/main/java/org/codehaus/mojo/versions/DependencyUpdatesRenderer.java @@ -72,10 +72,9 @@ protected void renderBody() renderSummaryTotalsTable( allUpdates ); - renderSummaryTable( "report.overview.dependencyManagement", dependencyManagementUpdates, - "report.overview.noDependencyManagement" ); + renderDependencyManagementSummary(); - renderSummaryTable( "report.overview.dependency", dependencyUpdates, "report.overview.noDependency" ); + renderDependencySummary(); sink.section1_(); @@ -94,7 +93,18 @@ protected void renderBody() sink.section1_(); } - private void renderSummaryTable( String titleKey, Map contents, String emptyKey ) + protected void renderDependencySummary() + { + renderSummaryTable( "report.overview.dependency", dependencyUpdates, "report.overview.noDependency" ); + } + + protected void renderDependencyManagementSummary() + { + renderSummaryTable( "report.overview.dependencyManagement", dependencyManagementUpdates, + "report.overview.noDependencyManagement" ); + } + + protected void renderSummaryTable( String titleKey, Map contents, String emptyKey ) { sink.section2(); sink.sectionTitle2(); @@ -114,7 +124,7 @@ private void renderSummaryTable( String titleKey, Map allUpdates ) + protected void renderSummaryTotalsTable( Map allUpdates ) { int numInc = 0; int numMin = 0; @@ -203,7 +213,7 @@ else if ( details.getOldestUpdate( of( MAJOR ) ) != null ) sink.table_(); } - private void renderDependencyDetail( Dependency dependency, ArtifactVersions details ) + protected void renderDependencyDetail( Dependency dependency, ArtifactVersions details ) { sink.section2(); sink.sectionTitle2(); diff --git a/src/main/java/org/codehaus/mojo/versions/ParentUpdatesReport.java b/src/main/java/org/codehaus/mojo/versions/ParentUpdatesReport.java new file mode 100644 index 0000000000..cbc99740f3 --- /dev/null +++ b/src/main/java/org/codehaus/mojo/versions/ParentUpdatesReport.java @@ -0,0 +1,145 @@ +package org.codehaus.mojo.versions; + +/* + * 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. + */ + +import javax.inject.Inject; + +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.maven.artifact.manager.WagonManager; +import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException; +import org.apache.maven.artifact.metadata.ArtifactMetadataSource; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.model.Dependency; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.apache.maven.reporting.MavenReportException; +import org.apache.maven.repository.RepositorySystem; +import org.codehaus.mojo.versions.api.ArtifactVersions; +import org.codehaus.mojo.versions.utils.DependencyBuilder; +import org.codehaus.plexus.i18n.I18N; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; + +/** + * Generates a report on available updates for parent artifacts + * + * @author Andrzej Jarmoniuk + * @since 2.13.0 + */ +@Mojo( name = "parent-updates-report", + requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = false ) +public class ParentUpdatesReport extends AbstractVersionsReport +{ + @Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true ) + protected List reactorProjects; + + @Inject + protected ParentUpdatesReport( I18N i18n, RepositorySystem repositorySystem, ArtifactResolver artifactResolver, + ArtifactMetadataSource artifactMetadataSource, WagonManager wagonManager ) + { + super( i18n, repositorySystem, artifactResolver, artifactMetadataSource, wagonManager ); + } + + /** + * {@inheritDoc} + */ + public boolean isExternalReport() + { + return false; + } + + /** + * {@inheritDoc} + */ + public boolean canGenerateReport() + { + if ( getProject().getParent() == null ) + { + getLog().warn( "Project does not have a parent." ); + return false; + } + + if ( reactorProjects.contains( getProject().getParent() ) ) + { + getLog().warn( "Parent project is part of the reactor." ); + return false; + } + + return true; + } + + /** + * generates an empty report in case there are no sources to generate a report with + * + * @param locale the locale to generate the report for. + * @param sink the report formatting tool + */ + @SuppressWarnings( "deprecation" ) + protected void doGenerateReport( Locale locale, Sink sink ) throws MavenReportException + { + try + { + ArtifactVersions artifactVersions = getHelper().lookupArtifactVersions( project.getParentArtifact(), + false ); + artifactVersions.setIncludeSnapshots( allowSnapshots ); + Dependency parent = DependencyBuilder.newBuilder() + .withGroupId( artifactVersions.getGroupId() ) + .withArtifactId( artifactVersions.getArtifactId() ) + .withVersion( artifactVersions.getArtifact().getVersion() ) + .withScope( artifactVersions.getArtifact().getScope() ) + .withType( artifactVersions.getArtifact().getType() ) + .withClassifier( artifactVersions.getArtifact().getClassifier() ) + .build(); + new DependencyUpdatesRenderer( sink, getI18n(), getOutputName(), locale, + singletonMap( parent, artifactVersions ), emptyMap() ) + { + @Override + protected void renderSummaryTotalsTable( Map allUpdates ) + { + } + + @Override + protected void renderDependencyManagementSummary() + { + } + } + .renderBody(); + } + catch ( ArtifactMetadataRetrievalException e ) + { + throw new MavenReportException( e.getMessage(), e ); + } + } + + /** + * {@inheritDoc} + */ + public String getOutputName() + { + return "parent-updates-report"; + } +} diff --git a/src/main/resources/parent-updates-report.properties b/src/main/resources/parent-updates-report.properties new file mode 100644 index 0000000000..632d111778 --- /dev/null +++ b/src/main/resources/parent-updates-report.properties @@ -0,0 +1,58 @@ +# 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. + +report.title=Parent Updates Report +report.description=Provides information on parent artifact updates +report.overview.title=Overview +report.overview.text=This report summarizes newer versions that may be available for your project's parent artifacts +report.overview.dependency=Parent artifact +report.overview.noDependency=This project does not declare a parent artifact +report.overview.numUpToDate=# of parent artifacts using the latest version available +report.overview.numNewerMajorAvailable=# of parent artifacts where the next version available is a major version update +report.overview.numNewerMinorAvailable=# of parent artifacts where the next version available is a minor version update +report.overview.numNewerIncrementalAvailable=# of parent artifacts where the next version available is an incremental \ + version update +report.overview.numNewerVersionAvailable=# of parent artifacts where the next version available is smaller than an \ + incremental version update +report.overview.property=Summary of parent artifacts associated with artifact versions +report.overview.noProperty=No properties associated with artifacts have been defined. +report.detail.title=Parent Update +report.detail.text= +report.status=Status +report.groupId=Group Id +report.artifactId=Artifact Id +report.currentVersion=Current Version +report.scope=Scope +report.classifier=Classifier +report.type=Type +report.nextVersion=Next Version +report.latestSubIncremental=Latest Subincremental +report.nextIncremental=Next Incremental +report.latestIncremental=Latest Incremental +report.nextMinor=Next Minor +report.latestMinor=Latest Minor +report.nextMajor=Next Major +report.latestMajor=Latest Major +report.updateVersions=Newer versions +report.noUpdatesAvailable=No newer versions available. +report.otherUpdatesAvailable=There is at least one newer version available. +report.incrementalUpdatesAvailable=There is at least one newer incremental version available. \ + Incremental updates are typically passive. +report.minorUpdatesAvailable=There is at least one newer minor version available. \ + Minor updates are sometimes passive. +report.majorUpdatesAvailable=There is at least one newer major version available. \ + Major updates are rarely passive. diff --git a/src/site/apt/index.apt b/src/site/apt/index.apt index 66d643fe39..e95618a672 100644 --- a/src/site/apt/index.apt +++ b/src/site/apt/index.apt @@ -116,6 +116,9 @@ Versions Maven Plugin * {{{./property-updates-report-mojo.html}versions:property-updates-report}} produces a report of those properties which are used to control artifact versions and which properties have newer versions available. + * {{{./parent-updates-report-mojo.html}versions:parent-updates-report}} produces a report on possible parent artifact + upgrades. + * Usage General instructions on how to use the Versions Plugin can be found on the {{{./usage.html}usage page}}. Some more diff --git a/src/test/java/org/codehaus/mojo/versions/DependencyUpdatesReportTest.java b/src/test/java/org/codehaus/mojo/versions/DependencyUpdatesReportTest.java index ed0b789267..12c149f223 100644 --- a/src/test/java/org/codehaus/mojo/versions/DependencyUpdatesReportTest.java +++ b/src/test/java/org/codehaus/mojo/versions/DependencyUpdatesReportTest.java @@ -27,7 +27,6 @@ import java.util.Locale; import java.util.Set; -import org.apache.maven.artifact.DefaultArtifact; import org.apache.maven.doxia.module.xhtml5.Xhtml5SinkFactory; import org.apache.maven.doxia.sink.SinkFactory; import org.apache.maven.model.Dependency; @@ -35,9 +34,8 @@ import org.apache.maven.model.Model; import org.apache.maven.project.MavenProject; import org.apache.maven.reporting.MavenReportException; -import org.apache.maven.repository.RepositorySystem; -import org.codehaus.mojo.versions.utils.DependencyBuilder; import org.codehaus.mojo.versions.model.RuleSet; +import org.codehaus.mojo.versions.utils.DependencyBuilder; import org.codehaus.mojo.versions.utils.MockUtils; import org.junit.Test; @@ -49,9 +47,6 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.anyOf; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; /** * Basic tests for {@linkplain DependencyUpdatesReport}. @@ -65,7 +60,7 @@ private static class TestDependencyUpdatesReport extends DependencyUpdatesReport @SuppressWarnings( "deprecation" ) TestDependencyUpdatesReport() { - super( mockI18N(), mockRepositorySystem(), null, mockArtifactMetadataSource(), null ); + super( mockI18N(), MockUtils.mockRepositorySystem(), null, mockArtifactMetadataSource(), null ); siteTool = MockUtils.mockSiteTool(); project = new MavenProject(); @@ -133,20 +128,6 @@ public TestDependencyUpdatesReport withIgnoredVersions( this.ignoredVersions = ignoredVersions; return this; } - - private static RepositorySystem mockRepositorySystem() - { - RepositorySystem repositorySystem = mock( RepositorySystem.class ); - when( repositorySystem.createDependencyArtifact( any( Dependency.class ) ) ).thenAnswer( - invocation -> - { - Dependency dependency = invocation.getArgument( 0 ); - return new DefaultArtifact( dependency.getGroupId(), dependency.getArtifactId(), - dependency.getVersion(), dependency.getScope(), dependency.getType(), - dependency.getClassifier(), null ); - } ); - return repositorySystem; - } } private static Dependency dependencyOf( String artifactId ) diff --git a/src/test/java/org/codehaus/mojo/versions/ParentUpdatesReportTest.java b/src/test/java/org/codehaus/mojo/versions/ParentUpdatesReportTest.java new file mode 100644 index 0000000000..95c81c19be --- /dev/null +++ b/src/test/java/org/codehaus/mojo/versions/ParentUpdatesReportTest.java @@ -0,0 +1,87 @@ +package org.codehaus.mojo.versions; + +/* + * 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. + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Locale; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DefaultArtifact; +import org.apache.maven.doxia.module.xhtml5.Xhtml5SinkFactory; +import org.apache.maven.doxia.sink.SinkFactory; +import org.apache.maven.model.Model; +import org.apache.maven.project.MavenProject; +import org.apache.maven.reporting.MavenReportException; +import org.junit.Test; + +import static org.codehaus.mojo.versions.utils.MockUtils.mockArtifactMetadataSource; +import static org.codehaus.mojo.versions.utils.MockUtils.mockI18N; +import static org.codehaus.mojo.versions.utils.MockUtils.mockRepositorySystem; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; + +/** + * Basic tests for {@linkplain ParentUpdatesReport}. + * + * @author Andrzej Jarmoniuk + */ +public class ParentUpdatesReportTest +{ + @Test + public void testAllowSnapshots() throws IOException, MavenReportException + { + OutputStream os = new ByteArrayOutputStream(); + SinkFactory sinkFactory = new Xhtml5SinkFactory(); + new ParentUpdatesReport( mockI18N(), mockRepositorySystem(), null, + mockArtifactMetadataSource( new HashMap() + {{ + put( "default-artifact", new String[] {"1.0.0", "1.0.1", "1.1.0", "2.0.0", "2.0.1-SNAPSHOT"} ); + }} ), null ) + {{ + allowSnapshots = true; + project = new MavenProject( new Model() + {{ + setGroupId( "default-group" ); + setArtifactId( "child-artifact" ); + setVersion( "1.0.0" ); + }} ); + project.setParent( new MavenProject( new Model() + {{ + setGroupId( "default-group" ); + setArtifactId( "default-artifact" ); + setVersion( "1.0.0" ); + }} ) ); + reactorProjects = new ArrayList<>(); + project.setParentArtifact( new DefaultArtifact( project.getParent().getGroupId(), + project.getParent().getArtifactId(), project.getParent().getVersion(), + Artifact.SCOPE_COMPILE, "pom", "default", null ) ); + }}.generate( sinkFactory.createSink( os ), sinkFactory, Locale.getDefault() ); + + String output = os.toString(); + assertThat( output, allOf( containsString( "1.0.0" ), + containsString( "1.0.1" ), containsString( "1.1.0" ), + containsString( "2.0.0" ), containsString( "2.0.1-SNAPSHOT" ) ) ); + } +} diff --git a/src/test/java/org/codehaus/mojo/versions/utils/MockUtils.java b/src/test/java/org/codehaus/mojo/versions/utils/MockUtils.java index 71e885f889..efc108dd25 100644 --- a/src/test/java/org/codehaus/mojo/versions/utils/MockUtils.java +++ b/src/test/java/org/codehaus/mojo/versions/utils/MockUtils.java @@ -25,11 +25,14 @@ import java.util.Map; import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DefaultArtifact; import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException; import org.apache.maven.artifact.metadata.ArtifactMetadataSource; import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.apache.maven.doxia.tools.SiteTool; import org.apache.maven.doxia.tools.SiteToolException; +import org.apache.maven.model.Dependency; +import org.apache.maven.repository.RepositorySystem; import org.codehaus.plexus.i18n.I18N; import static org.mockito.ArgumentMatchers.any; @@ -111,4 +114,18 @@ public static SiteTool mockSiteTool() } return siteTool; } + + public static RepositorySystem mockRepositorySystem() + { + RepositorySystem repositorySystem = mock( RepositorySystem.class ); + when( repositorySystem.createDependencyArtifact( any( Dependency.class ) ) ).thenAnswer( + invocation -> + { + Dependency dependency = invocation.getArgument( 0 ); + return new DefaultArtifact( dependency.getGroupId(), dependency.getArtifactId(), + dependency.getVersion(), dependency.getScope(), dependency.getType(), + dependency.getClassifier(), null ); + } ); + return repositorySystem; + } }