Skip to content

Add in-app updater for Linux#3465

Merged
ivan-ottinger merged 18 commits into
trunkfrom
add-linux-in-app-updater
May 15, 2026
Merged

Add in-app updater for Linux#3465
ivan-ottinger merged 18 commits into
trunkfrom
add-linux-in-app-updater

Conversation

@ivan-ottinger
Copy link
Copy Markdown
Contributor

@ivan-ottinger ivan-ottinger commented May 13, 2026

Related issues

Part of RSM-1314 — Linux release prep.

How AI was used in this PR

AI-assisted: drafted the Linux branch in apps/studio/src/updates.ts, the unit tests, and this PR body. Author reviewed.

Proposed Changes

Adds Linux support to the in-app update flow. Electron's built-in autoUpdater is macOS/Windows only, so this PR adds a custom polling branch in apps/studio/src/updates.ts that hits the same WPCOM endpoint, parses the agreed Linux response, and shows an OS dialog when an update is available. The dialog has a Download button that opens the user's browser to the .deb URL via shellOpenExternalWrapper; the user installs manually via sudo apt install.

CleanShot 2026-05-13 at 14 29 43@2x

Response handling

Server response Client behavior
200 { version, downloadUrl } Show dialog with the new version + Download/Later buttons. On Download, validate URL is http:/https: then open via shellOpenExternalWrapper. Reschedule.
204 No update. Silent on auto-poll; "No updates available" dialog on manual check (shared with Mac/Windows).
404 Should not happen. Log to Sentry and treat as no-op.
Other errors Log to Sentry. Reschedule.

The server runs version_compare() and returns 204 when the client is current, so the client trusts the response without further comparison.

Out of scope

  • Linux dev/release distribution to AppsCDN — tracked in #3464 and RSM-2587. This PR is independent: the WPCOM studio-app/updates endpoint is already deployed and serves Linux responses today.
  • Automatic install.deb installation requires root, can't be silent. Flow is intentionally "open browser, user installs."

Testing Instructions

Automated: unit tests cover the three main response paths (200, 204, server error). They run in CI; locally:

npx vitest run apps/studio/src/tests/updates.test.ts

Visual verification of the dialog on Linux: because no Linux release exists in AppsCDN yet, the server returns 204 for any version. To exercise the dialog locally, apply this temporary diff to force-trigger the 200 path, then revert when done. Run on a Linux machine (or VM):

