Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
134 commits
Select commit Hold shift + click to select a range
4c433b0
add structure for initial local deployments skill
CarsonDavis Jun 9, 2026
c13a831
Add seed-golden bootstrap: build mmgis_golden from committed baseline
CarsonDavis Jun 9, 2026
5d9ed46
Merge remote-tracking branch 'origin/development' into feature/mmgis-…
CarsonDavis Jun 9, 2026
4846162
Add deployment-mode foundation for lean deployments
CarsonDavis Jun 9, 2026
64a7f26
Gate sidecar proxy, spawner, and mmgis-stac creation in lean mode
CarsonDavis Jun 9, 2026
fb5e601
Gate Datasets and Geodatasets in lean mode; expose DEPLOYMENT_MODE to…
CarsonDavis Jun 9, 2026
d8f6e67
Gate Missions serving, link shortener, and Missions-bound utils in le…
CarsonDavis Jun 9, 2026
3d7ba9f
Gate Draw out of lean deployments
CarsonDavis Jun 9, 2026
b254d30
Hide the populate-from-COG button in lean Configure
CarsonDavis Jun 9, 2026
c141d60
Harden boot and runtime for managed hosting
CarsonDavis Jun 9, 2026
f02c825
Add static frontend mode: SERVER flag, static dispatcher, ServiceUrls
CarsonDavis Jun 10, 2026
624063b
Resolve static-mode COG range, projection WKT, and time histogram
CarsonDavis Jun 10, 2026
129a4ef
Merge branch 'lean/pr-03-datasets-geodatasets' into lean/pr-08-publis…
CarsonDavis Jun 10, 2026
9d3e11e
Add lean publish flow: Deployments module, publish task, Configure page
CarsonDavis Jun 10, 2026
d161d19
Address publish-flow review findings
CarsonDavis Jun 10, 2026
cb834de
Add lean AWS infrastructure recipes and deploy pipeline
CarsonDavis Jun 10, 2026
110f37b
Merge branch 'lean/pr-08-publish-flow' into lean/pr-11-aws-infra
CarsonDavis Jun 10, 2026
d7642c7
Address AWS infra review findings
CarsonDavis Jun 10, 2026
7b84891
Merge branch 'lean/pr-05-missions-gates' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
90755e4
Repoint lean asset uploads to the shared S3 bucket
CarsonDavis Jun 10, 2026
ebcb43a
Poll deployment status and surface publish completion in Configure
CarsonDavis Jun 10, 2026
58ff7e5
Polish deployment completion toasts
CarsonDavis Jun 10, 2026
c07400d
Merge branch 'lean/pr-08-publish-flow' into lean/pr-11-aws-infra
CarsonDavis Jun 10, 2026
385093d
Merge branch 'lean/pr-11-aws-infra' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
b183dbc
Add lean follow-up ledger and next-steps doc
CarsonDavis Jun 10, 2026
1fd4df2
Enable admin WebSocket flows in the lean task definition
CarsonDavis Jun 10, 2026
0ae6b2a
Merge branch 'lean/pr-11-aws-infra' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
a2f9671
chore: bump version to 4.2.10-20260610 [version bump]
github-actions[bot] Jun 10, 2026
01a331e
Record vision-review ledgers in the lean follow-up doc
CarsonDavis Jun 10, 2026
177690c
Record Express Mode API mismatch from staging pre-flight
CarsonDavis Jun 10, 2026
f0cdbeb
Skip the publish-task themes build when dist/ is already baked
CarsonDavis Jun 10, 2026
249e6aa
Merge branch 'lean/pr-08-publish-flow' into lean/pr-11-aws-infra
CarsonDavis Jun 10, 2026
3f557d5
Merge branch 'lean/pr-11-aws-infra' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
62749d1
Align infra recipes and deploy pipeline with real Express Mode behavior
CarsonDavis Jun 10, 2026
4636447
Merge branch 'lean/pr-11-aws-infra' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
aef3164
Mark Express Mode recipe mismatch as fixed in PR 11
CarsonDavis Jun 10, 2026
e9551de
Grant the publish role the full CloudFront tag-action family
CarsonDavis Jun 10, 2026
1bd4bae
Merge branch 'lean/pr-11-aws-infra' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
fb838de
Upload the dashboard bundle in the layout the static index expects
CarsonDavis Jun 10, 2026
3390c7e
Merge branch 'lean/pr-08-publish-flow' into lean/pr-11-aws-infra
CarsonDavis Jun 10, 2026
8ab1848
Merge branch 'lean/pr-11-aws-infra' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
c976ab9
Add design notes for the deployments-registry redesign
CarsonDavis Jun 10, 2026
975a958
Bake the index Pug placeholders into static dashboards
CarsonDavis Jun 10, 2026
f39b00d
Merge branch 'lean/pr-08-publish-flow' into lean/pr-11-aws-infra
CarsonDavis Jun 10, 2026
d8ac03a
Merge branch 'lean/pr-11-aws-infra' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
d5f125a
Ship the baked config at Missions/<mission>/config.json
CarsonDavis Jun 10, 2026
d1535f6
Merge branch 'lean/pr-08-publish-flow' into lean/pr-11-aws-infra
CarsonDavis Jun 10, 2026
86aa43c
Merge branch 'lean/pr-11-aws-infra' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
ef35caa
Copy dashboard assets by mission folder name, not registry name
CarsonDavis Jun 10, 2026
9fd523c
Merge branch 'lean/pr-08-publish-flow' into lean/pr-11-aws-infra
CarsonDavis Jun 10, 2026
04642f2
Merge branch 'lean/pr-11-aws-infra' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
afa90ef
Add new-developer orientation for the lean deployment effort
CarsonDavis Jun 10, 2026
76a7e46
Remove local working docs from the branch
CarsonDavis Jun 10, 2026
c349e4f
Invalidate the dashboard CDN after publish and update uploads
CarsonDavis Jun 10, 2026
fc730d6
Grant the publish role cloudfront:CreateInvalidation
CarsonDavis Jun 10, 2026
5c12f70
Merge branch 'lean/pr-08-publish-flow' into lean/pr-11-aws-infra
CarsonDavis Jun 10, 2026
c768875
Merge branch 'lean/pr-11-aws-infra' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
e9bdff3
Gate Configure components by declared capability, not hardcoded action
CarsonDavis Jun 10, 2026
be79d30
Drop the unused STATIC_MODE flag and document the env naming rule
CarsonDavis Jun 10, 2026
7e737af
Ask isLeanMode() instead of comparing the mode string in Configure
CarsonDavis Jun 10, 2026
1937cdf
Consume isLeanMode() in the Panel nav gates
CarsonDavis Jun 10, 2026
3721f29
Merge branch 'lean/pr-03-datasets-geodatasets' into lean/pr-06-config…
CarsonDavis Jun 10, 2026
a4041f0
Ask isStaticBuild() instead of comparing SERVER strings
CarsonDavis Jun 10, 2026
698c980
Merge branch 'lean/pr-07-static-frontend' into lean/pr-09-static-bakes
CarsonDavis Jun 10, 2026
b61f7d6
Consume isStaticBuild() in the TimeUI and Map_ static gates
CarsonDavis Jun 10, 2026
2dd38c9
Merge branch 'lean/pr-03-datasets-geodatasets' into lean/pr-08-base
CarsonDavis Jun 10, 2026
28448b2
Merge branch 'lean/pr-07-static-frontend' into lean/pr-08-base
CarsonDavis Jun 10, 2026
2e58666
Merge branch 'lean/pr-08-base' into lean/pr-08-publish-flow
CarsonDavis Jun 10, 2026
66ff6b8
Adopt the shared predicates, status constants, and MMGIS_ env names
CarsonDavis Jun 10, 2026
4381d73
Merge branch 'lean/pr-08-publish-flow' into lean/pr-11-aws-infra
CarsonDavis Jun 10, 2026
d5e4707
Track the MMGIS_ rename of the publish RunTask overrides
CarsonDavis Jun 10, 2026
908a2e7
Pin the admin CloudFront origin-request policy the recipe actually uses
CarsonDavis Jun 10, 2026
88d5cb3
Merge branch 'lean/pr-01-foundation' into lean/pr-02-sidecar-proxy
CarsonDavis Jun 10, 2026
674b34a
Merge branch 'lean/pr-01-foundation' into lean/pr-03-datasets-geodata…
CarsonDavis Jun 10, 2026
fa35144
Merge branch 'lean/pr-01-foundation' into lean/pr-04-draw
CarsonDavis Jun 10, 2026
cb89329
Merge branch 'lean/pr-01-foundation' into lean/pr-05-missions-gates
CarsonDavis Jun 10, 2026
c8807fd
Merge branch 'lean/pr-01-foundation' into lean/pr-07-static-frontend
CarsonDavis Jun 10, 2026
495d2ed
Merge branch 'lean/pr-01-foundation' into lean/pr-12-hardening
CarsonDavis Jun 10, 2026
625e53c
Merge branch 'lean/pr-03-datasets-geodatasets' into lean/pr-06-config…
CarsonDavis Jun 10, 2026
9cbd20d
Merge branch 'lean/pr-07-static-frontend' into lean/pr-09-static-bakes
CarsonDavis Jun 10, 2026
6f4985d
Merge branch 'lean/pr-11-aws-infra' into lean/pr-10-base
CarsonDavis Jun 10, 2026
68b6a42
Merge branch 'lean/pr-03-datasets-geodatasets' into lean/pr-08-base
CarsonDavis Jun 10, 2026
f0763f0
Merge branch 'lean/pr-08-base' into lean/pr-08-publish-flow
CarsonDavis Jun 10, 2026
1b4d3c1
Merge branch 'lean/pr-08-publish-flow' into lean/pr-11-aws-infra
CarsonDavis Jun 10, 2026
f8a3b5b
Merge branch 'lean/pr-05-missions-gates' into lean/pr-10-base
CarsonDavis Jun 10, 2026
450b9c5
Merge branch 'lean/pr-10-base' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
0f76dbe
Restore CRLF line endings in .gitignore
CarsonDavis Jun 10, 2026
9f55bb2
Restore CRLF line endings in the predicate-converted files
CarsonDavis Jun 10, 2026
8a3e764
Merge branch 'lean/pr-07-static-frontend' into lean/pr-09-static-bakes
CarsonDavis Jun 10, 2026
78436fc
Restore CRLF line endings in TimeUI and Map_
CarsonDavis Jun 10, 2026
15799d7
Merge branch 'lean/pr-01-foundation' into lean/pr-02-sidecar-proxy
CarsonDavis Jun 10, 2026
bf3db1b
Merge branch 'lean/pr-01-foundation' into lean/pr-03-datasets-geodata…
CarsonDavis Jun 10, 2026
4468741
Merge branch 'lean/pr-01-foundation' into lean/pr-04-draw
CarsonDavis Jun 10, 2026
84c916b
Merge branch 'lean/pr-01-foundation' into lean/pr-05-missions-gates
CarsonDavis Jun 10, 2026
e559e5f
Merge branch 'lean/pr-01-foundation' into lean/pr-12-hardening
CarsonDavis Jun 10, 2026
4f174d5
Merge branch 'lean/pr-03-datasets-geodatasets' into lean/pr-06-config…
CarsonDavis Jun 10, 2026
b67e805
Merge branch 'lean/pr-03-datasets-geodatasets' into lean/pr-08-base
CarsonDavis Jun 10, 2026
58ff870
Merge branch 'lean/pr-07-static-frontend' into lean/pr-08-base
CarsonDavis Jun 10, 2026
064c0fd
Merge branch 'lean/pr-08-base' into lean/pr-08-publish-flow
CarsonDavis Jun 10, 2026
1ecf55f
Merge branch 'lean/pr-08-publish-flow' into lean/pr-11-aws-infra
CarsonDavis Jun 10, 2026
f95edc9
Merge branch 'lean/pr-11-aws-infra' into lean/pr-10-base
CarsonDavis Jun 10, 2026
96553ab
Merge branch 'lean/pr-05-missions-gates' into lean/pr-10-base
CarsonDavis Jun 10, 2026
c6d6e6f
Merge branch 'lean/pr-10-base' into lean/pr-10-s3-upload
CarsonDavis Jun 10, 2026
1b26da4
Compute shapefile .prj in the browser in every mode
CarsonDavis Jun 11, 2026
d090628
Gate proj42wkt in lean; frontend computes WKT client-side everywhere
CarsonDavis Jun 11, 2026
2247218
Correct proj42wkt disposition: Python shellout, gated in lean
CarsonDavis Jun 11, 2026
6526b44
chore: bump version to 4.2.11-20260611 [version bump]
github-actions[bot] Jun 11, 2026
2c40fc4
Consolidate the full-only utils routes into one isFull() block
CarsonDavis Jun 11, 2026
bfffc45
Merge pull request #129 from NASA-IMPACT/lean/pr-01-foundation
CarsonDavis Jun 11, 2026
d9b656a
Merge pull request #130 from NASA-IMPACT/lean/pr-02-sidecar-proxy
CarsonDavis Jun 11, 2026
8a71078
Merge remote-tracking branch 'origin/feature/mmgis-deployment-skill' …
CarsonDavis Jun 11, 2026
6393818
Merge pull request #131 from NASA-IMPACT/lean/pr-03-datasets-geodatasets
CarsonDavis Jun 11, 2026
931d371
Merge pull request #132 from NASA-IMPACT/lean/pr-04-draw
CarsonDavis Jun 11, 2026
14317ec
Merge pull request #133 from NASA-IMPACT/lean/pr-05-missions-gates
CarsonDavis Jun 11, 2026
95b8f3c
Merge pull request #136 from NASA-IMPACT/lean/pr-07-static-frontend
CarsonDavis Jun 11, 2026
23ec03b
Merge remote-tracking branch 'origin/feature/mmgis-deployment-skill' …
CarsonDavis Jun 11, 2026
42972dd
Merge pull request #135 from NASA-IMPACT/lean/pr-12-hardening
CarsonDavis Jun 11, 2026
0b903df
Merge pull request #134 from NASA-IMPACT/lean/pr-06-configure-polish
CarsonDavis Jun 11, 2026
e529964
Merge pull request #137 from NASA-IMPACT/lean/pr-09-static-bakes
CarsonDavis Jun 11, 2026
55a9f67
Merge remote-tracking branch 'origin/feature/mmgis-deployment-skill' …
CarsonDavis Jun 11, 2026
075974f
Merge pull request #138 from NASA-IMPACT/lean/pr-08-publish-flow
CarsonDavis Jun 11, 2026
9528c03
Merge pull request #139 from NASA-IMPACT/lean/pr-11-aws-infra
CarsonDavis Jun 11, 2026
310a265
Merge remote-tracking branch 'origin/feature/mmgis-deployment-skill' …
CarsonDavis Jun 11, 2026
24f3514
Merge pull request #140 from NASA-IMPACT/lean/pr-10-s3-upload
CarsonDavis Jun 11, 2026
7d9f5df
Fix CI: upgrade chart.js to v4 and fix Docker micromamba step
CarsonDavis Jun 16, 2026
4dda128
Escape baked static globals by context in publish-static
CarsonDavis Jun 16, 2026
c0b368a
Fix S3 CopySource over-encoding in publish asset copy
CarsonDavis Jun 16, 2026
e33c455
Resolve deployment bucket from stack output on delete to avoid orphans
CarsonDavis Jun 16, 2026
e1080af
Tidy comment wording in publish-static escaping helper
CarsonDavis Jun 16, 2026
dbc8998
Guard TiTiler/pgSTAC URL builders against a null base in static builds
CarsonDavis Jun 16, 2026
6b7888d
Guard buildStacItemsUrl and its consumer against a null base (follow-…
CarsonDavis Jun 16, 2026
9115c13
Route shapefile .prj generation through calls.api to restore full-mod…
CarsonDavis Jun 16, 2026
451d899
Shapefile .prj: try in-browser conversion first, fall back to GDAL ba…
CarsonDavis Jun 16, 2026
cba765a
Update lean ADR docs and proj42wkt comment for hybrid .prj and delete…
CarsonDavis Jun 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .claude/skills/mmgis-deployment/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
name: mmgis-deployment
description: Use when deploying MMGIS locally — standing up or booting a dev instance, running several MMGIS deployments or git worktrees in parallel, tearing one down, or debugging a local deployment that won't start or serve.
---

# Deploying MMGIS locally

## Overview

A **deployment** is one MMGIS instance: a server process on a `PORT`, a database named `DB_NAME`, an `.env`, and its own `node_modules`, in one directory. All deployments share a single Postgres+PostGIS container and coexist simply by using different `PORT` and `DB_NAME` values. A git worktree is just one convenient way to get an independent directory — a separate clone, or the main checkout itself, works the same.

Each deployment has its **own database**, so its Configure page, users, and missions are fully independent and persistent. New deployments start from a frozen baseline database (`mmgis_golden`) so they boot as a working app (admin user + a mission) with no manual setup.

To understand the internals (the single-instance deploy and the multi-instance pattern), read `references/deployment-model.md`.

## Prerequisites

- The shared DB container must be running. From the main checkout: `npm run db:start`.
- `mmgis_golden` must exist. On a fresh machine run `scripts/seed-golden.sh` once — it builds the baseline (admin/admin + the `arst` mission) from the committed seed via a temporary server; no existing database needed. Set `MAPBOX_TOKEN` (env or main `.env`) first if you want basemaps to render — tokens are never committed to git. `scripts/refresh-golden.sh` instead re-snapshots the baseline from a live database you already have.

## Routing — which script for which intent

Scripts live in `scripts/` next to this file. Invoke them by path; each takes a deployment directory (default: current dir) or, for `create`/`teardown`, a name.

| Intent | Command |
|--------|---------|
| Understand how local deploy works | read `references/deployment-model.md` |
| Provision a new deployment | `scripts/create.sh <name>` (new worktree) or `scripts/create.sh --here` |
| Boot the dashboard | `scripts/start.sh <dir>` — prints the dashboard URL when healthy |
| Stop the server | `scripts/stop.sh <dir>` |
| See everything running | `scripts/list.sh` |
| Diagnose a sick deployment | `scripts/doctor.sh <dir>` |
| Run tests | `scripts/test.sh <dir> [unit\|e2e\|all]` |
| Remove a deployment | `scripts/teardown.sh <name\|dir>` |
| Bootstrap the baseline on a fresh machine | `scripts/seed-golden.sh` |
| Re-baseline from a live DB | `scripts/refresh-golden.sh [source-db]` |

A typical new-feature flow: `create.sh <name>` → `start.sh <dir>` → open the dashboard URL. When done: `teardown.sh <name>`.

## Autonomy and safety

Run these **autonomously** when the user's intent is clear: `create`, `start`, `stop`, `list`, `doctor`, `test`, and `seed-golden` when no golden exists (it refuses to overwrite one without `--force`).

**Confirm with the user first** — show exactly what will change, then wait for explicit approval — before:

- `teardown.sh` — drops a database and removes a worktree. It refuses when the deployment has uncommitted or unpushed work unless `--force`; never bypass that for the user without surfacing the warning. Show its printed plan and get a yes.
- `refresh-golden.sh`, or `seed-golden.sh --force` — both overwrite the baseline all future deployments clone from.

## Common mistakes

- **Using `npm start` in a coexisting deployment.** Its `prestart` hook starts another DB container fighting for port 5432. Coexisting deployments use `start:no-docker` (which `start.sh` does). The main checkout may still use `npm start`.
- **Cloning a deployment DB from the live `mmgis` database.** Postgres can't use a connected database as a clone template. Always clone the frozen `mmgis_golden` (`create.sh` does this).
- **Expecting `refresh-golden` to update existing deployments.** It only changes the baseline for *future* clones; existing deployments keep their own databases.
- **Reaching for `FORCE_CONFIG_PATH`.** It forces one read-only mission and bypasses the Configure system — special-case only, not how normal deployments are configured.
- **Running e2e while another server holds port 8888.** Playwright would attach to it and test the wrong code. `test.sh e2e` guards against this.
59 changes: 59 additions & 0 deletions .claude/skills/mmgis-deployment/references/deployment-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# MMGIS local deployment model

How MMGIS runs on a developer machine — both the simple single-instance case and the multi-instance pattern this skill automates.

## What a deployment is

A deployment is one MMGIS instance: a server process on a `PORT`, a database named `DB_NAME`, an `.env`, and its own `node_modules`, in one directory. The only backing service required is **one Postgres+PostGIS container**. STAC / TiTiler / tipg / veloserver are optional (`--profile stac`, `--profile veloserver`) and off by default — not needed for a dashboard or for tests.

## The three run modes (from package.json)

- **`npm start`** — has a `prestart` hook that runs `docker-compose -f docker-compose.db.yml up -d --wait` to bring up a DB container, then `node scripts/init-db.js && node scripts/server.js`. The vanilla single-machine path.
- **`npm run start:no-docker`** — `init-db.js` + `server.js` with **no** `prestart` hook (npm only fires `prestart` before `start`). Assumes a DB is already reachable. **This is what coexisting deployments use**, because the `prestart` container always binds host port 5432 and would collide when a shared container already holds it.
- **`npm run start:test`** — `NODE_ENV=test PORT=8888 node scripts/server.js`. What Playwright auto-launches for e2e.

## Database

- **Auto-init.** `init-db.js` creates the `$DB_NAME` database, the `postgis` + `btree_gist` extensions, the `session` table, and spatial indexes. Idempotent ("already exists → nothing to do").
- **Auto-schema.** `server.js` runs Sequelize `.sync()`, which creates all tables on boot. **No manual migrations.**
- **Cross-branch schema.** `.sync()` reconciles *additive* changes automatically. Dropping/renaming columns across branches is the only case needing manual care.

## Ports

- In `NODE_ENV=development`: the API + WebSocket run on `PORT`; the **dashboard UI is served by webpack-dev-server on `PORT+1`**. `/` on `PORT` redirects to `PORT+1`. So dev uses **two** adjacent ports.
- In test/production: everything is on `PORT` (no `+1`).
- The API healthcheck is `GET /api/utils/healthcheck` on `PORT` (returns "Alive and Well!"). It comes up before webpack finishes its first compile, so the API can be healthy while the dashboard is still compiling for a few seconds.

## Coexisting many deployments

They differ only in `PORT` and `DB_NAME`, against one shared container:

- **One shared Postgres container.** `docker-compose.db.yml` hardcodes host `5432:5432`, so only one can run. Start it once from the main checkout (`npm run db:start`); every deployment points at `localhost:5432` with a distinct `DB_NAME`. Coexisting deployments launch with `start:no-docker`.
- **Ports stepped by 10** (dev burns `PORT` and `PORT+1`), starting at 8888 and skipping any already in an `.env` or listening.
- **`.env` and `node_modules` are gitignored** — never present in a fresh worktree; each deployment provisions its own (`npm install --force`; `--force` is required by this dependency tree, not `--legacy-peer-deps`).

## Config: how a deployment gets a working dashboard

A fresh database has no admin and no missions, so the landing page would be empty. Two ways to populate it:

- **Golden clone (default).** A frozen baseline database `mmgis_golden` holds a baseline admin (`admin`, permission `111`) and a mission. Each new deployment's database is created as `CREATE DATABASE <db> TEMPLATE mmgis_golden`, so it boots already populated and then evolves independently. Postgres requires the template to have no active connections — that's why `mmgis_golden` is frozen (nothing connects to it) and why you must never clone from the live `mmgis` database.
- **First-run signup.** Without a golden, the `/first_signup` endpoint makes the first account created a full admin (permission `111`); you then build missions in the Configure page.

Where the golden itself comes from — it lives only in the local Docker volume, so each machine builds its own:

- **Seed (fresh machine).** `seed-golden.sh` builds it from the committed `seed/baseline-mission.json`: it boots a temporary server against a scratch database (Sequelize creates the schema), seeds an admin + the baseline mission through MMGIS's own APIs (`first_signup` → `login` → `/api/configure/add`), then renames the scratch DB to `mmgis_golden`. The basemap token is injected at seed time from `MAPBOX_TOKEN` (env or the main checkout's `.env`) — **tokens are never committed to git**; the seed file holds a `{{MAPBOX_TOKEN}}` placeholder.
- **Snapshot (existing machine).** `refresh-golden.sh` re-baselines from a live database via `pg_dump` (default source: `mmgis`).

