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.lang3.StringUtils; 020import org.apache.commons.release.plugin.SharedFunctions; 021import org.apache.maven.plugin.AbstractMojo; 022import org.apache.maven.plugin.MojoExecutionException; 023import org.apache.maven.plugin.MojoFailureException; 024import org.apache.maven.plugins.annotations.LifecyclePhase; 025import org.apache.maven.plugins.annotations.Mojo; 026import org.apache.maven.plugins.annotations.Parameter; 027import org.apache.maven.project.MavenProject; 028import org.apache.maven.scm.ScmException; 029import org.apache.maven.scm.ScmFileSet; 030import org.apache.maven.scm.command.add.AddScmResult; 031import org.apache.maven.scm.command.checkin.CheckInScmResult; 032import org.apache.maven.scm.manager.BasicScmManager; 033import org.apache.maven.scm.manager.ScmManager; 034import org.apache.maven.scm.provider.ScmProvider; 035import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository; 036import org.apache.maven.scm.provider.svn.svnexe.SvnExeScmProvider; 037import org.apache.maven.scm.repository.ScmRepository; 038 039import java.io.File; 040import java.io.IOException; 041import java.util.ArrayList; 042import java.util.Arrays; 043import java.util.List; 044 045/** 046 * This class checks out the dev distribution location, copies the distributions into that directory 047 * structure under the <code>target/commons-release-plugin/scm</code> directory. Then commits the 048 * distributions back up to SVN. Also, we include the built and zipped site as well as the RELEASE-NOTES.txt. 049 * 050 * @author chtompki 051 * @since 1.0 052 */ 053@Mojo(name = "stage-distributions", 054 defaultPhase = LifecyclePhase.DEPLOY, 055 threadSafe = true, 056 aggregator = true) 057public class CommonsDistributionStagingMojo extends AbstractMojo { 058 059 /** 060 * The {@link MavenProject} object is essentially the context of the maven build at 061 * a given time. 062 */ 063 @Parameter(defaultValue = "${project}", required = true) 064 private MavenProject project; 065 066 /** 067 * The {@link File} that contains a file to the root directory of the working project. Typically 068 * this directory is where the <code>pom.xml</code> resides. 069 */ 070 @Parameter(defaultValue = "${basedir}") 071 private File basedir; 072 073 /** 074 * The main working directory for the plugin, namely <code>target/commons-release-plugin</code>, but 075 * that assumes that we're using the default maven <code>${project.build.directory}</code>. 076 */ 077 @Parameter(defaultValue = "${project.build.directory}/commons-release-plugin", property = "commons.outputDirectory") 078 private File workingDirectory; 079 080 /** 081 * The location to which to checkout the dist subversion repository under our working directory, which 082 * was given above. 083 */ 084 @Parameter(defaultValue = "${project.build.directory}/commons-release-plugin/scm", 085 property = "commons.distCheckoutDirectory") 086 private File distCheckoutDirectory; 087 088 /** 089 * The location of the RELEASE-NOTES.txt file such that multimodule builds can configure it. 090 */ 091 @Parameter(defaultValue = "${basedir}/RELEASE-NOTES.txt", property = "commons.releaseNotesLocation") 092 private File releaseNotesFile; 093 094 /** 095 * A boolean that determines whether or not we actually commit the files up to the subversion repository. 096 * If this is set to <code>true</code>, we do all but make the commits. We do checkout the repository in question 097 * though. 098 */ 099 @Parameter(property = "commons.release.dryRun", defaultValue = "false") 100 private Boolean dryRun; 101 102 /** 103 * The url of the subversion repository to which we wish the artifacts to be staged. Typicallly 104 * this would need to be of the form: 105 * <code>scm:svn:https://dist.apache.org/repos/dist/dev/commons/foo</code>. Note. that the prefix to the 106 * substring <code>https</code> is a requirement. 107 */ 108 @Parameter(defaultValue = "", property = "commons.distSvnStagingUrl") 109 private String distSvnStagingUrl; 110 111 /** 112 * A parameter to generally avoid running unless it is specifically turned on by the consuming module. 113 */ 114 @Parameter(defaultValue = "false", property = "commons.release.isDistModule") 115 private Boolean isDistModule; 116 117 /** 118 * The username for the distribution subversion repository. This is typically your apache id. 119 */ 120 @Parameter(property = "user.name") 121 private String username; 122 123 /** 124 * The password associated with {@link CommonsDistributionStagingMojo#username}. 125 */ 126 @Parameter(property = "user.password") 127 private String password; 128 129 @Override 130 public void execute() throws MojoExecutionException, MojoFailureException { 131 if (!isDistModule) { 132 getLog().info("This module is marked as a non distribution " 133 + "or assembly module, and the plugin will not run."); 134 return; 135 } 136 if (StringUtils.isEmpty(distSvnStagingUrl)) { 137 getLog().warn("commons.distSvnStagingUrl is not set, the commons-release-plugin will not run."); 138 return; 139 } 140 if (!workingDirectory.exists()) { 141 getLog().info("Current project contains no distributions. Not executing."); 142 return; 143 } 144 getLog().info("Preparing to stage distributions"); 145 try { 146 ScmManager scmManager = new BasicScmManager(); 147 scmManager.setScmProvider("svn", new SvnExeScmProvider()); 148 ScmRepository repository = scmManager.makeScmRepository(distSvnStagingUrl); 149 ScmProvider provider = scmManager.getProviderByRepository(repository); 150 SvnScmProviderRepository providerRepository = (SvnScmProviderRepository) repository.getProviderRepository(); 151 providerRepository.setUser(username); 152 providerRepository.setPassword(password); 153 if (!distCheckoutDirectory.exists()) { 154 SharedFunctions.initDirectory(getLog(), distCheckoutDirectory); 155 } 156 ScmFileSet scmFileSet = new ScmFileSet(distCheckoutDirectory); 157 getLog().info("Checking out dist from: " + distSvnStagingUrl); 158 provider.checkOut(repository, scmFileSet); 159 File copiedReleaseNotes = copyReleaseNotesToWorkingDirectory(); 160 List<File> filesToCommit = copyDistributionsIntoScmDirectoryStructure(copiedReleaseNotes); 161 if (!dryRun) { 162 ScmFileSet scmFileSetToCommit = new ScmFileSet(distCheckoutDirectory, filesToCommit); 163 AddScmResult addResult = provider.add( 164 repository, 165 scmFileSetToCommit, 166 "Staging release: " + project.getArtifactId() + ", version: " + project.getVersion() 167 ); 168 if (addResult.isSuccess()) { 169 getLog().info("Staging release: " + project.getArtifactId() + ", version: " + project.getVersion()); 170 CheckInScmResult checkInResult = provider.checkIn( 171 repository, 172 scmFileSetToCommit, 173 "Staging release: " + project.getArtifactId() + ", version: " + project.getVersion() 174 ); 175 if (!checkInResult.isSuccess()) { 176 getLog().error("Committing dist files failed: " + checkInResult.getCommandOutput()); 177 throw new MojoExecutionException( 178 "Committing dist files failed: " + checkInResult.getCommandOutput() 179 ); 180 } 181 } else { 182 getLog().error("Adding dist files failed: " + addResult.getCommandOutput()); 183 throw new MojoExecutionException("Adding dist files failed: " + addResult.getCommandOutput()); 184 } 185 } else { 186 getLog().info("Would have committed to: " + distSvnStagingUrl); 187 getLog().info("Staging release: " + project.getArtifactId() + ", version: " + project.getVersion()); 188 } 189 } catch (ScmException e) { 190 getLog().error("Could not commit files to dist: " + distSvnStagingUrl, e); 191 throw new MojoExecutionException("Could not commit files to dist: " + distSvnStagingUrl, e); 192 } 193 } 194 195 /** 196 * A utility method that takes the <code>RELEASE-NOTES.txt</code> file from the base directory of the 197 * project and copies it into {@link CommonsDistributionStagingMojo#workingDirectory}. 198 * 199 * @return the RELEASE-NOTES.txt file that exists in the <code>target/commons-release-notes/scm</code> 200 * directory for the purpose of adding it to the scm change set in the method 201 * {@link CommonsDistributionStagingMojo#copyDistributionsIntoScmDirectoryStructure(File)}. 202 * @throws MojoExecutionException if an {@link IOException} occurrs as a wrapper so that maven 203 * can properly handle the exception. 204 */ 205 private File copyReleaseNotesToWorkingDirectory() throws MojoExecutionException { 206 StringBuffer copiedReleaseNotesAbsolutePath; 207 getLog().info("Copying RELEASE-NOTES.txt to working directory."); 208 copiedReleaseNotesAbsolutePath = new StringBuffer(workingDirectory.getAbsolutePath()); 209 copiedReleaseNotesAbsolutePath.append("/scm/"); 210 copiedReleaseNotesAbsolutePath.append(releaseNotesFile.getName()); 211 File copiedReleaseNotes = new File(copiedReleaseNotesAbsolutePath.toString()); 212 SharedFunctions.copyFile(getLog(), releaseNotesFile, copiedReleaseNotes); 213 return copiedReleaseNotes; 214 } 215 216 /** 217 * Copies the list of files at the root of the {@link CommonsDistributionStagingMojo#workingDirectory} into 218 * the directory structure of the distribution staging repository. Specifically: 219 * <ul> 220 * <li>root:</li> 221 * <li><ul> 222 * <li>site.zip</li> 223 * <li>RELEASE-NOTES.txt</li> 224 * <li>source:</li> 225 * <li><ul> 226 * <li>-src artifacts....</li> 227 * </ul></li> 228 * <li>binaries:</li> 229 * <li><ul> 230 * <li>-bin artifacts....</li> 231 * </ul></li> 232 * </ul></li> 233 * </ul> 234 * 235 * @param copiedReleaseNotes is the RELEASE-NOTES.txt file that exists in the 236 * <code>target/commons-release-plugin/scm</code> directory. 237 * @return a {@link List} of {@link File}'s in the directory for the purpose of adding them to the maven 238 * {@link ScmFileSet}. 239 * @throws MojoExecutionException if an {@link IOException} occurrs so that Maven can handle it properly. 240 */ 241 private List<File> copyDistributionsIntoScmDirectoryStructure(File copiedReleaseNotes) 242 throws MojoExecutionException { 243 List<File> workingDirectoryFiles = Arrays.asList(workingDirectory.listFiles()); 244 String scmBinariesRoot = buildDistBinariesRoot(); 245 String scmSourceRoot = buildDistSourceRoot(); 246 SharedFunctions.initDirectory(getLog(), new File(scmBinariesRoot)); 247 SharedFunctions.initDirectory(getLog(), new File(scmSourceRoot)); 248 List<File> filesForMavenScmFileSet = new ArrayList<>(); 249 File copy; 250 for (File file : workingDirectoryFiles) { 251 if (file.getName().contains("src")) { 252 copy = new File(scmSourceRoot + "/" + file.getName()); 253 SharedFunctions.copyFile(getLog(), file, copy); 254 filesForMavenScmFileSet.add(copy); 255 } else if (file.getName().contains("bin")) { 256 copy = new File(scmBinariesRoot + "/" + file.getName()); 257 SharedFunctions.copyFile(getLog(), file, copy); 258 filesForMavenScmFileSet.add(copy); 259 } else if (file.getName().contains("scm")) { 260 getLog().debug("Not copying scm directory over to the scm directory because it is the scm directory."); 261 //do nothing because we are copying into scm 262 } else { 263 copy = new File(distCheckoutDirectory.getAbsolutePath() + "/" + file.getName()); 264 SharedFunctions.copyFile(getLog(), file, copy); 265 filesForMavenScmFileSet.add(copy); 266 } 267 } 268 filesForMavenScmFileSet.add(copiedReleaseNotes); 269 return filesForMavenScmFileSet; 270 } 271 272 /** 273 * Build the path for the distribution binaries directory. 274 * 275 * @return the local absolute path into the checkedout subversion repository that is where 276 * the binaries distributions are to be copied. 277 */ 278 private String buildDistBinariesRoot() { 279 StringBuffer buffer = new StringBuffer(distCheckoutDirectory.getAbsolutePath()); 280 buffer.append("/binaries"); 281 return buffer.toString(); 282 } 283 284 /** 285 * Build the path for the distribution source directory. 286 * 287 * @return the local absolute path into the checkedout subversion repository that is where 288 * the source distributions are to be copied. 289 */ 290 private String buildDistSourceRoot() { 291 StringBuffer buffer = new StringBuffer(distCheckoutDirectory.getAbsolutePath()); 292 buffer.append("/source"); 293 return buffer.toString(); 294 } 295 296 /** 297 * This method is the setter for the {@link CommonsDistributionStagingMojo#basedir} field, specifically 298 * for the usage in the unit tests. 299 * 300 * @param basedir is the {@link File} to be used as the project's root directory when this mojo 301 * is invoked. 302 */ 303 protected void setBasedir(File basedir) { 304 this.basedir = basedir; 305 } 306}