78 lines
6.2 KiB
Markdown
78 lines
6.2 KiB
Markdown
# Desktop builds and releases
|
||
|
||
## Platforms
|
||
|
||
| Platform | Build runner | Packages | In-app update |
|
||
|---|---|---|---|
|
||
| macOS, Apple Silicon | `macos-15` (arm64) | signed/notarized DMG + ZIP | ZIP via Squirrel.Mac |
|
||
| Windows, x64 | `windows-2025` | unsigned per-user NSIS installer | NSIS |
|
||
| Linux, x64 | `ubuntu-24.04` | AppImage + `.deb` | AppImage replacement; `.deb` through dpkg/apt with elevation if needed |
|
||
|
||
Windows signing is deliberately disabled for this first release. SmartScreen may warn about an unknown publisher on initial installation. AppImage is the primary Linux download because it is self-contained and updates without administrator permission. The installed `electron-updater` 6.8.9 also supports `.deb` updates; an update may request an administrator password. Both use the same download/restart banner.
|
||
|
||
The custom app bar stays on all three platforms. macOS keeps its left-side traffic lights; Windows/Linux use Electron's native right-side Window Controls Overlay, with CSS space reserved from the overlay's actual geometry. Windows/Linux hide the native menu until Alt is pressed. macOS keeps its system menu.
|
||
|
||
The existing `build/icon.icns` is the source for all platforms: electron-builder converts it to ICO/Linux PNGs. Linux's desktop identity is `learnharness.desktop`. Public package-maintainer metadata uses the owner's GitHub no-reply address; support goes through [GitHub Issues](https://github.com/amosblomqvist/learnharness/issues).
|
||
|
||
## Build pipeline
|
||
|
||
Each OS installs its own dependencies and builds its own Next standalone tree:
|
||
|
||
1. `npm ci`
|
||
2. Native PDF/SVG renderer tests for that OS, running in Electron's bundled Node (no display needed).
|
||
3. `npm run build` (`output: "standalone"`).
|
||
4. `node scripts/build-server.mjs` — copy static assets, refuse leaked data/keys, remove only standalone-root `dist/` and `node_modules/electron/` (old installers and the development runtime), check prompt assets. Runtime dependencies' own `dist/` directories and native renderer binaries stay intact.
|
||
5. `node scripts/smoke-server.mjs --electron` — boot the standalone server in Electron's bundled Node, using empty temporary data/auth, then check the home route and GET-only APIs.
|
||
6. `npx electron-builder <platform flags> --publish <policy>`.
|
||
|
||
The shell/updater live in `app.asar`; the standalone server and native renderer binaries ship unpacked as `extraResources`. **Do not cross-build the server payload.** Next traces the build machine's native binaries, so a macOS-built standalone tree is not a Windows/Linux payload.
|
||
|
||
`npm run desktop:dist` remains the local macOS signing path, using the `LH_NOTARY` keychain profile. For a local Windows/Linux build, run steps 1–5 above, then:
|
||
|
||
```sh
|
||
# On Windows:
|
||
npx electron-builder --win --x64 --publish never
|
||
|
||
# On Linux:
|
||
npx electron-builder --linux --x64 --publish never
|
||
```
|
||
|
||
## CI vs release
|
||
|
||
`.github/workflows/ci.yml` runs on main pushes and pull requests:
|
||
|
||
- Typecheck, full deterministic test suite, and lint on Ubuntu.
|
||
- Native renderer tests plus standalone build/boot-smoke on macOS, Windows, and Linux.
|
||
|
||
`.github/workflows/release.yml` has two deliberately separate modes:
|
||
|
||
- **Manual / Run workflow:** unsigned rehearsal installers saved as workflow artifacts for 7 days. No signing secrets needed, no release created, and nothing published. macOS notarization is skipped. Use the Windows/Linux artifacts for the first human smoke pass. These are not production macOS installers.
|
||
- **Push `v<package.json version>`:** gates first, then one job creates or reuses a **single draft GitHub Release** before the three OS jobs package/upload their artifacts and update metadata in parallel. This avoids competing draft creation. A published or ambiguous existing release fails the preparation step instead of being modified. Separate runs for the same ref are serialized, never cancelled mid-upload. The tag must match the package version. macOS must sign and notarize successfully; missing secrets fail loudly. The workflow never publishes the draft.
|
||
|
||
The three jobs upload distinct updater feeds: `latest-mac.yml`, `latest.yml`, `latest-linux.yml`, with the matching ZIP/NSIS/AppImage/deb assets and blockmaps. Do not publish a partially populated draft. The app uses the baked GitHub feed without a token: **the repository must be public before end users can download updates.** Making the repository public and publishing the release remain the owner's actions.
|
||
|
||
## macOS CI secrets
|
||
|
||
Add these under repository **Settings → Secrets and variables → Actions**:
|
||
|
||
| Secret | Value |
|
||
|---|---|
|
||
| `MAC_CSC_LINK` | Base64-encoded Developer ID Application `.p12` backup, including its private key |
|
||
| `MAC_CSC_KEY_PASSWORD` | Password protecting that `.p12` |
|
||
| `APPLE_ID` | Apple ID used for notarization |
|
||
| `APPLE_APP_SPECIFIC_PASSWORD` | App-specific password for that Apple ID |
|
||
| `APPLE_TEAM_ID` | `JS6XN22HY6` |
|
||
|
||
The workflow maps the certificate secrets to electron-builder's `CSC_LINK` / `CSC_KEY_PASSWORD`. GitHub's built-in `GITHUB_TOKEN` supplies `GH_TOKEN` with job-scoped `contents: write`; no separate GitHub token secret is required. Signing credentials are present only for the packaging step, not the Next build. Never commit certificates, passwords, or `.env.local`.
|
||
|
||
## First release runbook
|
||
|
||
1. Push the implementation; run the manual Desktop release workflow.
|
||
2. Human smoke on Windows/Linux: install/open, window buttons and dragging, sidebar at narrow widths, zoom, setup/keys, session/resume, native PDF/SVG verification and demo helper. macOS remains signed/notarized through the production path.
|
||
3. Add macOS signing/notarization secrets.
|
||
4. Bump `package.json` and the lockfile to the agreed release version; run local gates and push a matching `v...` tag.
|
||
5. Wait for **all three** packaging jobs. Verify all installer assets and updater feeds are in the draft.
|
||
6. Owner makes the repository public, publishes the complete draft, then checks the real GitHub update path using a prior installed version.
|
||
|
||
A full human pass is required before the first three-platform release. Subsequent content-only updates rely on the deterministic suite plus the per-OS build/boot-smoke matrix. Changes to `electron/`, native modules, packaging/updater configuration, or Electron itself call for another platform smoke pass.
|