From 3c8d927459663a4cbad563f851c03ed94f253ce6 Mon Sep 17 00:00:00 2001
From: gregorycallea <gregory.callea@hotmail.it>
Date: Tue, 24 Jul 2018 19:18:08 +0200
Subject: [PATCH] Skip specified attached artifacts from deploy

---
 .../plugins/deploy/AttachedArtifact.java      | 195 ++++++++++++++++++
 .../maven/plugins/deploy/DeployMojo.java      |  32 ++-
 src/site/fml/faq.fml                          |  31 +++
 .../maven/plugins/deploy/DeployMojoTest.java  | 128 +++++++++++-
 4 files changed, 380 insertions(+), 6 deletions(-)
 create mode 100644 src/main/java/org/apache/maven/plugins/deploy/AttachedArtifact.java

diff --git a/src/main/java/org/apache/maven/plugins/deploy/AttachedArtifact.java b/src/main/java/org/apache/maven/plugins/deploy/AttachedArtifact.java
new file mode 100644
index 00000000..2907e94e
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/deploy/AttachedArtifact.java
@@ -0,0 +1,195 @@
+package org.apache.maven.plugins.deploy;
+
+/*
+ * 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 org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.shared.utils.StringUtils;
+
+import java.util.List;
+
+/**
+ * The attached artifact data
+ *
+ * @author <a href="mailto:gregory.callea@gmail.com">Gregory Callea</a>
+ *
+ */
+@Mojo( name = "artifact" )
+public class AttachedArtifact
+{
+
+    /**
+     * GroupId of the attached artifact
+     */
+    @Parameter( property = "groupId" )
+    private String groupId;
+
+    /**
+     * ArtifactId of the attached artifact
+     */
+    @Parameter( property = "artifactId" )
+    private String artifactId;
+
+    /**
+     * Version of the attached artifact
+     */
+    @Parameter( property = "version" )
+    private String version;
+
+
+    /**
+     * Packaging of the attached artifact
+     */
+    @Parameter( property = "packaging" )
+    private String packaging;
+
+    /**
+     * Classifier to the attached artifact
+     */
+    @Parameter( property = "classifier" )
+    private String classifier;
+
+
+    public AttachedArtifact( )
+    {
+
+    }
+
+    AttachedArtifact( String groupId, String artifactId, String version, String packaging )
+    {
+        this.groupId = groupId;
+        this.artifactId = artifactId;
+        this.version = version;
+        this.packaging = packaging;
+    }
+
+    /**
+     * Validate the attached artifact
+     *
+     * @throws MojoExecutionException If the attached artifact misses some required parameter
+     */
+    private void validate() throws MojoExecutionException
+    {
+
+        if ( StringUtils.isEmpty( groupId ) || StringUtils.isEmpty( artifactId )
+                || StringUtils.isEmpty( version ) || StringUtils.isEmpty( packaging ) )
+        {
+            throw new MojoExecutionException(
+                    "The artifact information is incomplete: 'groupId', 'artifactId', "
+                            + "'version', 'packaging' are required" );
+        }
+
+    }
+
+    /**
+     * Check if attached artifact exists on provided lists
+     *
+     * @param attachedArtifacts The attached artifact lists
+     * @return The found attached artifact
+     * @throws MojoExecutionException If no attached artifact that matches the current one is found
+     */
+    Artifact checkIfExists( final List<Artifact> attachedArtifacts ) throws MojoExecutionException
+    {
+        this.validate();
+        for ( final Artifact attachedArtifact : attachedArtifacts )
+        {
+            if ( this.groupId.equals( attachedArtifact.getGroupId() )
+                    && this.artifactId.equals( attachedArtifact.getArtifactId() )
+                    && this.version.equals( attachedArtifact.getVersion() )
+                    && this.packaging.equals( attachedArtifact.getType() )
+                    && (
+                    this.classifier == null && attachedArtifact.getClassifier() == null
+                            || ( this.classifier != null
+                            && attachedArtifact.getClassifier() != null
+                            && this.classifier.equals( attachedArtifact.getClassifier() )
+                    )
+            ) )
+            {
+                return attachedArtifact;
+            }
+        }
+        throw new MojoExecutionException(
+                "No attached artifact " + this.toString() + " to exclude from deploy found" );
+    }
+
+    public String getGroupId()
+    {
+        return groupId;
+    }
+
+    public String getArtifactId()
+    {
+        return artifactId;
+    }
+
+    public String getPackaging()
+    {
+        return packaging;
+    }
+
+    public String getClassifier()
+    {
+        return classifier;
+    }
+
+    public String getVersion()
+    {
+        return version;
+    }
+
+    public AttachedArtifact setGroupId( String groupId )
+    {
+        this.groupId = groupId;
+        return this;
+    }
+
+    public AttachedArtifact setArtifactId( String artifactId )
+    {
+        this.artifactId = artifactId;
+        return this;
+    }
+
+    public AttachedArtifact setPackaging( String packaging )
+    {
+        this.packaging = packaging;
+        return this;
+    }
+
+    public AttachedArtifact setClassifier( String classifier )
+    {
+        this.classifier = classifier;
+        return this;
+    }
+
+    public AttachedArtifact setVersion( String version )
+    {
+        this.version = version;
+        return this;
+    }
+
+    @Override
+    public String toString()
+    {
+        return this.groupId + ":" + this.artifactId + ":" + this.packaging
+                + ( this.classifier == null ? "" : ":" + this.classifier ) + ":" + this.version;
+    }
+}
\ No newline at end of file
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 812b22d9..9fb1330b 100644
--- a/src/main/java/org/apache/maven/plugins/deploy/DeployMojo.java
+++ b/src/main/java/org/apache/maven/plugins/deploy/DeployMojo.java
@@ -26,6 +26,7 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.ArtifactUtils;
 import org.apache.maven.artifact.repository.ArtifactRepository;
 import org.apache.maven.plugin.MojoExecutionException;
