Skip to content

fork: the server and CLI update from the desktop app's repository - #1

Merged
scuttletoby merged 3 commits into
mainfrom
fork/release-repository
Nov 12, 2026
Merged

scuttletoby merged 3 commits into
mainfrom
fork/release-repository

Conversation

@scuttletoby

@scuttletoby scuttletoby commented Nov 12, 2026 •

Copy link
Copy Markdown
Owner

Fork patch 1 of the series: the server and CLI update from the same repository as the desktop app.

What changes

  • Upstream already has a build setting for the desktop updater feed and the web client's release links: T3CODE_DESKTOP_UPDATE_REPOSITORY, else GITHUB_REPOSITORY in a GitHub Actions build, else upstream pingdotgg/t3code.
  • This patch makes every bundle that carries @t3tools/shared/cliRelease (server, desktop main process, web) read the same setting in the same order, baked in as __T3CODE_RELEASE_REPOSITORY__ (scripts/lib/release-repository.ts).
  • cliRelease.ts uses it for the release index (t3 update), archive and SHA256SUMS downloads (the Update button's pinned-runtime install, the desktop app's SSH installs) and the releases page link.
  • Unset in a local build: nothing changes, builds use upstream.
  • A malformed value fails the build, so a typo can never ship a build that quietly updates from upstream.
  • Upstream files touched: three vite.config.ts files (one import and one define entry each), apps/web/tsconfig.json (one include), cliRelease.ts, one line in docs/operations/release.md. build-desktop-artifact.ts is not touched.

Why

A fork build must only ever be replaced by another fork build. With this, Update and t3 update list and download only the fork's releases; an upstream version that has no fork release fails safely ("no such release") and the fork stays installed. T3CODE_RELEASE_BASE_URL and the managed-policy mirror still override it at run time, as before.

Review round 1

Round 0 added a second setting, T3CODE_RELEASE_REPOSITORY, which the desktop feed read only as a fallback while the server read it first. A build that set only T3CODE_DESKTOP_UPDATE_REPOSITORY shipped a desktop app on the fork's feed with a server that updated from upstream. Now there is one setting and one order, and a test checks the server's value against the desktop updater's own resolver (resolveGitHubPublishConfig) for every combination. The former name is still accepted when it names the same repository, and fails the build otherwise (#2's release workflow still sets it).

Tested

  • scripts/lib/release-repository.test.ts: 6 pass; 5 of them fail against the round-0 code. Scripts release-repository, build-desktop-artifact, public-config: 57 pass.
  • packages/shared cliRelease tests: 6 pass (index, download and checksum URLs use the build's repository; upstream stays the default).
  • apps/server cli/update, cloud/pinnedRuntime, cloud/selfUpdate: 52 pass.
  • Typecheck of scripts, shared, server, web; vp fmt, lint, knip (pre-commit).
  • vp pack of the server with only T3CODE_DESKTOP_UPDATE_REPOSITORY set: the bundle contains resolveCliReleaseRepository("<owner>/<repo>"). With only a disagreeing T3CODE_RELEASE_REPOSITORY, the build fails with the "disagrees" message.

Not covered

  • The desktop app has no typecheck script; its change is one import and one define entry.
  • Mobile builds are not built by the fork and keep the upstream default.
  • Links in the triage prompt and startup-recovery page still point at upstream (they are about reporting issues, not updates).

🤖 Generated with Claude Code

noreply and others added 2 commits November 12, 2026 08:50
…OSITORY)

A build made with T3CODE_RELEASE_REPOSITORY=<owner>/<repo> lists, downloads and
links releases only from that repository: the server's `t3 update`, the Update
button's pinned-runtime download, the desktop app's SSH installs, and the
desktop app's own electron-updater feed. Without the setting nothing changes
(upstream pingdotgg/t3code). A malformed value fails the build instead of
quietly falling back to upstream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without T3CODE_RELEASE_REPOSITORY, an Actions build takes GITHUB_REPOSITORY, as
the desktop updater feed already does. Reusable workflows (release-desktop.yml
re-bundles the server for the CLI executable) do not pass the caller's env on,
so the explicit variable alone would let a fork's CLI archive name upstream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@scuttletoby

