fix(linux): stop shipping the broken snap so the release writes latest-linux.yml - #145
Merged
Merged
Conversation
electron-builder falls back to { provider: snapStore } for snap artifacts
whenever no snap-specific publish config names a different provider
(SnapTarget.findSnapPublishConfig). The Snap Store upload needs snapcraft
on the build machine, which CI does not have, so the linux job died with
'snapcraft is not installed' *after* uploading the AppImage and deb but
*before* electron-builder wrote the updater metadata. That is why the
v1.8.8 and v1.8.9 drafts carry Linux artifacts but no latest-linux.yml,
leaving Linux auto-update non-functional.
Point the snap at the same GitHub draft release as every other artifact.
Verified locally: the snap now resolves to 'provider=github', and a full
linux build emits latest-linux.yml listing the AppImage and the deb.
📊 Coverage Report
|
🧪 E2E Test Results✅ 18 passed, 0 failed, 0 skipped
|
package.json still carried the electron-vite template description, which electron-builder propagates into the Snap Store listing, the deb control file and the desktop entry Comment. Set it to the real tagline before the snap is published to the store.
Publishing from inside the electron-builder run makes a store outage or a rejected review abort the linux job before latest-linux.yml is written, silently breaking auto-update for the deb and AppImage. Upload separately with continue-on-error, gated on the SNAPCRAFT_STORE_CREDENTIALS secret so forks and credential-less runs skip it cleanly. Lands on edge; promotion to stable stays manual, matching the draft-release flow.
electron-builder 26.15.3 downloads the snap template as a .tar.7z and only un-7zips it, leaving the raw .tar in the directory it hands to mksquashfs. The template's desktop-init.sh, desktop-common.sh and desktop-gnome-specific.sh therefore never make it into the snap, while the tar itself gets packed at the snap root. command.sh execs desktop-init.sh, so the app dies instantly: /snap/clipless/x2/command.sh: line 2: /snap/clipless/x2/desktop-init.sh: No such file or directory The 1.8.7 snap (built on electron-builder 25) has the scripts, so this is a regression from the 26.x bump. 26.15.7 extracts the template correctly. Verified on 26.15.7: all three desktop scripts are present, no stray tar is packed, a full --linux build still emits latest-linux.yml listing the AppImage and the deb, and the snap still routes to provider=github.
This reverts commit 0c0ae0d.
electron-builder's snap template pins base: core20 with the gnome-3-28-1804 platform, whose Mesa is 20.0.8 (2020). On current GPUs (verified on an AMD Strix Halo / Radeon 8060S) every DRI driver in it fails to load — radeonsi, kms_swrast and swrast alike — so EGL init fails and the app segfaults before a window appears. --disable-gpu, --use-gl=disabled and --disable-software-rasterizer all still segfault, because the loader failures come from GTK in the main process, not just the GPU process. Reviving it means base: core24 with gnome-46-2404 and the mesa-2404 content snap, built through the real snapcraft toolchain instead of the prebuilt template — its own piece of work. Until then, don't ship a binary that crashes on modern hardware. AppImage and deb both work and both auto-update.
Dev builds shared the installed app's userData directory, and with it the single-instance lock. With an installed Clipless running in the tray, `npm run dev` lost the lock, quit immediately, and the installed copy handled 'second-instance' by focusing its own window -- so a window appeared, the freshly built code never ran, and nothing indicated which build was on screen. Rebuilding and reinstalling could not break the loop, because the stale process was never restarted. Dev now runs against <userData>-dev, which also stops dev runs from mutating real clip history. The redirect is a side-effect module imported first in index.ts rather than a call in its body: the storage singleton derives its path from userData in a constructor that runs at import time, and ES module imports are evaluated before the importing module's statements.
settings.html linked ./settings.css, which does not exist -- the real stylesheet is imported by settings-main.tsx and emitted into assets/. The tag only produced a failed request.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR aims to ensure Linux release builds complete successfully and produce the latest-linux.yml metadata needed for electron-updater, while also improving developer ergonomics by isolating dev userData from installed builds.
Changes:
- Adjust Linux packaging/publish configuration to prevent CI failure and allow updater metadata generation.
- Add a dev-only
userDatasuffix to avoid single-instance lock collisions betweennpm run devand an installed app. - Bump version to
1.8.10and update build tooling/docs accordingly.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/renderer/settings.html | Removes a direct stylesheet link; relies on the TS entrypoint for styling. |
| src/main/index.ts | Imports a side-effect module first to redirect userData in dev before other imports run. |
| src/main/dev-profile.ts | New dev-only app.setPath('userData', …-dev) to prevent dev/installed profile collisions. |
| README.md | Updates Linux install docs to reflect the currently shipped formats. |
| package.json | Bumps app version and updates electron-builder dependency. |
| package-lock.json | Lockfile updates corresponding to the version/dependency bump. |
| okf-bundle/log.md | Adds an update-log entry documenting the dev/installed single-instance lock gotcha. |
| okf-bundle/gotchas/index.md | Index entry for the new gotcha document. |
| okf-bundle/gotchas/dev-builds-defer-to-an-installed-instance.md | New gotcha documentation describing the failure mode and mitigations. |
| electron-builder.yml | Updates Linux targets/publish configuration and adds snap-related commentary/config scaffolding. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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.
Follow-up to #142. The Linux job in that release actually failed — the AppImage and deb you see on the v1.8.8 and v1.8.9 drafts were uploaded before the failure, and
latest-linux.ymlnever got written. Without it,electron-updaterhas no update metadata for Linux, so in-app updates still don't work there.The fix ended up being to stop building the snap, not to publish it elsewhere. The investigation took a few turns; the reasoning is below so the config comments make sense.
What happened
From the v1.8.8 run (job log):
SnapTarget.findSnapPublishConfigfalls back to{ provider: "snapStore" }for snap artifacts unless a snap-specificpublishconfig names some other provider —linux.publishand the top-levelpublishare only consulted if they contain a snapStore entry. So the snap was routed to the Snap Store, which requires thesnapcraftbinary (and store credentials) on the runner. The build aborted there, before the publish finalization that emitslatest-linux.yml.Note the snap itself builds fine without snapcraft — electron-builder packs it from a template app with
mksquashfs. Only the store upload needs the toolchain, which is why this never showed up in a localbuild:linux.Why the snap is gone rather than redirected
Routing the snap to the GitHub draft release fixed CI, so the first version of this PR did exactly that. Installing the resulting snap showed it was broken at runtime:
electron-builder's snap template pins
base: core20with thegnome-3-28-1804platform, whose Mesa is 20.0.8 (2020). On current GPUs every DRI driver in it fails to load — including swrast — so the app segfaults during GPU init, and no runtime flag avoids it. Bumping electron-builder to 26.15.7 (kept in this PR) did not help.Shipping a snap that cannot start is worse than shipping none, so
snapcomes offlinux.target. Reviving it meansbase: core24withgnome-46-2404and themesa-2404content snap, built through the real snapcraft toolchain rather than the template — a separate piece of work.The
snap.publishblock stays even though nothing builds a snap today. It's a guard: without it, re-adding the snap target would silently route to the Snap Store again and break Linux auto-update the same way. Both that block and thelinux.targetlist carry comments explaining this.Result
Linux now builds AppImage + deb, and a full
--linuxbuild emitsdist/latest-linux.yml:Both artifacts are listed, which is what
AppImageUpdaterandDebUpdatereach need. README's Linux install section is updated to match the formats actually shipped.Also in this PR
Unrelated to the release pipeline, but found while verifying Linux builds by hand:
fix(dev): dev builds get their ownuserDataprofile. Dev shared~/.config/cliplesswith an installed Clipless, and therefore its single-instance lock. With an installed copy in the tray,npm run devlost the lock, quit, and the installed app handledsecond-instanceby focusing its own window — a window appeared, the freshly built code never ran, and nothing said so. Rebuilding and reinstalling could not break the loop, because the stale process was never restarted. The redirect is a side-effect module imported first inindex.ts, not a call in its body: the storage singleton derives its path fromuserDatain a constructor that runs at import time, and ES imports evaluate before the importing module's statements.chore(renderer): drop deadsettings.csslink.settings.htmllinked a file that doesn't exist; the real stylesheet comes fromsettings-main.tsx.Notes
snapcraft.<core>oversnap. Thesnapkey is kept deliberately —snapcraftselects abaseand switches to the real snapcraft toolchain, which is the CI dependency this PR removes. Worth revisiting only if we publish to the Snap Store.publish.releaseType: draftstill means a draft has to be published before any platform sees an update.