diff --git a/plugin/src/main/java/org/jenkinsci/plugins/workflow/cps/CpsFlowExecution.java b/plugin/src/main/java/org/jenkinsci/plugins/workflow/cps/CpsFlowExecution.java index 6ab7264fe..2b403cb66 100644 --- a/plugin/src/main/java/org/jenkinsci/plugins/workflow/cps/CpsFlowExecution.java +++ b/plugin/src/main/java/org/jenkinsci/plugins/workflow/cps/CpsFlowExecution.java @@ -28,8 +28,6 @@ import com.cloudbees.groovy.cps.Env; import com.cloudbees.groovy.cps.Envs; import com.cloudbees.groovy.cps.Outcome; -import com.cloudbees.groovy.cps.impl.ConstantBlock; -import com.cloudbees.groovy.cps.impl.ThrowBlock; import com.cloudbees.groovy.cps.sandbox.Invoker; import com.cloudbees.jenkins.support.api.Component; import com.cloudbees.jenkins.support.api.Container; @@ -107,6 +105,7 @@ import hudson.AbortException; import hudson.BulkChange; import hudson.Extension; +import hudson.Functions; import hudson.init.Terminator; import hudson.model.Item; import hudson.model.Job; @@ -840,51 +839,17 @@ public void onFailure(Throwable t) { } /** - * Used by {@link #loadProgramAsync(File)} to propagate a failure to load the persisted execution state. - *

- * Let the workflow interrupt by throwing an exception that indicates how it failed. + * Used to propagate a failure to load the persisted execution state. * @param promise same as {@link #programPromise} but more strongly typed */ private void loadProgramFailed(final Throwable problem, SettableFuture promise) { - FlowHead head; - - synchronized(this) { - if (heads == null || heads.isEmpty()) { - head = null; - } else { - head = getFirstHead(); - } - } - - if (head==null) { - // something went catastrophically wrong and there's no live head. fake one - head = new FlowHead(this); - try { - head.newStartNode(new FlowStartNode(this, iotaStr())); - } catch (IOException e) { - LOGGER.log(Level.FINE, "Failed to persist", e); - } + try { + Functions.printStackTrace(problem, owner.getListener().getLogger()); + } catch (Exception x) { + LOGGER.log(Level.WARNING, x, () -> "failed to log problem to " + owner); } - - - CpsThreadGroup g = new CpsThreadGroup(this); - final FlowHead head_ = head; - - promise.set(g); - runInCpsVmThread(new FutureCallback<>() { - @Override public void onSuccess(CpsThreadGroup g) { - CpsThread t = g.addThread( - new Continuable(new ThrowBlock(new ConstantBlock( - problem instanceof AbortException || problem instanceof FlowInterruptedException ? problem : new IOException("Failed to load build state", problem)))), - head_, null - ); - t.resume(new Outcome(null,null)); - } - @Override public void onFailure(Throwable t) { - LOGGER.log(Level.WARNING, "Failed to set program failure on " + owner, t); - croak(t); - } - }); + promise.setException(problem); + croak(new AbortException("Failed to load program")); } /** Report a fatal error in the VM. */ diff --git a/plugin/src/test/java/org/jenkinsci/plugins/workflow/cps/CpsFlowExecutionTest.java b/plugin/src/test/java/org/jenkinsci/plugins/workflow/cps/CpsFlowExecutionTest.java index 1ca6f0408..5ab076d9c 100644 --- a/plugin/src/test/java/org/jenkinsci/plugins/workflow/cps/CpsFlowExecutionTest.java +++ b/plugin/src/test/java/org/jenkinsci/plugins/workflow/cps/CpsFlowExecutionTest.java @@ -608,6 +608,29 @@ private void trustedShell(final boolean pos) throws Throwable { }); } + @Issue("JENKINS-50407") + @Test public void shellLoadingError() throws Throwable { + sessions.then(r -> { + WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p"); + p.setDefinition(new CpsFlowDefinition("semaphore 'wait'", true)); + SemaphoreStep.waitForStart("wait/1", p.scheduleBuild2(0).waitForStart()); + }); + sessions.then(r -> { + r.assertLogContains("IllegalStateException: decorator problem here", + r.assertBuildStatus(Result.FAILURE, + r.waitForCompletion(r.jenkins.getItemByFullName("p", WorkflowJob.class).getLastBuild()))); + }); + } + @TestExtension("shellLoadingError") public static final class BrokenDecorator extends GroovyShellDecorator { + static int count; + @Override + public void configureShell(CpsFlowExecution context, GroovyShell shell) { + if (count++ == 1) { + throw new IllegalStateException("decorator problem here"); + } + } + } + /** * This field shouldn't be visible to regular script. */