Copy link
Copy Markdown
Owner Author

lens: correctness+design+tests · reviewer: opus high (t3lens-cdt) · read at: 7c9513c
covered: packages/shared/src/cliRelease.ts, scripts/lib/release-repository.ts (+ both tests), apps/server/vite.config.ts (pack config incl. bundle/bootstrap/exe modes), apps/desktop/vite.config.ts, apps/web/vite.config.ts, apps/web/tsconfig.json, scripts/build-desktop-artifact.ts resolveGitHubPublishConfig; callers read: apps/server/src/cloud/pinnedRuntime.ts, apps/server/src/cloud/selfUpdate.ts (failure paths), apps/server/src/cli/update.ts (isUnpublishedRelease); upstream pingdotgg#21210 (555fb6f) and 6c35048 read from the fork's own history (nothing sent to upstream).
checks:

  • Tests: packages/shared src/cliRelease.test.ts 6/6 and scripts lib/release-repository.test.ts 4/4 pass locally. Mutation: with CLI_RELEASE_REPOSITORY hard-wired back to upstream, "lists and downloads releases only from the repository the build names" fails, so the test is real.
  • Every server pack mode (bundle, bootstrap, exe) shares the one define block (apps/server/vite.config.ts:91-131), so the CLI archive and the SEA binary both carry the value. Mobile bundles cliRelease without the define and stays upstream (documented in the PR).
  • Hardest case, Update button asks for an upstream-only version (desktop app on upstream 0.1.10, server on 0.1.9-fork.1): download from the fork's releases 404s -> PinnedRuntimeInstallError -> selfUpdate.ts:295-301 fails with "HTTP 404 from github.com". Nothing is installed, the fork stays, and the failure is reported, not silent.
  • State question: a rerun build is deterministic. A malformed value fails the build (release-repository.ts:24-28) instead of silently going upstream. An Actions build with no setting uses its own repository. The updater's crash, rollback and two-trigger paths are upstream code this PR doesn't change; only the URL source changes. A rollback to an older fork build downloads from the fork, as it should. Not re-traced: the runtime override T3CODE_RELEASE_BASE_URL path (unchanged lines).
  • Nothing personal: commits are by t3code-fork noreply@users.noreply.github.com, and the diff has no names, hosts or paths. Rebase cost: upstream touched apps/web/vite.config.ts 30x, scripts/build-desktop-artifact.ts 39x, apps/server/vite.config.ts 16x and cliRelease.ts 12x since 2026-09-12. The edits are one-line inserts, so conflicts stay small.
  • Upstream fix(web): follow configured desktop update repository pingdotgg/t3code#21210 claim: false as stated. The desktop app's updater feed has read T3CODE_DESKTOP_UPDATE_REPOSITORY since 6c35048 (build-desktop-artifact.ts:2695). fix(web): follow configured desktop update repository pingdotgg/t3code#21210 only makes the web client's desktop release links follow that setting (desktopUpdate.logic.ts:5-48, apps/web/vite.config.ts:52-55,323). Neither touches cliRelease.ts, so fork: the server and CLI update from the desktop app's repository #1 does not duplicate it, but see the finding.
    finding: minor scripts/lib/release-repository.ts:19-22 (with apps/web/vite.config.ts:52-56, scripts/build-desktop-artifact.ts:2697-2703). fork: the server and CLI update from the desktop app's repository #1 adds a second repository setting next to upstream's documented T3CODE_DESKTOP_UPDATE_REPOSITORY (docs/operations/release.md:477-479), and the two have opposite precedence. The desktop feed and web links read the desktop setting first. The server, CLI and desktop SSH installs read only T3CODE_RELEASE_REPOSITORY. Trace (node, outside Actions): only T3CODE_DESKTOP_UPDATE_REPOSITORY=fork-owner/t3code -> desktop feed and links follow the fork, server bundle follows pingdotgg/t3code. Both set, a/ vs b/ -> desktop a, server b. This is the "updates from upstream without saying so" split that fork: the server and CLI update from the desktop app's repository #1 is meant to prevent. The fork's own Actions builds agree through GITHUB_REPOSITORY, hence minor. Fix: extend fix(web): follow configured desktop update repository pingdotgg/t3code#21210's setting instead of adding a parallel one. resolveReleaseRepository should read T3CODE_RELEASE_REPOSITORY || T3CODE_DESKTOP_UPDATE_REPOSITORY || Actions GITHUB_REPOSITORY, and fail when both are set and differ. web vite.config.ts and build-desktop-artifact.ts should use that one resolved value. Use one validator (release-repository.ts:8-12 copies desktopUpdate.logic.ts:5-12), and add T3CODE_RELEASE_REPOSITORY to release.md:478. Add a test for the desktop-only and both-set cases. (high)
    optional nits:
  • The 404 message on an upstream-only version could say "this build updates from , which has no v release" (selfUpdate.ts:295-301). Today the user sees only "HTTP 404".

