Skip to content

Commit a95cb34

Browse files
committed
Handle NOT_BUILT and ABORTED as other results
Use case -------- My use case is a post build script which does not need to fully complete. The step can be cancelled while waiting and causes the triggering build to fail. As a result I need the build step result to be passed through the BlockingBehavior so that I can set it to never block and thus be considered a SUCCESS even when NOT_BUILT or ABORTED. See: https://phabricator.wikimedia.org/T352319 Solution -------- When a triggered job is aborted (InterruptedException), the build step was throwing an AbortException which marks the parent job has having been aborted. This change catches it as an ABORTED result and passes it through the BlockingBehavior to determine the build step outcome. Jenkins Result defines ordinal ranking the results as: SUCCESS, UNSTABLE, FAILURE, NOT_BUILT, ABORTED. The NOT_BUILT and ABORTED results are thus worse than a FAILURE and would be matched as such in BlockingBehavior mapBuildStepResult() and mapBuildResult() which uses isWorseOrEqualTo() for comparison. BREAKING CHANGE: since ABORTED has a worse ordinal than FAILURE, the aborted build step causes the build to now be marked FAILURE. This is reflected in testCancelsDownstreamBuildWhenInterrupted() test which now becomes a SUCCESS (since the test blocking behavior is to never block). When a job is cancelled from the build queue (CancellationException), catch it, set the result to NOT_BUILD and pass it through the BlockingBehavior. This lets one to configure the build step to never fail or mark the build unstable when previously the exception would bubble up and call the build to fail. Add a test testCancelledFromBuildQueue() to cover the CancellationException() is caught and it results in a SUCCESS (since the test blocking behavior is to never block). ResultConditionTest covers the BlockingBehavior is able to map NOT_BUILD and ABORTED since it has two tests explicitly cancelling and interrupting jobs. Examples -------- When a build is aborted, by aborting it: Waiting for the completion of downstream-project downstream-project #7 started. downstream-project #7 completed. Result was ABORTED Build step 'Trigger/call builds on other projects' marked build as failure Finished: FAILURE When it is waiting in the build queue and cancelled: Waiting for the completion of downstream-project Not built: downstream-project has been cancelled while waiting in the queue. Build step 'Trigger/call builds on other projects' marked build as failure Finished: FAILURE
1 parent 458bf27 commit a95cb34

File tree

2 files changed

+67
-30
lines changed

2 files changed

+67
-30
lines changed

