/*
* Copyright (c) 2012-2014, CloudBees, Inc., SOASTA, Inc.
* All Rights Reserved.
*/
package com.soasta.jenkins;
import hudson.AbortException;
import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.JDK;
import hudson.tasks.Builder;
import hudson.util.ArgumentListBuilder;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import hudson.util.QuotedStringTokenizer;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import java.io.IOException;
import java.util.logging.Logger;
/**
* @author Kohsuke Kawaguchi
*/
public class MakeAppTouchTestable extends Builder {
/**
* URL of the server to use (deprecated).
*/
private final String url;
/**
* ID of the server to use.
* @see CloudTestServer
*/
private final String cloudTestServerID;
private final InputType inputType;
/**
* The input file associated with inputType.
* In order to keep the backwards compatibility of the project file name
* from before inputType was added, we are not changing the projectFile to
* inputFile.
*/
private final String projectFile;
private final String target;
private final String launchURL;
private final boolean backupModifiedFiles;
private final String additionalOptions;
private final String javaOptions;
private final String DEFAULT_JAVA_OPTION = "-Xmx1024m";
@DataBoundConstructor
public MakeAppTouchTestable(String url, String cloudTestServerID, String inputType, String projectFile,
String target, String launchURL, boolean backupModifiedFiles, String additionalOptions,
String javaOptions) {
this.url = url;
this.cloudTestServerID = cloudTestServerID;
this.inputType = InputType.getInputType(inputType);
this.projectFile = projectFile;
this.target = target;
this.launchURL = launchURL;
this.backupModifiedFiles = backupModifiedFiles;
this.additionalOptions = additionalOptions;
this.javaOptions = javaOptions;
}
public String getUrl() {
return url;
}
public String getCloudTestServerID() {
return cloudTestServerID;
}
public InputType getInputType() {
return inputType;
}
public String getProjectFile() {
return projectFile;
}
public String getTarget() {
return target;
}
public String getLaunchURL() {
return launchURL;
}
public boolean getBackupModifiedFiles() {
return backupModifiedFiles;
}
public String getAdditionalOptions() {
return additionalOptions;
}
public String getJavaOptions() {
return javaOptions;
}
public CloudTestServer getServer() {
return CloudTestServer.getByID(cloudTestServerID);
}
public Object readResolve() throws IOException {
if (cloudTestServerID != null)
return this;
// We don't have a server ID.
// This means the builder config is based an older version the plug-in.
// Look up the server by URL instead.
// We'll use the ID going forward.
CloudTestServer s = CloudTestServer.getByURL(getUrl());
LOGGER.info("Matched server URL " + getUrl() + " to ID: " + s.getId() + "; re-creating.");
return new MakeAppTouchTestable(url, s.getId(), inputType.getInputType(), projectFile,
target, launchURL, backupModifiedFiles, additionalOptions, javaOptions);
}
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
ArgumentListBuilder args = new ArgumentListBuilder();
JDK java = build.getProject().getJDK();
if (java!=null)
args.add(java.getHome()+"/bin/java");
else
args.add("java");
CloudTestServer s = getServer();
if (s==null)
throw new AbortException("No TouchTest server is configured in the system configuration.");
EnvVars envs = build.getEnvironment(listener);
FilePath path = new MakeAppTouchTestableInstaller(s).performInstallation(build.getBuiltOn(), listener);
args.add(DEFAULT_JAVA_OPTION)
.add(new QuotedStringTokenizer(envs.expand(javaOptions)).toArray())
.add("-jar")
.add(path.child("MakeAppTouchTestable.jar"))
.add("-overwriteapp")
.add("-url").add(s.getUrl());
if(s.getApitoken() != null && !s.getApitoken().isEmpty()) {
args.add("-apitoken")
.add(s.getApitoken());
}
else {
args.add("-username",s.getUsername());
if (s.getPassword() != null) {
args.add("-password")
.addMasked(s.getPassword().getPlainText());
}
}
args.add(inputType.getInputType(), envs.expand(projectFile));
if (target!=null && !target.trim().isEmpty())
args.add("-target", envs.expand(target));
if (launchURL!=null && !launchURL.trim().isEmpty())
args.add("-launchURL", envs.expand(launchURL));
if (!backupModifiedFiles)
args.add("-nobackup");
args.add(new QuotedStringTokenizer(envs.expand(additionalOptions)).toArray());
int r = launcher.launch().cmds(args).pwd(build.getWorkspace()).stdout(listener).join();
return r==0;
}
@Extension
public static class DescriptorImpl extends AbstractCloudTestBuilderDescriptor {
@Override
public String getDisplayName() {
return "Make App TouchTestable";
}
/**
* Called automatically by Jenkins whenever the "projectFile"
* field (known to the user as the input file) is modified by
* the user.
* @param value of the new path.
*/
public FormValidation doCheckProjectFile(@AncestorInPath AbstractProject project, @QueryParameter String value) throws IOException {
if (value == null || value.trim().isEmpty()) {
return FormValidation.error("Input file is required.");
} else {
// Make sure the directory exists.
return validateFileMask(project, value);
}
}
/**
* Called automatically by Jenkins to fill the drop-down "inputType".
* @return items the values of "inputType".
*/
public ListBoxModel doFillInputTypeItems() {
ListBoxModel items = new ListBoxModel();
// Because the Project is first, it's the default.
items.add("Project", InputType.PROJECT.toString());
items.add("APK", InputType.APK.toString());
items.add("IPA", InputType.IPA.toString());
items.add("iOS App Bundle", InputType.APP.toString());
return items;
}
}
static enum InputType {
APK ("-apk"),
APP ("-appbundle"),
IPA ("-ipa"),
PROJECT ("-project");
String inputType;
private InputType(String inputType) {
this.inputType = inputType;
}
private String getInputType() {
return inputType;
}
// The inputType should never be anything than the options
// listed in the doFillInputTypeItems() as it is a drop-down.
// The values given should always be valid.
private static InputType getInputType(String inputType) {
return InputType.valueOf(inputType);
}
}
private static final Logger LOGGER = Logger.getLogger(MakeAppTouchTestable.class.getName());
}