Review round 1: a second setting (T3CODE_RELEASE_REPOSITORY) with the opposite
precedence let a build set only T3CODE_DESKTOP_UPDATE_REPOSITORY and ship a
desktop app on the fork's feed with a server that updates from upstream.

The server, CLI and desktop SSH installs now read upstream's own setting in
upstream's order (T3CODE_DESKTOP_UPDATE_REPOSITORY, then GITHUB_REPOSITORY),
so all three readers agree by construction, and the patch no longer touches
build-desktop-artifact.ts or the web config's repository line. The former
name is still accepted when it names the same repository and fails the build
otherwise. A test checks the server's value against the desktop updater feed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@scuttletoby scuttletoby changed the title fork: builds name the repository they update from (T3CODE_RELEASE_REPOSITORY) fork: the server and CLI update from the desktop app's repository Nov 12, 2026
@scuttletoby

Copy link
Copy Markdown
Owner Author

fix round 1: one repository setting, one order, for desktop and server (head c798cf342e)

Finding Fix Test
T3CODE_RELEASE_REPOSITORY and T3CODE_DESKTOP_UPDATE_REPOSITORY had opposite precedence, so a build could ship the desktop app on the fork's feed with a server updating from upstream The server reads upstream's own setting in upstream's order (T3CODE_DESKTOP_UPDATE_REPOSITORY, then GITHUB_REPOSITORY); build-desktop-artifact.ts and the web config's repository line are back to upstream Server value checked against the desktop updater's resolveGitHubPublishConfig for every combination
The old name could still disagree silently Accepted only when it names the same repository; otherwise the build fails 3 disagreeing combinations throw

New tests: 6 pass; 5 fail on the round-0 code. Scripts 57, shared cliRelease 6, server update tests 52 pass; typecheck clean (desktop has no typecheck script). #2's fork-release.yml still sets T3CODE_RELEASE_REPOSITORY; it is harmless there now and can be renamed or dropped in #2.

🤖 Generated with Claude Code

@scuttletoby

Copy link
Copy Markdown
Owner Author

