de.jutzig.github.release.plugin.UploadMojo.java Source code

Java tutorial

Introduction

Here is the source code for de.jutzig.github.release.plugin.UploadMojo.java

Source

package de.jutzig.github.release.plugin;

/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed 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.
 */

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.FileSet;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.settings.Server;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
import org.apache.maven.settings.crypto.SettingsDecrypter;
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
import org.codehaus.plexus.util.FileUtils;
import org.kohsuke.github.GHAsset;
import org.kohsuke.github.GHRelease;
import org.kohsuke.github.GHReleaseBuilder;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GitHub;

/**
 * Goal which attaches a file to a GitHub release
 * 
 * @goal release
 * 
 * @phase deploy
 */
public class UploadMojo extends AbstractMojo implements Contextualizable {

    /**
     * Server id for github access.
     * 
     * @parameter default-value="github" expression="github"
     */
    private String serverId;

    /**
     * The tag name this release is based on.
     * 
     * @parameter expression="${project.version}"
     */
    private String tag;

    /**
     * The name of the release
     * 
     * @parameter expression="${release.name}"
     */
    private String releaseName;

    /**
     * The release description
     * 
     * @parameter expression="${project.description}"
     */
    private String description;

    /**
     * The commitish to use
     *
     * @parameter expression="github.commitish"
     */
    private String commitish;

    /**
     * Whether or not the release should be draft
     *
     * @parameter expression="github.draft"
     */
    private Boolean draft;

    /**
     * The github id of the project. By default initialized from the project scm connection
     * 
     * @parameter default-value="${project.scm.connection}" expression="${release.repositoryId}"
     * @required
     */
    private String repositoryId;

    /**
    * The Maven settings
    *
    * @parameter expression="${settings}
    */
    private Settings settings;

    /**
     * The Maven session
     *
     * @parameter expression="${session}"
     */
    private MavenSession session;

    /**
     * The file to upload to the release. Default is ${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging} (the main artifact)
     *
     * @parameter default-value="${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging}" expression="${release.artifact}"
     */
    private String artifact;

    /**
     * A specific <code>fileSet</code> rule to select files and directories for upload to the release.
     *
     * @parameter
     */
    private FileSet fileSet;

    /**
     * A list of <code>fileSet</code> rules to select files and directories for upload to the release.
     *
     * @parameter
     */
    private List<FileSet> fileSets;

    /**
     * Flag to indicate to overwrite the asset in the release if it already exists. Default is false
     *
     * @parameter default-value=false
     */
    private Boolean overwriteArtifact;

    @Requirement
    private PlexusContainer container;

    /**
     * If this is a prerelease. Will be set by default according to ${project.version} (see {@link #guessPreRelease(String)}.
     * 
     * @parameter
     */
    private Boolean prerelease;

    public void execute() throws MojoExecutionException {
        if (releaseName == null)
            releaseName = tag;
        if (prerelease == null)
            prerelease = guessPreRelease(tag);
        repositoryId = computeRepositoryId(repositoryId);
        GHRelease release = null;
        try {
            GitHub gitHub = createGithub(serverId);
            GHRepository repository = gitHub.getRepository(repositoryId);
            release = findRelease(repository, releaseName);
            if (release == null) {
                getLog().info("Creating release " + releaseName);
                GHReleaseBuilder builder = repository.createRelease(tag);
                if (description != null)
                    builder.body(description);
                if (commitish != null)
                    builder.commitish(commitish);
                if (draft != null)
                    builder.draft(draft);
                builder.prerelease(prerelease);
                builder.name(releaseName);
                release = builder.create();
            } else {
                getLog().info("Release " + releaseName + " already exists. Not creating");
            }
        } catch (IOException e) {
            getLog().error(e);
            throw new MojoExecutionException("Failed to create release", e);
        }
        try {
            if (artifact != null && !artifact.trim().isEmpty()) {
                File asset = new File(artifact);
                if (asset.exists())
                    uploadAsset(release, asset);
            }

            if (fileSet != null)
                uploadAssets(release, fileSet);

            if (fileSets != null)
                for (FileSet set : fileSets)
                    uploadAssets(release, set);

        } catch (IOException e) {

            getLog().error(e);
            throw new MojoExecutionException("Failed to upload assets", e);
        }

    }

    private void uploadAsset(GHRelease release, File asset) throws IOException {
        getLog().info("Processing asset " + asset.getPath());
        URL url = new URL(MessageFormat.format("https://uploads.github.com/repos/{0}/releases/{1}/assets?name={2}",
                repositoryId, Long.toString(release.getId()), asset.getName()));

        List<GHAsset> existingAssets = release.getAssets();
        for (GHAsset a : existingAssets) {
            if (a.getName().equals(asset.getName())) {
                if (overwriteArtifact) {
                    getLog().info("  Deleting existing asset");
                    a.delete();
                } else {
                    getLog().warn("Asset " + asset.getName() + " already exists. Skipping");
                    return;
                }
            }
        }

        getLog().info("  Upload asset");
        // for some reason this doesn't work currently
        release.uploadAsset(asset, "application/zip");
    }

