1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.gpg;
20
21 import java.io.File;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.Reader;
25 import java.io.Writer;
26 import java.nio.file.Files;
27 import java.util.ArrayList;
28 import java.util.List;
29
30 import org.apache.maven.artifact.handler.ArtifactHandler;
31 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
32 import org.apache.maven.model.Model;
33 import org.apache.maven.model.Parent;
34 import org.apache.maven.model.building.DefaultModelBuildingRequest;
35 import org.apache.maven.model.building.ModelBuildingRequest;
36 import org.apache.maven.model.building.ModelProblem;
37 import org.apache.maven.model.building.ModelProblemCollector;
38 import org.apache.maven.model.building.ModelProblemCollectorRequest;
39 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
40 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
41 import org.apache.maven.model.validation.ModelValidator;
42 import org.apache.maven.plugin.MojoExecutionException;
43 import org.apache.maven.plugin.MojoFailureException;
44 import org.apache.maven.plugins.annotations.Component;
45 import org.apache.maven.plugins.annotations.Mojo;
46 import org.apache.maven.plugins.annotations.Parameter;
47 import org.apache.maven.project.MavenProject;
48 import org.codehaus.plexus.util.FileUtils;
49 import org.codehaus.plexus.util.ReaderFactory;
50 import org.codehaus.plexus.util.StringUtils;
51 import org.codehaus.plexus.util.WriterFactory;
52 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
53 import org.eclipse.aether.RepositorySystem;
54 import org.eclipse.aether.artifact.Artifact;
55 import org.eclipse.aether.artifact.DefaultArtifact;
56 import org.eclipse.aether.deployment.DeployRequest;
57 import org.eclipse.aether.deployment.DeploymentException;
58 import org.eclipse.aether.repository.RemoteRepository;
59
60
61
62
63
64
65
66 @Mojo(name = "sign-and-deploy-file", requiresProject = false, threadSafe = true)
67 public class SignAndDeployFileMojo extends AbstractGpgMojo {
68
69
70
71
72 @Parameter(property = "gpg.ascDirectory")
73 private File ascDirectory;
74
75
76
77
78 @Parameter(property = "groupId")
79 private String groupId;
80
81
82
83
84 @Parameter(property = "artifactId")
85 private String artifactId;
86
87
88
89
90 @Parameter(property = "version")
91 private String version;
92
93
94
95
96
97 @Parameter(property = "packaging")
98 private String packaging;
99
100
101
102
103 @Parameter(property = "classifier")
104 private String classifier;
105
106
107
108
109 @Parameter(property = "generatePom.description")
110 private String description;
111
112
113
114
115 @Parameter(property = "file", required = true)
116 private File file;
117
118
119
120
121 @Parameter(property = "pomFile")
122 private File pomFile;
123
124
125
126
127 @Parameter(property = "generatePom", defaultValue = "true")
128 private boolean generatePom;
129
130
131
132
133
134 @Parameter(property = "url", required = true)
135 private String url;
136
137
138
139
140
141 @Parameter(property = "repositoryId", defaultValue = "remote-repository", required = true)
142 private String repositoryId;
143
144
145
146
147
148
149 @Parameter(property = "javadoc")
150 private File javadoc;
151
152
153
154
155
156
157 @Parameter(property = "sources")
158 private File sources;
159
160
161
162
163
164
165
166 @Parameter(property = "retryFailedDeploymentCount", defaultValue = "1")
167 private int retryFailedDeploymentCount;
168
169
170
171
172
173 @Parameter(property = "types")
174 private String types;
175
176
177
178
179
180 @Parameter(property = "classifiers")
181 private String classifiers;
182
183
184
185
186
187 @Parameter(property = "files")
188 private String files;
189
190
191
192 @Component
193 private RepositorySystem repositorySystem;
194
195
196
197
198 @Component
199 private ModelValidator modelValidator;
200
201
202
203
204
205
206 @Component
207 private MavenProject project;
208
209
210
211
212 @Component
213 private ArtifactHandlerManager artifactHandlerManager;
214
215 private void initProperties() throws MojoExecutionException {
216
217 if (pomFile != null) {
218 generatePom = false;
219
220 Model model = readModel(pomFile);
221
222 processModel(model);
223 }
224
225 if (packaging == null && file != null) {
226 packaging = FileUtils.getExtension(file.getName());
227 }
228 }
229
230 @Override
231 protected void doExecute() throws MojoExecutionException, MojoFailureException {
232 if (settings.isOffline()) {
233 throw new MojoFailureException("Cannot deploy artifacts when Maven is in offline mode");
234 }
235
236 initProperties();
237
238 validateArtifactInformation();
239
240 if (!file.exists()) {
241 throw new MojoFailureException(file.getPath() + " not found.");
242 }
243
244
245 List<Artifact> artifacts = new ArrayList<>();
246
247
248 ArtifactHandler handler = artifactHandlerManager.getArtifactHandler(packaging);
249 Artifact main = new DefaultArtifact(
250 groupId,
251 artifactId,
252 classifier == null || classifier.trim().isEmpty() ? handler.getClassifier() : classifier,
253 handler.getExtension(),
254 version)
255 .setFile(file);
256
257 File localRepoFile = new File(
258 session.getRepositorySession().getLocalRepository().getBasedir(),
259 session.getRepositorySession().getLocalRepositoryManager().getPathForLocalArtifact(main));
260 if (file.equals(localRepoFile)) {
261 throw new MojoFailureException("Cannot deploy artifact from the local repository: " + file);
262 }
263 artifacts.add(main);
264
265 if (!"pom".equals(packaging)) {
266 if (pomFile == null && generatePom) {
267 pomFile = generatePomFile();
268 }
269 if (pomFile != null) {
270 artifacts.add(
271 new DefaultArtifact(main.getGroupId(), main.getArtifactId(), null, "pom", main.getVersion())
272 .setFile(pomFile));
273 }
274 }
275
276 if (sources != null) {
277 artifacts.add(
278 new DefaultArtifact(main.getGroupId(), main.getArtifactId(), "sources", "jar", main.getVersion())
279 .setFile(sources));
280 }
281
282 if (javadoc != null) {
283 artifacts.add(
284 new DefaultArtifact(main.getGroupId(), main.getArtifactId(), "javadoc", "jar", main.getVersion())
285 .setFile(javadoc));
286 }
287
288 if (files != null) {
289 if (types == null) {
290 throw new MojoExecutionException("You must specify 'types' if you specify 'files'");
291 }
292 if (classifiers == null) {
293 throw new MojoExecutionException("You must specify 'classifiers' if you specify 'files'");
294 }
295 String[] files = this.files.split(",", -1);
296 String[] types = this.types.split(",", -1);
297 String[] classifiers = this.classifiers.split(",", -1);
298 if (types.length != files.length) {
299 throw new MojoExecutionException("You must specify the same number of entries in 'files' and "
300 + "'types' (respectively " + files.length + " and " + types.length + " entries )");
301 }
302 if (classifiers.length != files.length) {
303 throw new MojoExecutionException("You must specify the same number of entries in 'files' and "
304 + "'classifiers' (respectively " + files.length + " and " + classifiers.length + " entries )");
305 }
306 for (int i = 0; i < files.length; i++) {
307 File file = new File(files[i]);
308 if (!file.isFile()) {
309
310 file = new File(project.getBasedir(), files[i]);
311 }
312 if (file.isFile()) {
313 Artifact artifact;
314 String ext =
315 artifactHandlerManager.getArtifactHandler(types[i]).getExtension();
316 if (StringUtils.isWhitespace(classifiers[i])) {
317 artifact = new DefaultArtifact(
318 main.getGroupId(), main.getArtifactId(), null, ext, main.getVersion());
319 } else {
320 artifact = new DefaultArtifact(
321 main.getGroupId(), main.getArtifactId(), classifiers[i], ext, main.getVersion());
322 }
323 artifacts.add(artifact.setFile(file));
324 } else {
325 throw new MojoExecutionException("Specified side artifact " + file + " does not exist");
326 }
327 }
328 } else {
329 if (types != null) {
330 throw new MojoExecutionException("You must specify 'files' if you specify 'types'");
331 }
332 if (classifiers != null) {
333 throw new MojoExecutionException("You must specify 'files' if you specify 'classifiers'");
334 }
335 }
336
337
338 AbstractGpgSigner signer = newSigner(null);
339 signer.setOutputDirectory(ascDirectory);
340 signer.setBaseDirectory(new File("").getAbsoluteFile());
341
342 getLog().info("Signer '" + signer.signerName() + "' is signing " + artifacts.size() + " file"
343 + ((artifacts.size() > 1) ? "s" : "") + " with key " + signer.getKeyInfo());
344
345 ArrayList<Artifact> signatures = new ArrayList<>();
346 for (Artifact a : artifacts) {
347 signatures.add(new DefaultArtifact(
348 a.getGroupId(),
349 a.getArtifactId(),
350 a.getClassifier(),
351 a.getExtension() + AbstractGpgSigner.SIGNATURE_EXTENSION,
352 a.getVersion())
353 .setFile(signer.generateSignatureForArtifact(a.getFile())));
354 }
355 artifacts.addAll(signatures);
356
357
358 RemoteRepository deploymentRepository = repositorySystem.newDeploymentRepository(
359 session.getRepositorySession(), new RemoteRepository.Builder(repositoryId, "default", url).build());
360 try {
361 deploy(deploymentRepository, artifacts);
362 } catch (DeploymentException e) {
363 throw new MojoExecutionException(
364 "Error deploying attached artifacts " + artifacts + ": " + e.getMessage(), e);
365 }
366 }
367
368
369
370
371
372
373 private void processModel(Model model) {
374 Parent parent = model.getParent();
375
376 if (this.groupId == null) {
377 this.groupId = model.getGroupId();
378 if (this.groupId == null && parent != null) {
379 this.groupId = parent.getGroupId();
380 }
381 }
382 if (this.artifactId == null) {
383 this.artifactId = model.getArtifactId();
384 }
385 if (this.version == null) {
386 this.version = model.getVersion();
387 if (this.version == null && parent != null) {
388 this.version = parent.getVersion();
389 }
390 }
391 if (this.packaging == null) {
392 this.packaging = model.getPackaging();
393 }
394 }
395
396
397
398
399
400
401
402
403 private Model readModel(File pomFile) throws MojoExecutionException {
404 try (Reader reader = ReaderFactory.newXmlReader(pomFile)) {
405 return new MavenXpp3Reader().read(reader);
406 } catch (FileNotFoundException e) {
407 throw new MojoExecutionException("POM not found " + pomFile, e);
408 } catch (IOException e) {
409 throw new MojoExecutionException("Error reading POM " + pomFile, e);
410 } catch (XmlPullParserException e) {
411 throw new MojoExecutionException("Error parsing POM " + pomFile, e);
412 }
413 }
414
415
416
417
418
419
420
421 private File generatePomFile() throws MojoExecutionException {
422 Model model = generateModel();
423
424 try {
425 File tempFile = Files.createTempFile("mvndeploy", ".pom").toFile();
426 tempFile.deleteOnExit();
427
428 try (Writer fw = WriterFactory.newXmlWriter(tempFile)) {
429 new MavenXpp3Writer().write(fw, model);
430 }
431
432 return tempFile;
433 } catch (IOException e) {
434 throw new MojoExecutionException("Error writing temporary pom file: " + e.getMessage(), e);
435 }
436 }
437
438
439
440
441
442
443 private void validateArtifactInformation() throws MojoFailureException {
444 Model model = generateModel();
445
446 ModelBuildingRequest request =
447 new DefaultModelBuildingRequest().setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_STRICT);
448
449 List<String> result = new ArrayList<>();
450
451 SimpleModelProblemCollector problemCollector = new SimpleModelProblemCollector(result);
452
453 modelValidator.validateEffectiveModel(model, request, problemCollector);
454
455 if (!result.isEmpty()) {
456 StringBuilder msg = new StringBuilder("The artifact information is incomplete or not valid:\n");
457 for (String e : result) {
458 msg.append(" - " + e + '\n');
459 }
460 throw new MojoFailureException(msg.toString());
461 }
462 }
463
464
465
466
467
468
469 private Model generateModel() {
470 Model model = new Model();
471
472 model.setModelVersion("4.0.0");
473
474 model.setGroupId(groupId);
475 model.setArtifactId(artifactId);
476 model.setVersion(version);
477 model.setPackaging(packaging);
478
479 model.setDescription(description);
480
481 return model;
482 }
483
484
485
486
487
488
489
490
491 protected void deploy(RemoteRepository deploymentRepository, List<Artifact> artifacts) throws DeploymentException {
492 int retryFailedDeploymentCount = Math.max(1, Math.min(10, this.retryFailedDeploymentCount));
493 DeploymentException exception = null;
494 for (int count = 0; count < retryFailedDeploymentCount; count++) {
495 try {
496 if (count > 0) {
497
498 getLog().info("Retrying deployment attempt " + (count + 1) + " of " + retryFailedDeploymentCount);
499
500 }
501 DeployRequest deployRequest = new DeployRequest();
502 deployRequest.setRepository(deploymentRepository);
503 deployRequest.setArtifacts(artifacts);
504
505 repositorySystem.deploy(session.getRepositorySession(), deployRequest);
506 exception = null;
507 break;
508 } catch (DeploymentException e) {
509 if (count + 1 < retryFailedDeploymentCount) {
510 getLog().warn("Encountered issue during deployment: " + e.getLocalizedMessage());
511 getLog().debug(e);
512 }
513 if (exception == null) {
514 exception = e;
515 }
516 }
517 }
518 if (exception != null) {
519 throw exception;
520 }
521 }
522
523 private static class SimpleModelProblemCollector implements ModelProblemCollector {
524
525 private final List<String> result;
526
527 SimpleModelProblemCollector(List<String> result) {
528 this.result = result;
529 }
530
531 public void add(ModelProblemCollectorRequest req) {
532 if (!ModelProblem.Severity.WARNING.equals(req.getSeverity())) {
533 result.add(req.getMessage());
534 }
535 }
536 }
537 }