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