WIP: feat(home): build a pull request's version to help test it #59

Draft
gab9281 wants to merge 8 commits from gab9281/LongshotLauncher:feat/pr-builds into main
Member

Warning

This pull request is incomplete. Keep it in draft; don't merge it yet.

  • Depends on unmerged work: it needs Longshot pull request 353 merged, and a maintainer to make the pull request key and list it in ReleaseKeys.PullRequestBuiltIn (here) and scripts/release_keys.json (Longshot). Until then the picker lists pull requests but installs nothing.
  • Never installed end to end: no pull request build exists yet, so the download, the signature check with the pull request key, the build and the update to a newer push have never run against real files.
  • Seen on Linux only: nobody has run it on Windows or on a Steam Deck.
  • Untranslated: 26 new texts are missing from all 12 language catalogs, so they show in English.
  • The order needs PR 353's counts: until its pull-requests.json is published, the list is oldest first.
  • Saves are shared: the separate saves the mock showed are not built; a tester is asked to export their saves first.
  • The compile and checks ran locally only: this repository's CI skips Compile and test on a pull request, so CI here checks only the title.

Why

To try a Longshot pull request today, a tester has to check it out and build it from source, so most of the hardware we need never runs a change before it is merged. This adds Build another version > A pull request to the launcher, to help the team get a better-quality port by testing features before they are merged: a tester picks a pull request by number or title, filtered to testing wanted by default (drafts are never listed), and the launcher installs the builder CI made of it.

Requirements and source

  • Source: MagicBeanDev/Longshot issue 337, the idea and the mock.
  • Requires: MagicBeanDev/Longshot pull request 353, the CI that builds non-draft pull requests and publishes them as pr-<number>-<8 hex> pre-releases signed with the pull request key. Until it is merged and the key is made (RELEASING.md there, "The pull request key"), this picker lists pull requests but installs none, and says why.

