Skip to content

Instantly share code, notes, and snippets.

@adamretter
Created July 16, 2013 15:11
Show Gist options
  • Save adamretter/6009613 to your computer and use it in GitHub Desktop.
Save adamretter/6009613 to your computer and use it in GitHub Desktop.
Jenkins plugin outer config class
package hudson.plugins.scalabuilder;
import hudson.EnvVars;
import hudson.Extension;
import hudson.Launcher;
import hudson.FilePath;
import hudson.model.BuildListener;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Computer;
import hudson.model.Hudson;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.tasks.Shell;
import hudson.util.FormValidation;
//import hudson.Util;
import java.io.IOException;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
public class ScalaBuilder extends Builder {
private boolean debug;
private String libDir;
private SourceBlock sourceBlock;
private int port;
private boolean suspend;
private String pathToScala;
@DataBoundConstructor
public ScalaBuilder(final String libDir, final SourceBlock sourceBlock, final String pathToScala,
final boolean debug, final String port, final boolean suspend) {
this.libDir = libDir;
this.sourceBlock = sourceBlock;
if(pathToScala.isEmpty()) {
this.pathToScala = "scala";
} else {
this.pathToScala = pathToScala;
}
this.debug = debug;
if(port == null || port.isEmpty()) {
this.port = 0;
if(this.debug) {
System.out.println("Warning, setting to randomly generated port as none specified");
}
} else {
this.port = Integer.parseInt(port);
}
this.suspend = suspend;
}
public boolean getDebug() {
return debug;
}
public String getLibDir() {
return libDir;
}
public SourceBlock getSourceBlock() {
return sourceBlock;
}
public String getPort() {
return Integer.toString(port);
}
public boolean getSuspend() {
return suspend;
}
public String getPathToScala() {
return pathToScala;
}
public void setDebug(final boolean setDebug) {
this.debug = setDebug;
}
public void setLibDir(final String libDir) {
this.libDir = libDir;
}
public void setSourceBlock(final SourceBlock sourceBlock) {
this.sourceBlock = sourceBlock;
}
public void setPort(final String port) {
this.port = Integer.parseInt(port);
}
public void setSuspend(final boolean suspend) {
this.suspend = suspend;
}
public void setPathToScala(final String pathToScala) {
this.pathToScala = pathToScala;
}
/**
* Creates a scala script file in a temporary name in the specified
* directory.
*/
private FilePath createScriptFile(final FilePath dir) throws IOException,
InterruptedException {
final FilePath filePath;
if(getSourceBlock().isUrlSource()) {
final URL url = new URL(getSourceBlock().getSrcUrl());
filePath = dir.createTempFile("jenkins", ".scala");
filePath.copyFrom(url);
} else {
filePath = dir.createTextTempFile("jenkins", ".scala", getSourceBlock().getSrcUrl(), false);
}
return filePath;
}
private String getClasspathStringFromDir(final String dir, final boolean isUnix) {
if(dir.isEmpty()) {
return "";
}
final char separator = isUnix ? ':' : ';';
// build classpath from files copied
final StringBuilder full_classpath_builder = new StringBuilder();
final String[] dirs = dir.split(";");
for(final String d : dirs) {
final String[] libFileList = new File(d).list();
if(libFileList == null) {
/*
* Should never get here as the directories are
* verified in the form
*/
continue;
}
if(libFileList.length > 0) {
for(String libFile : libFileList) {
if(libFile.endsWith(".jar")) {
full_classpath_builder.append("jars/");
full_classpath_builder.append(libFile);
full_classpath_builder.append(separator);
}
}
}
}
final String full_classpath = full_classpath_builder.toString();
if(!full_classpath.isEmpty()) {
return String.format(" -cp %s", full_classpath_builder.toString());
} else {
return full_classpath;
}
}
// Expand any environment variables referred to in the passed string
private String expandLibDir(final String str, final EnvVars env) {
String expanded = str;
for(final String key : env.keySet()) {
// System.out.println("key: "+key + " value "+env.get(key));
expanded = expanded.replace("${" + key + "}", env.get(key));
}
return expanded;
}
public void copyDirectory(final AbstractBuild<?, ?> build,
final BuildListener listener, final FilePath from,
final FilePath to, final String path) {
listener.getLogger().println("Copying directory contents: " + path);
try {
new FilePath(build.getWorkspace().getChannel(), to.getRemote() + "/jars").mkdirs();
final List<FilePath> files = new FilePath(new File(from.getRemote() + path)).list();
for(final FilePath fl :files) {
if(fl.isDirectory()) {
copyDirectory(build, listener, from, to, path + "/" + fl.getName());
} else if(fl.getName().endsWith(".jar")) {
// Copy if changed (altered size/timestamp)
final FilePath existingFl = new FilePath(build.getWorkspace().getChannel(), to.getRemote() + "/jars" + path + "/" + fl.getName());
final FilePath newFl = new FilePath(new File(from.getRemote() + path + "/" + fl.getName()));
if(!existingFl.exists()) {
//listener.getLogger().println("File doesnt already exist: " + fl.getName() + ", copying");
newFl.copyToWithPermission(existingFl);
} else {
// get file sizes
/*listener.getLogger().println("Existing file length: "+existingFl.getRemote() + " is " + existingFl.length());
listener.getLogger().println("New file length: "+newFl.getRemote() + " is " + newFl.length());
listener.getLogger().println("Existing file last modified: "+existingFl.getRemote() + " is " + existingFl.lastModified() + " " + new Date(existingFl.lastModified()));
listener.getLogger().println("New file last modified: "+newFl.getRemote() + " is " + newFl.lastModified() + " " + new Date(newFl.lastModified()));*/
if(existingFl.length() == newFl.length() && existingFl.lastModified() / 1000 == newFl.lastModified() / 1000) { // strip milliseconds
/*listener.getLogger().println("File not copied - same as existing: " + fl.getName());*/
} else {
//listener.getLogger().println("File has changed: " + fl.getName() + ", copying");
newFl.copyToWithPermission(existingFl);
}
}
}
}
} catch(final Throwable ex) {
listener.getLogger().println("Failed to copy one or more files in directory: "
+ path + ", exception: "+ex.getMessage());
ex.printStackTrace();
}
}
@Override
public boolean perform(final AbstractBuild<?, ?> build, final Launcher launcher,
final BuildListener listener) throws InterruptedException, IOException {
// Get source/dest directories for library file copy
final FilePath projectWorkspaceOnSlave = build.getWorkspace();
final EnvVars env = build.getEnvironment(listener);
final String expandedLibDir = expandLibDir(libDir, env);
final String[] dirs = expandedLibDir.split(";");
for(final String d : dirs) {
final FilePath dir = new FilePath(new File(d));
if(dir.exists()) {
listener.getLogger().println("Directory specified by user for"
+ "libraries to copy exists, copying files from " + dir
+ " to " + projectWorkspaceOnSlave.getRemote());
copyDirectory(build, listener, dir, projectWorkspaceOnSlave, "");
/*
* listener.getLogger().println( "Number of files copied: " +
* dir.copyRecursiveTo("*.jar", projectWorkspaceOnSlave));
*/
}
}
// launcher.launch().cmds("echo",
// "blah").stdout(listener.getLogger()).pwd(build.getWorkspace()).join();
// Shell shell = new Shell("echo hello");
// shell.perform(build, launcher, listener);
// Write the scala source to file
FilePath src_tmp = null;
try {
src_tmp = createScriptFile(build.getWorkspace());
// Get a string to use as a scala classpath (*nix only)
final String full_classpath = getClasspathStringFromDir(expandedLibDir,
launcher.isUnix());
final String debugOptionsStr = debug ? (" -J-debug -J-Xrunjdwp:transport=dt_socket,server=y,suspend="
+ (suspend ? "y" : "n") + ",address=" + port) : "";
// TODO: swap for // SCALA_HOME!!!! windows? or write scala tool
// installer plugin
final String scala_launch_cmd = pathToScala + " -nocompdaemon "
+ full_classpath + " " + src_tmp.getRemote() + debugOptionsStr;
// TODO: Let user specify scala command line options
final Shell shell = new Shell(scala_launch_cmd);
listener.getLogger().println("Scala launch command is: "
+ scala_launch_cmd);
return shell.perform(build, launcher, listener);
} finally {
// Cleanup - delete temporary scala source file
if(src_tmp != null) {
src_tmp.delete();
}
}
}
public static Computer findSlaveNode(final String nodeName) {
Computer slaveNode = null;
for(final Computer currentNode : Hudson.getInstance().getComputers()) {
if(currentNode.getDisplayName().equals(nodeName)) {
slaveNode = currentNode;
break;
}
}
return slaveNode;
}
@Extension
public static class DescriptorImpl extends BuildStepDescriptor<Builder> {
@Override
public boolean isApplicable(final Class<? extends AbstractProject> jobType) {
return true;
}
@Override
public String getDisplayName() {
return "Execute Scala script";
}
public FormValidation doCheckLibDir(final StaplerRequest req,
@AncestorInPath final AbstractProject context,
@QueryParameter final String value) {
final String[] dirs = value.split(";");
if(dirs.length == 0) {
return FormValidation.ok();
} else if (dirs.length == 1 && dirs[0].isEmpty()) {
return FormValidation.ok();
}
for(final String dir : dirs) {
if(!new File(dir).exists()) {
return FormValidation.error("Cannot detect directory: "
+ dir
+ ", please check it exists on the Jenkins server (not the execution node!)");
}
}
return FormValidation.ok();
}
public FormValidation doCheckPort(final StaplerRequest req,
@AncestorInPath final AbstractProject context,
@QueryParameter final String value) {
FormValidation validationResult;
try {
final int port = Integer.parseInt(value);
if(port < 1 || port > 65535) {
validationResult = FormValidation.error("The entered TCP Port must be between 1 and 65,535 inclusive! Please enter a valid TCP Port number...");
} else {
validationResult = FormValidation.ok();
}
} catch(final NumberFormatException nfe) {
validationResult = FormValidation.error("The entered TCP Port is not a valid number! Please enter a valid TCP Port number...");
}
return validationResult;
}
public FormValidation doCheckSrcUrl(final StaplerRequest req,
@AncestorInPath final AbstractProject context,
@QueryParameter final String value) {
FormValidation validationResult;
try {
final URL url = new URL(value);
validationResult = FormValidation.ok();
} catch(final MalformedURLException mue) {
validationResult = FormValidation.error("The entered URL is invalid! Please enter a valid URL...");
}
return validationResult;
}
@Override
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
return super.configure(req, json); //To change body of generated methods, choose Tools | Templates.
}
@Override
public synchronized void load() {
super.load(); //To change body of generated methods, choose Tools | Templates.
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment