Skip to content

feat(desktop): publish releases to a dedicated update channel - #89

Merged
elkaix merged 3 commits into
mainfrom
feat/desktop-release-channel
Aug 16, 2026
Merged

feat(desktop): publish releases to a dedicated update channel#89
elkaix merged 3 commits into
mainfrom
feat/desktop-release-channel

Conversation

@elkaix

@elkaix elkaix commented Aug 16, 2026

Copy link
Copy Markdown
Member

Related Issue

Follow-up to #87, which fixed the desktop update client. This fixes the update channel.

Problem

Auto-update is broken in shipped desktop builds, independently of the client-side bugs fixed in #87.

electron-updater's public GitHub provider resolves the repository's newest release. This repository
publishes CLI releases continuously, so https://github.com/PyModel/pythinker-code/releases/latest
resolves to a CLI release — currently @pymodel/pythinker-code@0.19.0 — which contains no desktop
assets and no latest-mac.yml. Every desktop update check therefore 404s.

One repository was publishing two unrelated release streams into one "latest" channel.

What changed

  • Desktop releases now publish to PyModel/pythinker-desktop-releases, a repository that holds
    desktop artifacts and update metadata only, so "latest release" unambiguously means the desktop app.
  • releaseType: "release" is set explicitly. electron-publish's GitHub publisher otherwise
    creates a draft, and a draft is invisible to the updater — this would have failed quietly.
  • Both release jobs mint a GitHub App installation token for the publish step. GITHUB_TOKEN is
    scoped to the repository running the workflow and cannot publish into another one.
  • Both jobs now assert app-update.yml exists inside the packaged application. A build without it
    cannot self-update, and after fix(desktop): pin the Host port and stop reporting non-updatable builds as errors #87 the app reports itself as non-updatable rather than erroring — so
    shipping such a build would be a silent regression. The check is the last command in its step, so a
    missing file fails the job.

Required before the next desktop release

Two repository secrets, from a GitHub App installed on PyModel/pythinker-desktop-releases with
Contents: write:

  • DESKTOP_RELEASES_APP_ID
  • DESKTOP_RELEASES_APP_PRIVATE_KEY

There is deliberately no fallback to GITHUB_TOKEN. A fallback would publish to the wrong
repository and silently recreate this bug; a missing secret must fail the release loudly instead.
Nothing breaks before then — this workflow only runs on desktop-v* tags.

Alternatives considered and rejected

  • Generic provider against the code.pythinker.com CDN. Needs its own credential and atomic
    publishing, cache-control correctness, and availability monitoring that the GitHub path gets free.
  • One repository with allowPrerelease and a desktop channel. This can be made to work with
    semver-valid tags, since the provider scans the Atom feed and skips invalid tags. Rejected because
    Atom retention is not a channel contract: enough CLI releases can push every desktop entry out of
    the feed, and the failure returns silently.

Migration note

Clients on the existing v0.1.0 pre-release cannot learn about the new repository and will need one
manual reinstall. That cost is already sunk — their update path is broken today regardless — and it
only grows with the installed base, which is why this moves now rather than later.

Verification

  • pnpm --filter @pymodel/pythinker-desktop exec vitest run80 passed, including a new
    assertion that the publish target is the releases repo with releaseType: release. Recorded red
    against the old config first.
  • pnpm run lint — exit 0.
  • Workflow shape asserted mechanically: the app-token step present in both jobs, no publish step left
    on GITHUB_TOKEN, and a packaged-feed guard in both jobs.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset. — @pymodel/pythinker-desktop is private and changeset-ignored.
  • Ran gen-docs skill, or this PR needs no doc update.

electron-updater's public GitHub provider resolves the repository's
newest release. This repository publishes CLI releases continuously, so
that is almost always a CLI release carrying no latest-mac.yml, and every
desktop update check fails. Publish desktop releases to
PyModel/pythinker-desktop-releases instead, where the latest release
always describes the desktop app.

Releases are created with releaseType: release because the publisher
otherwise creates a draft, and a draft is invisible to the updater.

Publishing across repositories needs an installation token; GITHUB_TOKEN
cannot reach another repository. There is deliberately no fallback: a
fallback would publish to the wrong repository and silently restore the
bug this fixes, so a missing secret fails the release.

Both jobs now also assert that app-update.yml is present inside the
packaged application. Without it a build cannot self-update, and the app
reports itself as non-updatable, so shipping one is a silent regression.
@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@elkaix, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

Limit details: You’ve used all 3 included reviews currently available under your plan.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9d40f680-c0fb-4b6f-a1f7-857c7087c39a

📥 Commits

Reviewing files that changed from the base of the PR and between 27294e8 and 2535de1.

📒 Files selected for processing (3)
  • .github/workflows/desktop-release.yml
  • apps/desktop/package.json
  • apps/desktop/tests/packaging-config.spec.ts

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Aug 16, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@pymodel/pythinker-code@2535de1
npx https://pkg.pr.new/@pymodel/pythinker-code@2535de1

commit: 2535de1

@elkaix
elkaix merged commit 4111dcf into main Aug 16, 2026
11 checks passed
@elkaix
elkaix deleted the feat/desktop-release-channel branch August 16, 2026 06:04
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.

1 participant