What changed

  • Picker (App/Views/ChannelPickerView.axaml, ChannelPickerViewModel): a Pull request choice beside the channels.
    • The search and the list sit in a box of their own. The dialog stays inside the window: its choices scroll, and Cancel and Build stay at the bottom.
    • A search box matches a number (332, #332) or words of the title or the author, either as typed or as letters in order within one word (rendr targts).
    • Drafts (Forgejo's mark, or a WIP: or [WIP] title, as Longshot's CI reads it) are never listed, since CI builds none.
    • A pull request labelled for the other system only is never listed (platform/windows on Linux; platform/linux or platform/steam-deck on Windows). A search that finds only those says so.
    • A Testing wanted filter is on by default; off, every other open pull request shows.
    • The order puts the pull requests the fewest people other than their author have commented on or reviewed first, then the oldest. The counts come from Longshot's pull-requests.json (PR 353); without that file, the list is oldest first.
    • Each row shows its build state (ready, built from an earlier push, or none, with the reason) and has a Read button that opens the pull request on git.magicbean.dev.
    • The pick stays among the rows shown: the search or the filter never leaves a hidden pull request to build. Nothing can be picked without a pull request key.
    • When the builds can't be read, it says so with Try again, rather than showing every pull request as not built.
    • A warning that the code is unreviewed and that saves are shared ("export them first").
    • A note when the launcher trusts no pull request key yet.
  • Core:
    • Core/PullRequests.cs: the open pull requests from the public API (read twice when an answer may come next time, as the release list is), the fuzzy match, and the newest build of each: the build of the pull request's head when there is one, else the one published last.
    • Versions: the pr track, the pr-<number>-<8 hex> tags, and "PR #222 · 1a2b3c4d" as the shown name.
    • UpdatePolicy: a pull request build's only update is a newer build of the same pull request; no channel's version updates to or from one.
    • Releases and ReleaseTrust:
      • pr- tags are verified with the pull request keys only, never the release key, and vice versa.
      • ReleaseKeys.PullRequestBuiltIn is empty until a maintainer adds the key.
      • LONGSHOT_LAUNCHER_PR_TRUST adds one key for a test key.
  • Home (HomeView.axaml, HomeViewModel, LauncherService):
    • A pull request build's tag is marked pr and has a Read button.
    • Its update line reads "A newer push is built".
    • Its meta line says when the pull request was merged or closed.
    • The update check asks about the installed pull requests all at once, after the launcher's own update. An unreadable answer about one is skipped rather than failing the whole check, and a pull request whose builds were deleted is offered no update.
  • Scripts: scripts/release.py's download(to=) streams a large file, and leaves nothing behind when it fails. The file is kept identical to Longshot's, which PR 353 changes.
  • Docs: docs/releases.md, "Pull request builds", identical to Longshot's copy in PR 353.
  • Tests:
    • tests/launcher_core/PullRequestChecks.cs: tags, drafts, update policy, key separation, list and one parsing (an error page is none), newest builds (the head's first), and match order.
    • tests/test_launcher_core.py: the pr entries in the track vectors.

Testing

Tested (Linux, .NET SDK 10.0.114, at f5a070e):

  • dotnet build LongshotLauncher.slnx --configuration Release -p:RestoreLockedMode=true, as CI runs it: 0 warnings, 0 errors.
  • python -I tests/test_launcher_core.py --strict --longshot <Longshot main>: passes, with PullRequestChecks among the core's checks.
  • python -I tests/test_release.py: 28 tests, OK.
  • scripts/translations.py: no faults.
  • A /code-review high of the whole pull request: its 10 findings are fixed in 1abc2db.
  • The launcher at 1abc2db on Linux: the picker opens, lists, filters and searches, and the dialog stays inside the window (Screenshots).

This repository's CI skips Compile and test on a pull request, so on this PR it checks only the title; the runs above are local.

Not tested:

  • Everything in the incomplete list at the top.

Needs other testers: once a pull request build is published, one install from the picker on Linux and on Windows.

Screenshots

The launcher at 1abc2db on Linux (KDE, 125% scale), against git.magicbean.dev's open pull requests. No pull request build is published yet and this launcher has no pull request key, so every row says Not built yet and none can be picked.

Build another version: the new choice.

The version picker with "A pull request"

Testing wanted (the default): only the pull requests a maintainer labelled.

Testing wanted on

Testing wanted off: every open pull request that is not a draft. The dialog stays inside the window, and its choices scroll.

Testing wanted off

A search: vk.

Searching vk

Risk and rollback

  • A pull request build runs code nobody has reviewed. The picker says so before installing, shows testing wanted by default, and installs only builds signed with the pull request key.
  • Saves are shared with the player's other versions; the picker asks to export them first. Separate saves, which the mock showed, are not in this PR.
  • Reverting the commit removes the picker. Installed pull request builds stay as versions the player can delete.

🤖 Generated with Claude Code

> [!WARNING] > **This pull request is incomplete. Keep it in draft; don't merge it yet.** > > - **Depends on unmerged work:** it needs [Longshot pull request 353](https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/353) merged, and a maintainer to make the pull request key and list it in `ReleaseKeys.PullRequestBuiltIn` (here) and `scripts/release_keys.json` (Longshot). Until then the picker lists pull requests but installs nothing. > - **Never installed end to end:** no pull request build exists yet, so the download, the signature check with the pull request key, the build and the update to a newer push have never run against real files. > - **Seen on Linux only:** nobody has run it on Windows or on a Steam Deck. > - **Untranslated:** 26 new texts are missing from all 12 language catalogs, so they show in English. > - **The order needs PR 353's counts:** until its `pull-requests.json` is published, the list is oldest first. > - **Saves are shared:** the separate saves the mock showed are not built; a tester is asked to export their saves first. > - **The compile and checks ran locally only:** this repository's CI skips *Compile and test* on a pull request, so CI here checks only the title. ## Why To try a Longshot pull request today, a tester has to check it out and build it from source, so most of the hardware we need never runs a change before it is merged. This adds **Build another version > A pull request** to the launcher, to help the team get a better-quality port by testing features before they are merged: a tester picks a pull request by number or title, filtered to *testing wanted* by default (drafts are never listed), and the launcher installs the builder CI made of it. ## Requirements and source - **Source:** [MagicBeanDev/Longshot issue 337](https://git.magicbean.dev/MagicBeanDev/Longshot/issues/337), the idea and the mock. - **Requires:** [MagicBeanDev/Longshot pull request 353](https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/353), the CI that builds non-draft pull requests and publishes them as `pr-<number>-<8 hex>` pre-releases signed with the **pull request key**. Until it is merged and the key is made (RELEASING.md there, "The pull request key"), this picker lists pull requests but installs none, and says why. ## What changed - **Picker** (`App/Views/ChannelPickerView.axaml`, `ChannelPickerViewModel`): a *Pull request* choice beside the channels. - The search and the list sit in a box of their own. The dialog stays inside the window: its choices scroll, and Cancel and Build stay at the bottom. - A search box matches a number (`332`, `#332`) or words of the title or the author, either as typed or as letters in order within one word (`rendr targts`). - Drafts (Forgejo's mark, or a `WIP:` or `[WIP]` title, as Longshot's CI reads it) are never listed, since CI builds none. - A pull request labelled for the other system only is never listed (`platform/windows` on Linux; `platform/linux` or `platform/steam-deck` on Windows). A search that finds only those says so. - A **Testing wanted** filter is on by default; off, every other open pull request shows. - The order puts the pull requests the fewest people other than their author have commented on or reviewed first, then the oldest. The counts come from Longshot's `pull-requests.json` (PR 353); without that file, the list is oldest first. - Each row shows its build state (**ready**, **built from an earlier push**, or none, with the reason) and has a **Read** button that opens the pull request on git.magicbean.dev. - The pick stays among the rows shown: the search or the filter never leaves a hidden pull request to build. Nothing can be picked without a pull request key. - When the builds can't be read, it says so with *Try again*, rather than showing every pull request as not built. - A warning that the code is unreviewed and that saves are shared ("export them first"). - A note when the launcher trusts no pull request key yet. - **Core:** - `Core/PullRequests.cs`: the open pull requests from the public API (read twice when an answer may come next time, as the release list is), the fuzzy match, and the newest build of each: the build of the pull request's head when there is one, else the one published last. - `Versions`: the `pr` track, the `pr-<number>-<8 hex>` tags, and "PR #222 · 1a2b3c4d" as the shown name. - `UpdatePolicy`: a pull request build's only update is a newer build of the same pull request; no channel's version updates to or from one. - `Releases` and `ReleaseTrust`: - `pr-` tags are verified with the pull request keys only, never the release key, and vice versa. - `ReleaseKeys.PullRequestBuiltIn` is empty until a maintainer adds the key. - `LONGSHOT_LAUNCHER_PR_TRUST` adds one key for a test key. - **Home** (`HomeView.axaml`, `HomeViewModel`, `LauncherService`): - A pull request build's tag is marked `pr` and has a **Read** button. - Its update line reads "A newer push is built". - Its meta line says when the pull request was merged or closed. - The update check asks about the installed pull requests all at once, after the launcher's own update. An unreadable answer about one is skipped rather than failing the whole check, and a pull request whose builds were deleted is offered no update. - **Scripts:** `scripts/release.py`'s `download(to=)` streams a large file, and leaves nothing behind when it fails. The file is kept identical to Longshot's, which PR 353 changes. - **Docs:** `docs/releases.md`, "Pull request builds", identical to Longshot's copy in PR 353. - **Tests:** - `tests/launcher_core/PullRequestChecks.cs`: tags, drafts, update policy, key separation, list and one parsing (an error page is none), newest builds (the head's first), and match order. - `tests/test_launcher_core.py`: the `pr` entries in the track vectors. ## Testing **Tested** (Linux, .NET SDK 10.0.114, at f5a070e): - `dotnet build LongshotLauncher.slnx --configuration Release -p:RestoreLockedMode=true`, as CI runs it: 0 warnings, 0 errors. - `python -I tests/test_launcher_core.py --strict --longshot <Longshot main>`: passes, with `PullRequestChecks` among the core's checks. - `python -I tests/test_release.py`: 28 tests, OK. - `scripts/translations.py`: no faults. - A `/code-review high` of the whole pull request: its 10 findings are fixed in 1abc2db. - The launcher at 1abc2db on Linux: the picker opens, lists, filters and searches, and the dialog stays inside the window (Screenshots). This repository's CI skips *Compile and test* on a pull request, so on this PR it checks only the title; the runs above are local. **Not tested:** - Everything in the incomplete list at the top. **Needs other testers:** once a pull request build is published, one install from the picker on Linux and on Windows. ## Screenshots The launcher at 1abc2db on Linux (KDE, 125% scale), against git.magicbean.dev's open pull requests. No pull request build is published yet and this launcher has no pull request key, so every row says *Not built yet* and none can be picked. **Build another version: the new choice.** ![The version picker with "A pull request"](https://git.magicbean.dev/attachments/7299eb7a-4f71-4ad4-8894-caaef1b22653) **Testing wanted (the default): only the pull requests a maintainer labelled.** ![Testing wanted on](https://git.magicbean.dev/attachments/71fe3a5c-b529-4340-87cc-21cc960f0ee3) **Testing wanted off: every open pull request that is not a draft. The dialog stays inside the window, and its choices scroll.** ![Testing wanted off](https://git.magicbean.dev/attachments/1562cdaa-6114-40f2-b8a9-07081fa87981) **A search: `vk`.** ![Searching vk](https://git.magicbean.dev/attachments/45d14077-7a59-4c18-89d8-f7d8894e6c39) ## Risk and rollback - A pull request build runs code nobody has reviewed. The picker says so before installing, shows *testing wanted* by default, and installs only builds signed with the pull request key. - Saves are shared with the player's other versions; the picker asks to export them first. Separate saves, which the mock showed, are not in this PR. - Reverting the commit removes the picker. Installed pull request builds stay as versions the player can delete. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Build another version > A pull request: the open pull requests of Longshot, found by number or by
words of the title or author (fuzzy), the ones labelled "testing wanted" first, each with its build
and a Read button. A pull request's build is a pre-release pr-<number>-<commit> on no channel,
taken only with a pull request key that never stands in for the release key (none built in yet);
its one update is a newer push of the same pull request. docs/releases.md, "Pull request builds".

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
fix(release): release.download streams a large file with to=, as Longshot's
All checks were successful
CI / Compile and test (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
cbbe01bdfc
The same file in both repositories: Longshot's scripts/pr_builds.py downloads workflow artifacts with it (MagicBeanDev/Longshot pull request 353).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
fix(home): the picker compiles, and a word typed short matches within one word
All checks were successful
CI / Compile and test (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 2s
b4025c1e8d
- ChannelPickerView.axaml declared xmlns:c twice (Avalonia AVLN1001: not valid XAML).
- PullRequests.Match: letters in order are looked for within one word of the title or the author, from its first letter
  ("rendr" is "render"); across the whole title "hud" matched "their ... amateurwizrd", which the core's check caught.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Author
Member

b4025c1: the first compile found two faults, both fixed. ChannelPickerView.axaml declared xmlns:c twice, which is not valid XAML. The core's check caught the search's letters-in-order match running across the whole title and author, so hud matched an unrelated pull request; it now matches within one word, from its first letter. Locally the Release build has 0 warnings, and test_launcher_core.py --strict and test_release.py pass. This repository's CI skips the compile on pull requests.

b4025c1: the first compile found two faults, both fixed. ChannelPickerView.axaml declared `xmlns:c` twice, which is not valid XAML. The core's check caught the search's letters-in-order match running across the whole title and author, so `hud` matched an unrelated pull request; it now matches within one word, from its first letter. Locally the Release build has 0 warnings, and test_launcher_core.py --strict and test_release.py pass. This repository's CI skips the compile on pull requests.
feat(home): the pull request picker in a box of its own, drafts never listed
All checks were successful
CI / Compile and test (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
f81cd5fa15
- The search and the list sit in a box of their own (Border.prbox), set off from the channels above.
- A draft (Forgejo's mark, or a WIP: or [WIP] title, as Longshot's CI reads it: PullRequests.IsDraft) is never listed: CI
  builds none. Testing wanted is still on at first; off, every other open pull request shows. A row with no build says
  "Not built yet": every pull request out of draft gets one.
- The choice's line: help the team get a better-quality port by testing features before they are merged.
- docs/releases.md: the same (identical in Longshot's pull request 353).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
fix(home): the version picker stays inside the window
All checks were successful
CI / Compile and test (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 5s
4ae4e76a1c
With the pull request list open the picker grew past the window's 600 px, and Cancel and Build went off its bottom. The
dialog is now at most 560 px: its title stays at the top, Cancel and Build at the bottom, and the choices between them
scroll (the pull request list keeps its own, shorter scroll).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Author
Member

f81cd5f, 4ae4e76, from trying the picker: the search and the list sit in a box of their own; a draft (Forgejo's mark, or a WIP: or [WIP] title) is never listed, and Testing wanted stays on by default, showing every other open pull request when it is off; the choice now reads "Help the team get a better-quality port by testing features before they are merged"; and the dialog is at most 560 px tall, so Cancel and Build no longer go past the window's bottom (the choices scroll). Release build 0 warnings; test_launcher_core.py --strict and test_release.py pass.

f81cd5f, 4ae4e76, from trying the picker: the search and the list sit in a box of their own; a draft (Forgejo's mark, or a WIP: or [WIP] title) is never listed, and Testing wanted stays on by default, showing every other open pull request when it is off; the choice now reads "Help the team get a better-quality port by testing features before they are merged"; and the dialog is at most 560 px tall, so Cancel and Build no longer go past the window's bottom (the choices scroll). Release build 0 warnings; test_launcher_core.py --strict and test_release.py pass.
fix(home): the code review's findings on the pull request picker
All checks were successful
CI / Compile and test (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 5s
1abc2db50d
- The update check: the launcher's own update is checked before the pull requests, which are asked all at once; one that
  answers what cannot be read (an error page) is skipped, not the end of the check; a pull request whose builds are gone
  (closed, back to draft) is offered no update; and its newest build is its head's when there is one, not merely the one
  published last (a draft a failed run left is published again later).
- The picker: nothing is picked or built without a pull request key; the pick stays among the rows shown, so the query or
  the filter never leaves a hidden one to build; the rows are worked out once per change; and when the builds cannot be
  read, it says so with Try again instead of "Not built yet" everywhere.
- PullRequests reads twice for an answer that may come next time, as Releases does, and with ReleaseManifest's Opt.
- scripts/release.py: download(to=) leaves no part of a file behind when it fails (as Longshot's).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Author
Member

1abc2db fixes a /code-review high of the whole pull request (10 findings). The update check now checks the launcher's own update first and asks about installed pull requests all at once; an error page about one is skipped rather than failing the check; a pull request whose builds were deleted is offered no update; and a pull request's newest build is its head's. The picker never builds a pull request the search or filter hides, picks nothing without a pull request key, says so when the builds can't be read, and works out its rows once per change. PullRequests also retries a transient failure, and release.py's download(to=) leaves no partial file. Release build: 0 warnings. test_launcher_core.py --strict and test_release.py pass, and CI passed on 1abc2db (the title only: this repository's CI skips compiling on pull requests).

Still incomplete, and kept in draft. The description now opens with what is missing: Longshot PR 353 and the pull request key, an end-to-end install, Windows, real screenshots, the translations, and separate saves.

1abc2db fixes a `/code-review high` of the whole pull request (10 findings). The update check now checks the launcher's own update first and asks about installed pull requests all at once; an error page about one is skipped rather than failing the check; a pull request whose builds were deleted is offered no update; and a pull request's newest build is its head's. The picker never builds a pull request the search or filter hides, picks nothing without a pull request key, says so when the builds can't be read, and works out its rows once per change. PullRequests also retries a transient failure, and release.py's download(to=) leaves no partial file. Release build: 0 warnings. test_launcher_core.py --strict and test_release.py pass, and CI passed on 1abc2db (the title only: this repository's CI skips compiling on pull requests). **Still incomplete, and kept in draft.** The description now opens with what is missing: Longshot PR 353 and the pull request key, an end-to-end install, Windows, real screenshots, the translations, and separate saves.
Author
Member

Real screenshots of the picker at 1abc2db (Linux, KDE) replace the mock in the description: the new choice, Testing wanted on (the default), off (every pull request that isn't a draft), and a search. Still a draft: see the incomplete list at the top.

Real screenshots of the picker at 1abc2db (Linux, KDE) replace the mock in the description: the new choice, Testing wanted on (the default), off (every pull request that isn't a draft), and a search. Still a draft: see the incomplete list at the top.
feat(home): the picker hides other systems' pull requests and shows first those fewest people looked at
All checks were successful
CI / Compile and test (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
f5a070e17d
- A pull request labelled for the other system only is not listed: platform/windows on Linux, platform/linux or
  platform/steam-deck on Windows (PullRequest.For); one with neither label, or both, is. A search that finds only those
  says so ("Only pull requests for Windows match: #12.").
- The order: those the fewest people other than their author have commented on or reviewed first, then the oldest
  (PullRequests.Order), from Longshot's pull-requests.json (PullRequests.CountsAsync: its pull request 353's counts); without
  that file, the oldest first. A pull request's Created is read from the list.
- docs/releases.md: the same as Longshot's copy.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Author
Member

f5a070e: a pull request labelled for the other system only is no longer listed (platform/windows on Linux; platform/linux or platform/steam-deck on Windows), and a search that finds only those says so. The order now puts first the pull requests the fewest people other than their author have looked at, then the oldest, from Longshot's pull-requests.json (PR 353's counts); without that file, oldest first. Release build 0 warnings; the core checks, with new ones for the platform rule, the counts file and the order, pass; CI passed. Still a draft.

f5a070e: a pull request labelled for the other system only is no longer listed (platform/windows on Linux; platform/linux or platform/steam-deck on Windows), and a search that finds only those says so. The order now puts first the pull requests the fewest people other than their author have looked at, then the oldest, from Longshot's pull-requests.json (PR 353's counts); without that file, oldest first. Release build 0 warnings; the core checks, with new ones for the platform rule, the counts file and the order, pass; CI passed. Still a draft.
All checks were successful
CI / Compile and test (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
This pull request is marked as a work in progress.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'feat/pr-builds:gab9281-feat/pr-builds'
git switch 'gab9281-feat/pr-builds'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/LongshotLauncher!59
No description provided.