@@ -42,7 +43,7 @@
 
 /**
  * Deploys an artifact to remote repository.
- * 
+ *
  * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
  * @author <a href="mailto:jdcasey@apache.org">John Casey (refactoring only)</a>
  * @version $Id$
@@ -75,7 +76,7 @@ public class DeployMojo
      * 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.
      * <strong>(experimental)</strong>
-     * 
+     *
      * @since 2.8
      */
     @Parameter( defaultValue = "false", property = "deployAtEnd" )
@@ -98,7 +99,7 @@ public class DeployMojo
 
     /**
      * The alternative repository to use when the project has a snapshot version.
-     * 
+     *
      * @since 2.8
      * @see DeployMojo#altDeploymentRepository
      */
@@ -107,7 +108,7 @@ public class DeployMojo
 
     /**
      * The alternative repository to use when the project has a final version.
-     * 
+     *
      * @since 2.8
      * @see DeployMojo#altDeploymentRepository
      */
@@ -116,12 +117,20 @@ public class DeployMojo
 
     /**
      * Set this to 'true' to bypass artifact deploy
-     * 
+     *
      * @since 2.4
      */
     @Parameter( property = "maven.deploy.skip", defaultValue = "false" )
     private boolean skip;
 
+    /**
+     * The attached artifacts to exclude from deploy
+     *
+     * @since 3.0
+     */
+    @Parameter( property = "skipAttachedArtifacts" )
+    private List<AttachedArtifact> skipAttachedArtifacts;
+
     /**
      * Component used to deploy project.
      */
@@ -154,6 +163,19 @@ public void execute()
 
             ArtifactRepository repo = getDeploymentRepository( pdr );
 
+            final List<Artifact> attachedArtifacts = pdr.getProject().getAttachedArtifacts();
+            if ( skipAttachedArtifacts != null )
+            {
+                for ( final AttachedArtifact attachedArtifactToSkip : skipAttachedArtifacts )
+                {
+                    final Artifact toSkip = attachedArtifactToSkip.checkIfExists( attachedArtifacts );
+                    attachedArtifacts.remove( toSkip );
+                    getLog().info( "Skipping artifact ["
+                            + toSkip
+                            + "]" );
+                }
+            }
+
             if ( !deployAtEnd )
             {
                 deployProject( getSession().getProjectBuildingRequest(), pdr, repo );
diff --git a/src/site/fml/faq.fml b/src/site/fml/faq.fml
index 532cb5a9..384e2250 100644
--- a/src/site/fml/faq.fml
+++ b/src/site/fml/faq.fml
@@ -77,6 +77,37 @@ under the License.
 </plugin>]]></source>
        </answer>
      </faq>
