Skip to content

Add Desktop Mode Beta: install builds from PR branches, trunk, or stable - #425

Draft
epeicher wants to merge 2 commits into
trunkfrom
deskmod-57-allow-beta-channels
Draft

Add Desktop Mode Beta: install builds from PR branches, trunk, or stable#425
epeicher wants to merge 2 commits into
trunkfrom
deskmod-57-allow-beta-channels

Conversation

@epeicher

@epeicher epeicher commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

What it does

Adds Desktop Mode Beta, a companion plugin that switches the installed desktop-mode plugin to any open PR's CI build, the trunk build, or back to the latest stable release, from a Tools → Desktop Mode Beta page or an OS Settings → Beta tab. Modeled on Jetpack Beta, including its key structural decision: the switcher is a separate plugin (desktop-mode-beta/), never part of the wp.org distribution.

Rationale

Testing a branch on a live blog currently means building a zip locally and uploading it by hand. Every PR already gets a built, publicly downloadable desktop-mode.zip (the Playground preview flow uploads pr-<number>-<head-sha>.zip to the rolling ci-artifacts release). This PR turns those artifacts into one-click installable channels.

Keeping the installer out of desktop-mode itself matters twice over: install-from-GitHub machinery never ships to wp.org (Plugin Check and guideline exposure), and a branch build that breaks the desktop shell can't take down the tool needed to switch back to stable. The Tools page deliberately has zero desktop-mode dependencies.

Implementation

Discovery (includes/github.php): open PRs and the latest release come from the GitHub API (transient-cached, unauthenticated is plenty; DESKTOP_MODE_BETA_GITHUB_TOKEN optional). Whether a PR's head build actually exists is checked with redirect-only HEAD probes against the public download URLs (zero API quota), parallelized via Requests::request_multiple(). Trunk is described by a fixed-name trunk.json asset (plain download, no API call).

Install (includes/installer.php): the client sends only { source, id }; the server resolves the URL from data it fetched itself, so nothing outside the repo's releases is installable. The zip is installed with Plugin_Upgrader::install( $url, [ 'overwrite_package' => true ] ) (the same in-place overwrite as any plugin update) and recorded in the desktop_mode_beta_current option. Endpoints are admin-ajax (mirroring the plugins-window pattern; the upgrader classes are admin-only), gated by nonce + update_plugins / install_plugins.

Guard rails: while a beta build is active, auto_update_plugin is filtered off for desktop-mode (an overnight wp.org auto-update would silently replace the build under test) and the Plugins screen shows a warning. The UI offers one-click "Update to latest build" when the tracked PR gets new commits, flags closed PRs, and "Back to stable" reinstalls the latest release and clears the record. Development checkouts are protected: if the installed desktop-mode folder is a source tree (contains .git as a file or directory, package.json, vite.config.js, src/, or .wp-env.json; a packaged zip ships none of these), switching is refused server-side before any network work and the UI disables the install buttons with an explanation, so a wp-env bind mount can never lose its working tree to a stray click. The desktop_mode_beta_allow_dev_overwrite filter opts out deliberately.

UI: one hand-written assets/beta.js renders both surfaces: wpd-* components inside the shell (the tab is registered through the public desktop_mode_register_settings_tab() API, a nice dogfood of that surface), classic wp-admin markup on the Tools page. Requests route through wp.desktop.fetch when the shell is present.

CI/packaging: new trunk-build.yml publishes trunk.zip + trunk.json to ci-artifacts on every trunk push (fixed names + --clobber, no cleanup needed; zip uploaded before json so the manifest never announces a missing zip). bin/package-beta.sh / npm run package:beta builds the companion zip, and release.yml now attaches desktop-mode-beta.zip to every release.

The desktop-mode plugin source is untouched.

Testing instructions

npm run test:php -- --filter=Tests_DesktopModeBeta_Channels   # 24 tests: discovery, readiness, resolution, guards

Full PHPUnit (1633), lint, typecheck, and vitest (2297) are green; discovery was also smoke-tested live against the real GitHub API from wp-env (6 open PRs listed, per-PR build readiness verified against actual ci-artifacts assets, stable resolved to the real v0.9.7 asset), and the dev-checkout guard was verified live in a bind-mounted wp-env instance (a real switch call is refused with desktop_mode_beta_dev_checkout).

Manual: npm run package:beta, upload the zip on a test site (or wp-env with a mapping for desktop-mode-beta/), then Tools → Desktop Mode Beta or OS Settings → Beta. In a wp-env instance that bind-mounts a checkout, the dev-checkout guard disables switching by design; test real switches on a site with copied files (e.g. a wordpress-develop sync) or a throwaway install.

Two things only go fully live after merge: the trunk channel (its workflow must exist on trunk to fire) and builds for PRs whose last push predates the preview workflow (they show "Build pending" until re-pushed).

Open WordPress Playground Preview

…anches, trunk, or stable

A separate plugin (desktop-mode-beta/, never distributed on wp.org)
that switches the installed desktop-mode plugin to any open PR's CI
build, the trunk build, or the latest stable release, Jetpack Beta
style. Builds are discovered from the ci-artifacts release the PR
preview flow already populates; a new trunk-build.yml workflow adds
fixed-name trunk.zip + trunk.json assets. Release zips now also attach
desktop-mode-beta.zip.
@AllTerrainDeveloper

Copy link
Copy Markdown
Collaborator

Do not click Install in a wp-env instance that bind-mounts a checkout — the overwrite would land in the working tree. Test real switches on a site with copied files (e.g. a wordpress-develop sync) or a throwaway install.

Can't we just add safeguards for this?

Installing a build replaces the desktop-mode plugin folder in place —
in a wp-env instance that bind-mounts a checkout, that would clobber
the working tree. The companion now detects source-tree markers
(.git — file or dir, package.json, vite.config.js, src/,
.wp-env.json; a packaged zip ships none of them) and refuses to
switch server-side, while the UI disables the install buttons with an
explanation. The desktop_mode_beta_allow_dev_overwrite filter opts
out deliberately.
@epeicher

epeicher commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

Done in df5de27. The companion now detects when the installed desktop-mode folder is a source checkout (.git as a file or directory, so worktrees too, plus package.json, vite.config.js, src/, or .wp-env.json; a packaged zip ships none of these) and refuses to switch server-side before any network work, with the UI disabling the install buttons and explaining why. A packaged install is unaffected, and desktop_mode_beta_allow_dev_overwrite opts out deliberately.

@epeicher epeicher changed the title Add Desktop Mode Beta — install builds from PR branches, trunk, or stable Add Desktop Mode Beta: install builds from PR branches, trunk, or stable Jul 28, 2026
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.

2 participants