fix: restore Linux builds and headless remote setup after Electron 41 upgrade#361
Merged
Conversation
Linux release builds have failed since v2.4.31, and headless remote daemon setup has been broken for users on the same versions. Both regressed with the Electron 37 -> 41 upgrade (#352). Electron removed ELECTRON_OZONE_PLATFORM_HINT in 38 and ignores it from 39 on, so the variable every headless launch path relied on became a no-op. Ozone also resolves its platform before the app's main script runs, so the app.commandLine.appendSwitch('ozone-platform', 'headless') fallback in index.ts could never have selected headless either. With neither mechanism live, Chromium's auto default falls back to X11 and exits with "Missing X server or $DISPLAY". Pass --ozone-platform=headless --disable-gpu as argv from every headless launch site instead: the CI verification script, the generated systemd unit, and both the npm and PyPI wrappers. --disable-gpu stays in index.ts because it is read late enough to still apply. The env var is left in the wrapper environments so older Pane builds keep working. Contract parity now covers the argument builder in both wrappers, so the npm and Python launch paths cannot drift.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Problem
Two failures, one cause.
Linux release builds have failed since v2.4.31. The
build (ubuntu-latest)job fails at Verify packaged headless remote setup, which skipspublish-windows,publish-release, andchecksums. As a result v2.4.31, v2.4.32 and v2.4.33 are all stuck as draft releases with no Windows installers, nolatest.yml(Windows auto-update manifest), and noSHA256SUMS.txt.Headless remote daemon setup is broken for users on those same versions — this is not only a CI problem.
Root cause
The Electron 37 → 41.10.3 upgrade (#352) landed immediately after v2.4.30, the last green build.
Every headless launch path relied on
ELECTRON_OZONE_PLATFORM_HINT=headless. That variable was removed in Electron 38 and is ignored from 39 on. On Electron 37 it worked only as an implementation side-effect: the hint was never validated against an allow-list, soheadlesspassed straight through onto--ozone-platform.The intended fallback could not cover it either. Electron resolves the Ozone platform in
PreEarlyInitialization(), before the main script executes, soapp.commandLine.appendSwitch('ozone-platform', 'headless')inindex.tswas always too late to select a platform.With neither mechanism live, Chromium's
autodefault (the default since Chromium 140 / Electron 38) resolves to Wayland if present, else X11 — and with$DISPLAYunset it exits withMissing X server or $DISPLAY/The platform failed to initialize.Fix
Pass
--ozone-platform=headless --disable-gpuas argv from every headless launch site. An explicit--ozone-platformalways wins: the hint path is skipped entirely when the switch is already present.scripts/test-packaged-headless-remote-setup.shmain/src/daemon/setupRemoteHost.tspackages/runpane/src/installers.tsbuildPaneDaemonArgs()prepends flags on Linuxpackages/runpane-py/src/runpane/installers.pybuild_pane_daemon_args()main/src/index.tsozone-platformswitch, keep--disable-gpu(still read late enough), correct the commentdocs/SELF_HOSTED_REMOTE_DAEMON.mdspawnPane/spawn_paneare only used for--remote-setup, never the GUI (which useslaunchPaneClient), so prepending headless flags there is safe.The env var is left in the wrapper environments so older Pane builds keep working.
Verification
pnpm typecheck— cleannode scripts/test-runpane-contract.js— passes, now including a new npm↔Python parity assertion on the argument builder so the two launch paths cannot driftMain-process tests: 389 passed. The 4 failures are pre-existing local
better-sqlite3native-binding errors, identical on an untouched worktree, and unrelated to this changeProven against the real job.
Build & Releasedoes not run on PRs (only onreleasepushes,v*tags, andworkflow_dispatch), so this PR's checks cannot exercise it. I dispatched it against this branch: run 29958580933 —Verify packaged headless remote setup: success, the step that has failed on every release since v2.4.31. All builds completed on macOS, Linux, and both Windows arches.All four build jobs in that run then failed at the same later step,
upload-artifact, withThe artifact name is not valid: ... Contains the following character: Forward slash /— because the artifact name embedsgithub.ref_nameand this branch isfix/headless-ozone-platform. Tags (v2.4.34) contain no slash, so this cannot affect a release. See follow-up below.Follow-up (not in this PR)
name: pane-${{ matrix.os }}-${{ github.ref_name }}cannot be uploaded from any branch containing/, which makesworkflow_dispatchunusable for validating the release pipeline before cutting a tag — exactly what was needed here. Worth sanitizing the slug (or usinggithub.run_id), but it touches the upload/download pairing across four jobs, so it is kept out of this fix.latest.yml, and noSHA256SUMS.txt. Their tags point at commits without this fix, so re-running the workflow on them will not help; completing distribution needs a fresh tag once this merges.