git checkout add-linux-in-app-updater
git apply <<'PATCH'
diff --git a/apps/studio/src/updates.ts b/apps/studio/src/updates.ts
--- a/apps/studio/src/updates.ts
+++ b/apps/studio/src/updates.ts
@@ -222,6 +222,12 @@ async function showUpdateReadyToInstallNotice() {
 }
 
 function setupLinuxUpdates() {
+	// TEMP for local testing — REMOVE BEFORE COMMIT
+	console.log( 'TEMP: bypassing shouldPoll for local Linux updater test' );
+	void pollLinuxUpdates();
+	return;
+	// END TEMP
+
 	if ( ! shouldPoll ) {
 		console.log( 'Skipping Linux auto-updates', {
 			env: process.env.NODE_ENV,
@@ -238,6 +244,22 @@ function setupLinuxUpdates() {
 async function pollLinuxUpdates() {
 	updaterState = 'checking-for-update';
 
+	// TEMP for local testing — REMOVE BEFORE COMMIT
+	// Stubs a fake 200 response so the dialog renders without a real WPCOM call.
+	// Swap to `return;` (in the try) to test the 204 path instead.
+	console.log( 'TEMP: stubbing fake 200 response' );
+	try {
+		await showLinuxUpdateAvailableNotice(
+			'1.9.9',
+			'https://appscdn.wordpress.com/downloads/wordpress-com-studio/linux-arm64/v1.9.9/update/studio_1.9.9_arm64.deb'
+		);
+	} finally {
+		showManualCheckDialogs = false;
+		rescheduleLinuxOrFinish();
+	}
+	return;
+	// END TEMP
+
 	const url = new URL( 'https://public-api.wordpress.com/wpcom/v2/studio-app/updates' );
 	url.searchParams.append( 'platform', process.platform );
 	url.searchParams.append( 'studioArch', process.arch );
PATCH
npm start

Within ~1 second of the main window appearing, the dialog should render. Verify:

  • Title "New Version Available", message "Studio 1.9.9 is available".
  • Install command on its own line: sudo apt install ~/Downloads/studio_1.9.9_arm64.deb.
  • Click "Download" → browser opens to the URL (it'll 404 — that's expected; verifying shellOpenExternalWrapper fires).
  • Click "Later" → dialog dismisses.
  • Help → Check for Updates triggers the same dialog.

When done, revert:

git checkout apps/studio/src/updates.ts

No regression on Mac/Windows: npm start on Mac or Windows should behave exactly as before this PR (no entry into the Linux branch).

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?
  • Unit tests pass (npx vitest run apps/studio/src/tests/updates.test.ts).
  • Visual smoke test on a Linux machine confirms the dialog renders correctly.
  • Mac/Windows update flow is unchanged.

@ivan-ottinger ivan-ottinger self-assigned this May 13, 2026
@ivan-ottinger ivan-ottinger marked this pull request as ready for review May 13, 2026 14:00
@ivan-ottinger ivan-ottinger requested a review from Copilot May 13, 2026 14:01
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Linux support to Studio’s in-app update flow by bypassing Electron’s platform-limited autoUpdater and implementing a custom polling + dialog-based update prompt on Linux.

Changes:

  • Add a Linux-specific update path in setupUpdates() that polls the WPCOM studio-app/updates endpoint and shows a “Download / Later” dialog with manual install instructions.
  • Implement Linux polling/rescheduling logic and .deb filename extraction for the apt install hint.
  • Add Vitest unit tests covering the primary Linux response paths (200/204/error) via manualCheckForUpdates().

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
apps/studio/src/updates.ts Adds Linux polling updater branch, update-available dialog, and rescheduling logic.
apps/studio/src/tests/updates.test.ts Adds unit tests validating Linux updater dialog behavior and error reporting.
Comments suppressed due to low confidence (1)

apps/studio/src/updates.ts:295

  • rescheduleLinuxOrFinish() assigns a new setTimeout without clearing any existing one. If pollLinuxUpdates() can be entered concurrently (e.g., via repeated manual checks), multiple timers may accumulate and cause bursts of polling. Clear any existing timeout (and set it back to null) before scheduling the next poll.
function rescheduleLinuxOrFinish() {
	if ( ! shouldPoll ) {
		updaterState = 'done';
		return;
	}
	updaterState = 'polling';
	timeout = setTimeout( () => {
		console.log( 'Automatically polling for Linux update' );
		void pollLinuxUpdates();
	}, AUTO_UPDATE_INTERVAL_MS );
}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/studio/src/updates.ts
Comment thread apps/studio/src/updates.ts Outdated
Comment thread apps/studio/src/updates.ts
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment thread apps/studio/src/updates.ts
@wpmobilebot
Copy link
Copy Markdown
Collaborator

wpmobilebot commented May 13, 2026

📊 Performance Test Results

Comparing 5f18545 vs trunk

app-size

Metric trunk 5f18545 Diff Change
App Size (Mac) 1409.64 MB 1409.65 MB +0.01 MB ⚪ 0.0%

site-editor

Metric trunk 5f18545 Diff Change
load 1478 ms 1499 ms +21 ms ⚪ 0.0%

site-startup

Metric trunk 5f18545 Diff Change
siteCreation 8591 ms 8594 ms +3 ms ⚪ 0.0%
siteStartup 4923 ms 4929 ms +6 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@ivan-ottinger ivan-ottinger requested review from a team and gavande1 May 14, 2026 09:14
Copy link
Copy Markdown
Contributor

@gavande1 gavande1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally on Linux using the temp-patch flow from the testing instructions — the dialog renders correctly with the version, install command, and working Download link. LGTM!

ivan-ottinger added a commit that referenced this pull request May 15, 2026
## Related issues
Follow-up to [#3464](#3464)
(Linux dev build distribution).

## How AI was used in this PR
AI-assisted: spotted the duplicated arch/version in the uploaded
filename while verifying the first trunk Distribute Dev Builds run,
drafted the fix and PR body. Author reviewed.

## Proposed Changes

The Linux DEBs uploaded to AppsCDN by [build
#16245](https://buildkite.com/automattic/studio/builds/16245) landed
with names like:

- `studio_1.9.0dev51_amd64-x64-v1.9.0-dev51.deb`
- `studio_1.9.0dev51_arm64-arm64-v1.9.0-dev51.deb`

Both arch (`amd64` + `x64`) and version (`1.9.0dev51` + `v1.9.0-dev51`)
appear twice. The cause: `electron-installer-debian` produces filenames
like `studio_1.9.0~dev51_amd64.deb`, then `create_versioned_file` (in
this Fastfile) blindly appends `-<arch>-v<version>` to the base name.
For Mac the input is a clean `Studio.app.zip`, so the result is
`studio-x64-v1.9.0.zip`; for Linux the input already carries
arch+version, so the suffix is duplicated. WordPress's media library
then strips the `~`, mashing the version slug.

This change renames the Linux DEB to a clean `studio.deb` stem in
`linux_deb_path` before returning the path. `create_versioned_file` then
produces the Mac-style `studio-<arch>-v<version>.deb` (e.g.
`studio-x64-v1.9.0-dev51.deb`). The rename is idempotent — retries pick
up the already-renamed file.

**Why this is cosmetic-only:**
- Version checking goes through the WPCOM `studio-app/updates` endpoint,
which compares the `version` field in upload metadata (passed explicitly
to `upload_file_to_apps_cdn`), not the filename.
- `apt`/`dpkg` install behavior reads the DEB's internal control file,
not the filename.
- The CDN `media_url` is composed from `platform/version/build_number`,
not the filename — only `Content-Disposition` after the 302 changes.

User-visible impact: the filename users see in their Downloads folder
after clicking the in-app update button (#3465) becomes readable.

## Testing Instructions
- After this lands on trunk, watch the next Distribute Dev Builds run.
- Open the `cdn-link` annotation on the Distribute Dev Builds job.
- Click the **Linux - x64 (DEB)** and **Linux - ARM64 (DEB)** links and
confirm the downloaded filenames look like `studio-x64-v<version>.deb` /
`studio-arm64-v<version>.deb`.

## Pre-merge Checklist
- [ ] Have you checked for TypeScript, React or other console errors?
- [ ] CI is green.
@ivan-ottinger ivan-ottinger merged commit f0ae760 into trunk May 15, 2026
10 checks passed
@ivan-ottinger ivan-ottinger deleted the add-linux-in-app-updater branch May 15, 2026 14:07
ivan-ottinger added a commit that referenced this pull request May 19, 2026
## Related issues
Closes [RSM-2587](https://linear.app/a8c/issue/RSM-2587). Last remaining
gate before a first Linux beta can ship.

## How AI was used in this PR
AI-assisted: drafted the Buildkite group, Fastfile change, and PR body
following existing precedents (release-mac, release-windows, dev-linux).
Author reviewed.

## Proposed Changes

Two coupled changes so release lanes produce and upload Linux DEBs
alongside Mac/Windows:

**`.buildkite/release-build-and-distribute.yml`** — adds a
`release-linux` group with an `x64`/`arm64` matrix, mirroring the
structure of `release-mac` and `release-windows`. Uses the
Docker-on-`default`-queue pattern Apps Infra established for the dev
Linux build group (#3346): runs inside `$NODE_DOCKER_IMAGE` because
Amazon Linux on the `default` queue lacks `dpkg`/`fakeroot` required by
electron-forge's `MakerDeb`. The `Publish Release Builds` step now also
downloads `*.deb` artifacts and depends on `release-linux`.

**`fastlane/Fastfile`** — lifts the `release_tag.nil?` gate around the
`linux_x64_deb` / `linux_arm64_deb` entries in `update_builds`. On Linux
the `.deb` is the only artifact (serves as both update payload and full
installer), so the same entry covers dev and release builds.
`install_type: 'Update'` keeps the URL convention compatible with the
in-app updater's polling path (#3465).

## Testing Instructions

CI on this PR only validates parse/lint — the release pipeline doesn't
run for regular PRs. The first real verification happens when a release
is cut. After this lands:

1. When `code_freeze` / `new_beta_release` runs next, the triggered
release pipeline should show a 📦 Build for Linux group running both x64
and arm64 builds in parallel with Mac/Windows.
2. Both Linux jobs should produce a `.deb` artifact at
`apps/studio/out/make/deb/**/*.deb`.
3. The Publish Release Builds step should download all DEBs and
`distribute_release_build` should upload them to AppsCDN. Linux DEB URLs
should appear in the `cdn-link` annotation, the GitHub draft release,
and the Slack notification.

## Pre-merge Checklist
- [ ] Have you checked for TypeScript, React or other console errors?
- [ ] CI is green.
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.

4 participants