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