-
Notifications
You must be signed in to change notification settings - Fork 3k
feat(desktop): bridge Electron users to Tauri updates #8392
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
3d22a1a
feat(desktop): bridge Electron updates to Tauri
yiliang114 3742e4b
test(desktop): cover parseArguments validation in electron bridge man…
qwen-code-ci-bot f139b87
Merge remote-tracking branch 'origin/main' into codex/desktop-electro…
yiliang114 01c47d6
chore(desktop): address bridge review follow-ups
yiliang114 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| #!/usr/bin/env node | ||
|
|
||
| import crypto from 'node:crypto'; | ||
| import fs from 'node:fs'; | ||
| import path from 'node:path'; | ||
|
|
||
| const options = parseArguments(process.argv.slice(2)); | ||
| const assets = fs.readdirSync(options.assets).sort(); | ||
| const names = [ | ||
| 'Qwen-Code-Desktop-arm64.zip', | ||
| 'Qwen-Code-Desktop-x64.zip', | ||
| 'Qwen-Code-Desktop-arm64.dmg', | ||
| 'Qwen-Code-Desktop-x64.dmg', | ||
| ]; | ||
| const artifacts = names.map((name) => readArtifact(assets, name)); | ||
| const primary = artifacts[0]; | ||
|
|
||
| const lines = [ | ||
| `version: ${options.version}`, | ||
| 'files:', | ||
| ...artifacts.flatMap((artifact) => [ | ||
| ` - url: ${artifact.name}`, | ||
| ` sha512: ${artifact.sha512}`, | ||
| ` size: ${artifact.size}`, | ||
| ]), | ||
| `path: ${primary.name}`, | ||
| `sha512: ${primary.sha512}`, | ||
| `releaseDate: '${new Date().toISOString()}'`, | ||
| ]; | ||
| fs.writeFileSync(options.output, `${lines.join('\n')}\n`); | ||
|
|
||
| function readArtifact(assets, name) { | ||
| if (!assets.includes(name)) { | ||
| throw new Error(`Missing Electron bridge artifact: ${name}`); | ||
| } | ||
| const file = path.join(options.assets, name); | ||
| return { | ||
| name, | ||
| sha512: crypto | ||
| .createHash('sha512') | ||
| .update(fs.readFileSync(file)) | ||
| .digest('base64'), | ||
| size: fs.statSync(file).size, | ||
| }; | ||
| } | ||
|
|
||
| function parseArguments(args) { | ||
| const values = {}; | ||
| for (let index = 0; index < args.length; index += 2) { | ||
| const name = args[index]?.replace(/^--/, ''); | ||
| const value = args[index + 1]; | ||
| if (!name || value === undefined) throw new Error('Invalid arguments.'); | ||
| values[name] = value; | ||
| } | ||
| for (const required of ['assets', 'version', 'output']) { | ||
| if (!values[required]) throw new Error(`Missing --${required}`); | ||
| } | ||
| if ( | ||
| !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test( | ||
| values.version, | ||
| ) | ||
| ) { | ||
| throw new Error(`Invalid --version: ${values.version}`); | ||
| } | ||
| return values; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| # Electron-to-Tauri desktop update bridge | ||
|
|
||
| ## Context | ||
|
|
||
| The last published desktop release, `desktop-v0.0.5`, is an Electron app named `Qwen Code Desktop` with bundle identifier `com.alibaba.qwen-code`. Its macOS updater reads `latest-mac.yml` from the fixed `desktop-latest` release and installs a ZIP archive. | ||
|
|
||
| The new desktop shell is a Tauri app. It currently uses a different product name and bundle identifier and publishes `desktop-latest.json`, so the existing Electron app cannot discover or replace it. | ||
|
|
||
| ## Goals | ||
|
|
||
| - Let signed macOS Electron `0.0.5` installations update directly to the first stable Tauri release. | ||
| - Preserve the existing macOS application identity so the updater replaces the installed app bundle. | ||
| - Keep Tauri's signed updater feed for all releases after the migration. | ||
| - Make the bridge opt-in and one-time; later releases must not need Electron build tooling. | ||
|
|
||
| ## Non-goals | ||
|
|
||
| - Migrating Electron settings, sessions, or workspace state. The Tauri app may ask for a workspace on first launch. | ||
| - Bridging Windows or Linux Electron installations. | ||
| - Generating Electron differential blockmaps. Electron updater falls back to the checksum-verified full ZIP. | ||
|
|
||
| ## Compatibility contract | ||
|
|
||
| The Tauri bundle uses the legacy macOS identity: | ||
|
|
||
| - product name: `Qwen Code Desktop` | ||
| - bundle identifier: `com.alibaba.qwen-code` | ||
| - artifact prefix: `Qwen-Code-Desktop` | ||
| - signing identity: the existing Developer ID Application certificate | ||
|
|
||
| The bridge release must be newer than `0.0.5`. It publishes two updater views over the same signed app bundles: | ||
|
|
||
| 1. `latest-mac.yml` points legacy Electron clients at `Qwen-Code-Desktop-arm64.zip` or `Qwen-Code-Desktop-x64.zip`. | ||
| 2. `desktop-latest.json` points Tauri clients at the signed Tauri updater archives. | ||
|
|
||
| The ZIP is created from the already signed and notarized `.app`; it is not rebuilt by Electron tooling. | ||
|
|
||
| ## Release flow | ||
|
|
||
| `Desktop Release` gains an `electron_bridge` input, disabled by default. | ||
|
|
||
| - All macOS builds continue to produce the Tauri app, DMG, updater archive, and updater signature. | ||
| - When `electron_bridge` is enabled, each macOS build also creates a legacy-compatible ZIP. | ||
| - The publish job generates `latest-mac.yml` from the two ZIPs and two DMGs. | ||
| - A stable bridge release uploads the legacy metadata and payloads to `desktop-latest` together with `desktop-latest.json`. | ||
| - Later stable releases leave `electron_bridge` disabled. Updating `desktop-latest.json` does not remove the bridge files, so Electron installations that return later can still cross to Tauri. | ||
|
|
||
| Draft and prerelease runs may build and publish bridge artifacts for inspection, but they never update the stable feed. | ||
|
|
||
| ## Signing credentials | ||
|
|
||
| The repository already stores the Electron-era Apple certificate and App Store Connect API key under `MAC_CSC_*` and `APPLE_NOTARY_*` secret names. The workflow accepts those names as fallbacks for the newer Tauri names, so the Developer ID identity remains unchanged. | ||
|
|
||
| Tauri updater artifacts additionally require `TAURI_SIGNING_PRIVATE_KEY`; `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` is only needed for an encrypted private key. The private key must match the public key in the Tauri configuration before the first published Tauri release. | ||
|
|
||
| ## Validation | ||
|
|
||
| Automated release-helper tests verify: | ||
|
|
||
| - the legacy application identity, | ||
| - exact bridge artifact selection, | ||
| - SHA-512 and size values in `latest-mac.yml`, | ||
| - failure when a required bridge artifact is missing, | ||
| - existing Tauri updater manifest and version synchronization behavior. | ||
|
|
||
| Before the stable release, install the signed `desktop-v0.0.5` arm64 and x64 builds, point them at an isolated bridge feed, and verify both `0.0.5 -> Tauri bridge` and `Tauri bridge -> newer Tauri` updates. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.