001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.scm.plugin; 020 021import java.io.File; 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027import java.util.Map.Entry; 028import java.util.Objects; 029import java.util.Properties; 030 031import org.apache.commons.lang3.StringUtils; 032import org.apache.maven.plugin.AbstractMojo; 033import org.apache.maven.plugin.MojoExecutionException; 034import org.apache.maven.plugins.annotations.Component; 035import org.apache.maven.plugins.annotations.Parameter; 036import org.apache.maven.scm.ScmBranch; 037import org.apache.maven.scm.ScmException; 038import org.apache.maven.scm.ScmFileSet; 039import org.apache.maven.scm.ScmResult; 040import org.apache.maven.scm.ScmRevision; 041import org.apache.maven.scm.ScmTag; 042import org.apache.maven.scm.ScmVersion; 043import org.apache.maven.scm.manager.ScmManager; 044import org.apache.maven.scm.provider.ScmProviderRepository; 045import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost; 046import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository; 047import org.apache.maven.scm.repository.ScmRepository; 048import org.apache.maven.scm.repository.ScmRepositoryException; 049import org.apache.maven.settings.Server; 050import org.apache.maven.settings.Settings; 051import org.apache.maven.settings.building.SettingsProblem; 052import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest; 053import org.apache.maven.settings.crypto.SettingsDecrypter; 054import org.apache.maven.settings.crypto.SettingsDecryptionResult; 055import org.apache.maven.shared.model.fileset.FileSet; 056import org.apache.maven.shared.model.fileset.util.FileSetManager; 057 058/** 059 * @author <a href="evenisse@apache.org">Emmanuel Venisse</a> 060 * @author Olivier Lamy 061 */ 062public abstract class AbstractScmMojo extends AbstractMojo { 063 064 protected static final String VERSION_TYPE_BRANCH = "branch"; 065 066 protected static final String VERSION_TYPE_REVISION = "revision"; 067 068 protected static final String VERSION_TYPE_TAG = "tag"; 069 070 protected static final String[] VALID_VERSION_TYPES = {VERSION_TYPE_BRANCH, VERSION_TYPE_REVISION, VERSION_TYPE_TAG 071 }; 072 073 /** 074 * The SCM connection URL. 075 */ 076 @Parameter(property = "connectionUrl", defaultValue = "${project.scm.connection}") 077 private String connectionUrl; 078 079 /** 080 * The SCM connection URL for developers. 081 */ 082 @Parameter(property = "developerConnectionUrl", defaultValue = "${project.scm.developerConnection}") 083 private String developerConnectionUrl; 084 085 /** 086 * The type of connection to use (connection or developerConnection). 087 */ 088 @Parameter(property = "connectionType", defaultValue = "connection") 089 private String connectionType; 090 091 /** 092 * The working directory. 093 */ 094 @Parameter(property = "workingDirectory") 095 private File workingDirectory; 096 097 /** 098 * The user name. 099 */ 100 @Parameter(property = "username") 101 private String username; 102 103 /** 104 * The user password. 105 */ 106 @Parameter(property = "password") 107 private String password; 108 109 /** 110 * The private key. 111 */ 112 @Parameter(property = "privateKey") 113 private String privateKey; 114 115 /** 116 * The passphrase. 117 */ 118 @Parameter(property = "passphrase") 119 private String passphrase; 120 121 /** 122 * The url of tags base directory (used by svn protocol). It is not 123 * necessary to set it if you use the standard svn layout 124 * (branches/tags/trunk). 125 */ 126 @Parameter(property = "tagBase") 127 private String tagBase; 128 129 /** 130 * Comma separated list of includes file pattern. 131 */ 132 @Parameter(property = "includes") 133 private String includes; 134 135 /** 136 * Comma separated list of excludes file pattern. 137 */ 138 @Parameter(property = "excludes") 139 private String excludes; 140 141 @Component 142 private ScmManager manager; 143 144 @Component 145 private SettingsDecrypter settingsDecrypter; 146 147 /** 148 * The base directory. 149 */ 150 @Parameter(property = "basedir", required = true) 151 private File basedir; 152 153 @Parameter(defaultValue = "${settings}", readonly = true) 154 private Settings settings; 155 156 /** 157 * List of System properties to pass to the JUnit tests. 158 */ 159 @Parameter 160 private Properties systemProperties; 161 162 /** 163 * List of provider implementations. 164 */ 165 @Parameter 166 private Map<String, String> providerImplementations; 167 168 /** 169 * Should distributed changes be pushed to the central repository? 170 * For many distributed SCMs like Git, a change like a commit 171 * is only stored in your local copy of the repository. Pushing 172 * the change allows your to more easily share it with other users. 173 * 174 * @since 1.4 175 */ 176 @Parameter(property = "pushChanges", defaultValue = "true") 177 private boolean pushChanges; 178 179 /** 180 * A workItem for SCMs like RTC, TFS etc, that may require additional 181 * information to perform a pushChange operation. 182 * 183 * @since 1.9.5 184 */ 185 @Parameter(property = "workItem") 186 @Deprecated 187 private String workItem; 188 189 /** {@inheritDoc} */ 190 public void execute() throws MojoExecutionException { 191 if (systemProperties != null) { 192 // Add all system properties configured by the user 193 Iterator<Object> iter = systemProperties.keySet().iterator(); 194 195 while (iter.hasNext()) { 196 String key = (String) iter.next(); 197 198 String value = systemProperties.getProperty(key); 199 200 System.setProperty(key, value); 201 } 202 } 203 204 if (providerImplementations != null && !providerImplementations.isEmpty()) { 205 for (Entry<String, String> entry : providerImplementations.entrySet()) { 206 String providerType = entry.getKey(); 207 String providerImplementation = entry.getValue(); 208 getLog().info("Change the default '" + providerType + "' provider implementation to '" 209 + providerImplementation + "'."); 210 getScmManager().setScmProviderImplementation(providerType, providerImplementation); 211 } 212 } 213 } 214 215 protected void setConnectionType(String connectionType) { 216 this.connectionType = connectionType; 217 } 218 219 public String getConnectionUrl() { 220 boolean requireDeveloperConnection = !"connection".equals(connectionType.toLowerCase()); 221 if ((connectionUrl != null && !connectionUrl.isEmpty()) && !requireDeveloperConnection) { 222 return connectionUrl; 223 } else if (developerConnectionUrl != null && !developerConnectionUrl.isEmpty()) { 224 return developerConnectionUrl; 225 } 226 if (requireDeveloperConnection) { 227 throw new NullPointerException("You need to define a developerConnectionUrl parameter"); 228 } else { 229 throw new NullPointerException("You need to define a connectionUrl parameter"); 230 } 231 } 232 233 public void setConnectionUrl(String connectionUrl) { 234 this.connectionUrl = connectionUrl; 235 } 236 237 public File getWorkingDirectory() { 238 if (workingDirectory == null) { 239 return basedir; 240 } 241 242 return workingDirectory; 243 } 244 245 public File getBasedir() { 246 return this.basedir; 247 } 248 249 public void setWorkingDirectory(File workingDirectory) { 250 this.workingDirectory = workingDirectory; 251 } 252 253 public ScmManager getScmManager() { 254 return manager; 255 } 256 257 public ScmFileSet getFileSet() throws IOException { 258 if (includes != null || excludes != null) { 259 return new ScmFileSet(getWorkingDirectory(), includes, excludes); 260 } else { 261 return new ScmFileSet(getWorkingDirectory()); 262 } 263 } 264 265 public ScmRepository getScmRepository() throws ScmException { 266 ScmRepository repository; 267 268 try { 269 repository = getScmManager().makeScmRepository(getConnectionUrl()); 270 271 ScmProviderRepository providerRepo = repository.getProviderRepository(); 272 273 providerRepo.setPushChanges(pushChanges); 274 275 if (!(workItem == null || workItem.isEmpty())) { 276 providerRepo.setWorkItem(workItem); 277 } 278 279 if (!(username == null || username.isEmpty())) { 280 providerRepo.setUser(username); 281 } 282 283 if (!(password == null || password.isEmpty())) { 284 providerRepo.setPassword(password); 285 } 286 287 if (repository.getProviderRepository() instanceof ScmProviderRepositoryWithHost) { 288 ScmProviderRepositoryWithHost repo = (ScmProviderRepositoryWithHost) repository.getProviderRepository(); 289 290 loadInfosFromSettings(repo); 291 292 if (!(username == null || username.isEmpty())) { 293 repo.setUser(username); 294 } 295 296 if (!(password == null || password.isEmpty())) { 297 repo.setPassword(password); 298 } 299 300 if (!(privateKey == null || privateKey.isEmpty())) { 301 repo.setPrivateKey(privateKey); 302 } 303 304 if (!(passphrase == null || passphrase.isEmpty())) { 305 repo.setPassphrase(passphrase); 306 } 307 } 308 309 if (!(tagBase == null || tagBase.isEmpty()) 310 && repository.getProvider().equals("svn")) { 311 SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository(); 312 313 svnRepo.setTagBase(tagBase); 314 } 315 } catch (ScmRepositoryException e) { 316 if (!e.getValidationMessages().isEmpty()) { 317 for (String message : e.getValidationMessages()) { 318 getLog().error(message); 319 } 320 } 321 322 throw new ScmException("Can't load the scm provider.", e); 323 } catch (Exception e) { 324 throw new ScmException("Can't load the scm provider.", e); 325 } 326 327 return repository; 328 } 329 330 /** 331 * Load username password from settings if user has not set them in JVM properties 332 * 333 * @param repo not null 334 */ 335 private void loadInfosFromSettings(ScmProviderRepositoryWithHost repo) { 336 if (username == null || password == null) { 337 String host = repo.getHost(); 338 339 int port = repo.getPort(); 340 341 if (port > 0) { 342 host += ":" + port; 343 } 344 345 Server server = this.settings.getServer(host); 346 347 if (server != null) { 348 server = decrypt(server); 349 350 if (username == null) { 351 username = server.getUsername(); 352 } 353 354 if (password == null) { 355 password = server.getPassword(); 356 } 357 358 if (privateKey == null) { 359 privateKey = server.getPrivateKey(); 360 } 361 362 if (passphrase == null) { 363 passphrase = server.getPassphrase(); 364 } 365 } 366 } 367 } 368 369 private Server decrypt(Server server) { 370 SettingsDecryptionResult result = settingsDecrypter.decrypt(new DefaultSettingsDecryptionRequest(server)); 371 for (SettingsProblem problem : result.getProblems()) { 372 getLog().error(problem.getMessage(), problem.getException()); 373 } 374 375 return result.getServer(); 376 } 377 378 public void checkResult(ScmResult result) throws MojoExecutionException { 379 if (!result.isSuccess()) { 380 getLog().error("Provider message:"); 381 382 getLog().error(result.getProviderMessage() == null ? "" : result.getProviderMessage()); 383 384 getLog().error("Command output:"); 385 386 getLog().error(result.getCommandOutput() == null ? "" : result.getCommandOutput()); 387 388 throw new MojoExecutionException("Command failed: " + Objects.toString(result.getProviderMessage())); 389 } 390 } 391 392 public String getIncludes() { 393 return includes; 394 } 395 396 public void setIncludes(String includes) { 397 this.includes = includes; 398 } 399 400 public String getExcludes() { 401 return excludes; 402 } 403 404 public void setExcludes(String excludes) { 405 this.excludes = excludes; 406 } 407 408 public ScmVersion getScmVersion(String versionType, String version) throws MojoExecutionException { 409 if ((versionType == null || versionType.isEmpty()) && (version != null && !version.isEmpty())) { 410 throw new MojoExecutionException("You must specify the version type."); 411 } 412 413 if (version == null || version.isEmpty()) { 414 return null; 415 } 416 417 if (VERSION_TYPE_BRANCH.equals(versionType)) { 418 return new ScmBranch(version); 419 } 420 421 if (VERSION_TYPE_TAG.equals(versionType)) { 422 return new ScmTag(version); 423 } 424 425 if (VERSION_TYPE_REVISION.equals(versionType)) { 426 return new ScmRevision(version); 427 } 428 429 throw new MojoExecutionException("Unknown '" + versionType + "' version type."); 430 } 431 432 protected void handleExcludesIncludesAfterCheckoutAndExport(File checkoutDirectory) throws MojoExecutionException { 433 List<String> includes = new ArrayList<>(); 434 435 if (!StringUtils.isBlank(this.getIncludes())) { 436 String[] tokens = StringUtils.split(this.getIncludes(), ","); 437 for (int i = 0; i < tokens.length; ++i) { 438 includes.add(tokens[i]); 439 } 440 } 441 442 List<String> excludes = new ArrayList<>(); 443 444 if (!StringUtils.isBlank(this.getExcludes())) { 445 String[] tokens = StringUtils.split(this.getExcludes(), ","); 446 for (int i = 0; i < tokens.length; ++i) { 447 excludes.add(tokens[i]); 448 } 449 } 450 451 if (includes.isEmpty() && excludes.isEmpty()) { 452 return; 453 } 454 455 FileSetManager fileSetManager = new FileSetManager(); 456 457 FileSet fileset = new FileSet(); 458 fileset.setDirectory(checkoutDirectory.getAbsolutePath()); 459 fileset.setIncludes(excludes); // revert the order to do the delete 460 fileset.setExcludes(includes); 461 fileset.setUseDefaultExcludes(false); 462 463 try { 464 fileSetManager.delete(fileset); 465 } catch (IOException e) { 466 throw new MojoExecutionException( 467 "Error found while cleaning up output directory base on " + "excludes/includes configurations.", e); 468 } 469 } 470}