From 89c28fb8d095b7c76115124d868579a5d4529d0b Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Sun, 3 Jul 2022 20:18:39 +0200 Subject: [PATCH] [MDEPLOY-193] Deploy At End feature (no extension) (#20) This PR makes deployAtEnd work as expected even if maven-deploy-plugin is not used as extension. How it works: it uses mojo Context to store "state markers" (and params): * presence of marker means project was "processed" * value of state marker tells what should be done * if needed, other params are stored as well UTs adjusted to provide plugin context (was null before). --- src/it/deploy-at-end-fail/verify.groovy | 2 +- src/it/deploy-at-end-pass/verify.groovy | 2 +- .../maven/plugins/deploy/DeployMojo.java | 147 +++++++++++++----- .../maven/plugins/deploy/DeployMojoTest.java | 22 ++- 4 files changed, 127 insertions(+), 46 deletions(-) diff --git a/src/it/deploy-at-end-fail/verify.groovy b/src/it/deploy-at-end-fail/verify.groovy index 03398e8b..27d678a6 100644 --- a/src/it/deploy-at-end-fail/verify.groovy +++ b/src/it/deploy-at-end-fail/verify.groovy @@ -22,5 +22,5 @@ assert !( new File( basedir, "module1/target/repo/org/apache/maven/its/deploy/da File buildLog = new File( basedir, 'build.log' ) assert buildLog.exists() -assert buildLog.text.contains( "[INFO] Deploying org.apache.maven.its.deploy.dae.fail:dae:1.0 at end" ) +assert buildLog.text.contains( "[INFO] Deferring deploy for org.apache.maven.its.deploy.dae.fail:dae:1.0 at end" ) diff --git a/src/it/deploy-at-end-pass/verify.groovy b/src/it/deploy-at-end-pass/verify.groovy index d776935a..634808a5 100644 --- a/src/it/deploy-at-end-pass/verify.groovy +++ b/src/it/deploy-at-end-pass/verify.groovy @@ -22,5 +22,5 @@ assert new File( basedir, "module1/target/repo/org/apache/maven/its/deploy/dae/p File buildLog = new File( basedir, 'build.log' ) assert buildLog.exists() -assert buildLog.text.contains( "[INFO] Deploying org.apache.maven.its.deploy.dae.pass:dae:1.0 at end" ) +assert buildLog.text.contains( "[INFO] Deferring deploy for org.apache.maven.its.deploy.dae.pass:dae:1.0 at end" ) diff --git a/src/main/java/org/apache/maven/plugins/deploy/DeployMojo.java b/src/main/java/org/apache/maven/plugins/deploy/DeployMojo.java index 4aaa971a..bec6b384 100644 --- a/src/main/java/org/apache/maven/plugins/deploy/DeployMojo.java +++ b/src/main/java/org/apache/maven/plugins/deploy/DeployMojo.java @@ -19,10 +19,8 @@ * under the License. */ -import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -30,6 +28,7 @@ import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; @@ -55,23 +54,15 @@ public class DeployMojo private static final Pattern ALT_REPO_SYNTAX_PATTERN = Pattern.compile( "(.+?)::(.+)" ); - /** - * When building with multiple threads, reaching the last project doesn't have to mean that all projects are ready - * to be deployed - */ - private static final AtomicInteger READYPROJECTSCOUNTER = new AtomicInteger(); - - private static final List DEPLOYREQUESTS = - Collections.synchronizedList( new ArrayList() ); - - /** - */ @Parameter( defaultValue = "${project}", readonly = true, required = true ) private MavenProject project; @Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true ) private List reactorProjects; + @Parameter( defaultValue = "${plugin}", required = true, readonly = true ) + private PluginDescriptor pluginDescriptor; + /** * Whether every project should be deployed during its own deploy-phase or at the end of the multimodule build. If * set to {@code true} and the build fails, none of the reactor projects is deployed. @@ -143,63 +134,139 @@ public class DeployMojo @Component private ProjectDeployer projectDeployer; + private enum State + { + SKIPPED, DEPLOYED, TO_BE_DEPLOYED + } + + private static final String DEPLOY_PROCESSED_MARKER = DeployMojo.class.getName() + ".processed"; + + private static final String DEPLOY_ALT_RELEASE_DEPLOYMENT_REPOSITORY = + DeployMojo.class.getName() + ".altReleaseDeploymentRepository"; + + private static final String DEPLOY_ALT_SNAPSHOT_DEPLOYMENT_REPOSITORY = + DeployMojo.class.getName() + ".altSnapshotDeploymentRepository"; + + private static final String DEPLOY_ALT_DEPLOYMENT_REPOSITORY = + DeployMojo.class.getName() + ".altDeploymentRepository"; + + private void putState( State state ) + { + getPluginContext().put( DEPLOY_PROCESSED_MARKER, state.name() ); + } + + private void putPluginContextValue( String key, String value ) + { + if ( value != null ) + { + getPluginContext().put( key, value ); + } + } + + private String getPluginContextValue( Map pluginContext, String key ) + { + return (String) pluginContext.get( key ); + } + + private State getState( Map pluginContext ) + { + return State.valueOf( getPluginContextValue( pluginContext, DEPLOY_PROCESSED_MARKER ) ); + } + + private boolean hasState( MavenProject project ) + { + Map pluginContext = getSession().getPluginContext( pluginDescriptor, project ); + return pluginContext.containsKey( DEPLOY_PROCESSED_MARKER ); + } + public void execute() throws MojoExecutionException, MojoFailureException { - boolean addedDeployRequest = false; if ( Boolean.parseBoolean( skip ) || ( "releases".equals( skip ) && !ArtifactUtils.isSnapshot( project.getVersion() ) ) || ( "snapshots".equals( skip ) && ArtifactUtils.isSnapshot( project.getVersion() ) ) ) { getLog().info( "Skipping artifact deployment" ); + putState( State.SKIPPED ); } else { failIfOffline(); - // CHECKSTYLE_OFF: LineLength - // @formatter:off - ProjectDeployerRequest pdr = new ProjectDeployerRequest() - .setProject( project ) - .setRetryFailedDeploymentCount( getRetryFailedDeploymentCount() ) - .setAltReleaseDeploymentRepository( altReleaseDeploymentRepository ) - .setAltSnapshotDeploymentRepository( altSnapshotDeploymentRepository ) - .setAltDeploymentRepository( altDeploymentRepository ); - // @formatter:on - // CHECKSTYLE_ON: LineLength - - ArtifactRepository repo = getDeploymentRepository( pdr ); - if ( !deployAtEnd ) { + // CHECKSTYLE_OFF: LineLength + // @formatter:off + ProjectDeployerRequest pdr = new ProjectDeployerRequest() + .setProject( project ) + .setRetryFailedDeploymentCount( getRetryFailedDeploymentCount() ) + .setAltReleaseDeploymentRepository( altReleaseDeploymentRepository ) + .setAltSnapshotDeploymentRepository( altSnapshotDeploymentRepository ) + .setAltDeploymentRepository( altDeploymentRepository ); + // @formatter:on + // CHECKSTYLE_ON: LineLength + + ArtifactRepository repo = getDeploymentRepository( pdr ); + deployProject( getSession().getProjectBuildingRequest(), pdr, repo ); + putState( State.DEPLOYED ); } else { - DEPLOYREQUESTS.add( pdr ); - addedDeployRequest = true; + putState( State.TO_BE_DEPLOYED ); + putPluginContextValue( DEPLOY_ALT_RELEASE_DEPLOYMENT_REPOSITORY, altReleaseDeploymentRepository ); + putPluginContextValue( DEPLOY_ALT_SNAPSHOT_DEPLOYMENT_REPOSITORY, altSnapshotDeploymentRepository ); + putPluginContextValue( DEPLOY_ALT_DEPLOYMENT_REPOSITORY, altDeploymentRepository ); + getLog().info( "Deferring deploy for " + getProjectReferenceId( project ) + " at end" ); } } - boolean projectsReady = READYPROJECTSCOUNTER.incrementAndGet() == reactorProjects.size(); - if ( projectsReady ) + if ( allProjectsMarked() ) { - synchronized ( DEPLOYREQUESTS ) + for ( MavenProject reactorProject : reactorProjects ) { - while ( !DEPLOYREQUESTS.isEmpty() ) + Map pluginContext = getSession().getPluginContext( pluginDescriptor, reactorProject ); + State state = getState( pluginContext ); + if ( state == State.TO_BE_DEPLOYED ) { - ArtifactRepository repo = getDeploymentRepository( DEPLOYREQUESTS.get( 0 ) ); - - deployProject( getSession().getProjectBuildingRequest(), DEPLOYREQUESTS.remove( 0 ), repo ); + String altReleaseDeploymentRepository = + getPluginContextValue( pluginContext, DEPLOY_ALT_RELEASE_DEPLOYMENT_REPOSITORY ); + String altSnapshotDeploymentRepository = + getPluginContextValue( pluginContext, DEPLOY_ALT_SNAPSHOT_DEPLOYMENT_REPOSITORY ); + String altDeploymentRepository = + getPluginContextValue( pluginContext, DEPLOY_ALT_DEPLOYMENT_REPOSITORY ); + + ProjectDeployerRequest pdr = new ProjectDeployerRequest() + .setProject( reactorProject ) + .setRetryFailedDeploymentCount( getRetryFailedDeploymentCount() ) + .setAltReleaseDeploymentRepository( altReleaseDeploymentRepository ) + .setAltSnapshotDeploymentRepository( altSnapshotDeploymentRepository ) + .setAltDeploymentRepository( altDeploymentRepository ); + + ArtifactRepository repo = getDeploymentRepository( pdr ); + + deployProject( getSession().getProjectBuildingRequest(), pdr, repo ); } } } - else if ( addedDeployRequest ) + } + + private String getProjectReferenceId( MavenProject mavenProject ) + { + return mavenProject.getGroupId() + ":" + mavenProject.getArtifactId() + ":" + mavenProject.getVersion(); + } + + private boolean allProjectsMarked() + { + for ( MavenProject reactorProject : reactorProjects ) { - getLog().info( "Deploying " + project.getGroupId() + ":" + project.getArtifactId() + ":" - + project.getVersion() + " at end" ); + if ( !hasState( reactorProject ) ) + { + return false; + } } + return true; } private void deployProject( ProjectBuildingRequest pbr, ProjectDeployerRequest pir, ArtifactRepository repo ) diff --git a/src/test/java/org/apache/maven/plugins/deploy/DeployMojoTest.java b/src/test/java/org/apache/maven/plugins/deploy/DeployMojoTest.java index 6404f5ad..c8350c47 100644 --- a/src/test/java/org/apache/maven/plugins/deploy/DeployMojoTest.java +++ b/src/test/java/org/apache/maven/plugins/deploy/DeployMojoTest.java @@ -19,6 +19,7 @@ * under the License. */ +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -28,11 +29,13 @@ import java.util.Collections; import java.util.List; import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.plugin.testing.AbstractMojoTestCase; import org.apache.maven.plugin.testing.stubs.MavenProjectStub; import org.apache.maven.plugins.deploy.stubs.ArtifactDeployerStub; @@ -68,7 +71,6 @@ public class DeployMojoTest MavenProjectStub project = new MavenProjectStub(); - @Mock private MavenSession session; @InjectMocks @@ -78,7 +80,11 @@ public void setUp() throws Exception { super.setUp(); - + + session = mock( MavenSession.class ); + when( session.getPluginContext(any(PluginDescriptor.class), any(MavenProject.class))) + .thenReturn( new ConcurrentHashMap() ); + remoteRepo = new File( REMOTE_REPO ); remoteRepo.mkdirs(); @@ -144,7 +150,8 @@ public void testBasicDeploy() assertTrue( file.exists() ); MavenProject project = (MavenProject) getVariableValueFromObject( mojo, "project" ); - + + setVariableValueToObject( mojo, "pluginContext", new ConcurrentHashMap<>() ); setVariableValueToObject( mojo, "reactorProjects", Collections.singletonList( project ) ); artifact = ( DeployArtifactStub ) project.getArtifact(); @@ -251,8 +258,11 @@ public void testSkippingDeploy() assertTrue( file.exists() ); MavenProject project = (MavenProject) getVariableValueFromObject( mojo, "project" ); - + + setVariableValueToObject( mojo, "pluginDescriptor", new PluginDescriptor() ); + setVariableValueToObject( mojo, "pluginContext", new ConcurrentHashMap<>() ); setVariableValueToObject( mojo, "reactorProjects", Collections.singletonList( project ) ); + setVariableValueToObject( mojo, "session", session ); artifact = (DeployArtifactStub) project.getArtifact(); @@ -316,6 +326,7 @@ public void testBasicDeployWithPackagingAsPom() MavenProject project = (MavenProject) getVariableValueFromObject( mojo, "project" ); + setVariableValueToObject( mojo, "pluginContext", new ConcurrentHashMap<>() ); setVariableValueToObject( mojo, "reactorProjects", Collections.singletonList( project ) ); artifact = (DeployArtifactStub) project.getArtifact(); @@ -381,6 +392,7 @@ public void testDeployIfArtifactFileIsNull() MavenProject project = (MavenProject) getVariableValueFromObject( mojo, "project" ); + setVariableValueToObject( mojo, "pluginContext", new ConcurrentHashMap<>() ); setVariableValueToObject( mojo, "reactorProjects", Collections.singletonList( project ) ); artifact = (DeployArtifactStub) project.getArtifact(); @@ -422,6 +434,7 @@ public void testDeployWithAttachedArtifacts() MavenProject project = (MavenProject) getVariableValueFromObject( mojo, "project" ); + setVariableValueToObject( mojo, "pluginContext", new ConcurrentHashMap<>() ); setVariableValueToObject( mojo, "reactorProjects", Collections.singletonList( project ) ); artifact = (DeployArtifactStub) project.getArtifact(); @@ -523,6 +536,7 @@ public void _testBasicDeployWithScpAsProtocol() MavenProject project = (MavenProject) getVariableValueFromObject( mojo, "project" ); + setVariableValueToObject( mojo, "pluginContext", new ConcurrentHashMap<>() ); setVariableValueToObject( mojo, "reactorProjects", Collections.singletonList( project ) ); artifact = (DeployArtifactStub) project.getArtifact();