You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: bare-repo invariant in deploy, runbook, local setup
- docs/operations/deploy.md — boot sequence describes git clone --bare,
reconcile state machine now references update-ref / merge-tree /
commit-tree plumbing. Env table note for CFP_DATA_REPO_PATH calls
out emptyDir + re-clone-on-boot.
- docs/operations/runbook.md — recovery for "API won't boot" drops the
delete-PVC step (no PVC for data). The "Drop into the pod" snippet
inspects the bare repo via git --git-dir / `git show HEAD:.gitsheets`.
- .claude/CLAUDE.md — Local setup section: contributors clone --bare;
optional second clone for a working-tree browser; Pod boot bullet
reflects bare clone + emptyDir.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: .claude/CLAUDE.md
+6-5Lines changed: 6 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -100,10 +100,11 @@ Two background concerns keep that store in sync with the world:
100
100
## Local setup
101
101
102
102
1.`asdf install` — picks up Node from `.tool-versions`
103
-
2. Clone the data repo as a sibling: `git clone git@github.com:CodeForPhilly/codeforphilly-data.git ../codeforphilly-data` (checkout `fixture` for a small seed, or `published` for the full laddr import)
104
-
3.`cp .env.example .env` and edit — point `CFP_DATA_REPO_PATH` at your sibling clone (absolute path recommended; relative paths resolve from `apps/api/`, not repo root)
105
-
4.`npm install`
106
-
5.`npm run dev` — api + web concurrently
103
+
2.**Bare-clone** the data repo as a sibling: `git clone --bare git@github.com:CodeForPhilly/codeforphilly-data.git ../codeforphilly-data` (use `--branch fixture` for a small seed, or `--branch published` for the full laddr import). The app reads via gitsheets' tree-object interface and *requires* bare — see [specs/behaviors/storage.md](../specs/behaviors/storage.md) → "The data clone is bare."
104
+
3.*(optional)* If you want a working tree to browse/edit records by hand, clone *from* your bare into a second directory: `git clone ../codeforphilly-data ../codeforphilly-data-wt`. Push/pull between them; the app doesn't care.
105
+
4.`cp .env.example .env` and edit — point `CFP_DATA_REPO_PATH` at your bare sibling clone (absolute path recommended; relative paths resolve from `apps/api/`, not repo root)
106
+
5.`npm install`
107
+
6.`npm run dev` — api + web concurrently
107
108
108
109
```bash
109
110
npm install # install all workspaces
@@ -124,7 +125,7 @@ Typical change flow:
124
125
1.**Merge to `main`** — CI builds + tests; nothing deploys yet.
125
126
2.**Publish image** (currently manual) — `docker build --platform=linux/amd64 -t ghcr.io/codeforphilly/codeforphilly-ng:sandbox . && docker push …`. Apple-silicon dev machines must set the platform flag — cluster nodes are amd64.
126
127
3.**GitOps pickup** — `cfp-sandbox-cluster` projects from our `deploy/kustomize/`; on its own merge, applies via `kubectl apply -k`.
127
-
4.**Pod boot** — single replica, `Recreate` strategy. Container entrypoint clones the data repo on first boot (PVC persists across pods). Node boots: env → store load → **reconcile** (ff/rebase/escape-hatch against `origin/<CFP_DATA_BRANCH>`) → **push daemon** → routes → SPA. `/api/health/ready` returns 200 once stores are loaded *and* reconciled.
128
+
4.**Pod boot** — single replica, `Recreate` strategy. Container entrypoint bare-clones the data repo on every pod start (the data volume is `emptyDir`, so first boot = every fresh pod). Node boots: env → store load → **reconcile** (ff/replay/escape-hatch against `origin/<CFP_DATA_BRANCH>`) → **push daemon** → routes → SPA. `/api/health/ready` returns 200 once stores are loaded *and* reconciled.
128
129
5.**Live data updates** — independent of app deploy. Pushes to `published` trigger the [hot-reload webhook](../docs/operations/runbook.md#hot-reload-webhook); the pod rebuilds in-memory state in place, no restart.
129
130
130
131
Constraints worth knowing before touching anything deploy-shaped:
|`CFP_DATA_REPO_PATH`| ConfigMap |`/app/data`— bare gitdir, backed by an `emptyDir`; re-cloned on every pod boot|
216
224
|`CFP_DATA_REMOTE`| Secret | git URL (ssh in prod) |
217
225
|`CFP_DATA_BRANCH`| ConfigMap | e.g. `fixture` / `main`|
218
226
|`CFP_DATA_RELOAD_SECRET`|**Secret**| Shared bearer-token for the hot-reload webhook; when unset the `/api/_internal/reload-data` endpoint returns 503. See [runbook.md](runbook.md#hot-reload-webhook). |
Copy file name to clipboardExpand all lines: docs/operations/runbook.md
+9-5Lines changed: 9 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -23,7 +23,7 @@ Look for one of the four common boot failures:
23
23
|------------------|-------|-----|
24
24
|`[entrypoint] ERROR: CFP_DATA_REMOTE is unset`| The Secret containing `CFP_DATA_REMOTE` isn't reaching the pod. | Check `kubectl get secret codeforphilly-secrets -o yaml`; verify the SealedSecret in the GitOps repo decrypted successfully (look at the sealed-secrets controller logs). |
25
25
|`fatal: could not read Username for 'https://...'` or `Permission denied (publickey)`| Bad/missing data-repo credentials. | Verify the `codeforphilly-data-deploy-key` Secret holds a valid `id_ed25519` whose public key has push access to the data repo. See [secrets.md](secrets.md#data-repo-deploy-key). |
26
-
|`Failed to open public gitsheets store`|Working tree corrupt or missing `.gitsheets/` configs. | Exec into the pod, inspect `/app/data/.gitsheets/`. Recovery: `kubectl delete pvc codeforphilly-data -n <ns>`, then trigger a rollout — the entrypoint re-clones from `CFP_DATA_REMOTE`. |
26
+
|`Failed to open public gitsheets store`|Bare clone corrupt or missing `.gitsheets/` configs. | Exec into the pod, inspect `/app/data/refs/`, `/app/data/objects/`, and verify `.gitsheets/` exists in HEAD via `git --git-dir=/app/data show HEAD:.gitsheets`. Recovery: restart the pod — `data` is an `emptyDir`, so a fresh pod re-clones from `CFP_DATA_REMOTE` automatically. |
27
27
|`Failed to load private store (s3)`| Bucket creds wrong, bucket gone, or network ACL blocks egress. | Confirm `S3_*` env in the ConfigMap + Secret. From the pod, `curl $S3_ENDPOINT` to confirm reachability. |
28
28
|`environment variable ... is required`| A required env (`CFP_DATA_REPO_PATH`, `STORAGE_BACKEND`, `CFP_JWT_SIGNING_KEY`) is missing. | Manifest regression. Compare against `deploy/kustomize/base/configmap.yaml` + the GitOps repo's SealedSecret. |
0 commit comments