diff --git a/pom.xml b/pom.xml index 2b9afe19..3e5c4559 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.jenkins-ci.plugins plugin - 1.436 + 1.642.3 unity3d-plugin @@ -82,9 +82,16 @@ com.google.guava guava - r06 + 19.0 provided + + org.jenkins-ci.plugins.workflow + workflow-step-api + 2.2 + jar + true + diff --git a/src/main/java/org/jenkinsci/plugins/unity3d/Helper.java b/src/main/java/org/jenkinsci/plugins/unity3d/Helper.java new file mode 100644 index 00000000..a69541b8 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/unity3d/Helper.java @@ -0,0 +1,41 @@ +package org.jenkinsci.plugins.unity3d; + +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import hudson.util.ArgumentListBuilder; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * + * @author rstyrczula + */ +public class Helper { + public static Set toIntegerSet(String csvIntegers) { + Set result = new HashSet(); + if (! csvIntegers.trim().equals("")) { + result.addAll(Collections2.transform(Arrays.asList(csvIntegers.split(",")), new Function() { + public Integer apply(String s) { + return Integer.parseInt(s.trim()); + } + })); + } + return result; + } + + public static String findCommandlineArgument(ArgumentListBuilder args, String arg) { + return findArgFromList(args.toList(), arg); + } + + public static String findArgFromList(List a, String arg) { + String customArg = null; + for (int i = 0; i < a.size() - 1; i++) { + if (a.get(i).equals(arg)) { + customArg = a.get(i+1); + } + } + return customArg; + } +} diff --git a/src/main/java/org/jenkinsci/plugins/unity3d/Unity3dBuilder.java b/src/main/java/org/jenkinsci/plugins/unity3d/Unity3dBuilder.java index 56096089..096bc6a4 100644 --- a/src/main/java/org/jenkinsci/plugins/unity3d/Unity3dBuilder.java +++ b/src/main/java/org/jenkinsci/plugins/unity3d/Unity3dBuilder.java @@ -1,5 +1,6 @@ package org.jenkinsci.plugins.unity3d; +import hudson.AbortException; import hudson.CopyOnWrite; import hudson.EnvVars; import hudson.Extension; @@ -9,7 +10,6 @@ import hudson.model.BuildListener; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; -import hudson.model.Computer; import hudson.model.Result; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.Builder; @@ -21,21 +21,21 @@ import java.io.IOException; import java.io.ObjectStreamException; import java.io.PrintStream; -import java.util.Arrays; -import java.util.List; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.Future; import java.util.logging.Logger; -import com.google.common.base.Function; -import com.google.common.collect.Collections2; +import hudson.model.Run; +import hudson.model.TaskListener; +import hudson.tasks.BuildStepMonitor; +import jenkins.tasks.SimpleBuildStep; import net.sf.json.JSONObject; import org.jenkinsci.plugins.unity3d.io.Pipe; import org.jenkinsci.plugins.unity3d.io.StreamCopyThread; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; @@ -49,7 +49,7 @@ * * @author Jerome Lacoste */ -public class Unity3dBuilder extends Builder { +public class Unity3dBuilder extends Builder implements SimpleBuildStep { private static final Logger log = Logger.getLogger(Unity3dBuilder.class.getName()); /** @@ -66,7 +66,7 @@ public class Unity3dBuilder extends Builder { public Unity3dBuilder(String unity3dName, String argLine, String unstableReturnCodes) { this.unity3dName = unity3dName; this.argLine = argLine; - this.unstableReturnCodes = unstableReturnCodes; + this.unstableReturnCodes = Util.fixNull(unstableReturnCodes); } @SuppressWarnings("unused") @@ -90,8 +90,16 @@ public String getUnstableReturnCodes() { return unstableReturnCodes; } + /** + * @since 1.4 + */ + @DataBoundSetter + public void setUnstableReturnCodes(String unstableReturnCodes) { + this.unstableReturnCodes = Util.fixNull(unstableReturnCodes); + } + Set toUnstableReturnCodesSet() { - return toIntegerSet(unstableReturnCodes); + return Helper.toIntegerSet(unstableReturnCodes); } private String getArgLineOrGlobalArgLine() { @@ -114,11 +122,32 @@ private PerformException(String s) { super(s); } } - + + @Override + public BuildStepMonitor getRequiredMonitorService() { + return BuildStepMonitor.NONE; + } + + @Override // SimpleBuildStep.perform + public void perform(Run run, FilePath fp, Launcher lnchr, TaskListener tl) throws InterruptedException, AbortException { + try + { + _perform(run, fp, lnchr, tl); + } + catch (PerformException e) { + tl.fatalError(e.getMessage()); + throw new AbortException(e.getMessage()); + } catch (IOException e) { + Util.displayIOException(e, tl); + String errorMessage = Messages.Unity3d_ExecUnexpectedlyFailed(); + e.printStackTrace(tl.fatalError(errorMessage)); + } + } + @Override public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException { try { - _perform(build, launcher, listener); + _perform(build, build.getWorkspace(), launcher, listener); return true; } catch (PerformException e) { listener.fatalError(e.getMessage()); @@ -131,12 +160,12 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListene } } - private void _perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException, PerformException { + private void _perform(Run build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException, PerformException { EnvVars env = build.getEnvironment(listener); - - Unity3dInstallation ui = getAndConfigureUnity3dInstallation(listener, env); - ArgumentListBuilder args = prepareCommandlineArguments(build, launcher, ui, env); + Unity3dInstallation ui = getAndConfigureUnity3dInstallation(listener, env, workspace); + + ArgumentListBuilder args = prepareCommandlineArguments(build, workspace, launcher, ui, env); String customLogFile = findLogFileArgument(args); @@ -150,7 +179,7 @@ private void _perform(AbstractBuild build, Launcher launcher, BuildListener StreamCopyThread copierThread = new StreamCopyThread("Pipe editor.log to output thread.", pipe.getIn(), ca); try { copierThread.start(); - int r = launcher.launch().cmds(args).envs(env).stdout(ca).pwd(build.getWorkspace()).join(); + int r = launcher.launch().cmds(args).envs(env).stdout(ca).pwd(workspace).join(); // r == 11 means executeMethod could not be found ? checkProcResult(build, r); } finally { @@ -178,7 +207,7 @@ private void _perform(AbstractBuild build, Launcher launcher, BuildListener } } - private void checkProcResult(AbstractBuild build, int result) throws PerformException { + private void checkProcResult(Run build, int result) throws PerformException { log.info("Unity command line exited with error code: " + result); if (isBuildUnstable(result)) { log.info(Messages.Unity3d_BuildMarkedAsUnstableBecauseOfStatus(result)); @@ -200,39 +229,38 @@ private boolean isBuildSuccess(int result) { /** Find the -logFile argument from the built arg line **/ private String findLogFileArgument(ArgumentListBuilder args) { - String customLogFile = null; - List a = args.toList(); - for (int i = 0; i < a.size() - 1; i++) { - if (a.get(i).equals("-logFile")) { - customLogFile = a.get(i+1); - } - } - return customLogFile; + return Helper.findCommandlineArgument(args, "-logFile"); } - private ArgumentListBuilder prepareCommandlineArguments(AbstractBuild build, Launcher launcher, Unity3dInstallation ui, EnvVars vars) throws IOException, InterruptedException, PerformException { + private ArgumentListBuilder prepareCommandlineArguments(Run build, FilePath workspace, Launcher launcher, Unity3dInstallation ui, EnvVars vars) throws IOException, InterruptedException, PerformException { String exe; try { exe = ui.getExecutable(launcher); } catch (RuntimeException re) { throw new PerformException(re.getMessage()); } + + if(build instanceof AbstractBuild) { + AbstractBuild abstractBuild = (AbstractBuild)build; + FilePath moduleRoot = abstractBuild.getModuleRoot(); + String moduleRootRemote = moduleRoot.getRemote(); + Map buildParameters = abstractBuild.getBuildVariables(); - FilePath moduleRoot = build.getModuleRoot(); - String moduleRootRemote = moduleRoot.getRemote(); - Map buildParameters = build.getBuildVariables(); - - return createCommandlineArgs(exe, moduleRootRemote, vars, buildParameters); + return createCommandlineArgs(exe, moduleRootRemote, vars, buildParameters); + } else { + // build variables should be parsed by groovy script + return createCommandlineArgs(exe, workspace.getRemote(), vars, null); + } } - - private Unity3dInstallation getAndConfigureUnity3dInstallation(BuildListener listener, EnvVars env) throws PerformException, IOException, InterruptedException { + + private Unity3dInstallation getAndConfigureUnity3dInstallation(TaskListener listener, EnvVars env, FilePath workspace) throws PerformException, IOException, InterruptedException { Unity3dInstallation ui = getUnity3dInstallation(); if(ui==null) { throw new PerformException(Messages.Unity3d_NoUnity3dInstallation()); } - ui = ui.forNode(Computer.currentComputer().getNode(), listener); + ui = ui.forNode(workspace.toComputer().getNode(), listener); ui = ui.forEnvironment(env); return ui; } @@ -267,18 +295,6 @@ private Unity3dInstallation getUnity3dInstallation() { return null; } - static Set toIntegerSet(String csvIntegers) { - Set result = new HashSet(); - if (! csvIntegers.trim().equals("")) { - result.addAll(Collections2.transform(Arrays.asList(csvIntegers.split(",")), new Function() { - public Integer apply(String s) { - return Integer.parseInt(s.trim()); - } - })); - } - return result; - } - @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); @@ -310,7 +326,7 @@ public void setInstallations(Unity3dInstallation... antInstallations) { public FormValidation doCheckUnstableReturnCodes(@QueryParameter String value) { try { - toIntegerSet(value); + Helper.toIntegerSet(value); return FormValidation.ok(); } catch (RuntimeException re) { return FormValidation.error(Messages.Unity3d_InvalidParamUnstableReturnCodes(value)); diff --git a/src/main/java/org/jenkinsci/plugins/unity3d/Unity3dInstallation.java b/src/main/java/org/jenkinsci/plugins/unity3d/Unity3dInstallation.java index 5c082832..946ba51f 100644 --- a/src/main/java/org/jenkinsci/plugins/unity3d/Unity3dInstallation.java +++ b/src/main/java/org/jenkinsci/plugins/unity3d/Unity3dInstallation.java @@ -9,7 +9,7 @@ import hudson.model.Hudson; import hudson.model.Node; import hudson.model.TaskListener; -import hudson.remoting.Callable; +import jenkins.security.MasterToSlaveCallable; import hudson.slaves.NodeSpecific; import hudson.tools.ToolDescriptor; import hudson.tools.ToolInstallation; @@ -60,7 +60,7 @@ public Unity3dInstallation forNode(Node node, TaskListener log) throws IOExcepti * Gets the executable path of this Unity3dBuilder on the given target system. */ public String getExecutable(Launcher launcher) throws IOException, InterruptedException { - return launcher.getChannel().call(new Callable() { + return launcher.getChannel().call(new MasterToSlaveCallable() { public String call() throws IOException { return checkUnity3dExecutablePath(getHome()); } @@ -129,7 +129,7 @@ private static String checkUnity3dExecutablePath(String home) { * @throws IOException */ public Future pipeEditorLog(final Launcher launcher, final String customLogFile, final OutputStream ros) throws IOException { - return launcher.getChannel().callAsync(new Callable() { + return launcher.getChannel().callAsync(new MasterToSlaveCallable() { public Long call() throws IOException { return new PipeFileAfterModificationAction(getEditorLogFile(customLogFile).getAbsolutePath(), ros, true).call(); } @@ -144,7 +144,7 @@ public Long call() throws IOException { * @throws InterruptedException */ public String getEditorLogPath(final Launcher launcher, final String customLogFile) throws IOException, InterruptedException { - return launcher.getChannel().call(new Callable() { + return launcher.getChannel().call(new MasterToSlaveCallable() { public String call() throws IOException { return getEditorLogFile(customLogFile).getAbsolutePath(); } diff --git a/src/main/java/org/jenkinsci/plugins/unity3d/workflow/Unity3dBuilderStep.java b/src/main/java/org/jenkinsci/plugins/unity3d/workflow/Unity3dBuilderStep.java new file mode 100644 index 00000000..82908066 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/unity3d/workflow/Unity3dBuilderStep.java @@ -0,0 +1,136 @@ +package org.jenkinsci.plugins.unity3d.workflow; + +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.Util; +import hudson.model.Run; +import hudson.model.TaskListener; +import hudson.util.FormValidation; +import hudson.util.ListBoxModel; + +import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl; +import org.jenkinsci.plugins.workflow.steps.AbstractSynchronousNonBlockingStepExecution; +import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl; +import org.jenkinsci.plugins.workflow.steps.StepContextParameter; + +import org.kohsuke.stapler.DataBoundSetter; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; + +import com.google.inject.Inject; +import javax.annotation.CheckForNull; + +import org.jenkinsci.plugins.unity3d.Unity3dBuilder; +import org.jenkinsci.plugins.unity3d.Unity3dInstallation; +import org.jenkinsci.plugins.unity3d.Helper; +import org.jenkinsci.plugins.unity3d.Messages; + +/** + * + * @author rstyrczula + */ +public class Unity3dBuilderStep extends AbstractStepImpl { + + private final String unity3dName; + private String argLine; + @CheckForNull + private String unstableReturnCodes; + + @DataBoundConstructor + public Unity3dBuilderStep(String unity3dName) { + this.unity3dName = unity3dName; + this.argLine = DescriptorImpl.defaultArgLine; + this.unstableReturnCodes = DescriptorImpl.defaultUnstableReturnCodes; + } + + public String getUnity3dName() { + return unity3dName; + } + + public String getArgLine() { + return argLine; + } + + @DataBoundSetter + public void setArgLine(String argLine) { + this.argLine = argLine; + } + + public @CheckForNull String getUnstableReturnCodes() { + return unstableReturnCodes; + } + + @DataBoundSetter + public void setUnstableReturnCodes(@CheckForNull String unstableReturnCodes) { + this.unstableReturnCodes = Util.fixNull(unstableReturnCodes); + } + + public static class Execution extends AbstractSynchronousNonBlockingStepExecution { + + @StepContextParameter + private transient Run run; + + @StepContextParameter + private transient TaskListener listener; + + @StepContextParameter + private transient Launcher launcher; + + @StepContextParameter + private transient FilePath ws; + + @Inject(optional=true) + private transient Unity3dBuilderStep step; + + private transient Unity3dBuilder builder; + + @Override + protected Void run() throws Exception { + builder = new Unity3dBuilder(step.getUnity3dName(), step.getArgLine(), step.getUnstableReturnCodes()); + builder.perform(run, ws, launcher, listener); + builder = null; + return null; + } + } + + @Extension(optional = true) + public static final class DescriptorImpl extends AbstractStepDescriptorImpl { + + public static final String defaultArgLine = null; + public static final String defaultUnstableReturnCodes = ""; + + public DescriptorImpl() { + super(Execution.class); + } + + @Override + public String getDisplayName() { + Unity3dBuilder.DescriptorImpl di = new Unity3dBuilder.DescriptorImpl(); + return di.getDisplayName(); + } + + @Override + public String getFunctionName() { + return "unity3d"; + } + + public FormValidation doCheckUnstableReturnCodes(@QueryParameter String value) { + try { + Helper.toIntegerSet(value); + return FormValidation.ok(); + } catch (RuntimeException re) { + return FormValidation.error(Messages.Unity3d_InvalidParamUnstableReturnCodes(value)); + } + } + + public ListBoxModel doFillUnity3dNameItems() { + ListBoxModel items = new ListBoxModel(); + Unity3dInstallation.DescriptorImpl di = new Unity3dInstallation.DescriptorImpl(); + for(Unity3dInstallation installation : di.getInstallations()) { + items.add(installation.getName(), installation.getName()); + } + return items; + } + } +} \ No newline at end of file diff --git a/src/main/resources/index.jelly b/src/main/resources/index.jelly index 6975729b..2fe0e1b2 100644 --- a/src/main/resources/index.jelly +++ b/src/main/resources/index.jelly @@ -1,3 +1,4 @@ + diff --git a/src/main/resources/org/jenkinsci/plugins/unity3d/Unity3dBuilder/config.jelly b/src/main/resources/org/jenkinsci/plugins/unity3d/Unity3dBuilder/config.jelly index 802dd5af..e58e629c 100644 --- a/src/main/resources/org/jenkinsci/plugins/unity3d/Unity3dBuilder/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/unity3d/Unity3dBuilder/config.jelly @@ -1,12 +1,13 @@ + - + - + diff --git a/src/main/resources/org/jenkinsci/plugins/unity3d/workflow/Unity3dBuilderStep/config.jelly b/src/main/resources/org/jenkinsci/plugins/unity3d/workflow/Unity3dBuilderStep/config.jelly new file mode 100644 index 00000000..d31b089c --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/unity3d/workflow/Unity3dBuilderStep/config.jelly @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/org/jenkinsci/plugins/unity3d/workflow/Unity3dBuilderStep/help.html b/src/main/resources/org/jenkinsci/plugins/unity3d/workflow/Unity3dBuilderStep/help.html new file mode 100644 index 00000000..f900ad2e --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/unity3d/workflow/Unity3dBuilderStep/help.html @@ -0,0 +1,4 @@ +

For projects that use Unity3d as the build system.
+ This causes Jenkins to invoke the Unity3d Editor with the given command line arguments.
+ A non-zero exit code from Unity3d makes Jenkins mark the build as a failure. +

\ No newline at end of file diff --git a/src/main/resources/org/jenkinsci/plugins/unity3d/Unity3dBuilder/help-argLine.html b/src/main/webapp/help-argLine.html similarity index 100% rename from src/main/resources/org/jenkinsci/plugins/unity3d/Unity3dBuilder/help-argLine.html rename to src/main/webapp/help-argLine.html diff --git a/src/main/resources/org/jenkinsci/plugins/unity3d/Unity3dBuilder/help-name.html b/src/main/webapp/help-name.html similarity index 100% rename from src/main/resources/org/jenkinsci/plugins/unity3d/Unity3dBuilder/help-name.html rename to src/main/webapp/help-name.html diff --git a/src/test/java/org/jenkinsci/plugins/unity3d/Unity3dBuilderTest.java b/src/test/java/org/jenkinsci/plugins/unity3d/Unity3dBuilderTest.java index 006171be..b044b843 100644 --- a/src/test/java/org/jenkinsci/plugins/unity3d/Unity3dBuilderTest.java +++ b/src/test/java/org/jenkinsci/plugins/unity3d/Unity3dBuilderTest.java @@ -78,6 +78,20 @@ public void environmentAndBuildVariablesParsing() { assertEquals(expectedArgs, commandlineArgs.toList()); assertEquals("Serialized arg line not modified", argLine, builder.getArgLine()); } + + @Test + public void nullBuildVariables() { + EnvVars vars = new EnvVars(); + + argLine = "-quit -batchmode -ExecuteMethod foo"; + expectedArgs = asList(exe, "-projectPath", moduleRootRemote, "-quit", "-batchmode", "-ExecuteMethod", "foo"); + + Unity3dBuilder builder = new Unity3dBuilder("Unity 3.5", argLine, ""); + ArgumentListBuilder commandlineArgs = builder.createCommandlineArgs(exe, moduleRootRemote, vars, null); + + assertEquals(expectedArgs, commandlineArgs.toList()); + assertEquals("Serialized arg line not modified", argLine, builder.getArgLine()); + } @Test public void environmentAndBuildVariablesParsingWithEnvVarsThatReferencesBuildParameters() { @@ -100,11 +114,13 @@ public void environmentAndBuildVariablesParsingWithEnvVarsThatReferencesBuildPar @Test public void unstableErrorCodesParsing() throws Exception { ensureUnstableReturnCodesParsingWorks(new Integer[]{}, ""); + ensureUnstableReturnCodesParsingWorks(new Integer[]{}, null); ensureUnstableReturnCodesParsingWorks(new Integer[]{2, 3}, "2,3"); ensureUnstableReturnCodesParsingWorks(new Integer[]{-1}, "-1"); ensureUnstableReturnCodesParsingWorks(new Integer[]{2, 3}, "2, 3"); ensureUnstableReturnCodesParsingWorks(new Integer[]{2, 3}, " 2 ,3 "); ensureUnstableReturnCodesParsingFails(" 2 , ,,"); + ensureUnstableReturnCodesParsingFails("2.3"); } private void ensureUnstableReturnCodesParsingWorks(Integer[] expectedResultCodes, String unstableReturnCodes) throws Exception { diff --git a/src/test/java/org/jenkinsci/plugins/unity3d/io/PipeTest.java b/src/test/java/org/jenkinsci/plugins/unity3d/io/PipeTest.java index 20457c5f..5514c110 100644 --- a/src/test/java/org/jenkinsci/plugins/unity3d/io/PipeTest.java +++ b/src/test/java/org/jenkinsci/plugins/unity3d/io/PipeTest.java @@ -1,7 +1,7 @@ package org.jenkinsci.plugins.unity3d.io; import hudson.Launcher; -import hudson.remoting.Callable; +import jenkins.security.MasterToSlaveCallable; import hudson.remoting.Future; import hudson.remoting.VirtualChannel; import hudson.slaves.DumbSlave; @@ -70,7 +70,7 @@ private void doPipingFromRemoteTest(Launcher l) throws IOException, InterruptedE } - private static class PipingCallable implements Callable, Serializable { + private static class PipingCallable extends MasterToSlaveCallable implements Serializable { private final OutputStream out; public PipingCallable(OutputStream out) {