    private void uploadAssets(GHRelease release, FileSet fileset) throws IOException {
        List<File> assets = FileUtils.getFiles(new File(fileset.getDirectory()),
                StringUtils.join(fileset.getIncludes(), ','), StringUtils.join(fileset.getExcludes(), ','));
        for (File asset : assets)
            uploadAsset(release, asset);
    }

    private GHRelease findRelease(GHRepository repository, String releaseName2) throws IOException {
        List<GHRelease> releases = repository.getReleases();
        for (GHRelease ghRelease : releases) {
            if (ghRelease.getName().equals(releaseName2)) {
                return ghRelease;
            }
        }
        return null;
    }

    /**
     * @see <a href="https://maven.apache.org/scm/scm-url-format.html">SCM URL Format</a>
     */
    private static final Pattern REPOSITORY_PATTERN = Pattern.compile("^(scm:git[:|])?" + //Maven prefix for git SCM
            "(https?://github\\.com/|git@github\\.com:)" + //GitHub prefix for HTTP/HTTPS/SSH/Subversion scheme
            "([^/]+/[^/]*?)" + //Repository ID
            "(\\.git)?$" //Optional suffix ".git"
            , Pattern.CASE_INSENSITIVE);

    public static String computeRepositoryId(String id) {
        Matcher matcher = REPOSITORY_PATTERN.matcher(id);
        if (matcher.matches()) {
            return matcher.group(3);
        } else {
            return id;
        }
    }

    public GitHub createGithub(String serverId) throws MojoExecutionException, IOException {
        String usernameProperty = System.getProperty("username");
        String passwordProperty = System.getProperty("password");
        if (usernameProperty != null && passwordProperty != null) {
            getLog().debug("Using server credentials from system properties 'username' and 'password'");
            return GitHub.connectUsingPassword(usernameProperty, passwordProperty);
        }

        Server server = getServer(settings, serverId);
        if (server == null)
            throw new MojoExecutionException(
                    MessageFormat.format("Server ''{0}'' not found in settings", serverId));

        getLog().debug(MessageFormat.format("Using ''{0}'' server credentials", serverId));

        try {
            SettingsDecrypter settingsDecrypter = container.lookup(SettingsDecrypter.class);
            SettingsDecryptionResult result = settingsDecrypter
                    .decrypt(new DefaultSettingsDecryptionRequest(server));
            server = result.getServer();
        } catch (ComponentLookupException cle) {
            throw new MojoExecutionException("Unable to lookup SettingsDecrypter: " + cle.getMessage(), cle);
        }

        String serverUsername = server.getUsername();
        String serverPassword = server.getPassword();
        String serverAccessToken = server.getPrivateKey();
        if (StringUtils.isNotEmpty(serverUsername) && StringUtils.isNotEmpty(serverPassword))
            return GitHub.connectUsingPassword(serverUsername, serverPassword);
        else if (StringUtils.isNotEmpty(serverAccessToken))
            return GitHub.connectUsingOAuth(serverAccessToken);
        else
            throw new MojoExecutionException("Configuration for server " + serverId + " has no login credentials");
    }

    /**
     * Get server with given id
     * 
     * @param settings
     * @param serverId
     *            must be non-null and non-empty
     * @return server or null if none matching
     */
    protected Server getServer(final Settings settings, final String serverId) {
        if (settings == null)
            return null;
        List<Server> servers = settings.getServers();
        if (servers == null || servers.isEmpty())
            return null;

        for (Server server : servers)
            if (serverId.equals(server.getId()))
                return server;
        return null;
    }

    public void contextualize(Context context) throws ContextException {
        container = (PlexusContainer) context.get(PlexusConstants.PLEXUS_KEY);
    }

    /**
     * Guess if a version defined in POM should be considered as {@link #prerelease}.
     */
    static boolean guessPreRelease(String version) {
        boolean preRelease = version.endsWith("-SNAPSHOT") || StringUtils.containsIgnoreCase(version, "-alpha")
                || StringUtils.containsIgnoreCase(version, "-beta")
                || StringUtils.containsIgnoreCase(version, "-RC") || StringUtils.containsIgnoreCase(version, ".RC")
                || StringUtils.containsIgnoreCase(version, ".M")
                || StringUtils.containsIgnoreCase(version, ".BUILD_SNAPSHOT");
        return preRelease;
    }
}