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.nio.file.Files;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.LinkedHashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29
30 import org.apache.maven.api.Artifact;
31 import org.apache.maven.api.MojoExecution;
32 import org.apache.maven.api.ProducedArtifact;
33 import org.apache.maven.api.Project;
34 import org.apache.maven.api.RemoteRepository;
35 import org.apache.maven.api.di.Inject;
36 import org.apache.maven.api.model.DistributionManagement;
37 import org.apache.maven.api.model.Plugin;
38 import org.apache.maven.api.model.PluginExecution;
39 import org.apache.maven.api.plugin.MojoException;
40 import org.apache.maven.api.plugin.annotations.Mojo;
41 import org.apache.maven.api.plugin.annotations.Parameter;
42 import org.apache.maven.api.services.ArtifactDeployer;
43 import org.apache.maven.api.services.ArtifactDeployerRequest;
44 import org.apache.maven.api.services.ArtifactManager;
45 import org.apache.maven.api.services.ProjectManager;
46
47
48
49
50
51
52
53 @Mojo(name = "deploy", defaultPhase = "deploy")
54 public class DeployMojo extends AbstractDeployMojo {
55 private static final Pattern ALT_LEGACY_REPO_SYNTAX_PATTERN = Pattern.compile("(.+?)::(.+?)::(.+)");
56
57 private static final Pattern ALT_REPO_SYNTAX_PATTERN = Pattern.compile("(.+?)::(.+)");
58
59 @Inject
60 private Project project;
61
62 @Inject
63 private MojoExecution mojoExecution;
64
65
66
67
68
69
70
71
72 @Parameter(defaultValue = "true", property = "deployAtEnd")
73 private boolean deployAtEnd;
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 @Parameter(property = "altDeploymentRepository")
90 private String altDeploymentRepository;
91
92
93
94
95
96
97
98
99
100
101 @Parameter(property = "altSnapshotDeploymentRepository")
102 private String altSnapshotDeploymentRepository;
103
104
105
106
107
108
109
110
111
112
113 @Parameter(property = "altReleaseDeploymentRepository")
114 private String altReleaseDeploymentRepository;
115
116
117
118
119
120
121
122
123
124
125
126
127 @Parameter(property = "maven.deploy.skip", defaultValue = "false")
128 private String skip = Boolean.FALSE.toString();
129
130
131
132
133
134
135
136
137
138
139 @Parameter(defaultValue = "false", property = "allowIncompleteProjects")
140 private boolean allowIncompleteProjects;
141
142 private enum State {
143 SKIPPED,
144 DEPLOYED,
145 TO_BE_DEPLOYED
146 }
147
148 public DeployMojo() {}
149
150 private void putState(State state) {
151 session.getPluginContext(project).put(State.class.getName(), state);
152 }
153
154 private void putState(ArtifactDeployerRequest request) {
155 session.getPluginContext(project).put(ArtifactDeployerRequest.class.getName(), request);
156 }
157
158 private State getState(Project project) {
159 return (State) session.getPluginContext(project).get(State.class.getName());
160 }
161
162 private boolean hasState(Project project) {
163 return session.getPluginContext(project).containsKey(State.class.getName());
164 }
165
166 public void execute() {
167 if (Boolean.parseBoolean(skip)
168 || ("releases".equals(skip) && !session.isVersionSnapshot(project.getVersion()))
169 || ("snapshots".equals(skip) && session.isVersionSnapshot(project.getVersion()))) {
170 getLog().info("Skipping artifact deployment");
171 putState(State.SKIPPED);
172 } else {
173 failIfOffline();
174 warnIfAffectedPackagingAndMaven(project.getPackaging().id());
175
176 if (!deployAtEnd) {
177 getLog().info("Deploying deploy for " + project.getGroupId() + ":" + project.getArtifactId() + ":"
178 + project.getVersion() + " at end");
179 deploy(createDeployerRequest());
180 putState(State.DEPLOYED);
181 } else {
182
183 putState(State.TO_BE_DEPLOYED);
184 putState(createDeployerRequest());
185 if (!allProjectsMarked()) {
186 getLog().info("Deferring deploy for " + project.getGroupId() + ":" + project.getArtifactId() + ":"
187 + project.getVersion() + " at end");
188 }
189 }
190 }
191
192 if (allProjectsMarked()) {
193 deployAllAtOnce();
194 }
195 }
196
197 private boolean allProjectsMarked() {
198 return session.getProjects().stream().allMatch(p -> hasState(p) || !hasDeployExecution(p));
199 }
200
201 private boolean hasDeployExecution(Project p) {
202 String key = mojoExecution.getPlugin().getModel().getKey();
203 Plugin plugin = p.getBuild().getPluginsAsMap().get(key);
204 if (plugin != null) {
205 for (PluginExecution execution : plugin.getExecutions()) {
206 if (!execution.getGoals().isEmpty() && !"none".equalsIgnoreCase(execution.getPhase())) {
207 return true;
208 }
209 }
210 }
211 return false;
212 }
213
214 private void deployAllAtOnce() {
215 Map<RemoteRepository, Map<Integer, List<ProducedArtifact>>> flattenedRequests = new LinkedHashMap<>();
216
217 for (Project reactorProject : session.getProjects()) {
218 State state = getState(reactorProject);
219 if (state == State.TO_BE_DEPLOYED) {
220 ArtifactDeployerRequest request = (ArtifactDeployerRequest)
221 session.getPluginContext(reactorProject).get(ArtifactDeployerRequest.class.getName());
222 flattenedRequests
223 .computeIfAbsent(request.getRepository(), r -> new LinkedHashMap<>())
224 .computeIfAbsent(request.getRetryFailedDeploymentCount(), i -> new ArrayList<>())
225 .addAll(request.getArtifacts());
226 }
227 }
228
229 List<ArtifactDeployerRequest> requests = new ArrayList<>();
230 for (Map.Entry<RemoteRepository, Map<Integer, List<ProducedArtifact>>> entry1 : flattenedRequests.entrySet()) {
231 for (Map.Entry<Integer, List<ProducedArtifact>> entry2 :
232 entry1.getValue().entrySet()) {
233 requests.add(ArtifactDeployerRequest.builder()
234 .session(session)
235 .repository(entry1.getKey())
236 .retryFailedDeploymentCount(entry2.getKey())
237 .artifacts(entry2.getValue())
238 .build());
239 }
240 }
241
242 if (!requests.isEmpty()) {
243 requests.forEach(this::deploy);
244 } else {
245 getLog().info("No actual deploy requests");
246 }
247 }
248
249 private void deploy(ArtifactDeployerRequest request) {
250 try {
251 getLog().info("Deploying artifacts " + request.getArtifacts().toString() + " to repository "
252 + request.getRepository());
253 getArtifactDeployer().deploy(request);
254 } catch (MojoException e) {
255 throw e;
256 } catch (Exception e) {
257 throw new MojoException(e.getMessage(), e);
258 }
259 }
260
261 private ArtifactDeployerRequest createDeployerRequest() {
262 ProjectManager projectManager = getProjectManager();
263 Collection<ProducedArtifact> deployables = projectManager.getAllArtifacts(project);
264 Collection<ProducedArtifact> attachedArtifacts = projectManager.getAttachedArtifacts(project);
265
266 ArtifactManager artifactManager = getArtifactManager();
267 if (artifactManager.getPath(project.getPomArtifact()).isEmpty()) {
268 artifactManager.setPath(project.getPomArtifact(), project.getPomPath());
269 }
270
271 for (Artifact deployable : deployables) {
272 if (!isValidPath(deployable)) {
273 if (deployable == project.getMainArtifact().orElse(null)) {
274 if (attachedArtifacts.isEmpty()) {
275 throw new MojoException(
276 "The packaging for this project did not assign a file to the build artifact");
277 } else {
278 if (allowIncompleteProjects) {
279 getLog().warn("");
280 getLog().warn("The packaging plugin for this project did not assign");
281 getLog().warn(
282 "a main file to the project but it has attachments. Change packaging to 'pom'.");
283 getLog().warn("");
284 getLog().warn("Incomplete projects like this will fail in future Maven versions!");
285 getLog().warn("");
286 } else {
287 throw new MojoException("The packaging plugin for this project did not assign "
288 + "a main file to the project but it has attachments. Change packaging to 'pom'.");
289 }
290 }
291 } else {
292 throw new MojoException("The packaging for this project did not assign "
293 + "a file to the attached artifact: " + deployable);
294 }
295 }
296 }
297
298 ArtifactDeployerRequest request = ArtifactDeployerRequest.builder()
299 .session(session)
300 .repository(getDeploymentRepository(session.isVersionSnapshot(project.getVersion())))
301 .artifacts((Collection) deployables)
302 .retryFailedDeploymentCount(Math.max(1, Math.min(10, getRetryFailedDeploymentCount())))
303 .build();
304
305 return request;
306 }
307
308
309
310
311 RemoteRepository getDeploymentRepository(boolean isSnapshot) throws MojoException {
312 RemoteRepository repo = null;
313
314 String altDeploymentRepo;
315 if (isSnapshot && altSnapshotDeploymentRepository != null) {
316 altDeploymentRepo = altSnapshotDeploymentRepository;
317 } else if (!isSnapshot && altReleaseDeploymentRepository != null) {
318 altDeploymentRepo = altReleaseDeploymentRepository;
319 } else {
320 altDeploymentRepo = altDeploymentRepository;
321 }
322
323 if (altDeploymentRepo != null) {
324 getLog().info("Using alternate deployment repository " + altDeploymentRepo);
325
326 Matcher matcher = ALT_LEGACY_REPO_SYNTAX_PATTERN.matcher(altDeploymentRepo);
327
328 if (matcher.matches()) {
329 String id = matcher.group(1).trim();
330 String layout = matcher.group(2).trim();
331 String url = matcher.group(3).trim();
332
333 if ("default".equals(layout)) {
334 getLog().warn("Using legacy syntax for alternative repository. " + "Use \"" + id + "::" + url
335 + "\" instead.");
336 repo = createDeploymentArtifactRepository(id, url);
337 } else {
338 throw new MojoException(
339 altDeploymentRepo,
340 "Invalid legacy syntax and layout for repository.",
341 "Invalid legacy syntax and layout for alternative repository. Use \"" + id + "::" + url
342 + "\" instead, and only default layout is supported.");
343 }
344 } else {
345 matcher = ALT_REPO_SYNTAX_PATTERN.matcher(altDeploymentRepo);
346
347 if (!matcher.matches()) {
348 throw new MojoException(
349 altDeploymentRepo,
350 "Invalid syntax for repository.",
351 "Invalid syntax for alternative repository. Use \"id::url\".");
352 } else {
353 String id = matcher.group(1).trim();
354 String url = matcher.group(2).trim();
355
356 repo = createDeploymentArtifactRepository(id, url);
357 }
358 }
359 }
360
361 if (repo == null) {
362 DistributionManagement dm = project.getModel().getDistributionManagement();
363 if (dm != null) {
364 if (isSnapshot
365 && dm.getSnapshotRepository() != null
366 && isNotEmpty(dm.getSnapshotRepository().getId())
367 && isNotEmpty(dm.getSnapshotRepository().getUrl())) {
368 repo = session.createRemoteRepository(dm.getSnapshotRepository());
369 } else if (dm.getRepository() != null
370 && isNotEmpty(dm.getRepository().getId())
371 && isNotEmpty(dm.getRepository().getUrl())) {
372 repo = session.createRemoteRepository(dm.getRepository());
373 }
374 }
375 }
376
377 if (repo == null) {
378 String msg = "Deployment failed: repository element was not specified in the POM inside"
379 + " distributionManagement element or in -DaltDeploymentRepository=id::url parameter";
380
381 throw new MojoException(msg);
382 }
383
384 return repo;
385 }
386
387 private boolean isValidPath(Artifact a) {
388 return getArtifactManager().getPath(a).filter(Files::isRegularFile).isPresent();
389 }
390
391 private static boolean isNotEmpty(String str) {
392 return str != null && !str.isEmpty();
393 }
394
395 private ArtifactDeployer getArtifactDeployer() {
396 return session.getService(ArtifactDeployer.class);
397 }
398
399 private ArtifactManager getArtifactManager() {
400 return session.getService(ArtifactManager.class);
401 }
402
403 private ProjectManager getProjectManager() {
404 return session.getService(ProjectManager.class);
405 }
406 }