Wherever a branch name is shown, its open Gitea pull request stands beside it, linked to the pull request. The watcher reads the open pull requests once per poll cycle into PullRequestIndex; pages and API only read from there. Needs the Gitea connection and token; forks, private repositories for visitors, and gone branches show none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #81
111 lines
6.5 KiB
Markdown
111 lines
6.5 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
|
|
|
|
Werkator works on branches; the pull requests in Gitea do not show anywhere in its pages.
|
|
A reviewer who sees a red build on a branch has to look up the pull request of that branch in Gitea by hand.
|
|
The branch name alone often says little; `PR#12` is the name the work is discussed under.
|
|
|
|
## Non-Goals
|
|
|
|
- No builds of pull requests or of merge results; Werkator still builds branches.
|
|
- No pull requests from forks: their head branch belongs to another repository.
|
|
- No closed or merged pull requests; the link shows while a pull request is open.
|
|
- No other forge than Gitea, and nothing without a token.
|
|
- No change to the pull-request gate (`requirePullRequest`); it keeps reading `refs/pull/*/head` without a token.
|
|
|
|
## The Scenarios
|
|
|
|
### Feature: the open pull request beside every branch name
|
|
|
|
#### Background
|
|
|
|
- A branch *heads* a pull request when the pull request's head is that branch of the repository itself.
|
|
- The Gitea connection is `gitea.baseUrl`, `gitea.owner`, `gitea.repo` and `git.token` — the one the statuses are published with.
|
|
|
|
#### Scenario#81.01: Werkator knows the open pull requests of each repository
|
|
|
|
So that the pages can show them without asking Gitea on every request.
|
|
|
|
- **Given** a repository with a Gitea connection and a token
|
|
- **When** the watcher's poll cycle reaches origin
|
|
- **Then** it reads the open pull requests from Gitea, keyed by head branch
|
|
- **and** a pull request from a fork is left out
|
|
- **and** of several pull requests from one branch, the newest wins
|
|
- **and** a list longer than one page is read page by page
|
|
- **And** nothing is read before the first poll cycle, so CLI runs and tests never ask Gitea
|
|
|
|
##### Verified by
|
|
|
|
- [GiteaClientTest](../../src/test/kotlin/de/hoennig/werkator/gitea/GiteaClientTest.kt) — `readOpenPullRequests`: own branches only, the newest wins, paging, token header
|
|
- [PullRequestIndexTest](../../src/test/kotlin/de/hoennig/werkator/gitea/PullRequestIndexTest.kt) — nothing known before the first refresh, a refresh replaces one repository's list
|
|
- [WatcherTest](../../src/test/kotlin/de/hoennig/werkator/watcher/WatcherTest.kt) — every poll cycle that reached origin refreshes the pull requests
|
|
|
|
#### Scenario#81.02: Every branch name carries its pull request
|
|
|
|
So that a reader gets from a build to the discussion of its work in one click.
|
|
|
|
- **Given** a branch that heads an open pull request
|
|
- **When** the Latest, Branches, History or Current Builds view, the build page, the Repositories page or the home page shows the branch
|
|
- **Then** `PR#<number>` stands beside the branch name, linked to the pull request in Gitea
|
|
- **and** on a home card it is plain text, because the card is a link already
|
|
- **And** the JSON API carries it as `pullRequest` with `number` and `url`, so every poll renders it too
|
|
|
|
##### Verified by
|
|
|
|
- [BuildsApiControllerTest](../../src/test/kotlin/de/hoennig/werkator/server/BuildsApiControllerTest.kt) — `latest`, `history` and `current` carry the pull request
|
|
- [BranchListingTest](../../src/test/kotlin/de/hoennig/werkator/server/BranchListingTest.kt) — a branch row and its named pools carry it
|
|
- [RepoOverviewsTest](../../src/test/kotlin/de/hoennig/werkator/server/RepoOverviewsTest.kt) — the newest build of an overview carries it
|
|
- [UiControllerTest](../../src/test/kotlin/de/hoennig/werkator/server/UiControllerTest.kt) — the pages render the `PR#12` link, the Repositories page too
|
|
- [UiViewsTest](../../src/test/kotlin/de/hoennig/werkator/server/UiViewsTest.kt) — a link on the Repositories page, plain text on a home card
|
|
|
|
#### Scenario#81.03: A pull request is content
|
|
|
|
So that a private repository still gives out no content (RFC 0003, D8).
|
|
|
|
- **Given** a private repository and a visitor without the control token
|
|
- **When** a page or the API shows its builds
|
|
- **Then** no pull request is shown, like the branch name it belongs to
|
|
- **And** a build whose branch is gone from origin shows no pull request either
|
|
|
|
##### Verified by
|
|
|
|
- [BuildsApiControllerTest](../../src/test/kotlin/de/hoennig/werkator/server/BuildsApiControllerTest.kt) — a private repository and a gone branch carry no pull request
|
|
- [RepoOverviewsTest](../../src/test/kotlin/de/hoennig/werkator/server/RepoOverviewsTest.kt) — a hidden repository's newest build carries none
|
|
|
|
#### Scenario#81.04: Without Gitea nothing changes, and a Gitea outage does not blink
|
|
|
|
So that the feature costs nothing where it cannot work.
|
|
|
|
- **Given** no Gitea connection or no token, **then** no request is made and no pull request is shown
|
|
- **Given** Gitea fails or answers garbage, **then** the last known pull requests stay
|
|
- **and** the failure is logged once, not once per poll cycle, and so is the recovery
|
|
|
|
##### Verified by
|
|
|
|
- [GiteaClientTest](../../src/test/kotlin/de/hoennig/werkator/gitea/GiteaClientTest.kt) — `Disabled` without a request, `Error` instead of an exception
|
|
- [PullRequestIndexTest](../../src/test/kotlin/de/hoennig/werkator/gitea/PullRequestIndexTest.kt) — a failed read keeps the list, a disabled client drops it
|
|
|
|
## The Solution
|
|
|
|
`GiteaClient.readOpenPullRequests` lists `GET /api/v1/repos/{owner}/{repo}/pulls?state=open`, 50 per page, at most ten pages.
|
|
It keeps a pull request only when `head.repo_id` equals `base.repo_id`, and it takes the link from Gitea's own `html_url`.
|
|
|
|
`PullRequestIndex` holds the result per `RepoContext`.
|
|
The watcher refreshes it at the end of each repository's poll cycle, after a successful fetch: an unreachable origin most likely means an unreachable forge, and the last list stays.
|
|
The pages and the API only read from the index.
|
|
A page is polled every few seconds by every open browser; a slow forge must never slow a page down.
|
|
|
|
The DTOs `BuildResultDto`, `BranchDto` and `CurrentBuildDto` carry `pullRequest` (`number`, `url`).
|
|
`BuildResultDto.from` drops it for a gone branch, and `Redaction` drops it with the branch.
|
|
The views take it from the DTO, so the first paint and every poll say the same.
|
|
`werkator.js` renders the same link with `pullRequestLink`, and `UiFormats.pullRequest` writes the home card's text like the script does.
|
|
|
|
## Additional Changes
|
|
|
|
- `docs/configuration.md` explains the links under the `gitea` notes; `docs/llms.md` and the architecture skill name them.
|
|
- Version 1.23.0 with its release note.
|
|
1.22.0 is taken by the open branch of RFC 0006.
|