Note the golden does **not** survive `docker-compose down -v` or deleting the `mmgis_mmgis-local-db` volume — that wipes every database. Re-seed or re-snapshot afterward.

`FORCE_CONFIG_PATH=<file.json>` forces a single read-only mission from a file and **bypasses the Configure system** — a special case, not how normal deployments are configured.

## Tests

- **Unit (`npm run test:unit`)** — pure JS, no server, no database. Run anywhere in parallel, no collisions.
- **E2E (`npm run test:e2e`)** — Playwright auto-starts the server via `start:test`, which hardcodes `PORT=8888`, and `reuseExistingServer` is on locally. If another server is already on 8888, Playwright attaches to it and tests the wrong code. Run e2e only when 8888 is free (the `test.sh` helper enforces this). Parallel e2e across deployments would need app changes and is out of scope.

## Gotchas

- **CRLF in `.env`.** Some `.env` files use Windows line endings; a naive shell read yields values with a trailing `\r` (e.g. role `mmgis\r` → "role does not exist"). Always strip CR when reading `.env` in shell.
- **Stray DB containers.** Running plain `npm start` in a worktree spins up that worktree's *own* DB container (e.g. `mmgis-<name>-db-1`). These can linger (often in "Created" state) and confuse "which container is the shared one." The shared one is whichever publishes host port 5432.
51 changes: 51 additions & 0 deletions .claude/skills/mmgis-deployment/references/troubleshooting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Troubleshooting MMGIS deployments

