macsync/docs/release.md

112 lines
4 KiB
Markdown

# Release
`@mac-sync` ships three artefacts: the Mac client, the server, and the web
SPA. They are deployed independently.
## Version source
`VERSION.json` at the repo root is the canonical version. `app.manifest.yaml:5`
duplicates it for the manage-apps CLI. Bump both together.
## Mac client — `MacSyncApp`
Build (release):
```sh
make build-release # Makefile:63-65 -> swift build -c release --product MacSyncApp
```
Output: `.build/release/MacSyncApp`. The binary is a menu-bar executable; for
distribution wrap it as a `.app` bundle including:
- The binary itself
- A `webapp/` resource directory built from `web/dist/`
(`LocalWebServer.swift:138-141` looks here first)
- Info.plist with `LSUIElement=true` (menu-bar only)
- Privacy usage strings: `NSCalendarsFullAccessUsageDescription`,
`NSRemindersFullAccessUsageDescription`, `NSAppleEventsUsageDescription`,
`NSPhotoLibraryUsageDescription`, plus Full Disk Access via signed entitlement
The user instals through `deploy/install.sh` and a LaunchAgent
(`app.manifest.yaml:20-22`):
```sh
ssh plum 'bash -s' < deploy/install.sh
```
Or via the manage-apps CLI (`app.manifest.yaml:24-30`):
```sh
manage-apps start mac-sync plum # runs deploy/deploy-remote.sh
manage-apps stop mac-sync plum # launchctl unload
manage-apps status mac-sync # ssh plum 'pgrep -x MacSyncApp'
manage-apps logs mac-sync # tail of ~/Library/Application Support/MacSync/stderr.log
```
## Server — `mac-sync-server`
Build is implicit (Bun runs TypeScript directly). The systemd unit lives in
`deploy/systemd/` and is referenced by `app.manifest.yaml:42-44`.
```sh
manage-apps start mac-sync black # deploy/deploy-server.sh
manage-apps status mac-sync black # curl http://localhost:3201/health
manage-apps logs mac-sync black # journalctl -u mac-sync-server
manage-apps stop mac-sync black # sudo systemctl stop mac-sync-server
```
Migrations apply automatically on boot
(`src/server/src/app/server.ts:36-52`). To verify a fresh deploy:
```sh
curl http://10.0.0.11:3201/health/deep
# {"ok":true,"checks":{"db":"ok"}}
```
Environment is loaded by `loadConfig()` (`src/server/src/app/config.ts`);
required vars are documented in [dev-setup](./dev-setup.md#boot-the-server).
## Web SPA
Two distribution paths:
1. **Bundled with the Mac client.** Run `cd web && bun run build` before
`make build-release`; copy `web/dist/` into the `.app` bundle's
`webapp/` resource directory. Served at `http://localhost:8765/` by
`LocalWebServer` (`@packages/shared/Sources/MacSyncShared/WebServer/LocalWebServer.swift:94-109`).
2. **Hosted by the server.** The nginx config in `deploy/nginx/` (referenced
from the deploy scripts) can serve `web/dist/` from the same origin as the
API.
```sh
cd web
bun install
bun run build # tsc --noEmit && vite build (web/package.json:8)
```
Output goes to `web/dist/`. Verify with `bun run preview`.
## Rollback
- Mac client: previous `.app` bundle is preserved alongside the new one on
`plum`; relaunch via `launchctl load`.
- Server: `sudo systemctl restart mac-sync-server` after reverting the
source tree. Migrations are forward-only — see Caveats below.
- Web: redeploy the previous `web/dist/`. The bundle is fingerprinted by
Vite, so cache invalidation is automatic.
## Caveats
- **Migrations are forward-only.** `runMigrations(db, [...])`
(`src/server/src/app/server.ts:36-52`) appends; there is no `down` script.
A schema-breaking change requires manual SQL on the database.
- **iMessage `icloud.send_queue` is on a bespoke schema.** Versioning it is
independent of the generic per-module tables. Don't drop and recreate it on
a release — outgoing messages will be lost.
- **The Mac client retains `UserDefaults` watermarks across releases.** If a
release changes the inbound payload shape, bump `SyncManager.syncSchemaVersion`
(`@packages/imessage/Sources/IMessageSync/SyncManager.swift:47` for iMessage)
so the bootstrap resets the watermark.
- **Don't commit from a release script.** External tooling owns commits
(`CLAUDE.md:46`).