1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.deploy;
20
21 import java.io.File;
22 import java.util.ArrayList;
23 import java.util.LinkedHashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28
29 import org.apache.maven.RepositoryUtils;
30 import org.apache.maven.artifact.ArtifactUtils;
31 import org.apache.maven.model.Plugin;
32 import org.apache.maven.model.PluginExecution;
33 import org.apache.maven.plugin.MojoExecutionException;
34 import org.apache.maven.plugin.MojoFailureException;
35 import org.apache.maven.plugin.descriptor.PluginDescriptor;
36 import org.apache.maven.plugins.annotations.LifecyclePhase;
37 import org.apache.maven.plugins.annotations.Mojo;
38 import org.apache.maven.plugins.annotations.Parameter;
39 import org.apache.maven.project.MavenProject;
40 import org.apache.maven.project.artifact.ProjectArtifact;
41 import org.eclipse.aether.artifact.Artifact;
42 import org.eclipse.aether.deployment.DeployRequest;
43 import org.eclipse.aether.repository.RemoteRepository;
44 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
45
46
47
48
49
50
51
52 @Mojo(name = "deploy", defaultPhase = LifecyclePhase.DEPLOY, threadSafe = true)
53 public class DeployMojo extends AbstractDeployMojo {
54 private static final Pattern ALT_LEGACY_REPO_SYNTAX_PATTERN = Pattern.compile("(.+?)::(.+?)::(.+)");
55
56 private static final Pattern ALT_REPO_SYNTAX_PATTERN = Pattern.compile("(.+?)::(.+)");
57
58 @Parameter(defaultValue = "${project}", readonly = true, required = true)
59 private MavenProject project;
60
61 @Parameter(defaultValue = "${reactorProjects}", required = true, readonly = true)
62 private List<MavenProject> reactorProjects;
63
64 @Parameter(defaultValue = "${plugin}", required = true, readonly = true)
65 private PluginDescriptor pluginDescriptor;
66
67
68
69
70
71
72
73 @Parameter(defaultValue = "false", property = "deployAtEnd")
74 private boolean deployAtEnd;
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 @Parameter(property = "altDeploymentRepository")
91 private String altDeploymentRepository;
92
93
94
95
96
97
98
99
100
101
102 @Parameter(property = "altSnapshotDeploymentRepository")
103 private String altSnapshotDeploymentRepository;
104
105
106
107
108
109
110
111
112
113
114 @Parameter(property = "altReleaseDeploymentRepository")
115 private String altReleaseDeploymentRepository;
116
117
118
119
120
121
122
123
124
125
126
127
128 @Parameter(property = "maven.deploy.skip", defaultValue = "false")
129 private String skip = Boolean.FALSE.toString();
130
131
132
133
134
135
136
137
138
139
140 @Parameter(defaultValue = "false", property = "allowIncompleteProjects")
141 private boolean allowIncompleteProjects;
142
143 private enum State {
144 SKIPPED,
145 DEPLOYED,
146 TO_BE_DEPLOYED
147 }
148
149 private static final String DEPLOY_PROCESSED_MARKER = DeployMojo.class.getName() + ".processed";
150
151 private static final String DEPLOY_ALT_RELEASE_DEPLOYMENT_REPOSITORY =
152 DeployMojo.class.getName() + ".altReleaseDeploymentRepository";
153
154 private static final String DEPLOY_ALT_SNAPSHOT_DEPLOYMENT_REPOSITORY =
155 DeployMojo.class.getName() + ".altSnapshotDeploymentRepository";
156
157 private static final String DEPLOY_ALT_DEPLOYMENT_REPOSITORY =
158 DeployMojo.class.getName() + ".altDeploymentRepository";
159
160 private void putState(State state) {
161 getPluginContext().put(DEPLOY_PROCESSED_MARKER, state.name());
162 }
163
164 private void putPluginContextValue(String key, String value) {
165 if (value != null) {
166 getPluginContext().put(key, value);
167 }
168 }
169
170 private String getPluginContextValue(Map<String, Object> pluginContext, String key) {
171 return (String) pluginContext.get(key);
172 }
173
174 private State getState(Map<String, Object> pluginContext) {
175 return State.valueOf(getPluginContextValue(pluginContext, DEPLOY_PROCESSED_MARKER));
176 }
177
178 private boolean hasState(MavenProject project) {
179 Map<String, Object> pluginContext = session.getPluginContext(pluginDescriptor, project);
180 return pluginContext.containsKey(DEPLOY_PROCESSED_MARKER);
181 }
182
183 public void execute() throws MojoExecutionException, MojoFailureException {
184 State state;
185 if (Boolean.parseBoolean(skip)
186 || ("releases".equals(skip) && !ArtifactUtils.isSnapshot(project.getVersion()))
187 || ("snapshots".equals(skip) && ArtifactUtils.isSnapshot(project.getVersion()))) {
188 getLog().info("Skipping artifact deployment");
189 state = State.SKIPPED;
190 } else {
191 failIfOffline();
192 warnIfAffectedPackagingAndMaven(project.getPackaging());
193
194 if (!deployAtEnd) {
195
196 RemoteRepository deploymentRepository = getDeploymentRepository(
197 project,
198 altSnapshotDeploymentRepository,
199 altReleaseDeploymentRepository,
200 altDeploymentRepository);
201
202 DeployRequest request = new DeployRequest();
203 request.setRepository(deploymentRepository);
204 processProject(project, request);
205 deploy(request);
206 state = State.DEPLOYED;
207 } else {
208 putPluginContextValue(DEPLOY_ALT_SNAPSHOT_DEPLOYMENT_REPOSITORY, altSnapshotDeploymentRepository);
209 putPluginContextValue(DEPLOY_ALT_RELEASE_DEPLOYMENT_REPOSITORY, altReleaseDeploymentRepository);
210 putPluginContextValue(DEPLOY_ALT_DEPLOYMENT_REPOSITORY, altDeploymentRepository);
211 state = State.TO_BE_DEPLOYED;
212 }
213 }
214
215 putState(state);
216
217 List<MavenProject> allProjectsUsingPlugin = getAllProjectsUsingPlugin();
218
219 if (allProjectsMarked(allProjectsUsingPlugin)) {
220 deployAllAtOnce(allProjectsUsingPlugin);
221 } else if (state == State.TO_BE_DEPLOYED) {
222 getLog().info("Deferring deploy for " + project.getGroupId() + ":" + project.getArtifactId() + ":"
223 + project.getVersion() + " at end");
224 }
225 }
226
227 private void deployAllAtOnce(List<MavenProject> allProjectsUsingPlugin) throws MojoExecutionException {
228 Map<RemoteRepository, DeployRequest> requests = new LinkedHashMap<>();
229
230
231
232 for (MavenProject reactorProject : allProjectsUsingPlugin) {
233 Map<String, Object> pluginContext = session.getPluginContext(pluginDescriptor, reactorProject);
234 State state = getState(pluginContext);
235 if (state == State.TO_BE_DEPLOYED) {
236
237 RemoteRepository deploymentRepository = getDeploymentRepository(
238 reactorProject,
239 getPluginContextValue(pluginContext, DEPLOY_ALT_SNAPSHOT_DEPLOYMENT_REPOSITORY),
240 getPluginContextValue(pluginContext, DEPLOY_ALT_RELEASE_DEPLOYMENT_REPOSITORY),
241 getPluginContextValue(pluginContext, DEPLOY_ALT_DEPLOYMENT_REPOSITORY));
242
243 DeployRequest request = requests.computeIfAbsent(deploymentRepository, repo -> {
244 DeployRequest newRequest = new DeployRequest();
245 newRequest.setRepository(repo);
246 return newRequest;
247 });
248 processProject(reactorProject, request);
249 }
250 }
251
252 for (DeployRequest request : requests.values()) {
253 deploy(request);
254 }
255 }
256
257 private boolean allProjectsMarked(List<MavenProject> allProjectsUsingPlugin) {
258 for (MavenProject reactorProject : allProjectsUsingPlugin) {
259 if (!hasState(reactorProject)) {
260 return false;
261 }
262 }
263 return true;
264 }
265
266 private List<MavenProject> getAllProjectsUsingPlugin() {
267 ArrayList<MavenProject> result = new ArrayList<>();
268 for (MavenProject reactorProject : reactorProjects) {
269 if (hasExecution(reactorProject.getPlugin("org.apache.maven.plugins:maven-deploy-plugin"))) {
270 result.add(reactorProject);
271 }
272 }
273 return result;
274 }
275
276 private boolean hasExecution(Plugin plugin) {
277 if (plugin == null) {
278 return false;
279 }
280
281 for (PluginExecution execution : plugin.getExecutions()) {
282 if (!execution.getGoals().isEmpty() && !"none".equalsIgnoreCase(execution.getPhase())) {
283 return true;
284 }
285 }
286 return false;
287 }
288
289 private void processProject(final MavenProject project, DeployRequest request) throws MojoExecutionException {
290
291 Artifact pomArtifact = RepositoryUtils.toArtifact(new ProjectArtifact(project));
292
293 Artifact projectArtifact = RepositoryUtils.toArtifact(project.getArtifact());
294
295
296
297
298
299
300
301 if (ArtifactIdUtils.equalsId(pomArtifact, projectArtifact)) {
302 if (isFile(projectArtifact.getFile())) {
303 pomArtifact = projectArtifact;
304 }
305 projectArtifact = null;
306 }
307
308 if (isFile(pomArtifact.getFile())) {
309 request.addArtifact(pomArtifact);
310 } else {
311 throw new MojoExecutionException(
312 "The POM for project " + project.getArtifactId() + " could not be attached");
313 }
314
315
316 boolean isIncomplete = projectArtifact != null && !isFile(projectArtifact.getFile());
317 if (projectArtifact != null) {
318 if (!isIncomplete) {
319 request.addArtifact(projectArtifact);
320 } else if (!project.getAttachedArtifacts().isEmpty()) {
321 if (allowIncompleteProjects) {
322 getLog().warn("");
323 getLog().warn("The packaging plugin for project " + project.getArtifactId() + " did not assign");
324 getLog().warn("a main file to the project but it has attachments. Change packaging to 'pom'.");
325 getLog().warn("");
326 getLog().warn("Incomplete projects like this will fail in future Maven versions!");
327 getLog().warn("");
328 } else {
329 throw new MojoExecutionException("The packaging plugin for project " + project.getArtifactId()
330 + " did not assign a main file to the project but it has attachments. Change packaging"
331 + " to 'pom'.");
332 }
333 } else {
334 throw new MojoExecutionException("The packaging plugin for project " + project.getArtifactId()
335 + " did not assign a file to the build artifact");
336 }
337 }
338
339 for (org.apache.maven.artifact.Artifact attached : project.getAttachedArtifacts()) {
340 getLog().debug("Attaching for deploy: " + attached.getId());
341 request.addArtifact(RepositoryUtils.toArtifact(attached));
342 }
343 }
344
345 private boolean isFile(File file) {
346 return file != null && file.isFile();
347 }
348
349
350
351
352 RemoteRepository getDeploymentRepository(
353 final MavenProject project,
354 final String altSnapshotDeploymentRepository,
355 final String altReleaseDeploymentRepository,
356 final String altDeploymentRepository)
357 throws MojoExecutionException {
358 RemoteRepository repo = null;
359
360 String altDeploymentRepo;
361 if (ArtifactUtils.isSnapshot(project.getVersion()) && altSnapshotDeploymentRepository != null) {
362 altDeploymentRepo = altSnapshotDeploymentRepository;
363 } else if (!ArtifactUtils.isSnapshot(project.getVersion()) && altReleaseDeploymentRepository != null) {
364 altDeploymentRepo = altReleaseDeploymentRepository;
365 } else {
366 altDeploymentRepo = altDeploymentRepository;
367 }
368
369 if (altDeploymentRepo != null) {
370 getLog().info("Using alternate deployment repository " + altDeploymentRepo);
371
372 Matcher matcher = ALT_LEGACY_REPO_SYNTAX_PATTERN.matcher(altDeploymentRepo);
373
374 if (matcher.matches()) {
375 String id = matcher.group(1).trim();
376 String layout = matcher.group(2).trim();
377 String url = matcher.group(3).trim();
378
379 if ("default".equals(layout)) {
380 getLog().warn("Using legacy syntax for alternative repository. " + "Use \"" + id + "::" + url
381 + "\" instead.");
382 repo = getRemoteRepository(id, url);
383 } else {
384 throw new MojoExecutionException(
385 altDeploymentRepo,
386 "Invalid legacy syntax and layout for repository.",
387 "Invalid legacy syntax and layout for alternative repository. Use \"" + id + "::" + url
388 + "\" instead, and only default layout is supported.");
389 }
390 } else {
391 matcher = ALT_REPO_SYNTAX_PATTERN.matcher(altDeploymentRepo);
392
393 if (!matcher.matches()) {
394 throw new MojoExecutionException(
395 altDeploymentRepo,
396 "Invalid syntax for repository.",
397 "Invalid syntax for alternative repository. Use \"id::url\".");
398 } else {
399 String id = matcher.group(1).trim();
400 String url = matcher.group(2).trim();
401
402 repo = getRemoteRepository(id, url);
403 }
404 }
405 }
406
407 if (repo == null) {
408 repo = RepositoryUtils.toRepo(project.getDistributionManagementArtifactRepository());
409 }
410
411 if (repo == null) {
412 String msg = "Deployment failed: repository element was not specified in the POM inside"
413 + " distributionManagement element or in -DaltDeploymentRepository=id::url parameter";
414
415 throw new MojoExecutionException(msg);
416 }
417
418 return repo;
419 }
420 }