diff --git a/src/main/java/org/jenkinsci/plugins/workflow/support/steps/build/WaitForBuildStepExecution.java b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/build/WaitForBuildStepExecution.java index 3effe641..265a4062 100644 --- a/src/main/java/org/jenkinsci/plugins/workflow/support/steps/build/WaitForBuildStepExecution.java +++ b/src/main/java/org/jenkinsci/plugins/workflow/support/steps/build/WaitForBuildStepExecution.java @@ -3,9 +3,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; import hudson.AbortException; import hudson.console.ModelHyperlinkNote; +import hudson.model.Result; import hudson.model.Run; import hudson.model.TaskListener; import org.jenkinsci.plugins.workflow.steps.AbstractStepExecutionImpl; +import org.jenkinsci.plugins.workflow.steps.FlowInterruptedException; import org.jenkinsci.plugins.workflow.steps.StepContext; public class WaitForBuildStepExecution extends AbstractStepExecutionImpl { @@ -34,8 +36,21 @@ public boolean start() throws Exception { taskListener.getLogger().println("Waiting for " + runHyperLink + " to complete"); return false; } else { - taskListener.getLogger().println(runHyperLink + " is already complete"); - getContext().onSuccess(null); + Result result = run.getResult(); + if (result == null) { + taskListener.getLogger().println("Warning: " + runHyperLink + " already completed but getResult() returned null. Treating the result of this build as a failure"); + result = Result.FAILURE; + } + else { + taskListener.getLogger().println(runHyperLink + " already completed: " + result.toString()); + } + + StepContext context = getContext(); + if (!step.isPropagate() || result == Result.SUCCESS) { + context.onSuccess(new RunWrapper(run, false)); + } else { + context.onFailure(new FlowInterruptedException(result, false, new DownstreamFailureCause(run))); + } return true; } } diff --git a/src/test/java/org/jenkinsci/plugins/workflow/support/steps/build/WaitForBuildStepTest.java b/src/test/java/org/jenkinsci/plugins/workflow/support/steps/build/WaitForBuildStepTest.java index 4b0aa9ec..69777ba7 100644 --- a/src/test/java/org/jenkinsci/plugins/workflow/support/steps/build/WaitForBuildStepTest.java +++ b/src/test/java/org/jenkinsci/plugins/workflow/support/steps/build/WaitForBuildStepTest.java @@ -1,11 +1,8 @@ package org.jenkinsci.plugins.workflow.support.steps.build; -import hudson.model.Descriptor; import hudson.model.FreeStyleProject; import hudson.model.Result; import hudson.model.Run; -import hudson.tasks.Builder; -import hudson.util.DescribableList; import org.jenkinsci.plugins.workflow.actions.WarningAction; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode; @@ -22,8 +19,6 @@ import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.LoggerRule; import org.jvnet.hudson.test.JenkinsRule; -import org.jvnet.hudson.test.SleepBuilder; -import org.jvnet.hudson.test.UnstableBuilder; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -35,34 +30,30 @@ public class WaitForBuildStepTest { @Rule public LoggerRule logging = new LoggerRule(); @Test public void waitForBuild() throws Exception { - FreeStyleProject ds = j.createFreeStyleProject("ds"); - DescribableList> buildersList = ds.getBuildersList(); - buildersList.add(new SleepBuilder(500)); - buildersList.add(new FailureBuilder()); + Result dsResult = Result.FAILURE; + createWaitingDownStreamJob(dsResult); WorkflowJob us = j.jenkins.createProject(WorkflowJob.class, "us"); us.setDefinition(new CpsFlowDefinition( "def ds = build job: 'ds', waitForStart: true\n" + "def dsRunId = \"${ds.getFullProjectName()}#${ds.getNumber()}\"\n" + "def completeDs = waitForBuild runId: dsRunId\n" + "echo \"'ds' completed with status ${completeDs.getResult()}\"", true)); - j.assertLogContains("'ds' completed with status FAILURE", j.buildAndAssertSuccess(us)); + j.assertLogContains("'ds' completed with status " + dsResult.toString(), j.buildAndAssertSuccess(us)); } @Test public void waitForBuildPropagte() throws Exception { - FreeStyleProject ds = j.createFreeStyleProject("ds"); - DescribableList> buildersList = ds.getBuildersList(); - buildersList.add(new SleepBuilder(500)); - buildersList.add(new FailureBuilder()); + Result dsResult = Result.FAILURE; + createWaitingDownStreamJob(dsResult); WorkflowJob us = j.jenkins.createProject(WorkflowJob.class, "us"); us.setDefinition(new CpsFlowDefinition( "def ds = build job: 'ds', waitForStart: true\n" + "def dsRunId = \"${ds.getFullProjectName()}#${ds.getNumber()}\"\n" + "waitForBuild runId: dsRunId, propagate: true", true)); - j.assertLogContains("completed with status FAILURE", j.buildAndAssertStatus(Result.FAILURE, us)); + j.assertLogContains("completed with status " + dsResult.toString(), j.buildAndAssertStatus(dsResult, us)); } @SuppressWarnings("rawtypes") - @Test public void waitForBuildAlreadyComplete() throws Exception { + @Test public void waitForBuildAlreadyCompleteFailure() throws Exception { FreeStyleProject ds = j.createFreeStyleProject("ds"); ds.getBuildersList().add(new FailureBuilder()); Run ds1 = ds.scheduleBuild2(0).waitForStart(); @@ -70,15 +61,28 @@ public class WaitForBuildStepTest { j.waitForCompletion(ds1); WorkflowJob us = j.jenkins.createProject(WorkflowJob.class, "us"); us.setDefinition(new CpsFlowDefinition("waitForBuild runId: 'ds#1'", true)); - j.assertLogContains("is already complete", j.buildAndAssertSuccess(us)); + Result dsResult = Result.FAILURE; + j.assertLogContains("already completed: "+ dsResult.toString(), j.buildAndAssertSuccess(us)); + } + + @Issue("JENKINS-71342") + @SuppressWarnings("rawtypes") + @Test public void waitForBuildPropagateAlreadyCompleteFailure() throws Exception { + FreeStyleProject ds = j.createFreeStyleProject("ds"); + ds.getBuildersList().add(new FailureBuilder()); + Run ds1 = ds.scheduleBuild2(0).waitForStart(); + assertEquals(1, ds1.getNumber()); + j.waitForCompletion(ds1); + WorkflowJob us = j.jenkins.createProject(WorkflowJob.class, "us"); + us.setDefinition(new CpsFlowDefinition("waitForBuild runId: 'ds#1', propagate: true", true)); + Result dsResult = Result.FAILURE; + j.assertLogContains("already completed: "+ dsResult.toString(), j.buildAndAssertStatus(dsResult, us)); } @Issue("JENKINS-70983") @Test public void waitForUnstableBuildWithWarningAction() throws Exception { - FreeStyleProject ds = j.createFreeStyleProject("ds"); - DescribableList> buildersList = ds.getBuildersList(); - buildersList.add(new SleepBuilder(500)); - buildersList.add(new UnstableBuilder()); + Result dsResult = Result.UNSTABLE; + createWaitingDownStreamJob(dsResult); WorkflowJob us = j.jenkins.createProject(WorkflowJob.class, "us"); us.setDefinition(new CpsFlowDefinition( "def ds = build job: 'ds', waitForStart: true\n" + @@ -88,7 +92,7 @@ public class WaitForBuildStepTest { "} finally {\n" + " echo \"'ds' completed with status ${ds.getResult()}\"\n" + "}", true)); - j.assertLogContains("'ds' completed with status UNSTABLE", j.buildAndAssertStatus(Result.UNSTABLE, us)); + j.assertLogContains("'ds' completed with status " + dsResult.toString(), j.buildAndAssertStatus(dsResult, us)); WorkflowRun lastUpstreamRun = us.getLastBuild(); FlowNode buildTriggerNode = findFirstNodeWithDescriptor(lastUpstreamRun.getExecution(), WaitForBuildStep.DescriptorImpl.class); WarningAction action = buildTriggerNode.getAction(WarningAction.class); @@ -107,4 +111,23 @@ private static FlowNode findFirstNodeWithDescriptor(FlowExecution execution, Cla } return null; } + + private WorkflowJob createWaitingDownStreamJob(Result result) throws Exception { + WorkflowJob ds = j.jenkins.createProject(WorkflowJob.class, "ds"); + ds.setDefinition(new CpsFlowDefinition( + "import org.jenkinsci.plugins.workflow.support.steps.build.WaitForBuildAction\n" + + "@NonCPS\n" + + "boolean hasWaitForBuildAction() {\n" + + " return currentBuild.getRawBuild().getAction(WaitForBuildAction.class) != null\n" + + "}\n" + + "while(!hasWaitForBuildAction()) {\n" + + " sleep(time: 100, unit: 'MILLISECONDS')\n" + + "}\n" + + "catchError(buildResult: '" + result.toString() + "') {\n" + + " error('')\n" + + "}", false)); + return ds; + + } + }