The × on a running row deleted the stored result while the build ran on, holding its branch (a deploy waited behind it for hours) and unreachable for cancel. - `DELETE` answers 409 for a queued or running build. - `cancel` asks the executor (`BuildExecutor.isActive`) instead of the stored results, so an orphaned build can still be stopped. - Pending and running rows render the × as \"Cancel build\". PR-doc: `docs/prs/2026-10-03-PR#95-cancel-instead-of-delete-of-active-builds.md` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #95
74 lines
3.4 KiB
Markdown
74 lines
3.4 KiB
Markdown
> **WARNING:** This document describes only the change applied in this PR.
|
||
> It may already be outdated once the next PR is merged.
|
||
> Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
|
||
|
||
## The Problem
|
||
|
||
Every row of the build tables carries a ×, and on a running row it read as "cancel".
|
||
It meant "Delete stored build": the result was removed, and the build ran on.
|
||
Nothing showed it any more, but it still held its branch, so a deploy queued behind a mutation build waited for hours.
|
||
The cancel endpoint looked the build up in the stored results, so with the row gone it could not be stopped either.
|
||
When the build finished, the executor found no result to update and recorded a new one, so the deleted build turned up again.
|
||
This happened twice on werkator.javagil.de.
|
||
|
||
## The Scenarios
|
||
|
||
### Feature: A queued or running build is cancelled, never deleted
|
||
|
||
#### Scenario#95.01: Deleting a queued or running build is refused
|
||
|
||
So that no build runs on without a row to show and stop it.
|
||
|
||
- **Given** a build that is queued or running
|
||
- **When** its result is deleted through the API
|
||
- **Then** the answer is 409, naming the build and saying to cancel it first
|
||
- **And** the result and its artifacts stay
|
||
|
||
##### Verified by
|
||
|
||
- [BuildsApiControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/BuildsApiControllerRestTest.kt) — Scenario#95.01
|
||
|
||
#### Scenario#95.02: A build whose result is gone can still be cancelled
|
||
|
||
So that a build orphaned by an earlier version can be stopped without a restart.
|
||
|
||
- **Given** a running build whose stored result was deleted
|
||
- **When** it is cancelled through the route of its repository
|
||
- **Then** the executor cancels it
|
||
- **And** the route of another repository still does not reach it
|
||
|
||
##### Verified by
|
||
|
||
- [BuildsApiControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/BuildsApiControllerRestTest.kt) — Scenario#95.02
|
||
- [BuildExecutorIntegrationTest](../../src/test/kotlin/de/hoennig/werkator/build/BuildExecutorIntegrationTest.kt) — Scenario#95.02
|
||
|
||
#### Scenario#95.03: A queued or running row offers Cancel
|
||
|
||
So that the × does what it looks like it does.
|
||
|
||
- **Given** a history with a pending, a running and a finished build
|
||
- **When** the page is rendered
|
||
- **Then** the pending and the running row carry a cancel button, the finished row the delete button
|
||
|
||
##### Verified by
|
||
|
||
- [UiControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/UiControllerRestTest.kt) — Scenario#95.03
|
||
|
||
## The Solution
|
||
|
||
- `BuildExecutor.isActive(repo, artifactKey)` says whether a build is queued or executing in that repository.
|
||
It asks the executor, not the stored results, because a result can be gone while its build runs.
|
||
- `cancel` uses it in place of the lookup in the history; that also keeps a route from reaching another repository's build.
|
||
- `DELETE /api/builds/<key>` answers 409 for an active build.
|
||
- `builds.html` and `werkator.js` render the × of a pending or running row as "Cancel build", like the card on the current-builds page.
|
||
|
||
## Additional Changes
|
||
|
||
- Scenario#65.04 waits for the draining flag: it is set after the result is stored, and a slow CI read it too early.
|
||
- Version 1.30.1 with its release note.
|
||
|
||
## Follow-up PRs
|
||
|
||
- A queued build that is cancelled stays PENDING until its branch's worker reaches it, which can be hours behind a long build.
|
||
It could be recorded as CANCELLED at once.
|