View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.gpg;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.nio.file.Path;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import org.apache.maven.RepositoryUtils;
28  import org.apache.maven.plugin.MojoExecutionException;
29  import org.apache.maven.plugin.MojoFailureException;
30  import org.apache.maven.plugin.logging.Log;
31  import org.apache.maven.project.MavenProject;
32  import org.codehaus.plexus.util.FileUtils;
33  import org.codehaus.plexus.util.SelectorUtils;
34  import org.eclipse.aether.artifact.Artifact;
35  
36  import static java.util.Objects.requireNonNull;
37  
38  /**
39   * Collects project artifact, the POM, and attached artifacts to be signed.
40   *
41   * @since 3.1.0
42   */
43  public class FilesCollector {
44      private final MavenProject project;
45  
46      private static final String[] DEFAULT_EXCLUDES = new String[] {
47          "**/*.md5", "**/*.sha1", "**/*.sha256", "**/*.sha512", "**/*.asc", "**/.sigstore", "**/*.sigstore.json"
48      };
49  
50      private final String[] excludes;
51  
52      private final Log log;
53  
54      public FilesCollector(MavenProject project, String[] excludes, Log log) {
55          this.project = project;
56          this.log = log;
57          if (excludes == null || excludes.length == 0) {
58              this.excludes = DEFAULT_EXCLUDES;
59              return;
60          }
61          String[] newExcludes = new String[excludes.length];
62          for (int i = 0; i < excludes.length; i++) {
63              String pattern;
64              pattern = excludes[i].trim().replace('/', File.separatorChar).replace('\\', File.separatorChar);
65              if (pattern.endsWith(File.separator)) {
66                  pattern += "**";
67              }
68              newExcludes[i] = pattern;
69          }
70          this.excludes = newExcludes;
71      }
72  
73      public List<Item> collect() throws MojoExecutionException, MojoFailureException {
74          List<Item> items = new ArrayList<>();
75  
76          if (!"pom".equals(project.getPackaging())) {
77              // ----------------------------------------------------------------------------
78              // Project artifact
79              // ----------------------------------------------------------------------------
80  
81              Artifact artifact = RepositoryUtils.toArtifact(project.getArtifact());
82  
83              File file = artifact.getFile();
84  
85              if (file != null && file.isFile()) {
86                  items.add(new Item(file, null, artifact.getExtension()));
87              } else if (project.getAttachedArtifacts().isEmpty()) {
88                  throw new MojoFailureException("The project artifact has not been assembled yet. "
89                          + "Please do not invoke this goal before the lifecycle phase \"package\".");
90              } else {
91                  log.debug("Main artifact not assembled, skipping signature generation");
92              }
93          }
94  
95          // ----------------------------------------------------------------------------
96          // POM
97          // ----------------------------------------------------------------------------
98  
99          File pomToSign =
100                 new File(project.getBuild().getDirectory(), project.getBuild().getFinalName() + ".pom");
101 
102         try {
103             FileUtils.copyFile(project.getFile(), pomToSign);
104         } catch (IOException e) {
105             throw new MojoExecutionException("Error copying POM for signing.", e);
106         }
107 
108         items.add(new Item(pomToSign, null, "pom"));
109 
110         // ----------------------------------------------------------------------------
111         // Attached artifacts
112         // ----------------------------------------------------------------------------
113 
114         for (Artifact artifact : RepositoryUtils.toArtifacts(project.getAttachedArtifacts())) {
115             File file = artifact.getFile();
116 
117             if (isExcluded(artifact)) {
118                 log.debug("Skipping generation of signature for excluded " + file);
119                 continue;
120             }
121 
122             items.add(new Item(file, artifact.getClassifier(), artifact.getExtension()));
123         }
124 
125         return items;
126     }
127 
128     /**
129      * Tests whether or not a name matches at least one exclude pattern.
130      *
131      * @param artifact the artifact to match. Must not be <code>null</code>.
132      * @return <code>true</code> when the name matches at least one exclude pattern, or <code>false</code>
133      *         otherwise.
134      */
135     protected boolean isExcluded(Artifact artifact) {
136         final Path projectBasePath = project.getBasedir().toPath();
137         final Path artifactPath = artifact.getFile().toPath();
138         final String relativeArtifactPath =
139                 projectBasePath.relativize(artifactPath).toString();
140 
141         for (String exclude : excludes) {
142             if (SelectorUtils.matchPath(exclude, relativeArtifactPath)) {
143                 return true;
144             }
145         }
146 
147         return false;
148     }
149 
150     public static class Item {
151         private final File file;
152         private final String classifier;
153         private final String extension;
154 
155         public Item(File file, String classifier, String extension) {
156             this.file = requireNonNull(file);
157             this.classifier = classifier == null || classifier.trim().isEmpty() ? null : classifier; // nullable
158             this.extension = requireNonNull(extension);
159         }
160 
161         /**
162          * The artifact backing file, never {@code null}.
163          */
164         public File getFile() {
165             return file;
166         }
167 
168         /**
169          * The classifier, if present, or {@code null}.
170          */
171         public String getClassifier() {
172             return classifier;
173         }
174 
175         /**
176          * The file extension (without leading period), never {@code null}.
177          */
178         public String getExtension() {
179             return extension;
180         }
181     }
182 }