Skip to content

Commit

Permalink
Mark pipeline waitForBuild step with the downstream build result (#112)
Browse files Browse the repository at this point in the history
* Add a WarningAction to the waitForBuild step flownode for unsuccessful builds

* Log the result of the completed build

* Only add WarningAction when propagate: true

Update test

* Fix spotbugs error NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE

---------

Co-authored-by: Olivier Lamy <[email protected]>
  • Loading branch information
stuartrowe and olamy authored Apr 28, 2023
1 parent 7df200b commit 1fec530
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.console.ModelHyperlinkNote;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.listeners.RunListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jenkinsci.plugins.workflow.actions.WarningAction;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.steps.FlowInterruptedException;
import org.jenkinsci.plugins.workflow.steps.StepContext;

Expand All @@ -21,11 +24,24 @@ public void onCompleted(Run<?,?> run, @NonNull TaskListener listener) {
for (WaitForBuildAction action : run.getActions(WaitForBuildAction.class)) {
StepContext context = action.context;
LOGGER.log(Level.FINE, "completing {0} for {1}", new Object[] {run, context});
if (!action.propagate || run.getResult() == Result.SUCCESS) {

Result result = run.getResult();
if (result == null) { /* probably impossible */
result = Result.FAILURE;
}
try {
context.get(TaskListener.class).getLogger().println("Build " + ModelHyperlinkNote.encodeTo("/" + run.getUrl(), run.getFullDisplayName()) + " completed: " + result.toString());
if (action.propagate && result.isWorseThan(Result.SUCCESS)) {
context.get(FlowNode.class).addOrReplaceAction(new WarningAction(result));
}
} catch (Exception e) {
LOGGER.log(Level.WARNING, null, e);
}

if (!action.propagate || result == Result.SUCCESS) {
context.onSuccess(new RunWrapper(run, false));
} else {
Result result = run.getResult();
context.onFailure(new FlowInterruptedException(result != null ? result : /* probably impossible */ Result.FAILURE, false, new DownstreamFailureCause(run)));
context.onFailure(new FlowInterruptedException(result, false, new DownstreamFailureCause(run)));
}
}
run.removeActions(WaitForBuildAction.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,27 @@
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;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.graph.FlowGraphWalker;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.BuildWatcher;
import org.jvnet.hudson.test.FailureBuilder;
import org.jvnet.hudson.test.SleepBuilder;
import org.jvnet.hudson.test.JenkinsRule;
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;

public class WaitForBuildStepTest {

Expand Down Expand Up @@ -63,4 +72,39 @@ public class WaitForBuildStepTest {
us.setDefinition(new CpsFlowDefinition("waitForBuild runId: 'ds#1'", true));
j.assertLogContains("is already complete", j.buildAndAssertSuccess(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());
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" +
"try {\n" +
" waitForBuild runId: dsRunId, propagate: true\n" +
"} finally {\n" +
" echo \"'ds' completed with status ${ds.getResult()}\"\n" +
"}", true));
j.assertLogContains("'ds' completed with status UNSTABLE", j.buildAndAssertStatus(Result.UNSTABLE, us));
WorkflowRun lastUpstreamRun = us.getLastBuild();
FlowNode buildTriggerNode = findFirstNodeWithDescriptor(lastUpstreamRun.getExecution(), WaitForBuildStep.DescriptorImpl.class);
WarningAction action = buildTriggerNode.getAction(WarningAction.class);
assertNotNull(action);
assertEquals(action.getResult(), Result.UNSTABLE);
}

private static FlowNode findFirstNodeWithDescriptor(FlowExecution execution, Class<WaitForBuildStep.DescriptorImpl> cls) {
for (FlowNode node : new FlowGraphWalker(execution)) {
if (node instanceof StepAtomNode) {
StepAtomNode stepAtomNode = (StepAtomNode) node;
if (cls.isInstance(stepAtomNode.getDescriptor())) {
return stepAtomNode;
}
}
}
return null;
}
}

0 comments on commit 1fec530

Please sign in to comment.