org.kuali.maven.plugins.jenkins.SyncWorkspaceMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jenkins-maven-plugin Show documentation
Show all versions of jenkins-maven-plugin Show documentation
Automated management of Jenkins jobs via Maven. Much of the information needed by Jenkins when creating a job is already in the Maven pom.
The SCM information and CI url are present there. Jenkins jobs also typically have names that reflect the groupId, artifactId, and version in some manner.
This plugin automates the process of creating Jenkins jobs by harvesting information from the POM to create XML config files in the format Jenkins needs. The
Jenkins CLI wrapper is then used to create, update, read, and delete Jenkins jobs on the CI server.
/**
* 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;
}
}