1 package org.apache.maven.shared.release.phase;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Map;
30 import java.util.Set;
31
32 import org.apache.maven.artifact.Artifact;
33 import org.apache.maven.artifact.ArtifactUtils;
34 import org.apache.maven.project.MavenProject;
35 import org.apache.maven.shared.release.ReleaseExecutionException;
36 import org.apache.maven.shared.release.ReleaseFailureException;
37 import org.apache.maven.shared.release.ReleaseResult;
38 import org.apache.maven.shared.release.config.ReleaseDescriptor;
39 import org.apache.maven.shared.release.env.ReleaseEnvironment;
40 import org.apache.maven.shared.release.versions.DefaultVersionInfo;
41 import org.apache.maven.shared.release.versions.VersionInfo;
42 import org.apache.maven.shared.release.versions.VersionParseException;
43 import org.codehaus.plexus.component.annotations.Component;
44 import org.codehaus.plexus.component.annotations.Requirement;
45 import org.codehaus.plexus.components.interactivity.Prompter;
46 import org.codehaus.plexus.components.interactivity.PrompterException;
47
48
49
50
51
52
53
54
55 @Component( role = ReleasePhase.class, hint = "check-dependency-snapshots" )
56 public class CheckDependencySnapshotsPhase
57 extends AbstractReleasePhase
58 {
59 public static final String RESOLVE_SNAPSHOT_MESSAGE = "There are still some remaining snapshot dependencies.\n";
60
61 public static final String RESOLVE_SNAPSHOT_PROMPT = "Do you want to resolve them now?";
62
63 public static final String RESOLVE_SNAPSHOT_TYPE_MESSAGE = "Dependency type to resolve,";
64
65 public static final String RESOLVE_SNAPSHOT_TYPE_PROMPT =
66 "specify the selection number ( 0:All 1:Project Dependencies 2:Plugins 3:Reports 4:Extensions ):";
67
68
69
70
71 @Requirement
72 private Prompter prompter;
73
74
75
76
77
78
79
80
81
82 private String resolveSnapshot;
83
84 private String resolveSnapshotType;
85
86 @Override
87 public ReleaseResult execute( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
88 List<MavenProject> reactorProjects )
89 throws ReleaseExecutionException, ReleaseFailureException
90 {
91 ReleaseResult result = new ReleaseResult();
92
93 if ( !releaseDescriptor.isAllowTimestampedSnapshots() )
94 {
95 logInfo( result, "Checking dependencies and plugins for snapshots ..." );
96
97 for ( MavenProject project : reactorProjects )
98 {
99 checkProject( project, releaseDescriptor );
100 }
101 }
102 else
103 {
104 logInfo( result, "Ignoring SNAPSHOT dependencies and plugins ..." );
105 }
106 result.setResultCode( ReleaseResult.SUCCESS );
107
108 return result;
109 }
110
111 private void checkProject( MavenProject project, ReleaseDescriptor releaseDescriptor )
112 throws ReleaseFailureException, ReleaseExecutionException
113 {
114 Map<String, Artifact> artifactMap = ArtifactUtils.artifactMapByVersionlessId( project.getArtifacts() );
115
116 Set<Artifact> usedSnapshotDependencies = new HashSet<>();
117
118 if ( project.getParentArtifact() != null )
119 {
120 if ( checkArtifact( project.getParentArtifact(), artifactMap, releaseDescriptor ) )
121 {
122 usedSnapshotDependencies.add( project.getParentArtifact() );
123 }
124 }
125
126 Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();
127 usedSnapshotDependencies.addAll( checkDependencies( releaseDescriptor, artifactMap, dependencyArtifacts ) );
128
129
130
131 Set<Artifact> pluginArtifacts = project.getPluginArtifacts();
132 Set<Artifact> usedSnapshotPlugins = checkPlugins( releaseDescriptor, artifactMap, pluginArtifacts );
133
134
135
136 Set<Artifact> reportArtifacts = project.getReportArtifacts();
137 Set<Artifact> usedSnapshotReports = checkReports( releaseDescriptor, artifactMap, reportArtifacts );
138
139 Set<Artifact> extensionArtifacts = project.getExtensionArtifacts();
140 Set<Artifact> usedSnapshotExtensions = checkExtensions( releaseDescriptor, artifactMap, extensionArtifacts );
141
142
143
144 if ( !usedSnapshotDependencies.isEmpty() || !usedSnapshotReports.isEmpty()
145 || !usedSnapshotExtensions.isEmpty() || !usedSnapshotPlugins.isEmpty() )
146 {
147 if ( releaseDescriptor.isInteractive() || null != releaseDescriptor.getAutoResolveSnapshots() )
148 {
149 resolveSnapshots( usedSnapshotDependencies, usedSnapshotReports, usedSnapshotExtensions,
150 usedSnapshotPlugins, releaseDescriptor );
151 }
152
153 if ( !usedSnapshotDependencies.isEmpty() || !usedSnapshotReports.isEmpty()
154 || !usedSnapshotExtensions.isEmpty() || !usedSnapshotPlugins.isEmpty() )
155 {
156 StringBuilder message = new StringBuilder();
157
158 printSnapshotDependencies( usedSnapshotDependencies, message );
159 printSnapshotDependencies( usedSnapshotReports, message );
160 printSnapshotDependencies( usedSnapshotExtensions, message );
161 printSnapshotDependencies( usedSnapshotPlugins, message );
162 message.append( "in project '" + project.getName() + "' (" + project.getId() + ")" );
163
164 throw new ReleaseFailureException(
165 "Can't release project due to non released dependencies :\n" + message );
166 }
167 }
168 }
169
170 private Set<Artifact> checkPlugins( ReleaseDescriptor releaseDescriptor,
171 Map<String, Artifact> artifactMap, Set<Artifact> pluginArtifacts )
172 throws ReleaseExecutionException
173 {
174 Set<Artifact> usedSnapshotPlugins = new HashSet<>();
175 for ( Artifact artifact : pluginArtifacts )
176 {
177 if ( checkArtifact( artifact, artifactMap, releaseDescriptor ) )
178 {
179 boolean addToFailures;
180
181 if ( "org.apache.maven.plugins".equals( artifact.getGroupId() ) && "maven-release-plugin".equals(
182 artifact.getArtifactId() ) )
183 {
184
185
186 if ( releaseDescriptor.isSnapshotReleasePluginAllowed() )
187 {
188 addToFailures = false;
189 }
190 else if ( releaseDescriptor.isInteractive() )
191 {
192 try
193 {
194 String result;
195 if ( !releaseDescriptor.isSnapshotReleasePluginAllowed() )
196 {
197 prompter.showMessage( "This project relies on a SNAPSHOT of the release plugin. "
198 + "This may be necessary during testing.\n" );
199 result = prompter.prompt( "Do you want to continue with the release?",
200 Arrays.asList( "yes", "no" ), "no" );
201 }
202 else
203 {
204 result = "yes";
205 }
206
207 if ( result.toLowerCase( Locale.ENGLISH ).startsWith( "y" ) )
208 {
209 addToFailures = false;
210 }
211 else
212 {
213 addToFailures = true;
214 }
215 }
216 catch ( PrompterException e )
217 {
218 throw new ReleaseExecutionException( e.getMessage(), e );
219 }
220 }
221 else
222 {
223 addToFailures = true;
224 }
225 }
226 else
227 {
228 addToFailures = true;
229 }
230
231 if ( addToFailures )
232 {
233 usedSnapshotPlugins.add( artifact );
234 }
235 }
236 }
237 return usedSnapshotPlugins;
238 }
239
240 private Set<Artifact> checkDependencies( ReleaseDescriptor releaseDescriptor,
241 Map<String, Artifact> artifactMap,
242 Set<Artifact> dependencyArtifacts )
243 {
244 Set<Artifact> usedSnapshotDependencies = new HashSet<>();
245 for ( Artifact artifact : dependencyArtifacts )
246 {
247 if ( checkArtifact( artifact, artifactMap, releaseDescriptor ) )
248 {
249 usedSnapshotDependencies.add( getArtifactFromMap( artifact, artifactMap ) );
250 }
251 }
252 return usedSnapshotDependencies;
253 }
254
255 private Set<Artifact> checkReports( ReleaseDescriptor releaseDescriptor,
256 Map<String, Artifact> artifactMap, Set<Artifact> reportArtifacts )
257 {
258 Set<Artifact> usedSnapshotReports = new HashSet<>();
259 for ( Artifact artifact : reportArtifacts )
260 {
261 if ( checkArtifact( artifact, artifactMap, releaseDescriptor ) )
262 {
263
264 usedSnapshotReports.add( artifact );
265 }
266 }
267 return usedSnapshotReports;
268 }
269
270 private Set<Artifact> checkExtensions( ReleaseDescriptor releaseDescriptor,
271 Map<String, Artifact> artifactMap, Set<Artifact> extensionArtifacts )
272 {
273 Set<Artifact> usedSnapshotExtensions = new HashSet<>();
274 for ( Artifact artifact : extensionArtifacts )
275 {
276 if ( checkArtifact( artifact, artifactMap, releaseDescriptor ) )
277 {
278 usedSnapshotExtensions.add( artifact );
279 }
280 }
281 return usedSnapshotExtensions;
282 }
283
284 private static boolean checkArtifact( Artifact artifact,
285 Map<String, Artifact> artifactMapByVersionlessId,
286 ReleaseDescriptor releaseDescriptor )
287 {
288 Artifact checkArtifact = getArtifactFromMap( artifact, artifactMapByVersionlessId );
289
290 return checkArtifact( checkArtifact, releaseDescriptor );
291 }
292
293 private static Artifact getArtifactFromMap( Artifact artifact, Map<String, Artifact> artifactMapByVersionlessId )
294 {
295 String versionlessId = ArtifactUtils.versionlessKey( artifact );
296 Artifact checkArtifact = artifactMapByVersionlessId.get( versionlessId );
297
298 if ( checkArtifact == null )
299 {
300 checkArtifact = artifact;
301 }
302 return checkArtifact;
303 }
304
305 private static boolean checkArtifact( Artifact artifact, ReleaseDescriptor releaseDescriptor )
306 {
307 String versionlessKey = ArtifactUtils.versionlessKey( artifact.getGroupId(), artifact.getArtifactId() );
308 String releaseDescriptorResolvedVersion = releaseDescriptor.getDependencyReleaseVersion( versionlessKey );
309
310 boolean releaseDescriptorResolvedVersionIsSnapshot = releaseDescriptorResolvedVersion == null
311 || releaseDescriptorResolvedVersion.contains( Artifact.SNAPSHOT_VERSION );
312
313
314
315 boolean bannedVersion = artifact.isSnapshot()
316 && !artifact.getBaseVersion().equals( releaseDescriptor.getProjectOriginalVersion( versionlessKey ) )
317 && releaseDescriptorResolvedVersionIsSnapshot;
318
319
320
321 if ( bannedVersion && releaseDescriptor.isAllowTimestampedSnapshots() )
322 {
323 bannedVersion = artifact.getVersion().indexOf( Artifact.SNAPSHOT_VERSION ) >= 0;
324 }
325
326 return bannedVersion;
327 }
328
329 @Override
330 public ReleaseResult simulate( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
331 List<MavenProject> reactorProjects )
332 throws ReleaseExecutionException, ReleaseFailureException
333 {
334
335 return execute( releaseDescriptor, releaseEnvironment, reactorProjects );
336 }
337
338 public void setPrompter( Prompter prompter )
339 {
340 this.prompter = prompter;
341 }
342
343 private StringBuilder printSnapshotDependencies( Set<Artifact> snapshotsSet, StringBuilder message )
344 {
345 List<Artifact> snapshotsList = new ArrayList<>( snapshotsSet );
346
347 Collections.sort( snapshotsList );
348
349 for ( Artifact artifact : snapshotsList )
350 {
351 message.append( " " );
352
353 message.append( artifact );
354
355 message.append( "\n" );
356 }
357
358 return message;
359 }
360
361 private void resolveSnapshots( Set<Artifact> projectDependencies, Set<Artifact> reportDependencies,
362 Set<Artifact> extensionDependencies, Set<Artifact> pluginDependencies,
363 ReleaseDescriptor releaseDescriptor )
364 throws ReleaseExecutionException
365 {
366 try
367 {
368 String autoResolveSnapshots = releaseDescriptor.getAutoResolveSnapshots();
369 if ( resolveSnapshot == null )
370 {
371 prompter.showMessage( RESOLVE_SNAPSHOT_MESSAGE );
372 if ( autoResolveSnapshots != null )
373 {
374 resolveSnapshot = "yes";
375 prompter.showMessage( RESOLVE_SNAPSHOT_PROMPT + " " + resolveSnapshot );
376 }
377 else
378 {
379 resolveSnapshot = prompter.prompt( RESOLVE_SNAPSHOT_PROMPT, Arrays.asList( "yes", "no" ), "no" );
380 }
381 }
382
383 if ( resolveSnapshot.toLowerCase( Locale.ENGLISH ).startsWith( "y" ) )
384 {
385 if ( resolveSnapshotType == null )
386 {
387 prompter.showMessage( RESOLVE_SNAPSHOT_TYPE_MESSAGE );
388 int defaultAnswer = -1;
389 if ( autoResolveSnapshots != null )
390 {
391 if ( "all".equalsIgnoreCase( autoResolveSnapshots ) )
392 {
393 defaultAnswer = 0;
394 }
395 else if ( "dependencies".equalsIgnoreCase( autoResolveSnapshots ) )
396 {
397 defaultAnswer = 1;
398 }
399 else if ( "plugins".equalsIgnoreCase( autoResolveSnapshots ) )
400 {
401 defaultAnswer = 2;
402 }
403 else if ( "reports".equalsIgnoreCase( autoResolveSnapshots ) )
404 {
405 defaultAnswer = 3;
406 }
407 else if ( "extensions".equalsIgnoreCase( autoResolveSnapshots ) )
408 {
409 defaultAnswer = 4;
410 }
411 else
412 {
413 try
414 {
415 defaultAnswer = Integer.parseInt( autoResolveSnapshots );
416 }
417 catch ( NumberFormatException e )
418 {
419 throw new ReleaseExecutionException( e.getMessage(), e );
420 }
421 }
422 }
423 if ( defaultAnswer >= 0 && defaultAnswer <= 4 )
424 {
425 prompter.showMessage( RESOLVE_SNAPSHOT_TYPE_PROMPT + " " + autoResolveSnapshots );
426 resolveSnapshotType = Integer.toString( defaultAnswer );
427 }
428 else
429 {
430 resolveSnapshotType =
431 prompter.prompt( RESOLVE_SNAPSHOT_TYPE_PROMPT, Arrays.asList( "0", "1", "2", "3" ), "1" );
432 }
433 }
434
435 switch ( Integer.parseInt( resolveSnapshotType.toLowerCase( Locale.ENGLISH ) ) )
436 {
437
438 case 0:
439 processSnapshot( projectDependencies, releaseDescriptor, autoResolveSnapshots );
440 processSnapshot( pluginDependencies, releaseDescriptor, autoResolveSnapshots );
441 processSnapshot( reportDependencies, releaseDescriptor, autoResolveSnapshots );
442 processSnapshot( extensionDependencies, releaseDescriptor, autoResolveSnapshots );
443 break;
444
445
446 case 1:
447 processSnapshot( projectDependencies, releaseDescriptor, autoResolveSnapshots );
448 break;
449
450
451 case 2:
452 processSnapshot( pluginDependencies, releaseDescriptor, autoResolveSnapshots );
453 break;
454
455
456 case 3:
457 processSnapshot( reportDependencies, releaseDescriptor, autoResolveSnapshots );
458 break;
459
460
461 case 4:
462 processSnapshot( extensionDependencies, releaseDescriptor, autoResolveSnapshots );
463 break;
464
465 default:
466 }
467 }
468 }
469 catch ( PrompterException | VersionParseException e )
470 {
471 throw new ReleaseExecutionException( e.getMessage(), e );
472 }
473 }
474
475 private void processSnapshot( Set<Artifact> snapshotSet, ReleaseDescriptor releaseDescriptor,
476 String autoResolveSnapshots )
477 throws PrompterException, VersionParseException
478 {
479 Iterator<Artifact> iterator = snapshotSet.iterator();
480
481 while ( iterator.hasNext() )
482 {
483 Artifact currentArtifact = iterator.next();
484 String versionlessKey = ArtifactUtils.versionlessKey( currentArtifact );
485
486 VersionInfo versionInfo = new DefaultVersionInfo( currentArtifact.getBaseVersion() );
487 releaseDescriptor.addDependencyOriginalVersion( versionlessKey, versionInfo.toString() );
488
489 prompter.showMessage(
490 "Dependency '" + versionlessKey + "' is a snapshot (" + currentArtifact.getVersion() + ")\n" );
491 String message = "Which release version should it be set to?";
492 String result;
493 if ( null != autoResolveSnapshots )
494 {
495 result = versionInfo.getReleaseVersionString();
496 prompter.showMessage( message + " " + result );
497 }
498 else
499 {
500 result = prompter.prompt( message, versionInfo.getReleaseVersionString() );
501 }
502
503 releaseDescriptor.addDependencyReleaseVersion( versionlessKey, result );
504
505 iterator.remove();
506
507
508
509 VersionInfo nextVersionInfo = new DefaultVersionInfo( result );
510
511 String nextVersion;
512 if ( nextVersionInfo.compareTo( versionInfo ) > 0 )
513 {
514 nextVersion = nextVersionInfo.toString();
515 }
516 else
517 {
518 nextVersion = versionInfo.toString();
519 }
520
521 message = "What version should the dependency be reset to for development?";
522 if ( null != autoResolveSnapshots )
523 {
524 result = nextVersion;
525 prompter.showMessage( message + " " + result );
526 }
527 else
528 {
529 result = prompter.prompt( message, nextVersion );
530 }
531
532 releaseDescriptor.addDependencyDevelopmentVersion( versionlessKey, result );
533 }
534 }
535 }