`doctor.sh` points here. Find the symptom, apply the fix.

## Server won't start / healthcheck never passes

| Symptom | Cause | Fix |
|---------|-------|-----|
| `start.sh` times out; log shows DB connection error | Shared DB container not running | `npm run db:start` in the main checkout |
| Log: `role "mmgis\r" does not exist` or auth fails on a clean DB | CRLF in `.env` (trailing `\r` in values) | The scripts strip CR when reading `.env`; if you hand-edit, save with LF endings |
| Log: `database "<name>" does not exist` and it isn't created | `DB_NAME` mismatch, or DB never cloned | `doctor.sh`; if DB missing, `create.sh` (clones golden) or clone manually |
| `start.sh` says "already up on port N" | A server (maybe a stale one) is on that port | `stop.sh <dir>`, or find the listener with `lsof -nP -iTCP:N -sTCP:LISTEN` |
| API healthy but dashboard (PORT+1) returns 000/connection refused | webpack-dev-server still compiling | Wait ~30–90s for the first compile; re-curl |

## Port conflicts

| Symptom | Cause | Fix |
|---------|-------|-----|
| Two deployments show the same PORT in `list.sh` | They were configured with the same `PORT` (e.g. both copied 8888) | Edit one deployment's `.env` to a free port (`create.sh` picks free ports automatically for new deployments) |
| `start.sh` fails immediately, port in use | Another deployment or stray process holds the port | `lsof -nP -iTCP:<port> -sTCP:LISTEN`; stop the owner |
| Plain `npm start` failed with port 5432 in use | `npm start`'s `prestart` tried to start a second DB container | Use `start:no-docker` (what `start.sh` does); remove the stray container `docker rm mmgis-<name>-db-1` |

