All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.plugins.gpg.SignDeployedMojo Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.maven.plugins.gpg;

import javax.inject.Inject;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.codehaus.plexus.util.FileUtils;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.RequestTrace;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.deployment.DeployRequest;
import org.eclipse.aether.deployment.DeploymentException;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.util.artifact.SubArtifact;

/**
 * Resolves given artifacts from a given remote repository, signs them, and deploys the signatures next to signed
 * artifacts, and cleans up afterward. This mojo will use "own" local repository for all the operations to not
 * "pollute" user local repository, and also to be able to fully clean up (delete) after job done.
 *
 * @since 3.2.3
 */
@Mojo(name = "sign-deployed", requiresProject = false, threadSafe = true)
public class SignDeployedMojo extends AbstractGpgMojo {

    /**
     * URL where the artifacts are deployed.
     */
    @Parameter(property = "url", required = true)
    private String url;

    /**
     * Server ID to map on the <id> under <server> section of settings.xml. In most cases, this
     * parameter will be required for authentication.
     */
    @Parameter(property = "repositoryId", required = true)
    private String repositoryId;

    /**
     * Should generate coordinates "javadoc" sub-artifacts?
     */
    @Parameter(property = "javadoc", defaultValue = "true", required = true)
    private boolean javadoc;

    /**
     * Should generate coordinates "sources" sub-artifacts?
     */
    @Parameter(property = "sources", defaultValue = "true", required = true)
    private boolean sources;

    /**
     * If no {@link ArtifactCollectorSPI} is added, this Mojo will fall back to this parameter to collect GAVs that are
     * deployed and needs signatures deployed next to them. This parameter can contain multiple things:
     * 
    *
  • A path to an existing file, that contains one GAV spec at a line. File may also contain empty lines or * lines starting with {@code #} that will be ignored.
  • *
  • A comma separated list of GAV specs.
  • *
*

* Note: format of GAV entries must be {@code :[:[:]]:}. */ @Parameter(property = "artifacts") private String artifacts; @Component private RepositorySystem repositorySystem; @Inject private Map artifactCollectors; @Override protected void doExecute() throws MojoExecutionException, MojoFailureException { if (settings.isOffline()) { throw new MojoFailureException("Cannot deploy artifacts when Maven is in offline mode"); } Path tempDirectory = null; Set artifacts = new HashSet<>(); try { tempDirectory = Files.createTempDirectory("gpg-sign-deployed"); getLog().debug("Using temp directory " + tempDirectory); DefaultRepositorySystemSession signingSession = new DefaultRepositorySystemSession(session.getRepositorySession()); signingSession.setLocalRepositoryManager(repositorySystem.newLocalRepositoryManager( signingSession, new LocalRepository(tempDirectory.toFile()))); // remote repo where deployed artifacts are, and where signatures need to be deployed RemoteRepository deploymentRepository = repositorySystem.newDeploymentRepository( signingSession, new RemoteRepository.Builder(repositoryId, "default", url).build()); // get artifacts list getLog().debug("Collecting artifacts for signing..."); artifacts.addAll(collectArtifacts(signingSession, deploymentRepository)); getLog().info("Collected " + artifacts.size() + " artifact" + ((artifacts.size() > 1) ? "s" : "") + " for signing"); // create additional ones if needed if (sources || javadoc) { getLog().debug("Adding additional artifacts..."); List additions = new ArrayList<>(); for (Artifact artifact : artifacts) { if (artifact.getClassifier().isEmpty()) { if (sources) { additions.add(new SubArtifact(artifact, "sources", "jar")); } if (javadoc) { additions.add(new SubArtifact(artifact, "javadoc", "jar")); } } } artifacts.addAll(additions); } // resolve them all getLog().info("Resolving " + artifacts.size() + " artifact" + ((artifacts.size() > 1) ? "s" : "") + " artifacts for signing..."); List results = repositorySystem.resolveArtifacts( signingSession, artifacts.stream() .map(a -> new ArtifactRequest(a, Collections.singletonList(deploymentRepository), "gpg")) .collect(Collectors.toList())); artifacts = results.stream().map(ArtifactResult::getArtifact).collect(Collectors.toSet()); // sign all AbstractGpgSigner signer = newSigner(null); signer.setOutputDirectory(tempDirectory.toFile()); getLog().info("Signer '" + signer.signerName() + "' is signing " + artifacts.size() + " file" + ((artifacts.size() > 1) ? "s" : "") + " with key " + signer.getKeyInfo()); HashSet signatures = new HashSet<>(); for (Artifact a : artifacts) { signatures.add(new DefaultArtifact( a.getGroupId(), a.getArtifactId(), a.getClassifier(), a.getExtension() + AbstractGpgSigner.SIGNATURE_EXTENSION, a.getVersion()) .setFile(signer.generateSignatureForArtifact(a.getFile()))); } // deploy all signature getLog().info("Deploying artifact signatures..."); repositorySystem.deploy( signingSession, new DeployRequest() .setRepository(deploymentRepository) .setArtifacts(signatures) .setTrace(RequestTrace.newChild(null, this))); } catch (IOException e) { throw new MojoExecutionException("IO error: " + e.getMessage(), e); } catch (ArtifactResolutionException e) { throw new MojoExecutionException( "Error resolving deployed artifacts " + artifacts + ": " + e.getMessage(), e); } catch (DeploymentException e) { throw new MojoExecutionException("Error deploying signatures: " + e.getMessage(), e); } finally { if (tempDirectory != null) { getLog().info("Cleaning up..."); try { FileUtils.deleteDirectory(tempDirectory.toFile()); } catch (IOException e) { getLog().warn("Could not clean up temp directory " + tempDirectory); } } } } /** * Returns a collection of remotely deployed artifacts that needs to be signed and have signatures deployed * next to them. */ protected Collection collectArtifacts(RepositorySystemSession session, RemoteRepository remoteRepository) throws IOException { Collection result = null; for (ArtifactCollectorSPI artifactCollector : artifactCollectors.values()) { result = artifactCollector.collectArtifacts(session, remoteRepository); if (result != null) { break; } } if (result == null) { if (artifacts != null) { try { Path path = Paths.get(artifacts); if (Files.isRegularFile(path)) { try (Stream lines = Files.lines(path)) { result = lines.filter(l -> !l.isEmpty() && !l.startsWith("#")) .map(DefaultArtifact::new) .collect(Collectors.toSet()); } } } catch (InvalidPathException e) { // ignore } if (result == null) { result = Arrays.stream(artifacts.split(",")) .map(DefaultArtifact::new) .collect(Collectors.toSet()); } } } if (result == null) { throw new IllegalStateException("No source to collect from (set -Dartifacts=g:a:v... or add collector)"); } return result; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy