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 depenedencies 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() )
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 if ( resolveSnapshot == null )
369 {
370 prompter.showMessage( RESOLVE_SNAPSHOT_MESSAGE );
371 resolveSnapshot = prompter.prompt( RESOLVE_SNAPSHOT_PROMPT, Arrays.asList( "yes", "no" ), "no" );
372 }
373
374 if ( resolveSnapshot.toLowerCase( Locale.ENGLISH ).startsWith( "y" ) )
375 {
376 if ( resolveSnapshotType == null )
377 {
378 prompter.showMessage( RESOLVE_SNAPSHOT_TYPE_MESSAGE );
379 resolveSnapshotType =
380 prompter.prompt( RESOLVE_SNAPSHOT_TYPE_PROMPT, Arrays.asList( "0", "1", "2", "3" ), "1" );
381 }
382
383 switch ( Integer.parseInt( resolveSnapshotType.toLowerCase( Locale.ENGLISH ) ) )
384 {
385
386 case 0:
387 processSnapshot( projectDependencies, releaseDescriptor );
388 processSnapshot( pluginDependencies, releaseDescriptor );
389 processSnapshot( reportDependencies, releaseDescriptor );
390 processSnapshot( extensionDependencies, releaseDescriptor );
391 break;
392
393
394 case 1:
395 processSnapshot( projectDependencies, releaseDescriptor );
396 break;
397
398
399 case 2:
400 processSnapshot( pluginDependencies, releaseDescriptor );
401 break;
402
403
404 case 3:
405 processSnapshot( reportDependencies, releaseDescriptor );
406 break;
407
408
409 case 4:
410 processSnapshot( extensionDependencies, releaseDescriptor );
411 break;
412
413 default:
414 }
415 }
416 }
417 catch ( PrompterException | VersionParseException e )
418 {
419 throw new ReleaseExecutionException( e.getMessage(), e );
420 }
421 }
422
423 private void processSnapshot( Set<Artifact> snapshotSet, ReleaseDescriptor releaseDescriptor )
424 throws PrompterException, VersionParseException
425 {
426 Iterator<Artifact> iterator = snapshotSet.iterator();
427
428 while ( iterator.hasNext() )
429 {
430 Artifact currentArtifact = iterator.next();
431 String versionlessKey = ArtifactUtils.versionlessKey( currentArtifact );
432
433 VersionInfo versionInfo = new DefaultVersionInfo( currentArtifact.getBaseVersion() );
434 releaseDescriptor.addDependencyOriginalVersion( versionlessKey, versionInfo.toString() );
435
436 prompter.showMessage(
437 "Dependency '" + versionlessKey + "' is a snapshot (" + currentArtifact.getVersion() + ")\n" );
438 String result = prompter.prompt( "Which release version should it be set to?",
439 versionInfo.getReleaseVersionString() );
440
441 releaseDescriptor.addDependencyReleaseVersion( versionlessKey, result );
442
443 iterator.remove();
444
445
446
447 VersionInfo nextVersionInfo = new DefaultVersionInfo( result );
448
449 String nextVersion;
450 if ( nextVersionInfo.compareTo( versionInfo ) > 0 )
451 {
452 nextVersion = nextVersionInfo.toString();
453 }
454 else
455 {
456 nextVersion = versionInfo.toString();
457 }
458
459 result = prompter.prompt( "What version should the dependency be reset to for development?", nextVersion );
460
461 releaseDescriptor.addDependencyDevelopmentVersion( versionlessKey, result );
462 }
463 }
464 }