Infra: split dev and production (releases, services, deploy script)
- Production now runs from ~/freezmo-prod/current (release symlink) as a standalone server on :3207; releases are exported by scripts/deploy.sh, built and e2e-verified on a temp port, switched atomically, health- checked via the new /api/healthz (commit + build time) and rolled back automatically on failure. Last 5 releases kept; scripts/rollback.sh for manual rollback. Shared env + data live in ~/freezmo-prod/shared/ and are symlinked into every release (existing sync/push data migrated and verified identical). - Dev runs from ~/freezmo via freezmo-dev.service: next dev on :3217 (LAN only), its own build folder (.next-dev via NEXT_DIST_DIR), its own data (~/freezmo/.freezmo-dev), DEV badge, dev favicon/theme colour, "Freezmo Dev" PWA identity and never the production service worker. - /api/healthz + the Settings Details version row (short commit + build time). output: standalone in next.config (next start still works). - AGENTS.md documents the workflow rules: only dev updates during a task, temporary dirs/ports for test builds, and deploy-at-the-end as the only production path. README gains an Environments and deploy section. - Fix found by the rehearsal: lib/dev-mode.ts must not be a client module (a server component importing a constant from a "use client" module gets a truthy proxy, which baked dev metadata into production builds). - SW -> freezmo-v34.
This commit is contained in:
parent
5ca0de920c
commit
3f52ab47c2
22 changed files with 665 additions and 35 deletions
84
README.md
84
README.md
|
|
@ -25,6 +25,7 @@ no database, no data leaving your machine except the requests you send to Ollama
|
|||
- [Checking the connection](#checking-the-connection)
|
||||
- [Running on your network / from your phone](#running-on-your-network--from-your-phone)
|
||||
- [Keep it running as a service](#keep-it-running-as-a-service-systemd-user-service)
|
||||
- [Environments and deploy](#environments-and-deploy)
|
||||
- [Folders](#folders)
|
||||
- [Install as an app (PWA)](#install-as-an-app-pwa)
|
||||
- [Security notes](#security-notes)
|
||||
|
|
@ -494,37 +495,90 @@ Notes for phones:
|
|||
- Clipboard paste works best on desktop; on mobile use the camera or photo picker.
|
||||
- Add the page to your home screen for a full-screen, app-like experience.
|
||||
|
||||
### Keep it running as a service (systemd user service)
|
||||
### Keep it running as a service (systemd user services)
|
||||
|
||||
`deploy/freezmo.service` runs the production build under **systemd (user)**, so Freezmo comes back by
|
||||
itself after a reboot and restarts if it ever crashes:
|
||||
Two units keep Freezmo alive — **production** (`deploy/freezmo.service`, the
|
||||
release on :3207) and **dev** (`deploy/freezmo-dev.service`, hot reload on
|
||||
:3217). Both come back after a reboot and restart if they crash:
|
||||
|
||||
```bash
|
||||
npm run build # the service serves the built app
|
||||
mkdir -p ~/.config/systemd/user
|
||||
cp deploy/freezmo.service ~/.config/systemd/user/
|
||||
cp deploy/freezmo.service deploy/freezmo-dev.service ~/.config/systemd/user/
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable --now freezmo.service
|
||||
systemctl --user enable --now freezmo.service freezmo-dev.service
|
||||
loginctl enable-linger "$USER" # start at boot without logging in
|
||||
```
|
||||
|
||||
| Command | What it does |
|
||||
| --- | --- |
|
||||
| `systemctl --user status freezmo` | is it up? |
|
||||
| `journalctl --user -u freezmo -f` | live logs |
|
||||
| `systemctl --user restart freezmo` | after a rebuild |
|
||||
| `systemctl --user stop freezmo` | stop it |
|
||||
| `systemctl --user status freezmo freezmo-dev` | are they up? |
|
||||
| `journalctl --user -u freezmo-dev -f` | live dev logs (hot reload included) |
|
||||
| `journalctl --user -u freezmo -f` | live production logs |
|
||||
| `systemctl --user restart freezmo-dev` | after changing the dev unit |
|
||||
|
||||
It binds `0.0.0.0:3207` exactly like `npm start` and reads `.env.local` from the project directory.
|
||||
The unit refuses to start without `.next/BUILD_ID`, so run `npm run build` before enabling it — and
|
||||
again after pulling changes.
|
||||
Production is **never** rebuilt or restarted by hand — `scripts/deploy.sh`
|
||||
does that, and only with a pushed, verified commit. The dev instance reads
|
||||
`~/freezmo/.env.local`, builds into `.next-dev` and keeps its data in
|
||||
`.freezmo-dev/`, so nothing it does can touch production. See
|
||||
[Environments and deploy](#environments-and-deploy) and
|
||||
[AGENTS.md](AGENTS.md) for the rules.
|
||||
|
||||
Because the build and the running server share `.next/`, stop the service before a manual rebuild:
|
||||
---
|
||||
|
||||
## Environments and deploy
|
||||
|
||||
Freezmo runs as two separate instances so that live work never touches what
|
||||
you actually use:
|
||||
|
||||
| | **Dev** | **Production** |
|
||||
| --- | --- | --- |
|
||||
| Path | `~/freezmo` (the working tree) | `~/freezmo-prod/current` → `releases/<timestamp>-<sha>` |
|
||||
| Service | `freezmo-dev.service` | `freezmo.service` |
|
||||
| Port | **3217** — LAN only, hot reload | **3207** — the public URL |
|
||||
| Build | `.next-dev` (dev server) | standalone server inside the release |
|
||||
| Data | `~/freezmo/.freezmo-dev/` | `~/freezmo-prod/shared/freezmo/` (sync, push) |
|
||||
| Env | `~/freezmo/.env.local` | `~/freezmo-prod/shared/env.local` |
|
||||
| Look | DEV badge, dev favicon/theme, installs as **Freezmo Dev** | normal |
|
||||
|
||||
On your phone: **dev = `http://<lan-ip>:3217`**, production =
|
||||
`http://<lan-ip>:3207` (and the public URL). The dev instance is bound to the
|
||||
LAN only — nothing routes port 3217 through the public proxy — so source maps
|
||||
and the HMR socket never leave the network. It uses a different origin, which
|
||||
means separate storage and a separate service worker: the production worker is
|
||||
never registered there and both PWAs can be installed side by side.
|
||||
|
||||
`NEXT_DIST_DIR` keeps the build folders apart (dev uses `.next-dev`, a plain
|
||||
`npm run build` keeps producing `.next` for the test suites), so no build can
|
||||
clobber a running server.
|
||||
|
||||
### Deploying
|
||||
|
||||
```bash
|
||||
systemctl --user stop freezmo && npm run build && systemctl --user start freezmo
|
||||
scripts/deploy.sh # deploy the latest origin/main
|
||||
scripts/deploy.sh <commit> # a specific pushed commit
|
||||
scripts/deploy.sh --skip-tests # fast path (lint + build only)
|
||||
scripts/rollback.sh # back to the previous release
|
||||
```
|
||||
|
||||
`deploy.sh` refuses a dirty working tree and commits that aren't on
|
||||
`origin/main`, exports the commit into a fresh release directory, runs
|
||||
`npm ci`, lint and the production build there, starts the new release on a
|
||||
temporary port and runs the **full browser suite against it** (the running
|
||||
production is untouched), then links the shared env + data, writes
|
||||
`release.json`, flips the `current` symlink atomically, restarts the service
|
||||
and polls `/api/healthz` until the new commit answers. Any failure after the
|
||||
switch rolls back to the previous release automatically. The last 5 releases
|
||||
are kept. The running version (short commit + build time) is shown in
|
||||
Settings → Details.
|
||||
|
||||
Data that must survive deploys lives in `~/freezmo-prod/shared/`:
|
||||
`env.local`, and `freezmo/{sync,push}` (symlinked into every release as
|
||||
`.env.local` and `.freezmo`).
|
||||
|
||||
The day-to-day rules — only dev updates during a task, production changes only
|
||||
through the deploy script at the end — are written down in
|
||||
[AGENTS.md](AGENTS.md) so every future session follows them.
|
||||
|
||||
---
|
||||
|
||||
## Folders
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue