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

org.kuali.maven.plugins.jenkins.SyncWorkspaceMojo Maven / Gradle / Ivy

/**
 * Copyright 2011-2012 The Kuali Foundation
 *
 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
 *
 * 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.kuali.maven.plugins.jenkins;

import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;
import org.codehaus.plexus.util.cli.DefaultConsumer;
import org.codehaus.plexus.util.cli.StreamConsumer;
import org.kuali.maven.plugins.jenkins.helper.DirectoryFileFilter;
import org.kuali.maven.plugins.jenkins.helper.RsyncHelper;

/**
 * Synchronize a Jenkins workspace to another location using rsync. To use this mojo, the
 * rsync utility must be installed and in your path. If the mojo completes successfully the
 * destination directory will contain the exact same set of files as the source directory. For
 * the purposes of this mojo, "exactly the same" means, all the files on both sides have the same name, the same size,
 * and the same last modified date.
 *
 * @goal syncworkspace
 * @aggregator
 */
public class SyncWorkspaceMojo extends AbstractMojo {
    RsyncHelper helper = new RsyncHelper();

    /**
     * The Maven project object
     *
     * @parameter expression="${project}"
     * @readonly
     */
    private MavenProject project;

    /**
     * If true, the excludeTargetPattern will be used to exclude Maven build directories from the workspace
     * sync
     *
     * @parameter expression="${jenkins.excludeTarget}" default-value="true"
     */
    private boolean excludeTarget;

    /**
     * The pattern to use when matching Maven build directories. Any directories with this name get excluded by
     * rsync eg /foo/target will be excluded but /footarget will not
     *
     * @parameter expression="${jenkins.excludeTargetPattern}" default-value="target"
     */
    private String excludeTargetPattern;

    /**
     * If true, the Maven build will fail if rsync returns a non-zero exit value
     *
     * @parameter expression="${jenkins.failOnError}" default-value="true"
     */
    private boolean failOnError;

    /**
     * If true, rsync will compress files before transferring them. Equivalent to the -z
     * command line switch.
     *
     * @parameter expression="${jenkins.compress}" default-value="true"
     */
    private boolean compress;

    /**
     * If true, rsync will display statistics about the sync process. Equivalent to the
     * --stats command line switch.
     *
     * @parameter expression="${jenkins.stats}" default-value="true"
     */
    private boolean stats;

    /**
     * Comma separated list of integers that the plugin will silently ignore if they are returned as the exit value for
     * rsync
     *
     * @parameter expression="${jenkins.ignoreCodes}"
     */
    private String ignoreCodes;

    /**
     * If true, rsync emits verbose logging. Equivalent to the -vv command line switch
     *
     * @parameter expression="${jenkins.verbose}" default-value="false"
     */
    private boolean verbose;

    /**
     * The file where the list of directories to exclude is aggregated. The full path to this file is passed to
     * rsync with --exclude-from
     *
     * @parameter expression="${jenkins.excludesFile}" default-value="${project.build.directory}/jenkins/rsync-excludes"
     */
    private File excludesFile;

    /**
     * The base directory to scan for Maven build directories
     *
     * @parameter expression="${jenkins.basedir}" default-value="${project.basedir}"
     */
    private File basedir;

    /**
     * The source directory rsync pulls files from. For rsync, the trailing slash is
     * significant. A trailing slash on the source directory instructs rsync to place files
     * directly into the destination directory instead of creating a sub-directory under the
     * destination directory.
     *
     * @parameter expression="${jenkins.source}" default-value="${project.basedir}/"
     * @required
     */
    private String source;

    /**
     * The destination directory rsync pushes files to
     *
     * @parameter expression="${jenkins.destination}"
     * @required
     */
    private String destination;

    /**
     * The rsync executable
     *
     * @parameter expression="${jenkins.executable}" default-value="rsync"
     * @required
     */
    private String executable;

    @Override
    public void execute() throws MojoExecutionException {
        getLog().info("Src - " + source);
        getLog().info("Dst - " + destination);
        getLog().info("Exclude - " + excludeTarget);
        if (excludeTarget) {
            getLog().info("Base Dir - " + basedir.getAbsolutePath());
            getLog().info("Exclude Pattern - " + excludeTargetPattern);
            getLog().info("Excludes File - " + excludesFile);
        }
        prepareFileSystem();
        long start = System.currentTimeMillis();
        int exitValue = executeRsync();
        long elapsed = System.currentTimeMillis() - start;
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(3);
        nf.setMinimumFractionDigits(3);
        getLog().info("Sync time: " + nf.format(elapsed / 1000D) + "s");
        validateExitValue(exitValue);
    }

