Package org.apache.maven.plugins.scmpublish

Source Code of org.apache.maven.plugins.scmpublish.AbstractScmPublishMojo

package org.apache.maven.plugins.scmpublish;

/*
* 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.
*/

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.time.DurationFormatUtils;
import org.apache.maven.plugin.AbstractMojo;
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.Parameter;
import org.apache.maven.scm.CommandParameter;
import org.apache.maven.scm.CommandParameters;
import org.apache.maven.scm.ScmBranch;
import org.apache.maven.scm.ScmException;
import org.apache.maven.scm.ScmFileSet;
import org.apache.maven.scm.ScmResult;
import org.apache.maven.scm.command.add.AddScmResult;
import org.apache.maven.scm.command.checkin.CheckInScmResult;
import org.apache.maven.scm.manager.NoSuchScmProviderException;
import org.apache.maven.scm.manager.ScmManager;
import org.apache.maven.scm.provider.ScmProvider;
import org.apache.maven.scm.provider.ScmUrlUtils;
import org.apache.maven.scm.provider.svn.AbstractSvnScmProvider;
import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
import org.apache.maven.scm.repository.ScmRepository;
import org.apache.maven.scm.repository.ScmRepositoryException;
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.SettingsDecryptionRequest;
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
import org.apache.maven.shared.release.config.ReleaseDescriptor;
import org.apache.maven.shared.release.scm.ScmRepositoryConfigurator;

