Skip to content

Commit ec4350c

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, that does get 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 f60b7e3 commit ec4350c

File tree

2 files changed

+80
-41
lines changed

2 files changed

+80
-41
lines changed

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

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -150,58 +150,61 @@ 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(
171-
'/' + p.getUrl(), p.getFullDisplayName()));
172-
future.cancel(true);
173-
throw x; // rethrow so that the triggering project get flagged as cancelled
174-
}
155+
.println("Skipping " + ModelHyperlinkNote.encodeTo(p)
156+
+ ". The project was not triggered for some reason.");
157+
continue;
158+
}
175159

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();
176167
listener.getLogger()
177-
.println(HyperlinkNote.encodeTo(
178-
'/' + startedRun.getUrl(), startedRun.getFullDisplayName())
179-
+ " started.");
168+
.println(HyperlinkNote.encodeTo(
169+
'/' + startedRun.getUrl(), startedRun.getFullDisplayName())
170+
+ " started.");
180171

181172
Run completedRun = future.get();
182-
Result completedResult = completedRun.getResult();
173+
completedResult = completedRun.getResult();
183174
listener.getLogger()
184-
.println(HyperlinkNote.encodeTo(
185-
'/' + completedRun.getUrl(),
186-
completedRun.getFullDisplayName())
187-
+ " completed. Result was " + completedResult);
175+
.println(HyperlinkNote.encodeTo(
176+
'/' + completedRun.getUrl(),
177+
completedRun.getFullDisplayName())
178+
+ " completed. Result was " + completedResult);
179+
188180
BuildInfoExporterAction.addBuildInfoExporterAction(
189181
build,
190182
completedRun.getParent().getFullName(),
191183
completedRun.getNumber(),
192184
completedResult);
193185

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

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

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,44 @@ public void testCancelsDownstreamBuildWhenInterrupted() throws Exception {
247247
triggerProject.getLastBuild(),
248248
"Waiting for the completion of downstream-project",
249249
"Build aborting: cancelling queued project downstream-project",
250-
"Build was aborted",
251-
"Finished: ABORTED");
250+
// The test class configures the BlockingBehaviour to never
251+
// fail and that includes cancelled job.
252+
"Finished: SUCCESS"
253+
);
254+
assertNull("No downstream build has been run", downstreamProject.getLastBuild());
255+
assertEquals("No build left in queue", 0, r.jenkins.getQueue().countBuildableItems());
256+
}
257+
258+
@Test
259+
public void testCancelledFromBuildQueue() throws Exception {
260+
r.jenkins.setNumExecutors(1); // the downstream-project would be in the build queue
261+
262+
FreeStyleProject triggerProject = r.createFreeStyleProject("upstream-project");
263+
FreeStyleProject downstreamProject = r.createFreeStyleProject("downstream-project");
264+
265+
TriggerBuilder triggerBuilder = new TriggerBuilder(createTriggerConfig("downstream-project"));
266+
267+
triggerProject.getBuildersList().add(triggerBuilder);
268+
QueueTaskFuture<FreeStyleBuild> parentBuild = triggerProject.scheduleBuild2(0);
269+
270+
parentBuild.waitForStart();
271+
Thread.sleep(500);
272+
273+
assertEquals("Downstream project is in build queue",
274+
1, r.jenkins.getQueue().countBuildableItems());
275+
276+
// Cancel the queued build
277+
r.jenkins.getQueue().clear();
278+
parentBuild.get();
279+
280+
assertLines(
281+
triggerProject.getLastBuild(),
282+
"Waiting for the completion of downstream-project",
283+
"Not built: downstream-project has been cancelled while waiting in the queue.",
284+
// The test class configures the BlockingBehaviour to never
285+
// fail and that includes cancelled job.
286+
"Finished: SUCCESS"
287+
);
252288
assertNull("No downstream build has been run", downstreamProject.getLastBuild());
253289
assertEquals("No build left in queue", 0, r.jenkins.getQueue().countBuildableItems());
254290
}

0 commit comments

Comments
 (0)