+     <faq id="skipAttachedArtifacts">
+            <question>I don't want to deploy some attached artifact on a specific module.  Can I skip deployment for specific attached artifacts?</question>
+            <answer>
+              <p>
+                Yes, you can skip deployment of several attached artifacts by configuring the Deploy Plugin as follows:
+              </p>
+              <source>
+     <![CDATA[<plugin>
+       <groupId>org.apache.maven.plugins</groupId>
+       <artifactId>maven-deploy-plugin</artifactId>
+       <version>X.Y</version>
+       <configuration>
+         <skipAttachedArtifacts>
+           <artifact>
+             <groupId>group</groupId>
+             <artifactId>id</artifactId>
+             <version>1.0</version>
+             <packaging>jar</packaging>
+             <classifier>classifier</classifier>
+           </artifact>
+           <artifact>
+             <groupId>group</groupId>
+             <artifactId>id</artifactId>
+             <version>1.0</version>
+             <packaging>zip</packaging>
+           </artifact>
+         </skipAttachedArtifacts>
+       </configuration>
+     </plugin>]]></source>
+            </answer>
+     </faq>
      <faq id="deploy_deploy">
        <question>What does the message "The packaging for this project did not assign a file to the build artifact" mean when I run <code>deploy:deploy</code>?</question>
        <answer>
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 4db5b2de..be2f267c 100644
--- a/src/test/java/org/apache/maven/plugins/deploy/DeployMojoTest.java
+++ b/src/test/java/org/apache/maven/plugins/deploy/DeployMojoTest.java
@@ -522,7 +522,133 @@ public void testDeployWithAttachedArtifacts()
 
         assertEquals( 0, getSizeOfExpectedFiles( fileList, expectedFiles ) );               
     }
