# 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 backend-droplet # deploy/deploy-server.sh manage-apps status mac-sync backend-droplet # curl http://localhost:3201/health manage-apps logs mac-sync backend-droplet # journalctl -u mac-sync-server manage-apps stop mac-sync backend-droplet # 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://209.38.51.98:3201/health/deep # DO backend droplet (was black 10.0.0.11) # {"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`).