9.5 KiB
Packaging and releases
Capsule uses electron-builder 26.15.3 and electron-updater 6.8.9. The first distribution target is Windows x64 with a per-user NSIS installer. Build on Windows with Node 22.12+ and npm ci; the lockfile fixes the resolved dependency versions. No PHP server is needed to build the client.
1. Package the application
Run npm run dist:win. The installer and blockmap are written to dist/local/; npm run pack:win produces only dist/local/win-unpacked/.
Local builds are unsigned, named Capsule Local, and have application ID com.thetempusproject.capsule.local. They store sessions in %APPDATA%/capsule-local. Updates are deliberately disabled, and these installers must not be distributed as the public product. Windows may display an unknown-publisher warning.
Public builds use the permanent ID com.thetempusproject.capsule, executable Capsule.exe, and %APPDATA%/capsule/session.json, preserving the original app's session location. Both installers run per user and preserve app data on uninstall. Do not change the public ID, package name, install scope, or data path after shipping without an explicit migration. The current installer uses Electron's default icon; add an approved build/icon.ico before public branding is finalized.
2. Configure the release feed
Set CAPSULE_UPDATE_URL in the build environment to a permanent public HTTPS directory ending in /. No feed is hardcoded or configured yet. The URL must not contain login credentials, a query, or a fragment. The generated app-update.yml is baked into the signed application; site selection, login, and IPC cannot change it.
The host serves these files at that URL:
Capsule-<version>-x64-Setup.exeCapsule-<version>-x64-Setup.exe.blockmaplatest.yml, generated by electron-builder
Use immutable versioned filenames. Retain old release artifacts so downloads already in progress keep working. Serve the installer and blockmap as binary files, allow range requests for differential downloads, and avoid authentication/HTML challenge pages or response transformations. Metadata should revalidate (Cache-Control: no-cache); versioned artifacts can have long immutable cache lifetimes. Configure these response headers on the host; an upload request's headers alone do not configure every server.
Publishing latest.yml announces a release. Upload and verify both artifacts first. A running Capsule checks 15 seconds after launch and four hours after each scheduled check finishes. A manual check uses the same updater. No push service or background Windows service is installed. Immediate remote notifications can later request a check, but must never supply executable URLs or commands.
3. Update behavior
src/main/updateController.mjs owns the state machine. updates.js wires it into Electron, and updateIpc.mjs permits only the known top-level app window. The preload bridge exposes status, check, install, and a status subscription. The renderer receives no signing credentials, release tokens, site tokens, or updater configuration.
The updater automatically downloads a newer stable version. It validates checksums and the configured Windows publisher signature before reporting readiness. The user chooses Restart to update after saving work. Normal close, Windows shutdown, and logoff do not intentionally launch an update installer (autoInstallOnAppQuit = false). Reopening checks again and can reuse a valid cached download. No downgrade or prerelease is accepted. If a release is faulty, publish a higher version containing the reverted code; do not overwrite an existing installer or assume automatic rollback.
Network, metadata, signature, and download failures show a retryable error while leaving the installed app usable. The update panel is available on the login screen, so a broken site connection does not prevent a client update. Multiple app instances are prevented to avoid competing installs. Local and development builds never contact an update feed.
4. Sign a public release
Set these variables in a secure release environment:
CAPSULE_UPDATE_URL: the public read-only feed directory.CAPSULE_PUBLISHER_NAME: the exact common name on the signing certificate.CSC_LINK: a supported certificate location or base64 PFX, withCSC_KEY_PASSWORDwhen required; orCSC_NAME: the subject name of a signing identity already in the Windows certificate store, including a configured hardware-backed identity.
The certificate provider determines how its private key is accessed; not all certificates can be exported to a PFX. Store credentials in the CI secret store or machine certificate provider. Do not commit keys, .env files, or release credentials. This project does not load .env automatically.
Run npm run release:win. This invalidates any prior ready marker, runs the tests, compiles the client, requires code signing, and builds to dist/release/. Missing URL, missing publisher, missing signing identity, or a signing failure prevents a publishable build. Signature verification stays enabled. Signing identity configuration must be consistent across releases; plan certificate/publisher changes before the old certificate expires.
The output includes release-ready.json, a local record of the exact artifact hashes and feed. It is not served to clients and is not a substitute for Authenticode. Only files referenced by latest.yml plus the matching blockmap are publishable. Check both the installed executable and installer using Get-AuthenticodeSignature before the first public release.
5. Automate build and publication
scripts/release.ps1 is the CI-independent Windows entry point. It runs npm ci, tests, the signed build, and release verification. Without -Publish it performs no upload. Keep build jobs serialized for each feed; concurrent publishers can otherwise move latest.yml backward. No CI service is configured because this checkout has no remote and no runner was selected.
npm run publish:release verifies the build and prints its upload order without uploading. The included upload adapter supports an authenticated HTTPS PUT/WebDAV endpoint. It needs:
CAPSULE_UPLOAD_URL: HTTPS upload directory, ending in/, mapped by the host to the public feed.CAPSULE_UPLOAD_TOKEN: a bearer token with upload access, available only on the release machine.
The host must support authenticated PUT, conditional creation with If-None-Match: *, and atomic replacement of latest.yml. This adapter is not an S3 signing client, SFTP client, or TTP API route. If hosting uses one of those transports, add the corresponding publisher adapter before enabling publication; client update code does not need to change.
Run npm run publish:release -- --upload, or powershell -File scripts/release.ps1 -Publish for the complete pipeline. It verifies local checksums, uploads the installer and blockmap, verifies their publicly served bytes without credentials, and only then publishes latest.yml. Redirects are refused. Existing versioned objects are reused only if their public content matches. A failure stops the pipeline. If verification fails after latest.yml was uploaded, inspect and restore the feed pointer before retrying; the script does not claim a rollback.
Never publish dist/local/, win-unpacked/, build debug files, certificate files, or release-ready.json. Increment package.json and the lockfile version before each release (npm version patch --no-git-tag-version is one option). Release metadata accepts stable major.minor.patch versions only. A separate beta feed/channel can be added later.
Acceptance checks before public distribution
The automated tests exercise updater state transitions, duplicate checks, retryable failures, IPC boundaries, signing configuration, and artifact integrity. After npm run build, npm run smoke opens the compiled app hidden with an isolated dist/smoke-profile data directory, verifies the real preload/status bridge and a synthetic ready prompt, and captures dist/local/smoke.png and smoke-ready.png. It does not sign in or install anything. These checks do not substitute for an installed upgrade.
On a disposable Windows account or VM with the real signing identity and a staging HTTPS feed:
- Build and install signed
0.1.0; confirm the correct publisher, per-user installation, and launch. Save a site/session and preferences. - Build signed
0.1.1against the same staging feed and publish it. Open0.1.0, confirm the download and restart prompt, then restart and verify the running version and preserved session/preferences. - Close without accepting the update: it must not install at quit. Reopen and check that the cached update can become ready again.
- Test offline checks and an interrupted download, restore connectivity, and retry. Try an installer signed by a different publisher and a tampered file on the isolated feed; neither may become installable.
- Test a second launch, a normal uninstall (data retained), and reinstall. Confirm no unexpected elevation prompt. Verify the local build remains separate from the public product.
These installed/signed checks remain pending until a certificate and staging feed are provided. No live host, signing account, or public release is provisioned by the local build scripts.
References: electron-builder auto updates, Windows configuration. Consult the installed package APIs when newer online documentation describes features from a later major version.