lens: correctness r2 · reviewer: opus high (t3lens-1-2-r) · read at: c798cf3
covered: git diff 7c9513c..c798cf3 in full (r1 lens read at 7c9513c): scripts/lib/release-repository.ts and its test, scripts/build-desktop-artifact.ts resolveGitHubPublishConfig, apps/web/vite.config.ts (repository line and define), packages/shared/src/cliRelease.ts:1-20, docs/operations/release.md:477-480. Every caller of releaseRepositoryDefine re-read: apps/server/vite.config.ts:130, apps/desktop/vite.config.ts:15, apps/web/vite.config.ts:302.
checks:

  • Head re-checked with gh pr view: c798cf3, one fix commit on top of 7c9513c.
  • r1 minor (a second repository setting with the opposite order) is fixed at its root. There is now one setting, upstream's own T3CODE_DESKTOP_UPDATE_REPOSITORY, then GITHUB_REPOSITORY, then "" for upstream (release-repository.ts:22). This is the same order as resolveGitHubPublishConfig (build-desktop-artifact.ts:2694-2702, now back to upstream's lines) and apps/web/vite.config.ts:49-52 (also back to upstream). The old name is accepted only when it equals the resolved value. Otherwise the build fails (release-repository.ts:23-29). The fork no longer touches build-desktop-artifact.ts, so there is less rebase surface than in r1.
  • Tests: scripts lib/release-repository.test.ts 6/6 and packages/shared src/cliRelease.test.ts 6/6 pass at this head. Red without the fix: with the r1 release-repository.ts and build-desktop-artifact.ts put back and the new test file kept, 5 of 6 fail. One of them is the parity test, which runs resolveReleaseRepository against the desktop updater's real resolveGitHubPublishConfig for unset, desktop-only, GITHUB_REPOSITORY-only, both-differ and old-name cases.
  • Real build, not only unit tests: on a local merge of fork: the server and CLI update from the desktop app's repository #1 into fork: release workflow for v<upstream>-fork.N on GitHub-hosted runners #2 (never pushed), node apps/server/scripts/cli.ts build-exe with Node 26.12.1. (a) T3CODE_DESKTOP_UPDATE_REPOSITORY=scuttletoby/t3code: the SEA binary dist-exe/t3 holds CLI_RELEASE_REPOSITORY = resolveCliReleaseRepository("scuttletoby/t3code"). (b) Only GITHUB_ACTIONS=true and GITHUB_REPOSITORY=scuttletoby/t3code, which is what release-desktop.yml:376 build-exe gets: the binary also names the fork. So the archive's program follows the fork even though that job never sets the desktop variable.
  • State question. A rerun build is deterministic. An unset setting gives upstream (the intended default). A malformed value or a disagreeing old name fails the build, never a silent upstream fallback. Old state: a build environment that still sets only T3CODE_RELEASE_REPOSITORY= with no GITHUB_REPOSITORY now fails loudly instead of quietly building for upstream (test case 1 of "fails the build..."). The updater's crash, swap and rollback paths are unchanged, as in r1.
  • Behaviour change, checked: the GITHUB_ACTIONS gate is gone, so a local build with a stray GITHUB_REPOSITORY now bakes it in. This matches the desktop feed and web links, which never had the gate, and agreement between them is the point of the fix. So it is not a finding.
  • Cross-check with fork: release workflow for v<upstream>-fork.N on GitHub-hosted runners #2 (a1bc1cd): fork-release.yml build_bundle sets T3CODE_DESKTOP_UPDATE_REPOSITORY=${{ github.repository }}. That is the same name, and its value equals GITHUB_REPOSITORY, so the order cannot split them. The old name appears nowhere in fork: release workflow for v<upstream>-fork.N on GitHub-hosted runners #2. In either merge order, Update cannot quietly follow upstream: fork: release workflow for v<upstream>-fork.N on GitHub-hosted runners #2 without fork: the server and CLI update from the desktop app's repository #1 fails its own check-bundle/check-archives (tested, see the fork: release workflow for v<upstream>-fork.N on GitHub-hosted runners #2 comment), and fork: the server and CLI update from the desktop app's repository #1 without fork: release workflow for v<upstream>-fork.N on GitHub-hosted runners #2 publishes nothing.
  • Nothing personal in the diff. The commit author is t3code-fork noreply@users.noreply.github.com.
    findings: none
    optional nits:
  • The parity test covers the desktop feed but not apps/web/vite.config.ts:49-52, which is a separate inline copy of the same order. A one-line comment there pointing at release-repository.ts would keep the two together on a later upstream edit.
  • r1 nit still open: the HTTP 404 message for an upstream-only version could name the repository (selfUpdate.ts:295-301).

@scuttletoby
scuttletoby merged commit 1cfd0a7 into main Nov 12, 2026
2 checks passed
scuttletoby pushed a commit that referenced this pull request Nov 12, 2026
main now carries #1, #2 and a newer upstream. fork-tests.yml keeps this
branch's version (it replaces #2's copy) and ports what the drift check
listed: test-delta.test.cjs in the release checks, Vitest's module cache
on the server shards (saved only by pushes), and no test-cache report
upload. Web was already split in two; the built-client boot stays in the
Build job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants