Skip to content

ci(release): build the JS bundle once and start desktop targets independently - #490

Merged
incognitojam merged 1 commit into
mainfrom
ci/release-build-js-once
Sep 28, 2026
Merged

incognitojam merged 1 commit into
mainfrom
ci/release-build-js-once

Conversation

@incognitojam

Copy link
Copy Markdown
Owner

Note

Fork Release now starts each desktop target as soon as a single shared JS bundle is built, instead of waiting for all five CLI archives. A dry-run stable release took 10m17s, down from 17m07s.

Problem

Fork Release started desktop packaging only after the full five-platform CLI archive matrix had finished, including Windows ARM64. macOS and Linux need no CLI archive, and Windows only needs the Linux x64 archive for WSL. In a recent dry run, the desktop builds waited about nine minutes before starting.

Every desktop job also rebuilt the server, web client, and Electron main, which took 50–56 seconds per job. The CLI archive workflow built the server and web client again in its own bundle job, and so did the Windows WSL archive.

Change

  • A new fork-js-bundle.yml builds the server, web client, and Electron main once per run and uploads them as the js-bundle artifact. Desktop packaging downloads it and runs with --skip-build. The CLI archive jobs download it instead of running their own bundle job. This adapts the build-once structure from upstream pingdotgg/t3code#11606. That PR is already recorded as imported, but it only changed upstream's release.yml, which the fork does not use.
  • Fork Release calls one desktop build per target, as Fork Nightly already does. Each target waits only for the bundle. The Windows target builds its own Linux x64 archive for WSL instead of reusing the release's archive, as in the nightly. As a result, the stable Windows installer no longer embeds the exact linux-x64 archive attached to the release; it embeds another archive built from the same commit and bundle.
  • Fork Nightly and the manual desktop build also build the bundle once.
  • The bundle job sets T3CODE_COMMIT_HASH to the checked-out candidate. Previously the CLI bundle fell back to GITHUB_SHA, which is the head of the branch that started the run, not the candidate being released.
  • cli_archives_ready is removed from fork-desktop-build.yml because nothing uses it anymore.

Validation

Fork Release dry run from this branch, promoting v0.1.0-nightly.20260926.421:

Before After
Whole run 17m07s 10m17s
macOS desktop job starts 9m33s in 2m58s in
Windows desktop job starts 9m26s in 4m23s in
  • All jobs passed, and the release job assembled the same set of artifacts as the earlier dry run. None of the desktop jobs ran the JS build.
  • I compared the macOS app from this run with nightly .421, which built the same commit on macOS in the old way. dist-electron is byte-identical even though this run built it on Linux. The server bundle differs only in the release version and channel strings.
  • actionlint passes for all fork workflows, and the fork feature ledger check passes.

Not exercised:

  • Fork Nightly, because even its dry run creates a draft release.
  • The manual desktop build workflow.
  • Installing or launching the built artifacts.

Windows desktop packaging and the CLI archive chain now finish at about the same time. The Windows ARM64 CLI archive is the slowest part of that chain and will be addressed separately.


Written by an agent (Claude Code, claude-opus-5-5).

…endently

Fork Release started desktop packaging only after the full five-platform
CLI archive matrix finished, although macOS and Linux need no archive and
Windows only needs Linux x64 for WSL. It now calls one desktop build per
target, as Fork Nightly does, and the Windows target builds its own WSL
archive.

Every desktop job and the CLI archive bundle job also rebuilt the server,
web client, and Electron main. A single fork-js-bundle.yml job now builds
them once per run with the candidate's commit hash, and desktop packaging
uses --skip-build. This adapts the build-once graph from upstream
pingdotgg#11606, whose recorded import only changed upstream's
release.yml.
@incognitojam
incognitojam merged commit e63beda into main Sep 28, 2026
17 checks passed
@incognitojam
incognitojam deleted the ci/release-build-js-once branch September 28, 2026 15:24
incognitojam added a commit that referenced this pull request Sep 28, 2026
…the page (#504)

Opening the Pull Requests page with a search that is only a number or
`true`, such as `/pull-requests?q=13565`, crashed the page with
`(search.q ?? "").trim is not a function`. That is the URL for a search
by pull request number.

The router parses `13565` as a number and `true` as a boolean. The
page's search validator dropped a `q` that was not a string, but the
root route has no validator and passes the raw parameters through, so
the number still reached `.trim()`. The validator now keeps a number or
boolean `q` as its text, so `?q=13565` searches for pingdotgg#13565.

| Before (`?q=13565`) | After (`?q=490`) |
| --- | --- |
| ![Error
screen](https://github.com/user-attachments/assets/67f374ab-8c99-4a99-bc6a-65baad95b70e)
| ![Search box holds 490 and lists
#490](https://github.com/user-attachments/assets/4a415d3a-3026-4b8d-8f34-e3121c7d1a31)
|

## Validation

In a dev server on a copy of real data, `/pull-requests?q=490`,
`?q=true`, and `?q=fbjni` each load without an error. The search box
shows the value, and `?q=490` lists #490. Web typecheck passes. The
before screenshot was taken with the same page code before this change.

---
Written by an agent (Claude Code, claude-opus-5-5).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant