Record builds interrupted by a server shutdown as INTERRUPTED, not FAILED

A systemd stop killed the running build process and BuildExecutor
classified the death as FAILED, posting a red Gitea status; FAILED is
not restartable, so the startup recovery never re-enqueued the build.

A ContextClosedEvent listener now sets a shuttingDown flag, terminates
the process trees of executing builds, and drains until their
INTERRUPTED results are persisted. Queued builds stay PENDING without
starting a process; recovery re-enqueues both after the restart.
INTERRUPTED publishes as Gitea state "pending" instead of "failure",
since the build is going to be re-run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-10 17:26:10 +02:00
co-authored by Claude Fable 5
parent fbdc6f54b7
commit 40f54a1298
7 changed files with 236 additions and 20 deletions
@@ -121,10 +121,11 @@ class GiteaClientTest :
mapOf(
BuildStatus.SUCCESS to "success",
BuildStatus.FAILED to "failure",
BuildStatus.INTERRUPTED to "failure",
BuildStatus.CANCELLED to "failure",
BuildStatus.PENDING to "pending",
BuildStatus.RUNNING to "pending",
// interrupted builds are re-enqueued on startup, so the commit must not turn red
BuildStatus.INTERRUPTED to "pending",
)
expectedStates.forEach { (status, state) ->
@@ -11,10 +11,11 @@ class GiteaStateMappingTest :
test("maps each build status to its Gitea state") {
BuildStatus.SUCCESS.toGiteaState() shouldBe "success"
BuildStatus.FAILED.toGiteaState() shouldBe "failure"
BuildStatus.INTERRUPTED.toGiteaState() shouldBe "failure"
BuildStatus.CANCELLED.toGiteaState() shouldBe "failure"
BuildStatus.PENDING.toGiteaState() shouldBe "pending"
BuildStatus.RUNNING.toGiteaState() shouldBe "pending"
// interrupted builds are re-enqueued on startup, so the commit must not turn red
BuildStatus.INTERRUPTED.toGiteaState() shouldBe "pending"
}
test("maps Gitea states back to build statuses") {