Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .changeset/desktop-autoupdate-notifications.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"@electric-ax/agents-desktop": patch
---

Wire `electron-updater` so the desktop app can detect new releases. Phase
one of two:

* Adds a working **Check for Updates…** menu item (Electric Agents menu
on macOS, Help menu on Windows/Linux, plus the in-window app-icon
menu) and a quiet background check ~10s after launch.
* On Windows/Linux, signed-platform flow is wired end-to-end: downloads
in the background with a dock/taskbar progress bar, then prompts
"Restart now" to apply via `quitAndInstall()`.
* On macOS, ships as **notify-only** until Developer ID signing lands —
Squirrel.Mac can't swap an unsigned bundle, so we skip the download
entirely and prompt to open the GitHub releases page instead.
* Switches the publish provider from `github` to `generic` pointed at
the moving `agents-desktop-latest` tag, because the repo's overall
"latest" release is shared across packages and the GitHub provider
was picking the wrong one.
* Adds channel separation so canary builds publish to the `beta`
channel against an `agents-desktop-canary` URL — stable users never
auto-update to canaries.
26 changes: 22 additions & 4 deletions .github/workflows/agents_desktop_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -254,15 +254,29 @@ jobs:
if [[ "${{ inputs.sign }}" != "true" ]]; then
builder_args+=("-c.mac.identity=-")
fi
if [[ "${{ inputs.channel }}" == "canary" ]]; then
builder_args+=("-c.channel=beta")
builder_args+=("-c.publish.url=https://github.com/electric-sql/electric/releases/download/agents-desktop-canary")
fi

pnpm --filter @electric-ax/agents-desktop exec electron-builder "${builder_args[@]}" --publish never

- name: Package desktop app
if: ${{ matrix.id != 'macos' }}
shell: bash
env:
CSC_IDENTITY_AUTO_DISCOVERY: ${{ inputs.sign && 'true' || 'false' }}
GH_TOKEN: ${{ github.token }}
run: pnpm --filter @electric-ax/agents-desktop exec electron-builder ${{ matrix.builder_args }} --publish never
run: |
set -euo pipefail

builder_args=(${{ matrix.builder_args }})
if [[ "${{ inputs.channel }}" == "canary" ]]; then
builder_args+=("-c.channel=beta")
builder_args+=("-c.publish.url=https://github.com/electric-sql/electric/releases/download/agents-desktop-canary")
fi

pnpm --filter @electric-ax/agents-desktop exec electron-builder "${builder_args[@]}" --publish never

- name: Verify macOS app signatures
if: ${{ matrix.id == 'macos' }}
Expand Down Expand Up @@ -539,6 +553,13 @@ jobs:
exit 1
}

# Always include the original artifacts + electron-updater metadata
# (latest*.yml / beta*.yml + .blockmap files). The updater expects
# the filenames referenced in the yml; renamed copies are added on
# top for canary so users get stable "latest canary" download URLs.
cp packages/agents-desktop/release/*.{dmg,zip,exe,AppImage,deb,blockmap,yml,yaml} \
packages/agents-desktop/publish-assets/ 2>/dev/null || true

if [[ "$CHANNEL" == "canary" ]]; then
case "$MATRIX_ID" in
macos)
Expand All @@ -555,9 +576,6 @@ jobs:
copy_first "Electric-Agents-canary-linux-x64.deb" packages/agents-desktop/release/*.deb
;;
esac
else
cp packages/agents-desktop/release/*.{dmg,zip,exe,AppImage,deb,blockmap,yml,yaml} \
packages/agents-desktop/publish-assets/ 2>/dev/null || true
fi

assets=(packages/agents-desktop/publish-assets/*)
Expand Down
13 changes: 10 additions & 3 deletions packages/agents-desktop/electron-builder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ linux:
- x64

publish:
provider: github
owner: electric-sql
repo: electric
# Using `generic` (not `github`) because the electric-sql/electric repo
# publishes many packages to the same Releases page (changesets-style).
# GitHub's "latest" marker tracks whichever release was most recently
# tagged across all packages, so the `github` provider grabs the wrong
# release and can't find latest-mac.yml.
# Instead we point at the moving `agents-desktop-latest` tag that the
# release workflow maintains specifically for desktop stable builds.
# Canary builds override `url` to `agents-desktop-canary` at build time.
provider: generic
url: 'https://github.com/electric-sql/electric/releases/download/agents-desktop-latest'
1 change: 1 addition & 0 deletions packages/agents-desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"better-sqlite3": "^12.9.0",
"dockerode": "^5.0.0",
"e2b": ">=2.0.0",
"electron-updater": "^6.3.9",
"fix-path": "^4.0.0",
"jsdom": "^28.1.0",
"pino": "^10.3.1",
Expand Down
16 changes: 15 additions & 1 deletion packages/agents-desktop/src/app/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BrowserWindow } from 'electron'
import type { DesktopAppContext } from './context'
import * as AppLifecycle from './lifecycle'
import * as LoginItems from './login-items'
import { createDesktopUpdater } from './updater'
import * as CloudAuthInjection from '../cloud/auth-injection'
import * as ServerFetch from '../cloud/server-fetch'
import { createCredentialsController } from '../credentials/controller'
Expand Down Expand Up @@ -305,12 +306,20 @@ export function createDesktopMainController(ctx: DesktopAppContext) {
AppLifecycle.applyNativeAppearance(ctx, appearance)
}

const updater = createDesktopUpdater({
showOrCreateWindow,
})

const checkForUpdates = (): Promise<void> =>
updater.checkForUpdates({ triggeredManually: true })

const applicationMenuDeps: ApplicationMenu.ApplicationMenuDeps = {
windows,
createWindow,
sendCommand,
quitApp,
showAboutDialog,
checkForUpdates,
}

function showAboutDialog(): void {
Expand Down Expand Up @@ -345,7 +354,11 @@ export function createDesktopMainController(ctx: DesktopAppContext) {
win: BrowserWindow,
bounds: DesktopMenuPopupBounds
): void => {
ApplicationMenu.popupAppIconMenu({ showAboutDialog }, win, bounds)
ApplicationMenu.popupAppIconMenu(
{ showAboutDialog, checkForUpdates },
win,
bounds
)
}

const desktopIpcDeps: DesktopIpc.RegisterDesktopIpcDeps = {
Expand Down Expand Up @@ -472,6 +485,7 @@ export function createDesktopMainController(ctx: DesktopAppContext) {
syncLaunchAtLoginSetting,
connectConfiguredServers,
startDiscoveryLoop: localDiscovery.startDiscoveryLoop,
initializeUpdater: updater.initialize,
quitApp,
}
}
Loading
Loading