    protected List getArgs() {
        List args = new ArrayList();
        args.add("-a");
        if (compress) {
            args.add("-z");
        }
        if (verbose) {
            args.add("-vv");
        }
        if (stats) {
            args.add("--stats");
        }
        args.add("--delete");
        if (excludeTarget) {
            args.add("--delete-excluded");
            args.add("--exclude-from");
            args.add(excludesFile.getAbsolutePath());
        }
        args.add(source);
        args.add(destination);
        return args;
    }

    protected Commandline getCommandLine() {
        Commandline cl = new Commandline();
        cl.setExecutable(executable);
        cl.setWorkingDirectory(basedir);
        addArgs(cl, getArgs());
        return cl;
    }

    protected void addArgs(Commandline cl, List args) {
        if (args == null || args.size() == 0) {
            return;
        }
        for (String arg : args) {
            cl.createArg().setValue(arg);
        }
    }

    protected int executeRsync() throws MojoExecutionException {
        StreamConsumer stdout = new DefaultConsumer();
        StreamConsumer stderr = new DefaultConsumer();
        Commandline cl = getCommandLine();
        getLog().info(cl.toString());
        try {
            return CommandLineUtils.executeCommandLine(cl, stdout, stderr);
        } catch (CommandLineException e) {
            throw new MojoExecutionException("Error executing " + executable, e);
        }
    }

    protected List getAllowedCodes() {
        List codes = new ArrayList();
        codes.add(0);
        if (!StringUtils.isBlank(ignoreCodes)) {
            String[] tokens = StringUtils.split(ignoreCodes, ",");
            for (String token : tokens) {
                codes.add(new Integer(token.trim()));
            }
        }
        return codes;
    }

    protected boolean isFail(int exitValue) {
        List codes = getAllowedCodes();
        for (Integer code : codes) {
            if (exitValue == code.intValue()) {
                return false;
            }
        }
        return failOnError;
    }

    protected void validateExitValue(int exitValue) throws MojoExecutionException {
        if (isFail(exitValue)) {
            throw new MojoExecutionException("Non-zero exit value - " + exitValue);
        }
        if (exitValue != 0) {
            getLog().info("Ignoring non-zero exit value - " + exitValue);
        }
    }

    protected void prepareFileSystem() throws MojoExecutionException {
        // Only need to mess with the file system if we are excluding files
        if (!excludeTarget) {
            return;
        }

        // Scan the file system to identify Maven "target" directories
        // Create a file containing the list of "target" directories to exclude
        try {
            FileUtils.touch(excludesFile);
            DirectoryFileFilter dff = new DirectoryFileFilter();
            List excludeDirs = helper.getMatchingDirs(basedir, excludeTargetPattern, dff);
            List excludes = helper.getExcludesList(basedir, excludeDirs);
            int size = excludes.size();
            if (size == 1) {
                getLog().info("Excluding " + excludes.size() + " directory");
            } else {
                getLog().info("Excluding " + excludes.size() + " directories");
            }
            FileUtils.writeLines(excludesFile, excludes);
        } catch (IOException e) {
            throw new MojoExecutionException("Error preparing file system", e);
        }
    }

    public MavenProject getProject() {
        return project;
    }

    public boolean isExcludeTarget() {
        return excludeTarget;
    }

    public void setExcludeTarget(boolean excludeTarget) {
        this.excludeTarget = excludeTarget;
    }

    public String getDestination() {
        return destination;
    }

    public void setDestination(String destination) {
        this.destination = destination;
    }

    public boolean isFailOnError() {
        return failOnError;
    }

    public void setFailOnError(boolean failOnError) {
        this.failOnError = failOnError;
    }

    public boolean isVerbose() {
        return verbose;
    }

    public void setVerbose(boolean verbose) {
        this.verbose = verbose;
    }

    public File getExcludesFile() {
        return excludesFile;
    }

    public void setExcludesFile(File excludesFile) {
        this.excludesFile = excludesFile;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getExecutable() {
        return executable;
    }

    public void setExecutable(String executable) {
        this.executable = executable;
    }

    public String getExcludeTargetPattern() {
        return excludeTargetPattern;
    }

    public void setExcludeTargetPattern(String excludeTargetPattern) {
        this.excludeTargetPattern = excludeTargetPattern;
    }

    public File getBasedir() {
        return basedir;
    }

    public void setBasedir(File basedir) {
        this.basedir = basedir;
    }

    public String getIgnoreCodes() {
        return ignoreCodes;
    }

    public void setIgnoreCodes(String ignoreCodes) {
        this.ignoreCodes = ignoreCodes;
    }

    public boolean isCompress() {
        return compress;
    }

    public void setCompress(boolean compress) {
        this.compress = compress;
    }

    public boolean isStats() {
        return stats;
    }

    public void setStats(boolean stats) {
        this.stats = stats;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy