Skip to content
Agent Workspaces
Agent

updater

- `src/main/host/updates/update-service.ts` - `src/core/features/updates/node/wire-controller.ts` - `src/core/features/updates/browser/update-store.tsx` - `src/core/features/settings/browser/components/UpdateCard.tsx` - `packages/ui/src/react/components/update-card/`

From plugin
emdash
6.2k33 skills33 agents

Agent definition

updater.md

Risky Area: Updater And Packaging

Main Files

  • `src/main/host/updates/update-service.ts`
  • `src/core/features/updates/node/wire-controller.ts`
  • `src/core/features/updates/browser/update-store.tsx`
  • `src/core/features/settings/browser/components/UpdateCard.tsx`
  • `packages/ui/src/react/components/update-card/` (repo-relative)
  • `build/`
  • `package.json`
  • `electron-builder.config.ts`
  • `electron-builder.canary.config.ts`
  • `scripts/release/build.ts`
  • `scripts/release/notarize-mac.ts`
  • `scripts/release/rebuild-native.ts`
  • `scripts/release/upload-github-assets.ts`
  • `scripts/release/finalize-release.ts`
  • `.github/workflows/release-prod.yml`
  • `.github/workflows/release-canary.yml`

Rules

  • avoid changing updater defaults casually
  • treat signing, notarization, packaging targets, and native rebuild flow as release-critical
  • keep build output directories and packaging config stable unless the task is explicitly about release behavior

Download Lifecycle

The main-process update service owns the transfer. `downloadUpdate()` accepts a start request synchronously and retains the background promise; Wire returns the current state without waiting for the archive. Duplicate requests during downloading, downloaded, or installing are successful no-ops. Checks must not overwrite those states. Electron's progress, completion, and error events drive the renderer; a request timeout alone does not mean the transfer failed.

The shared update card receives explicit downloading/installing states and progress. Component-local pending state covers only request acknowledgement. Do not derive transfer progress from the lifetime of a button callback: downloads can start from the sidebar, toast, or recovery window, and continue while Settings is unmounted. State snapshots must not replace newer events received while awaiting the response.

Error summaries wrap below the card description. Full sanitized diagnostics are preserved separately for the Details disclosure and Copy details action; summaries and updater log messages remain bounded.

Regression coverage lives in `src/main/host/updates/update-service.test.ts`, `src/main/host/updates/utils.test.ts`, `src/renderer/tests/update-flow.test.ts`, and the shared card's tests and Storybook states.

Update Feed / Publishing Strategy

The stable release pipeline publishes to **GitHub Releases** (primary feed) and **Cloudflare R2** (legacy/migration feed). Both feeds are served from the same platform builds and are promoted by a single finalizer only after the complete supported architecture set builds and verifies.

Two feeds, one artifact set

electron-builder emits channel manifests named by the **first** publish provider's `channel`:

  • Stable: `provider: github` has no explicit channel → defaults to `latest` → emits `latest*.yml`.
  • Canary: `provider: github` sets `channel: 'canary'` → emits `canary*.yml`.

The R2 feed uses different channel names (`v1-stable`, `v1-canary`) that pre-date the GitHub migration. Rather than running a second packaging pass, `scripts/release/build.ts` calls `duplicateChannelManifests()` after the electron-builder step to copy `latest*.yml → v1-stable*.yml` (or `canary*.yml → v1-canary*.yml`). The duplicated manifests are kept local until platform verification finishes. `upload-github-assets.ts` then hashes the final files, refreshes manifest checksums and sizes, and uploads the artifacts and both manifest variants to the exact owned GitHub draft. On macOS this happens only after notarization and stapling.

Rollback-safe R2 promotion

Platform jobs never write the live R2 channel. `scripts/release/finalize-release.ts` first validates the exact owned GitHub release, the complete architecture inventory, and every update manifest entry. It downloads every referenced GitHub asset, verifies its SHA-512 against the manifest, uploads installer assets under an immutable `releases/<tag>/<run>-<attempt>/` prefix, and rewrites the R2 manifests to those keys. Before replacing any root `v1-stable*.yml` or `v1-canary*.yml` object, it snapshots the complete previous manifest set. Promotion or a confirmed GitHub publication failure restores that set. The original snapshot is journaled in R2 by tag, run, and commit before any root changes, so it survives retries and runner termination. If GitHub's response is ambiguous, the complete new R2 set is retained; a retry restores the journaled public roots when the release is still a draft or reconciles every root when publication actually succeeded.

Update channels on GitHub

The app does **not** override `autoUpdater.channel`; the GitHub provider resolves the channel naturally:

  • **Stable** (`allowPrerelease=false`): resolves to `latest`, fetches `latest*.yml` from the newest non-prerelease GitHub release.
  • **Canary** (`allowPrerelease=true`): resolves the target release tag from the Atom feed by matching the semver prerelease identifier of the installed version (`canary`) against each entry. Once a `-canary.N` tag is found it fetches `canary*.yml` from that release, as defined by `channel: 'canary'` in `electron-builder.canary.config.ts`.

The `UPDATE_CHANNEL` / `v1-stable` / `v1-canary` naming applies **only** to the flat R2 bucket (via the `generic` publish block's `channel`). It is kept as a log label in `update-service.ts` for diagnostics but is not passed to `autoUpdater.channel`.

R2 decommission path

R2 uploads continue until telemetry confirms all clients have migrated to the GitHub-backed feed. At that point:

1. Remove the `provider: generic` block from `electron-builder.config.ts` and `electron-builder.canary.config.ts`. 2. Remove R2 staging/promotion from `finalize-release.ts` and `duplicateChannelManifests` from `build.ts`. 3. Decommission the R2 bucket.

  • Canary publishes to GitHub as prereleases. `ALLOW_PRERELEASE` in `update-service.ts` is driven by `IS_CANARY` so canary clients accept prerelease versions automatically.
  • The `finalize-release.
Read more
@generalaction
Ships withemdash

Emdash is the Open-Source Agentic Development Environment (🧡 YC W26). Run multiple coding agents in parallel. Use any provider.

Stars on GitHub

Momentum

stars in total
6.2k
Star history of generalaction/emdash on GitHub, a chart by star-history.com

Other agents on emdash.

acp-runtime

acp-runtime

Agent

The ACP runtime is the domain service that serves the ACP API contract. It owns the host-scoped dependencies needed to run…

core-modules

core-modules

Agent

This page defines the target organization of `packages/core/src/`. Core is organized by module type so that shared domain APIs…

git-runtime

git-runtime

Agent

Git is split into a transport contract and a host-scoped runtime. Renderer, desktop, and workspace-server code share the Wire…

main-process

main-process

Agent

The main process is organized into domain modules under `src/main/core/`. Each domain typically has a `controller.ts` (RPC…

path-system

path-system

Agent

`@emdash/core/primitives/path/api` is the source of truth for portable file identity and lexical path operations. The detailed…

More agents from other plugins.

api-builder

api-builder

Agent

Deprecated placeholder. Do not use; follow root AGENTS.md and package instructions instead.

happier2d2.2k
@happier-dev@happier-devInstall Agent

Questions about updater.

Which coding agents can use updater?
updater installs in Claude Code and Cursor: each loads subagents from .claude/agents, so copy the file there. Flowy's auto-invocation runs in Claude Code only.