src/main/java/hudson/plugins/parameterizedtrigger/TriggerBuilder.java

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -150,55 +150,59 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListen
150150
continue;
151151
}
152152
for (QueueTaskFuture<AbstractBuild> future : futures.get(p)) {
153-
try {
154-
if (future == null) {
155-
listener.getLogger()
156-
.println("Skipping " + ModelHyperlinkNote.encodeTo(p)
157-
+ ". The project was not triggered for some reason.");
158-
continue;
159-
}
160-
153+
if (future == null) {
161154
listener.getLogger()
162-
.println("Waiting for the completion of "
163-
+ HyperlinkNote.encodeTo('/' + p.getUrl(), p.getFullDisplayName()));
164-
Run startedRun;
165-
try {
166-
startedRun = future.waitForStart();
167-
} catch (InterruptedException x) {
168-
listener.getLogger()
169-
.println("Build aborting: cancelling queued project "
170-
+ HyperlinkNote.encodeTo('/' + p.getUrl(), p.getFullDisplayName()));
171-
future.cancel(true);
172-
throw x; // rethrow so that the triggering project get flagged as cancelled
173-
}
155+
.println("Skipping " + ModelHyperlinkNote.encodeTo(p)
156+
+ ". The project was not triggered for some reason.");
157+
continue;
158+
}
174159

160+
listener.getLogger()
161+
.println("Waiting for the completion of "
162+
+ HyperlinkNote.encodeTo('/' + p.getUrl(), p.getFullDisplayName()));
163+
Run startedRun;
164+
Result completedResult;
165+
try {
166+
startedRun = future.waitForStart();
175167
listener.getLogger()
176168
.println(HyperlinkNote.encodeTo(
177169
'/' + startedRun.getUrl(), startedRun.getFullDisplayName())
178170
+ " started.");
179171

180172
Run completedRun = future.get();
181-
Result completedResult = completedRun.getResult();
173+
completedResult = completedRun.getResult();
182174
listener.getLogger()
183175
.println(HyperlinkNote.encodeTo(
184176
'/' + completedRun.getUrl(), completedRun.getFullDisplayName())
185177
+ " completed. Result was " + completedResult);
178+
186179
BuildInfoExporterAction.addBuildInfoExporterAction(
187180
build,
188181
completedRun.getParent().getFullName(),
189182
completedRun.getNumber(),
190183
completedResult);
191184

192-
if (buildStepResult && config.getBlock().mapBuildStepResult(completedResult)) {
193-
Result r = config.getBlock().mapBuildResult(completedResult);
194-
if (r != null) { // The blocking job is not a success
195-
build.setResult(r);
196-
}
197-
} else {
198-
buildStepResult = false;
199-
}
185+
} catch (InterruptedException x) {
186+
listener.getLogger()
187+
.println("Build aborting: cancelling queued project "
188+
+ HyperlinkNote.encodeTo('/' + p.getUrl(), p.getFullDisplayName()));
189+
future.cancel(true);
190+
throw x; // rethrow so that the triggering project get flagged as cancelled
200191
} catch (CancellationException x) {
201-
throw new AbortException(p.getFullDisplayName() + " aborted.");
192+
listener.getLogger()
193+
.println("Not built: " + p.getFullDisplayName()
194+
+ " has been cancelled while waiting in the queue.");
195+
completedResult = Result.NOT_BUILT;
196+
}
197+
198+
if (buildStepResult && config.getBlock().mapBuildStepResult(completedResult)) {
199+
200+
Result r = config.getBlock().mapBuildResult(completedResult);
201+
if (r != null) { // The blocking job is not a success
202+
build.setResult(r);
203+
}
204+
} else {
205+
buildStepResult = false;
202206
}
203207
}
204208
}

src/test/java/hudson/plugins/parameterizedtrigger/test/TriggerBuilderTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,39 @@ public void testCancelsDownstreamBuildWhenInterrupted() throws Exception {
253253
assertEquals("No build left in queue", 0, r.jenkins.getQueue().countBuildableItems());
254254
}
255255

256+
@Test
257+
public void testCancelledFromBuildQueue() throws Exception {
258+
r.jenkins.setNumExecutors(1); // the downstream-project would be in the build queue
259+
260+
FreeStyleProject triggerProject = r.createFreeStyleProject("upstream-project");
261+
FreeStyleProject downstreamProject = r.createFreeStyleProject("downstream-project");
262+
263+
TriggerBuilder triggerBuilder = new TriggerBuilder(createTriggerConfig("downstream-project"));
264+
265+
triggerProject.getBuildersList().add(triggerBuilder);
266+
QueueTaskFuture<FreeStyleBuild> parentBuild = triggerProject.scheduleBuild2(0);
267+
268+
parentBuild.waitForStart();
269+
Thread.sleep(500);
270+
271+
assertEquals(
272+
"Downstream project is in build queue", 1, r.jenkins.getQueue().countBuildableItems());
273+
274+
// Cancel the queued build
275+
r.jenkins.getQueue().clear();
276+
parentBuild.get();
277+
278+
assertLines(
279+
triggerProject.getLastBuild(),
280+
"Waiting for the completion of downstream-project",
281+
"Not built: downstream-project has been cancelled while waiting in the queue.",
282+
// The test class configures the BlockingBehaviour to never
283+
// fail and that includes cancelled job.
284+
"Finished: SUCCESS");
285+
assertNull("No downstream build has been run", downstreamProject.getLastBuild());
286+
assertEquals("No build left in queue", 0, r.jenkins.getQueue().countBuildableItems());
287+
}
288+
256289
@Test
257290
public void testConsoleOutputWithCounterParameters() throws Exception {
258291
r.createFreeStyleProject("project1");

0 commit comments

Comments
 (0)