> **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. ## Problem A build row offered one restart, and what it built depended on the page. Latest rebuilt the recorded commit, Branches built the origin head, History and the artifact page offered none. Which commit a click built was therefore a matter of where the user stood, and easy to get wrong. The restart also took a build name only: a "same commit" from an older History row would have repeated the newest run of that name. ## Non-Goals - A follow-up build never starts at the newest commit: without its predecessor's green run it would, e.g., deploy untested code. - A pull-request target build does not ask the forge for the newest head of its pull request; the watcher builds every new head. - The Current page keeps its cards without restarts; a running build is cancelled there, not restarted. ## Specification ### Feature: Two restarts on every build row #### Scenario#150.01: Every view offers both restarts on every row So that the button, not the page, says which commit is built. - **Given** a recorded build of `main` - **When** Latest, Branches or History is shown - **Then** its row has a button `Build commit again` and a button `Build the newest commit of main` ##### Verified by - [UiBuildListsRestTest](../../src/test/kotlin/de/hoennig/werkator/server/UiBuildListsRestTest.kt) — Scenario#150.01 - `werkator.js` (`restartButtons`) renders the same two buttons; checked in the browser against a demo instance #### Scenario#150.02: The same commit builds the run of its row So that an older History row repeats that run, not the newest run of its name. - **Given** two runs of `main@pitest`, an older one at commit `older1` and a newer one - **When** the same commit of the older run is restarted - **Then** commit `older1` is built with the `pitest` definition ##### Verified by - [BuildsApiControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/BuildsApiControllerRestTest.kt) — Scenario#150.02 #### Scenario#150.03: A refused restart stays visible, disabled, with its reason So that the user sees why a restart is not offered, and the columns stay aligned. - **Given** a run whose branch is gone, a follow-up run, a branch never built, and a viewer without the control token - **When** the rows are shown - **Then** the buttons that cannot run are disabled with `Branch deleted from origin`, `Runs after a green default; restart that one`, `Never built` or `Needs the control token` as title ##### Verified by - [UiBuildListsRestTest](../../src/test/kotlin/de/hoennig/werkator/server/UiBuildListsRestTest.kt) — Scenario#150.03 - [BuildRestartsUnitTest](../../src/test/kotlin/de/hoennig/werkator/server/BuildRestartsUnitTest.kt) — Scenario#150.03 #### Scenario#150.04: The newest commit is refused for a follow-up, a pull-request target and a gone branch So that no restart deploys a commit its predecessor has not passed, or hands the host's secrets to a stale pull request. - **Given** the follow-up `main@deploy` of `default`, a pull-request target run, and a run whose branch is gone - **When** the newest commit of each is requested - **Then** each is refused with its reason, and no build starts ##### Verified by - [BuildsApiControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/BuildsApiControllerRestTest.kt) — Scenario#150.04 - [BuildRestartsUnitTest](../../src/test/kotlin/de/hoennig/werkator/server/BuildRestartsUnitTest.kt) — Scenario#150.04 - on a demo instance: `main@deploy` ran after a green `main`, and its newest commit answered `409` #### Scenario#150.05: The artifact page offers both restarts of its build So that a build can be repeated from the page that shows why it failed. - **Given** the artifact page of a recorded build - **When** it is shown - **Then** it has a line `Restart:` with both buttons ##### Verified by - [UiArtifactPageRestTest](../../src/test/kotlin/de/hoennig/werkator/server/UiArtifactPageRestTest.kt) — Scenario#150.05 #### Scenario#150.06: A private repository hides the name in a follow-up's refusal So that a refusal does not reveal a build-definition name, which is content (RFC 0003, D8). - **Given** the refusal `Runs after a green secret-deploy; restart that one` of a private repository - **When** the row is redacted - **Then** the refusal reads `Runs after a green n/a; restart that one` ##### Verified by - [RedactionUnitTest](../../src/test/kotlin/de/hoennig/werkator/server/RedactionUnitTest.kt) — Scenario#150.06 #### Scenario#150.07: The API refuses the same commit of a gone branch with the reason So that a script gets the same answer the disabled button gives. - **Given** a run whose branch is gone from origin - **When** `POST /api/builds//restart` is sent for it - **Then** the answer is `409` with `Branch deleted from origin` in `error`, and no build starts ##### Verified by - [BuildsApiControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/BuildsApiControllerRestTest.kt) — Scenario#150.07 #### Scenario#150.08: The same commit of a pull-request target re-runs its pull request So that a target build is repeated with the pull request it judged, never with a branch build. - **Given** a recorded pull-request target run of pull request 7 - **When** its same commit is restarted - **Then** pull request 7 is built again as a pull-request target ##### Verified by - [BuildsApiControllerRestTest](../../src/test/kotlin/de/hoennig/werkator/server/BuildsApiControllerRestTest.kt) — Scenario#150.08 #### Scenario#150.09: A follow-up of a branch refuses the newest commit So that a follow-up never starts before its predecessor is green. - **Given** branch `feature/x` defines the follow-up `report` of `default` in its own `.werkator.yml` - **When** the newest commit of `report` on `feature/x` is asked for - **Then** it is refused ##### Verified by - [RestartPolicyUnitTest](../../src/test/kotlin/de/hoennig/werkator/server/RestartPolicyUnitTest.kt) — Scenario#150.09 ## Solution - `BuildRestarts` decides why a restart cannot run: `sameCommitRefusal` and `newestCommitRefusal`. `RestartPolicy` reads the follow-ups of each branch at its origin head, its own layer included, as the watcher does. It reads them once per branch and head commit, since the pages poll every few seconds; a branch gone from origin, or one whose configuration cannot be read, falls back to the primary configuration. - `BuildResultDto` and `BranchDto` carry both refusals; the rows, the template and `werkator.js` only show them. - `POST /api/builds//restart` builds a run again by its key. The existing `POST /api/builds/restart?atOriginHead=true` is the newest commit and now refuses with `409`. A pull-request target used to re-run its recorded pull request even at origin head (Scenario#117.07); now it is refused there. - The fragment `restartButtons` renders the two buttons for the build tables and the artifact page. The page flags `allowRestart` and `restartAtOriginHead` are gone. - The newest commit gets an icon of its own, an arrow to a line; the actions column is wider for the third button. - `BuildsApiController.restart` stood at Mutation-CRAP 14.07; the start and the answer moved into `start` and `accepted`, which `rebuild` shares. ## Additional Changes - `docs/deployment.md` names both forms of the restart and the `409` answer. - Version 1.41.0 with its release note.