-    
+
+    public void testDeployWithNotExistingAttachedArtifactsExcluded()
+            throws Exception {
+        File testPom = new File(getBasedir(),
+                "target/test-classes/unit/basic-deploy-with-attached-artifacts/" +
+                        "plugin-config.xml");
+
+        mojo = (DeployMojo) lookupMojo("deploy", testPom);
+
+        MockitoAnnotations.initMocks(this);
+
+        assertNotNull(mojo);
+
+        ProjectBuildingRequest buildingRequest = mock(ProjectBuildingRequest.class);
+        when(session.getProjectBuildingRequest()).thenReturn(buildingRequest);
+        MavenRepositorySystemSession repositorySession = new MavenRepositorySystemSession();
+        repositorySession.setLocalRepositoryManager(new SimpleLocalRepositoryManager(LOCAL_REPO));
+        when(buildingRequest.getRepositorySession()).thenReturn(repositorySession);
+
+        MavenProject project = (MavenProject) getVariableValueFromObject(mojo, "project");
+
+        setVariableValueToObject(mojo, "reactorProjects", Collections.singletonList(project));
+
+        artifact = (DeployArtifactStub) project.getArtifact();
+
+        File file = new File(getBasedir(),
+                "target/test-classes/unit/basic-deploy-with-attached-artifacts/target/" +
+                        "deploy-test-file-1.0-SNAPSHOT.jar");
+
+        artifact.setFile(file);
+
+        ArtifactRepositoryStub repo = getRepoStub(mojo);
+
+        repo.setAppendToUrl("basic-deploy-with-attached-artifacts");
+
+        setVariableValueToObject(mojo, "skipAttachedArtifacts", Collections.singletonList(new AttachedArtifact("org.apache.maven.test", "attached-artifact-not-existing-0", "1.0-SNAPSHOT", "jar")));
+
+        try
+        {
+            mojo.execute();
+
+            fail( "Did not throw mojo execution exception" );
+        }
+        catch( MojoExecutionException e )
+        {
+            //expected
+        }
+
+
+    }
+
+    public void testDeployWithAttachedArtifactsExcluded()
+            throws Exception
+    {
+        File testPom = new File( getBasedir(),
+                "target/test-classes/unit/basic-deploy-with-attached-artifacts/" +
+                        "plugin-config.xml" );
+
+        mojo = ( DeployMojo ) lookupMojo( "deploy", testPom );
+
+        MockitoAnnotations.initMocks( this );
+
+        assertNotNull( mojo );
+
+        ProjectBuildingRequest buildingRequest = mock ( ProjectBuildingRequest.class );
+        when( session.getProjectBuildingRequest() ).thenReturn( buildingRequest );
+        MavenRepositorySystemSession repositorySession = new MavenRepositorySystemSession();
+        repositorySession.setLocalRepositoryManager( new SimpleLocalRepositoryManager( LOCAL_REPO ) );
+        when( buildingRequest.getRepositorySession() ).thenReturn( repositorySession );
+
+        MavenProject project = (MavenProject) getVariableValueFromObject( mojo, "project" );
+
+        setVariableValueToObject( mojo, "reactorProjects", Collections.singletonList( project ) );
+
+        artifact = (DeployArtifactStub) project.getArtifact();
+
+        File file = new File( getBasedir(),
+                "target/test-classes/unit/basic-deploy-with-attached-artifacts/target/" +
+                        "deploy-test-file-1.0-SNAPSHOT.jar" );
+
+        artifact.setFile( file );
+
+        ArtifactRepositoryStub repo = getRepoStub( mojo );
+
+        repo.setAppendToUrl( "basic-deploy-with-attached-artifacts" );
+
+        setVariableValueToObject( mojo, "skipAttachedArtifacts", Collections.singletonList(new AttachedArtifact( "org.apache.maven.test", "attached-artifact-test-0", "1.0-SNAPSHOT","jar" ) ) );
+
+        mojo.execute();
+
+        //check the artifacts in remote repository
+        List<String> expectedFiles = new ArrayList<String>();
+        List<String> fileList = new ArrayList<String>();
+
+        expectedFiles.add( "org" );
+        expectedFiles.add( "apache" );
+        expectedFiles.add( "maven" );
+        expectedFiles.add( "test" );
+        expectedFiles.add( "maven-deploy-test" );
+        expectedFiles.add( "1.0-SNAPSHOT" );
+        expectedFiles.add( "maven-metadata.xml" );
+        expectedFiles.add( "maven-metadata.xml.md5" );
+        expectedFiles.add( "maven-metadata.xml.sha1" );
+        expectedFiles.add( "maven-deploy-test-1.0-SNAPSHOT.jar" );
+        expectedFiles.add( "maven-deploy-test-1.0-SNAPSHOT.jar.md5" );
+        expectedFiles.add( "maven-deploy-test-1.0-SNAPSHOT.jar.sha1" );
+        expectedFiles.add( "maven-deploy-test-1.0-SNAPSHOT.pom" );
+        expectedFiles.add( "maven-deploy-test-1.0-SNAPSHOT.pom.md5" );
+        expectedFiles.add( "maven-deploy-test-1.0-SNAPSHOT.pom.sha1" );
+        // as we are in SNAPSHOT the file is here twice
+        expectedFiles.add( "maven-metadata.xml" );
+        expectedFiles.add( "maven-metadata.xml.md5" );
+        expectedFiles.add( "maven-metadata.xml.sha1" );
+
+        remoteRepo = new File( remoteRepo, "basic-deploy-with-attached-artifacts" );
+
+        File[] files = remoteRepo.listFiles();
+
+        for (File file1 : files) {
+            addFileToList(file1, fileList);
+        }
+
+        assertEquals( expectedFiles.size(), fileList.size() );
+
+        assertEquals( 0, getSizeOfExpectedFiles( fileList, expectedFiles ) );
+    }
+
     @Ignore( "SCP is not part of Maven3 distribution. Aether handles transport extensions." )
     public void _testBasicDeployWithScpAsProtocol()
         throws Exception