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 javax.inject.Inject;
22
23 import java.io.IOException;
24 import java.nio.file.Files;
25 import java.nio.file.InvalidPathException;
26 import java.nio.file.Path;
27 import java.nio.file.Paths;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.stream.Collectors;
37 import java.util.stream.Stream;
38
39 import org.apache.maven.plugin.MojoExecutionException;
40 import org.apache.maven.plugin.MojoFailureException;
41 import org.apache.maven.plugins.annotations.Mojo;
42 import org.apache.maven.plugins.annotations.Parameter;
43 import org.codehaus.plexus.util.FileUtils;
44 import org.eclipse.aether.DefaultRepositorySystemSession;
45 import org.eclipse.aether.RepositorySystem;
46 import org.eclipse.aether.RepositorySystemSession;
47 import org.eclipse.aether.RequestTrace;
48 import org.eclipse.aether.artifact.Artifact;
49 import org.eclipse.aether.artifact.DefaultArtifact;
50 import org.eclipse.aether.deployment.DeployRequest;
51 import org.eclipse.aether.deployment.DeploymentException;
52 import org.eclipse.aether.repository.LocalRepository;
53 import org.eclipse.aether.repository.RemoteRepository;
54 import org.eclipse.aether.resolution.ArtifactRequest;
55 import org.eclipse.aether.resolution.ArtifactResolutionException;
56 import org.eclipse.aether.resolution.ArtifactResult;
57 import org.eclipse.aether.util.artifact.SubArtifact;
58
59
60
61
62
63
64
65
66 @Mojo(name = "sign-deployed", requiresProject = false, threadSafe = true)
67 public class SignDeployedMojo extends AbstractGpgMojo {
68
69
70
71
72 @Parameter(property = "url", required = true)
73 private String url;
74
75
76
77
78
79 @Parameter(property = "repositoryId", required = true)
80 private String repositoryId;
81
82
83
84
85 @Parameter(property = "javadoc", defaultValue = "true", required = true)
86 private boolean javadoc;
87
88
89
90
91 @Parameter(property = "sources", defaultValue = "true", required = true)
92 private boolean sources;
93
94
95
96
97
98
99
100
101
102
103
104
105 @Parameter(property = "artifacts")
106 private String artifacts;
107
108 private final RepositorySystem repositorySystem;
109
110 private final Map<String, ArtifactCollectorSPI> artifactCollectors;
111
112 @Inject
113 public SignDeployedMojo(RepositorySystem repositorySystem, Map<String, ArtifactCollectorSPI> artifactCollectors) {
114 this.repositorySystem = repositorySystem;
115 this.artifactCollectors = artifactCollectors;
116 }
117
118 @Override
119 protected void doExecute() throws MojoExecutionException, MojoFailureException {
120 if (settings.isOffline()) {
121 throw new MojoFailureException("Cannot deploy artifacts when Maven is in offline mode");
122 }
123
124 Path tempDirectory = null;
125 Set<Artifact> artifacts = new HashSet<>();
126 try {
127 tempDirectory = Files.createTempDirectory("gpg-sign-deployed");
128 getLog().debug("Using temp directory " + tempDirectory);
129
130 DefaultRepositorySystemSession signingSession =
131 new DefaultRepositorySystemSession(session.getRepositorySession());
132 signingSession.setLocalRepositoryManager(repositorySystem.newLocalRepositoryManager(
133 signingSession, new LocalRepository(tempDirectory.toFile())));
134
135
136 RemoteRepository deploymentRepository = repositorySystem.newDeploymentRepository(
137 signingSession, new RemoteRepository.Builder(repositoryId, "default", url).build());
138
139
140 getLog().debug("Collecting artifacts for signing...");
141 artifacts.addAll(collectArtifacts(signingSession, deploymentRepository));
142 getLog().info("Collected " + artifacts.size() + " artifact" + ((artifacts.size() > 1) ? "s" : "")
143 + " for signing");
144
145
146 if (sources || javadoc) {
147 getLog().debug("Adding additional artifacts...");
148 List<Artifact> additions = new ArrayList<>();
149 for (Artifact artifact : artifacts) {
150 if (artifact.getClassifier().isEmpty()) {
151 if (sources) {
152 additions.add(new SubArtifact(artifact, "sources", "jar"));
153 }
154 if (javadoc) {
155 additions.add(new SubArtifact(artifact, "javadoc", "jar"));
156 }
157 }
158 }
159 artifacts.addAll(additions);
160 }
161
162
163 getLog().info("Resolving " + artifacts.size() + " artifact" + ((artifacts.size() > 1) ? "s" : "")
164 + " artifacts for signing...");
165 List<ArtifactResult> results = repositorySystem.resolveArtifacts(
166 signingSession,
167 artifacts.stream()
168 .map(a -> new ArtifactRequest(a, Collections.singletonList(deploymentRepository), "gpg"))
169 .collect(Collectors.toList()));
170 artifacts = results.stream().map(ArtifactResult::getArtifact).collect(Collectors.toSet());
171
172
173 AbstractGpgSigner signer = newSigner(null);
174 signer.setOutputDirectory(tempDirectory.toFile());
175 getLog().info("Signer '" + signer.signerName() + "' is signing " + artifacts.size() + " file"
176 + ((artifacts.size() > 1) ? "s" : "") + " with key " + signer.getKeyInfo());
177
178 HashSet<Artifact> signatures = new HashSet<>();
179 for (Artifact a : artifacts) {
180 signatures.add(new DefaultArtifact(
181 a.getGroupId(),
182 a.getArtifactId(),
183 a.getClassifier(),
184 a.getExtension() + AbstractGpgSigner.SIGNATURE_EXTENSION,
185 a.getVersion())
186 .setFile(signer.generateSignatureForArtifact(a.getFile())));
187 }
188
189
190 getLog().info("Deploying artifact signatures...");
191 repositorySystem.deploy(
192 signingSession,
193 new DeployRequest()
194 .setRepository(deploymentRepository)
195 .setArtifacts(signatures)
196 .setTrace(RequestTrace.newChild(null, this)));
197 } catch (IOException e) {
198 throw new MojoExecutionException("IO error: " + e.getMessage(), e);
199 } catch (ArtifactResolutionException e) {
200 throw new MojoExecutionException(
201 "Error resolving deployed artifacts " + artifacts + ": " + e.getMessage(), e);
202 } catch (DeploymentException e) {
203 throw new MojoExecutionException("Error deploying signatures: " + e.getMessage(), e);
204 } finally {
205 if (tempDirectory != null) {
206 getLog().info("Cleaning up...");
207 try {
208 FileUtils.deleteDirectory(tempDirectory.toFile());
209 } catch (IOException e) {
210 getLog().warn("Could not clean up temp directory " + tempDirectory);
211 }
212 }
213 }
214 }
215
216
217
218
219
220 protected Collection<Artifact> collectArtifacts(RepositorySystemSession session, RemoteRepository remoteRepository)
221 throws IOException {
222 Collection<Artifact> result = null;
223 for (ArtifactCollectorSPI artifactCollector : artifactCollectors.values()) {
224 result = artifactCollector.collectArtifacts(session, remoteRepository);
225 if (result != null) {
226 break;
227 }
228 }
229 if (result == null) {
230 if (artifacts != null) {
231 try {
232 Path path = Paths.get(artifacts);
233 if (Files.isRegularFile(path)) {
234 try (Stream<String> lines = Files.lines(path)) {
235 result = lines.filter(l -> !l.isEmpty() && !l.startsWith("#"))
236 .map(DefaultArtifact::new)
237 .collect(Collectors.toSet());
238 }
239 }
240 } catch (InvalidPathException e) {
241
242 }
243 if (result == null) {
244 result = Arrays.stream(artifacts.split(","))
245 .map(DefaultArtifact::new)
246 .collect(Collectors.toSet());
247 }
248 }
249 }
250 if (result == null) {
251 throw new IllegalStateException("No source to collect from (set -Dartifacts=g:a:v... or add collector)");
252 }
253 return result;
254 }
255 }