/**
* Base class for the scm-publish mojos.
*/
public abstract class AbstractScmPublishMojo
    extends AbstractMojo
{
    /**
     * Location of the scm publication tree:
     * <code>scm:&lt;scm_provider&gt;&lt;delimiter&gt;&lt;provider_specific_part&gt;</code>.
     * Example:
     * <code>scm:svn:https://svn.apache.org/repos/infra/websites/production/maven/content/plugins/maven-scm-publish-plugin-LATEST/</code>
     */
    @Parameter ( property = "scmpublish.pubScmUrl", defaultValue = "${project.distributionManagement.site.url}",
                 required = true )
    protected String pubScmUrl;

    /**
     * If the checkout directory exists and this flag is activated, the plugin will try an SCM-update instead
     * of delete then checkout.
     */
    @Parameter ( property = "scmpublish.tryUpdate", defaultValue = "false" )
    protected boolean tryUpdate;

   /**
     * Location where the scm check-out is done. By default, scm checkout is done in build (target) directory,
     * which is deleted on every <code>mvn clean</code>. To avoid this and get better performance, configure
     * this location outside build structure and set <code>tryUpdate</code> to <code>true</code>.
     * See <a href="http://maven.apache.org/plugins/maven-scm-publish-plugin/various-tips.html#Improving_SCM_Checkout_Performance">
     * Improving SCM Checkout Performance</a> for more information.
     */
    @Parameter ( property = "scmpublish.checkoutDirectory",
                 defaultValue = "${project.build.directory}/scmpublish-checkout" )
    protected File checkoutDirectory;

    /**
     * Display list of added, deleted, and changed files, but do not do any actual SCM operations.
     */
    @Parameter ( property = "scmpublish.dryRun" )
    private boolean dryRun;

    /**
     * Run add and delete commands, but leave the actually checkin for the user to run manually.
     */
    @Parameter ( property = "scmpublish.skipCheckin" )
    private boolean skipCheckin;

    /**
     * SCM log/checkin comment for this publication.
     */
    @Parameter ( property = "scmpublish.checkinComment", defaultValue = "Site checkin for project ${project.name}" )
    private String checkinComment;

    /**
     * Patterns to exclude from the scm tree.
     */
    @Parameter
    protected String excludes;

    /**
     * Patterns to include in the scm tree.
     */
    @Parameter
    protected String includes;

    /**
     * List of SCM provider implementations.
     * Key is the provider type, eg. <code>cvs</code>.
     * Value is the provider implementation (the role-hint of the provider), eg. <code>cvs</code> or <code>cvs_native</code>.
     * @see ScmManager.setScmProviderImplementation
     */
    @Parameter
    private Map<String, String> providerImplementations;

    /**
     * The SCM manager.
     */
    @Component
    private ScmManager scmManager;

    /**
     * Tool that gets a configured SCM repository from release configuration.
     */
    @Component
    protected ScmRepositoryConfigurator scmRepositoryConfigurator;
   
    /**
     * The serverId specified in the settings.xml, which should be used for the authentication.
     */
    @Parameter
    private String serverId;

    /**
     * The SCM username to use.
     */
    @Parameter ( property = "username" )
    protected String username;

    /**
     * The SCM password to use.
     */
    @Parameter ( property = "password" )
    protected String password;

    /**
     * Use a local checkout instead of doing a checkout from the upstream repository. <b>WARNING</b>: This will only work
     * with distributed SCMs which support the file:// protocol
     * TODO: we should think about having the defaults for the various SCM providers provided via Modello!
     */
    @Parameter ( property = "localCheckout", defaultValue = "false" )
    protected boolean localCheckout;

    /**
     * The outputEncoding parameter of the site plugin. This plugin will corrupt your site
     * if this does not match the value used by the site plugin.
     */
    @Parameter ( property = "outputEncoding", defaultValue = "${project.reporting.outputEncoding}" )
    protected String siteOutputEncoding;

    /**
     * Do not delete files to the scm
     */
    @Parameter ( property = "scmpublish.skipDeletedFiles", defaultValue = "false" )
    protected boolean skipDeletedFiles;

    /**
     * Add each directory in a separated SCM command: this can be necessary if SCM does not support
     * adding subdirectories in one command.
     */
    @Parameter( defaultValue = "false" )
    protected boolean addUniqueDirectory;

    /**
     */
    @Parameter ( defaultValue = "${basedir}", readonly = true )
    protected File basedir;

    /**
     */
    @Component
    protected Settings settings;
   
    @Component
    private SettingsDecrypter settingsDecrypter;

    /**
     * Collections of paths not to delete when checking content to delete.
     * If your site has subdirectories published by an other mechanism/build
     */
    @Parameter
    protected String[] ignorePathsToDelete;

    /**
     * SCM branch to use. For github, you must configure with <code>gh-pages</code>.
     */
    @Parameter ( property = "scmpublish.scm.branch" )
    protected String scmBranch;

    /**
     * Configure svn automatic remote url creation.
     */
    @Parameter ( property = "scmpublish.automaticRemotePathCreation", defaultValue = "true" )
    protected boolean automaticRemotePathCreation;

    /**
     * Filename extensions of files which need new line normalization.
     */
    private final static String[] NORMALIZE_EXTENSIONS = { "html", "css", "js" };

    /**
     * Extra file extensions to normalize line ending (will be added to default
     * <code>html</code>,<code>css</code>,<code>js</code> list)
     */
    @Parameter
    protected String[] extraNormalizeExtensions;

    private Set<String> normalizeExtensions;

    protected ScmProvider scmProvider;

    protected ScmRepository scmRepository;

    protected void logInfo( String format, Object... params )
    {
        getLog().info( String.format( format, params ) );
    }

    protected void logWarn( String format, Object... params )
    {
        getLog().warn( String.format( format, params ) );
    }

    protected void logError( String format, Object... params )
    {
        getLog().error( String.format( format, params ) );
    }

    private File relativize( File base, File file )
    {
        return new File( base.toURI().relativize( file.toURI() ).getPath() );
    }

    protected boolean requireNormalizeNewlines( File f )
        throws IOException
    {
        if ( normalizeExtensions == null )
        {
            normalizeExtensions = new HashSet<String>( Arrays.asList( NORMALIZE_EXTENSIONS ) );
            if ( extraNormalizeExtensions != null )
            {
                normalizeExtensions.addAll( Arrays.asList( extraNormalizeExtensions ) );
            }
        }

        return FilenameUtils.isExtension( f.getName(), normalizeExtensions );
    }

    private ReleaseDescriptor setupScm()
        throws ScmRepositoryException, NoSuchScmProviderException
    {
        String scmUrl;
        if ( localCheckout )
        {
            // in the release phase we have to change the checkout URL
            // to do a local checkout instead of going over the network.

            String provider = ScmUrlUtils.getProvider( pubScmUrl );
            String delimiter = ScmUrlUtils.getDelimiter( pubScmUrl );
           
            String providerPart = "scm:" + provider + delimiter;

            // X TODO: also check the information from releaseDescriptor.getScmRelativePathProjectDirectory()
            // X TODO: in case our toplevel git directory has no pom.
            // X TODO: fix pathname once I understand this.
            scmUrl = providerPart + "file://" + "target/localCheckout";
            logInfo( "Performing a LOCAL checkout from " + scmUrl );
        }

        ReleaseDescriptor releaseDescriptor = new ReleaseDescriptor();
        releaseDescriptor.setInteractive( settings.isInteractiveMode() );

        if ( username == null || password == null )
        {
            for ( Server server : settings.getServers() )
            {
                if ( server.getId().equals( serverId ) )
                {
                    SettingsDecryptionRequest decryptionRequest = new DefaultSettingsDecryptionRequest( server );

                    SettingsDecryptionResult decryptionResult = settingsDecrypter.decrypt( decryptionRequest );

                    if ( !decryptionResult.getProblems().isEmpty() )
                    {
                        // todo throw exception?
                    }

                    if ( username == null )
                    {
                        username = decryptionResult.getServer().getUsername();
                    }

                    if ( password == null )
                    {
                        password = decryptionResult.getServer().getPassword();
                    }

                    break;
                }
            }
        }

        releaseDescriptor.setScmPassword( password );
        releaseDescriptor.setScmUsername( username );

        releaseDescriptor.setWorkingDirectory( basedir.getAbsolutePath() );
        releaseDescriptor.setLocalCheckout( localCheckout );
        releaseDescriptor.setScmSourceUrl( pubScmUrl );

        if ( providerImplementations != null )
        {
            for ( Map.Entry<String, String> providerEntry : providerImplementations.entrySet() )
            {
                logInfo( "Changing the default '%s' provider implementation to '%s'.", providerEntry.getKey(),
                         providerEntry.getValue() );
                scmManager.setScmProviderImplementation( providerEntry.getKey(), providerEntry.getValue() );
            }
        }

        scmRepository = scmRepositoryConfigurator.getConfiguredRepository( releaseDescriptor, settings );

        scmProvider = scmRepositoryConfigurator.getRepositoryProvider( scmRepository );

        return releaseDescriptor;
    }

    protected void checkoutExisting()
        throws MojoExecutionException
    {

        if ( scmProvider instanceof AbstractSvnScmProvider )
        {
            checkCreateRemoteSvnPath();
        }

        logInfo( "%s the pub tree from %s into %s", ( tryUpdate ? "Updating" : "Checking out" ), pubScmUrl, checkoutDirectory );

        if ( checkoutDirectory.exists() && !tryUpdate )

        {
            try
            {
                FileUtils.deleteDirectory( checkoutDirectory );
            }
            catch ( IOException e )
            {
                logError( e.getMessage() );

                throw new MojoExecutionException( "Unable to remove old checkout directory: " + e.getMessage(), e );
            }
        }

        boolean forceCheckout = false;

        if ( !checkoutDirectory.exists() )

        {
            if ( tryUpdate )
            {
                logInfo( "TryUpdate is configured but no local copy currently available: forcing checkout." );
            }
            checkoutDirectory.mkdirs();
            forceCheckout = true;
        }

        try
        {
            ScmFileSet fileSet = new ScmFileSet( checkoutDirectory, includes, excludes );

            ScmBranch branch = ( scmBranch == null ) ? null : new ScmBranch( scmBranch );

            ScmResult scmResult = null;
            if ( tryUpdate && !forceCheckout )
            {
                scmResult = scmProvider.update( scmRepository, fileSet, branch );
            }
            else
            {
                int attempt = 0;
                while ( scmResult == null )
                {
                    try
                    {
                        scmResult = scmProvider.checkOut( scmRepository, fileSet, branch );
                    }
                    catch ( ScmException e )
                    {
                        // give it max 2 times to retry
                        if ( attempt++ < 2 )
                        {
                            try
                            {
                                // wait 3 seconds
                                Thread.sleep( 3 * 1000 );
                            }
                            catch ( InterruptedException ie )
                            {
                                // noop
                            }
                        }
                        else
                        {
                            throw e;
                        }
                    }
                }
            }
            checkScmResult( scmResult, "check out from SCM" );
        }
        catch ( ScmException e )
        {
            logError( e.getMessage() );

            throw new MojoExecutionException( "An error occurred during the checkout process: " + e.getMessage(), e );
        }
        catch ( IOException e )
        {
            logError( e.getMessage() );

            throw new MojoExecutionException( "An error occurred during the checkout process: " + e.getMessage(), e );
        }
    }

    private void checkCreateRemoteSvnPath()
        throws MojoExecutionException
    {
        getLog().debug( "AbstractSvnScmProvider used, so we can check if remote url exists and eventually create it." );
        AbstractSvnScmProvider svnScmProvider = (AbstractSvnScmProvider) scmProvider;

        try
        {
            boolean remoteExists = svnScmProvider.remoteUrlExist( scmRepository.getProviderRepository(), null );

            if ( remoteExists )
            {
                return;
            }
        }
        catch ( ScmException e )
        {
            throw new MojoExecutionException( e.getMessage(), e );
        }

        String remoteUrl = ( (SvnScmProviderRepository) scmRepository.getProviderRepository() ).getUrl();

        if ( !automaticRemotePathCreation )
        {
            // olamy: return ?? that will fail during checkout IMHO :-)
            logWarn( "Remote svn url %s does not exist and automatic remote path creation disabled.",
                     remoteUrl );
            return;
        }

        logInfo( "Remote svn url %s does not exist: creating.", remoteUrl );

        File baseDir = null;
        try
        {

            // create a temporary directory for svnexec
            baseDir = File.createTempFile( "scm", "tmp" );
            baseDir.delete();
            baseDir.mkdirs();
            // to prevent fileSet cannot be empty
            ScmFileSet scmFileSet = new ScmFileSet( baseDir, new File( "" ) );

            CommandParameters commandParameters = new CommandParameters();
            commandParameters.setString( CommandParameter.SCM_MKDIR_CREATE_IN_LOCAL, Boolean.FALSE.toString() );
            commandParameters.setString( CommandParameter.MESSAGE, "Automatic svn path creation: " + remoteUrl );
            svnScmProvider.mkdir( scmRepository.getProviderRepository(), scmFileSet, commandParameters );

            // new remote url so force checkout!
            if ( checkoutDirectory.exists() )
            {
                FileUtils.deleteDirectory( checkoutDirectory );
            }
        }
        catch ( IOException e )
        {
            throw new MojoExecutionException( e.getMessage(), e );
        }
        catch ( ScmException e )
        {
            throw new MojoExecutionException( e.getMessage(), e );
        }
        finally
        {
            if ( baseDir != null )
            {
                try
                {
                    FileUtils.forceDeleteOnExit( baseDir );
                }
                catch ( IOException e )
                {
                    throw new MojoExecutionException( e.getMessage(), e );
                }
            }
        }
    }

    public void execute()
        throws MojoExecutionException, MojoFailureException
    {
        // setup the scm plugin with help from release plugin utilities
        try
        {
            setupScm();
        }
        catch ( ScmRepositoryException e )
        {
            throw new MojoExecutionException( e.getMessage(), e );
        }
        catch ( NoSuchScmProviderException e )
        {
            throw new MojoExecutionException( e.getMessage(), e );
        }

        boolean tmpCheckout = false;

        if ( checkoutDirectory.getPath().contains( "${project." ) )
        {
            try
            {
                tmpCheckout = true;
                checkoutDirectory = File.createTempFile( "maven-scm-publish", ".checkout" );
                checkoutDirectory.delete();
                checkoutDirectory.mkdir();
            }
            catch ( IOException ioe )
            {
                throw new MojoExecutionException( ioe.getMessage(), ioe );
            }
        }

        try
        {
            scmPublishExecute();
        }
        finally
        {
            if ( tmpCheckout )
            {
                FileUtils.deleteQuietly( checkoutDirectory );
            }
        }
    }

    /**
     * Check-in content from scm checkout.
     *
     * @throws MojoExecutionException
     */
    protected void checkinFiles()
        throws MojoExecutionException
    {
        if ( skipCheckin )
        {
            return;
        }

        ScmFileSet updatedFileSet = new ScmFileSet( checkoutDirectory );
        try
        {
            long start = System.currentTimeMillis();

            CheckInScmResult checkinResult =
                checkScmResult( scmProvider.checkIn( scmRepository, updatedFileSet, new ScmBranch( scmBranch ),
                                                     checkinComment ), "check-in files to SCM" );

            logInfo( "Checked in %d file(s) to revision %s in %s", checkinResult.getCheckedInFiles().size(),
                     checkinResult.getScmRevision(),
                     DurationFormatUtils.formatPeriod( start, System.currentTimeMillis(), "H' h 'm' m 's' s'" ) );
        }
        catch ( ScmException e )
        {
            throw new MojoExecutionException( "Failed to perform SCM checkin", e );
        }
    }

    protected void deleteFiles( Collection<File> deleted )
        throws MojoExecutionException
    {
        if ( skipDeletedFiles )
        {
            logInfo( "Deleting files is skipped." );
            return;
        }
        List<File> deletedList = new ArrayList<File>();
        for ( File f : deleted )
        {
            deletedList.add( relativize( checkoutDirectory, f ) );
        }
        ScmFileSet deletedFileSet = new ScmFileSet( checkoutDirectory, deletedList );
        try
        {
            getLog().info( "Deleting files: " + deletedList );

            checkScmResult( scmProvider.remove( scmRepository, deletedFileSet, "Deleting obsolete site files." ),
                            "delete files from SCM" );
        }
        catch ( ScmException e )
        {
            throw new MojoExecutionException( "Failed to delete removed files to SCM", e );
        }
    }

    /**
     * Add files to scm.
     *
     * @param added files to be added
     * @throws MojoFailureException
     * @throws MojoExecutionException
     */
    protected void addFiles( Collection<File> added )
        throws MojoFailureException, MojoExecutionException
    {
        List<File> addedList = new ArrayList<File>();
        Set<File> createdDirs = new HashSet<File>();
        Set<File> dirsToAdd = new TreeSet<File>();

        createdDirs.add( relativize( checkoutDirectory, checkoutDirectory ) );

        for ( File f : added )
        {
            for ( File dir = f.getParentFile(); !dir.equals( checkoutDirectory ); dir = dir.getParentFile() )
            {
                File relativized = relativize( checkoutDirectory, dir );
                //  we do the best we can with the directories
                if ( createdDirs.add( relativized ) )
                {
                    dirsToAdd.add( relativized );
                }
                else
                {
                    break;
                }
            }
            addedList.add( relativize( checkoutDirectory, f ) );
        }

        if ( addUniqueDirectory )
        { // add one directory at a time
            for ( File relativized : dirsToAdd )
            {
                try
                {
                    ScmFileSet fileSet = new ScmFileSet( checkoutDirectory, relativized );
                    getLog().info( "scm add directory: " + relativized );
                    AddScmResult addDirResult = scmProvider.add( scmRepository, fileSet, "Adding directory" );
                    if ( !addDirResult.isSuccess() )
                    {
                        getLog().warn( " Error adding directory " + relativized + ": " + addDirResult.getCommandOutput() );
                    }
                }
                catch ( ScmException e )
                {
                    //
                }
            }
        }
        else
        { // add all directories in one command
            try
            {
                List<File> dirs = new ArrayList<File>( dirsToAdd );
                ScmFileSet fileSet = new ScmFileSet( checkoutDirectory, dirs );
                getLog().info( "scm add directories: " + dirs );
                AddScmResult addDirResult = scmProvider.add( scmRepository, fileSet, "Adding directories" );
                if ( !addDirResult.isSuccess() )
                {
                    getLog().warn( " Error adding directories " + dirs + ": " + addDirResult.getCommandOutput() );
                }
            }
            catch ( ScmException e )
            {
                //
            }
        }

        // remove directories already added !
        addedList.removeAll( dirsToAdd );

        ScmFileSet addedFileSet = new ScmFileSet( checkoutDirectory, addedList );
        getLog().info( "scm add files: " + addedList );
        try
        {
                CommandParameters commandParameters = new CommandParameters();
                commandParameters.setString( CommandParameter.MESSAGE, "Adding new site files." );
                commandParameters.setString( CommandParameter.FORCE_ADD, Boolean.TRUE.toString() );
                checkScmResult( scmProvider.add( scmRepository, addedFileSet, commandParameters ),
                                "add new files to SCM" );
        }
        catch ( ScmException e )
        {
            throw new MojoExecutionException( "Failed to add new files to SCM", e );
        }
    }

    private<T extends ScmResult> T checkScmResult( T result, String failure )
        throws MojoExecutionException
    {
        if ( !result.isSuccess() )
        {
            String msg = "Failed to " + failure + ": " + result.getProviderMessage() + " " + result.getCommandOutput();
            logError( msg );
            throw new MojoExecutionException( msg );
        }
        return result;
    }

    public boolean isDryRun()
    {
        return dryRun;
    }

    public abstract void scmPublishExecute()
        throws MojoExecutionException, MojoFailureException;

    public void setPubScmUrl( String pubScmUrl )
    {
        // Fix required for Windows, which fit other OS as well
        if ( pubScmUrl.startsWith( "scm:svn:" ) )
        {
            pubScmUrl = pubScmUrl.replaceFirst( "file:/[/]*", "file:///" );
        }

        this.pubScmUrl = pubScmUrl;
    }

}
TOP

Related Classes of org.apache.maven.plugins.scmpublish.AbstractScmPublishMojo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.