001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.release.plugin.mojos; 018 019import org.apache.commons.codec.digest.DigestUtils; 020import org.apache.commons.lang3.StringUtils; 021import org.apache.commons.release.plugin.SharedFunctions; 022import org.apache.maven.artifact.Artifact; 023import org.apache.maven.plugin.AbstractMojo; 024import org.apache.maven.plugin.MojoExecutionException; 025import org.apache.maven.plugins.annotations.LifecyclePhase; 026import org.apache.maven.plugins.annotations.Mojo; 027import org.apache.maven.plugins.annotations.Parameter; 028import org.apache.maven.project.MavenProject; 029 030import java.io.File; 031import java.io.IOException; 032import java.io.PrintWriter; 033import java.nio.file.Files; 034import java.util.ArrayList; 035import java.util.Collections; 036import java.util.HashSet; 037import java.util.List; 038import java.util.Set; 039 040/** 041 * The purpose of this maven mojo is to detach the artifacts generated by the maven-assembly-plugin, 042 * which for the Apache Commons Project do not get uploaded to Nexus, and putting those artifacts 043 * in the dev distribution location for apache projects. 044 * 045 * @author chtompki 046 * @since 1.0 047 */ 048@Mojo(name = "detach-distributions", 049 defaultPhase = LifecyclePhase.VERIFY, 050 threadSafe = true, 051 aggregator = true) 052public class CommonsDistributionDetachmentMojo extends AbstractMojo { 053 054 /** 055 * A list of "artifact types" in the maven vernacular, to 056 * be detached from the deployment. For the time being we want 057 * all artifacts generated by the maven-assembly-plugin to be detached 058 * from the deployment, namely *-src.zip, *-src.tar.gz, *-bin.zip, 059 * *-bin.tar.gz, and the corresponding .asc pgp signatures. 060 */ 061 private static final Set<String> ARTIFACT_TYPES_TO_DETACH; 062 static { 063 Set<String> hashSet = new HashSet<>(); 064 hashSet.add("zip"); 065 hashSet.add("tar.gz"); 066 hashSet.add("zip.asc"); 067 hashSet.add("tar.gz.asc"); 068 ARTIFACT_TYPES_TO_DETACH = Collections.unmodifiableSet(hashSet); 069 } 070 071 /** 072 * This list is supposed to hold the maven references to the aformentioned artifacts so that we 073 * can upload them to svn after they've been detached from the maven deployment. 074 */ 075 private List<Artifact> detachedArtifacts = new ArrayList<>(); 076 077 /** 078 * The maven project context injection so that we can get a hold of the variables at hand. 079 */ 080 @Parameter(defaultValue = "${project}", required = true) 081 private MavenProject project; 082 083 /** 084 * The working directory in <code>target</code> that we use as a sandbox for the plugin. 085 */ 086 @Parameter(defaultValue = "${project.build.directory}/commons-release-plugin", 087 property = "commons.outputDirectory") 088 private File workingDirectory; 089 090 /** 091 * The subversion staging url to which we upload all of our staged artifacts. 092 */ 093 @Parameter(defaultValue = "", property = "commons.distSvnStagingUrl") 094 private String distSvnStagingUrl; 095 096 /** 097 * A parameter to generally avoid running unless it is specifically turned on by the consuming module. 098 */ 099 @Parameter(defaultValue = "false", property = "commons.release.isDistModule") 100 private Boolean isDistModule; 101 102 @Override 103 public void execute() throws MojoExecutionException { 104 if (!isDistModule) { 105 getLog().info("This module is marked as a non distribution " 106 + "or assembly module, and the plugin will not run."); 107 return; 108 } 109 if (StringUtils.isEmpty(distSvnStagingUrl)) { 110 getLog().warn("commons.distSvnStagingUrl is not set, the commons-release-plugin will not run."); 111 return; 112 } 113 getLog().info("Detaching Assemblies"); 114 for (Object attachedArtifact : project.getAttachedArtifacts()) { 115 if (ARTIFACT_TYPES_TO_DETACH.contains(((Artifact) attachedArtifact).getType())) { 116 detachedArtifacts.add((Artifact) attachedArtifact); 117 } 118 } 119 if (detachedArtifacts.isEmpty()) { 120 getLog().info("Current project contains no distributions. Not executing."); 121 return; 122 } 123 for (Artifact artifactToRemove : detachedArtifacts) { 124 project.getAttachedArtifacts().remove(artifactToRemove); 125 } 126 if (!workingDirectory.exists()) { 127 SharedFunctions.initDirectory(getLog(), workingDirectory); 128 } 129 copyRemovedArtifactsToWorkingDirectory(); 130 getLog().info(""); 131 sha1AndMd5SignArtifacts(); 132 } 133 134 /** 135 * A helper method to copy the newly detached artifacts to <code>target/commons-release-plugin</code> 136 * so that the {@link CommonsDistributionStagingMojo} can find the artifacts later. 137 * 138 * @throws MojoExecutionException if some form of an {@link IOException} occurs, we want it 139 * properly wrapped so that maven can handle it. 140 */ 141 private void copyRemovedArtifactsToWorkingDirectory() throws MojoExecutionException { 142 StringBuffer copiedArtifactAbsolutePath; 143 getLog().info("Copying detached artifacts to working directory."); 144 for (Artifact artifact: detachedArtifacts) { 145 File artifactFile = artifact.getFile(); 146 copiedArtifactAbsolutePath = new StringBuffer(workingDirectory.getAbsolutePath()); 147 copiedArtifactAbsolutePath.append("/"); 148 copiedArtifactAbsolutePath.append(artifactFile.getName()); 149 File copiedArtifact = new File(copiedArtifactAbsolutePath.toString()); 150 getLog().info("Copying: " + artifactFile.getName()); 151 SharedFunctions.copyFile(getLog(), artifactFile, copiedArtifact); 152 } 153 } 154 155 /** 156 * A helper method that creates md5 and sha1 signature files for our detached artifacts in the 157 * <code>target/commons-release-plugin</code> directory for the purpose of being uploade by 158 * the {@link CommonsDistributionStagingMojo}. 159 * 160 * @throws MojoExecutionException if some form of an {@link IOException} occurs, we want it 161 * properly wrapped so that maven can handle it. 162 */ 163 private void sha1AndMd5SignArtifacts() throws MojoExecutionException { 164 for (Artifact artifact : detachedArtifacts) { 165 if (!artifact.getFile().getName().contains("asc")) { 166 try { 167 String md5 = DigestUtils.md5Hex(Files.readAllBytes(artifact.getFile().toPath())); 168 getLog().info(artifact.getFile().getName() + " md5: " + md5); 169 PrintWriter md5Writer = new PrintWriter(getMd5FilePath(workingDirectory, artifact.getFile())); 170 md5Writer.println(md5); 171 String sha1 = DigestUtils.sha1Hex(Files.readAllBytes(artifact.getFile().toPath())); 172 getLog().info(artifact.getFile().getName() + " sha1: " + sha1); 173 PrintWriter sha1Writer = new PrintWriter(getSha1FilePath(workingDirectory, artifact.getFile())); 174 sha1Writer.println(sha1); 175 md5Writer.close(); 176 sha1Writer.close(); 177 } catch (IOException e) { 178 throw new MojoExecutionException("Could not sign file: " + artifact.getFile().getName(), e); 179 } 180 } 181 } 182 } 183 184 /** 185 * A helper method to create a file path for the <code>md5</code> signature file from a given file. 186 * 187 * @param workingDirectory is the {@link File} for the directory in which to make the <code>.md5</code> file. 188 * @param file the {@link File} whose name we should use to create the <code>.md5</code> file. 189 * @return a {@link String} that is the absolute path to the <code>.md5</code> file. 190 */ 191 private String getMd5FilePath(File workingDirectory, File file) { 192 StringBuffer buffer = new StringBuffer(workingDirectory.getAbsolutePath()); 193 buffer.append("/"); 194 buffer.append(file.getName()); 195 buffer.append(".md5"); 196 return buffer.toString(); 197 } 198 199 /** 200 * A helper method to create a file path for the <code>sha1</code> signature file from a given file. 201 * 202 * @param workingDirectory is the {@link File} for the directory in which to make the <code>.sha1</code> file. 203 * @param file the {@link File} whose name we should use to create the <code>.sha1</code> file. 204 * @return a {@link String} that is the absolute path to the <code>.sha1</code> file. 205 */ 206 private String getSha1FilePath(File workingDirectory, File file) { 207 StringBuffer buffer = new StringBuffer(workingDirectory.getAbsolutePath()); 208 buffer.append("/"); 209 buffer.append(file.getName()); 210 buffer.append(".sha1"); 211 return buffer.toString(); 212 } 213}