package com.g2one.hudson.grails;
import com.martiansoftware.jsap.JSAP;
import com.martiansoftware.jsap.JSAPResult;
import com.martiansoftware.jsap.UnflaggedOption;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.tasks.Builder;
import hudson.util.ArgumentListBuilder;
import hudson.util.FormValidation;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GrailsBuilder extends Builder {
private final String targets;
private final String name;
private String grailsWorkDir;
private String projectWorkDir;
private String projectBaseDir;
private String serverPort;
private String properties;
private Boolean forceUpgrade;
private Boolean nonInteractive;
@DataBoundConstructor
public GrailsBuilder(String targets, String name, String grailsWorkDir, String projectWorkDir, String projectBaseDir, String serverPort, String properties, Boolean forceUpgrade, Boolean nonInteractive) {
this.name = name;
this.targets = targets;
this.grailsWorkDir = grailsWorkDir;
this.projectWorkDir = projectWorkDir;
this.projectBaseDir = projectBaseDir;
this.serverPort = serverPort;
this.properties = properties;
this.forceUpgrade = forceUpgrade;
this.nonInteractive = nonInteractive;
}
public boolean getNonInteractive() {
return nonInteractive;
}
public void setNonInteractive(Boolean b) {
nonInteractive = b;
}
public boolean getForceUpgrade() {
return forceUpgrade;
}
public void setForceUpgrade(Boolean b) {
forceUpgrade = b;
}
public String getProperties() {
return properties;
}
public void setProperties(String properties) {
this.properties = properties;
}
public String getProjectBaseDir() {
return projectBaseDir;
}
public void setProjectBaseDir(String projectBaseDir) {
this.projectBaseDir = projectBaseDir;
}
public String getProjectWorkDir() {
return projectWorkDir;
}
public void setProjectWorkDir(String projectWorkDir) {
this.projectWorkDir = projectWorkDir;
}
public String getGrailsWorkDir() {
return grailsWorkDir;
}
public void setGrailsWorkDir(String grailsWorkDir) {
this.grailsWorkDir = grailsWorkDir;
}
public String getServerPort() {
return serverPort;
}
public void setServerPort(String serverPort) {
this.serverPort = serverPort;
}
public String getName() {
return name;
}
public String getTargets() {
return targets;
}
public GrailsInstallation getGrails() {
for (GrailsInstallation i : DESCRIPTOR.getInstallations()) {
if (name != null && i.getName().equals(name))
return i;
}
return null;
}
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {
List<String[]> targetsToRun = getTargetsToRun();
if (targetsToRun.size() > 0) {
String execName;
if (launcher.isUnix()) {
execName = "grails";
} else {
execName = "grails.bat";
}
GrailsInstallation grailsInstallation = getGrails();
Map<String, String> env = build.getEnvironment(listener);
if (grailsInstallation != null) {
env.put("GRAILS_HOME", grailsInstallation.getGrailsHome());
}
for (String[] targetsAndArgs : targetsToRun) {
String target = targetsAndArgs[0];
ArgumentListBuilder args = new ArgumentListBuilder();
if (grailsInstallation == null) {
args.add(execName);
} else {
File exec = grailsInstallation.getExecutable();
if (!grailsInstallation.getExists()) {
listener.fatalError(exec + " doesn't exist");
return false;
}
args.add(exec.getPath());
}
args.addKeyValuePairs("-D", build.getBuildVariables());
Map sytemProperties = new HashMap();
if (grailsWorkDir != null && !"".equals(grailsWorkDir.trim())) {
sytemProperties.put("grails.work.dir", grailsWorkDir.trim());
}
if (projectWorkDir != null && !"".equals(projectWorkDir.trim())) {
sytemProperties.put("grails.project.work.dir", projectWorkDir.trim());
}
if (serverPort != null && !"".equals(serverPort.trim())) {
sytemProperties.put("server.port", serverPort.trim());
}
if (sytemProperties.size() > 0) {
args.addKeyValuePairs("-D", sytemProperties);
}
args.addKeyValuePairsFromPropertyString("-D", properties, build.getBuildVariableResolver());
args.add(target);
boolean foundNonInteractive = false;
for (int i = 1; i < targetsAndArgs.length; i++) {
String arg = evalTarget(env, targetsAndArgs[i]);
if("--non-interactive".equals(arg)) {
foundNonInteractive = true;
}
args.add(arg);
}
if(nonInteractive != null && nonInteractive && !foundNonInteractive) {
args.add("--non-interactive");
}
if (!launcher.isUnix()) {
args.prepend("cmd.exe", "/C");
args.add("&&", "exit", "%%ERRORLEVEL%%");
}
try {
final FilePath basePath;
FilePath moduleRoot = build.getModuleRoot();
if (projectBaseDir != null && !"".equals(projectBaseDir.trim())) {
basePath = new FilePath(moduleRoot, projectBaseDir);
} else {
basePath = moduleRoot;
}
int r = launcher.launch().cmds(args).envs(env).stdout(listener).pwd(basePath).join();
if (r != 0) return false;
} catch (IOException e) {
Util.displayIOException(e, listener);
e.printStackTrace(listener.fatalError("command execution failed"));
return false;
}
}
} else {
listener.getLogger().println("Error: No Targets To Run!");
return false;
}
return true;
}
/**
* Method based on work from Kenji Nakamura
*
* @param env The enviroment vars map
* @param target The target with environment vars
* @return The target with evaluated environment vars
*/
@SuppressWarnings({"StaticMethodOnlyUsedInOneClass", "TypeMayBeWeakened"})
static String evalTarget(Map<String, String> env, String target) {
Binding binding = new Binding();
binding.setVariable("env", env);
binding.setVariable("sys", System.getProperties());
GroovyShell shell = new GroovyShell(binding);
Object result = shell.evaluate("return \"" + target + "\"");
if (result == null) {
return target;
} else {
return result.toString().trim();
}
}
protected List<String[]> getTargetsToRun() {
List<String[]> targetsToRun = new ArrayList<String[]>();
if(forceUpgrade) {
targetsToRun.add(new String[]{"upgrade", "--non-interactive"});
}
if (targets != null && targets.length() > 0) {
try {
JSAP jsap = new JSAP();
UnflaggedOption option = new UnflaggedOption("targets");
option.setGreedy(true);
jsap.registerParameter(option);
JSAPResult jsapResult = jsap.parse(this.targets);
String[] targets = jsapResult.getStringArray("targets");
for (String targetAndArgs : targets) {
String[] pieces = targetAndArgs.split(" ");
targetsToRun.add(pieces);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return targetsToRun;
}
@Extension
public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
public static final class DescriptorImpl extends Descriptor<Builder> {
private volatile GrailsInstallation[] installations = new GrailsInstallation[0];
DescriptorImpl() {
super(GrailsBuilder.class);
load();
}
public String getDisplayName() {
return "Build With Grails";
}
@Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
installations = req.bindParametersToList(GrailsInstallation.class, "grails.").toArray(new GrailsInstallation[0]);
save();
return true;
}
// public Builder newInstance(StaplerRequest req) {
// return req.bindParameters(GrailsBuilder.class, "grails.");
// }
@Override
public Builder newInstance(StaplerRequest req, JSONObject formData) throws FormException {
return req.bindJSON(clazz, formData);
}
public GrailsInstallation[] getInstallations() {
return installations;
}
public FormValidation doCheckGrailsHome(@QueryParameter String value) {
if (!Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) return FormValidation.ok();
File f = new File(Util.fixNull(value));
if (!f.isDirectory()) {
return FormValidation.error(f + " is not a directory");
}
if (!new File(f, "bin/grails").exists() && !new File(f, "bin/grails.bat").exists()) {
return FormValidation.error(f + " doesn't look like a Grails directory");
}
return FormValidation.ok();
}
}
}