## Database / golden

| Symptom | Cause | Fix |
|---------|-------|-----|
| `create.sh`: "mmgis_golden does not exist" | Baseline never created on this machine | `seed-golden.sh` (builds from the committed seed — works on a fresh machine) or `refresh-golden.sh` (snapshots a live `mmgis`) |
| Basemap blank in a fresh deployment | Golden was seeded without `MAPBOX_TOKEN` | Set `MAPBOX_TOKEN` and `seed-golden.sh --force`, or paste a token into the mission's basemap settings in Configure |
| Clone fails: "source database is being accessed by other users" | Tried to use a live DB as a TEMPLATE | Clone only from frozen `mmgis_golden`, never from `mmgis`; if refreshing golden, the dump/restore path avoids this |
| New deployment opens to an empty landing page | DB cloned from a golden that had no mission, or `first_signup` not done | Refresh golden from a DB that has the mission, or sign up the first admin and build a mission |
| `DROP DATABASE` fails: in use | Server still connected | `stop.sh <dir>` first; `teardown.sh` stops the server before dropping |

## Teardown

| Symptom | Cause | Fix |
|---------|-------|-----|
| "refusing: uncommitted or unpushed work" | Real source changes or local-only commits in the worktree | Review the listed files/commits; commit or push them, or re-run with `--force` if you truly want to discard |
| `git worktree remove` fails: "contains modified or untracked files" | Build churn (node_modules, lockfile) in the worktree | Expected — `teardown.sh` falls back to `--force` for the worktree removal step automatically |
| Branch still exists after teardown | `teardown.sh` removes the worktree + database but leaves the branch | Delete it yourself if unwanted: `git branch -D <branch>` |

## Tests

| Symptom | Cause | Fix |
|---------|-------|-----|
| e2e passes but didn't seem to test your changes | Playwright `reuseExistingServer` attached to another server on 8888 | `test.sh e2e` refuses when 8888 is busy; stop the other server and re-run |
| e2e can't start its server | Port 8888 occupied | Free it; `test.sh` reports the conflict |
| Unit tests fail to import modules | `node_modules` missing/incomplete | `npm install --force` in the deployment |

## Stray containers

`docker ps -a | grep postgis` may show extra `mmgis-<name>-db-1` containers from accidental `npm start` runs. The shared container is the one publishing host port 5432 (`docker ps --format '{{.Names}} {{.Ports}}'`). Remove strays: `docker rm <name>` (add `-f` if running).
Loading
Loading