Skip to content

Commit

Permalink
Merge pull request #116 from stuartrowe/JENKINS-71342
Browse files Browse the repository at this point in the history
[JENKINS-71342] WaitForBuild should proagate result of already completed downstream builds when propagate is enabled.
  • Loading branch information
jtnord authored Jul 14, 2023
2 parents 2449a9a + bf4097d commit 03f799b
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -35,50 +30,59 @@ public class WaitForBuildStepTest {
@Rule public LoggerRule logging = new LoggerRule();

@Test public void waitForBuild() throws Exception {
FreeStyleProject ds = j.createFreeStyleProject("ds");
DescribableList<Builder, Descriptor<Builder>> 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<Builder, Descriptor<Builder>> 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();
assertEquals(1, ds1.getNumber());
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<Builder, Descriptor<Builder>> 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" +
Expand All @@ -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);
Expand All @@ -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;

}

}

0 comments on commit 03f799b

Please sign in to comment.