Deploying SPB Git 2.0 to M1M32
Production is the git clone at M1M32:~/srv/spbgit/app, run by PM2 as
spbgit-server (manifest M1M32:~/dispatch/apps/spbgit.json, pinned to
M1M32, published through the MacLustr Tunnel as https://git.spboucher.ai).
origin of that clone is spbgit itself (https://git.spboucher.ai/spbgit.git,
bare repo ~/srv/git/spbgit.git on the same machine).
0. Pre-flight (laptop)
cd ~/Desktop/Cluster/projets/spbgit
npm run lint && npm run check:headers && npm test # 86 tests
python3 -I scripts/qa/screens.py http://127.0.0.1:7421 /tmp/qa --no-shots # dev server on 7421
git log --oneline -3 # 2.0.0 commits on main
git push origin main # → spbgit bare repo on M1M321. Deploy (M1M32)
ssh M1M32
export PATH=/opt/homebrew/bin:$PATH
cd ~/srv/spbgit/app
git fetch origin && git status # must be clean, on main
cp -a ~/srv/spbgit/cache ~/srv/spbgit/cache.bak-$(date +%Y%m%d) # optional: 2 GB, speeds up rollback
git pull --ff-only origin main
npm ci --omit=dev --no-audit --no-fund # @anthropic-ai/sdk, @fastify/compress added in 2.0
node scripts/fetch-fonts.mjs # woff2 are git-ignored
pm2 restart spbgit-server --update-env
pm2 logs spbgit-server --lines 20 --nostreamThe first home request after the restart recomputes 157 overviews with the
new cache shape (overview-v5.json, heatmap-v5.json, search-index-v2.json);
expect ~10 s once, then ≤ 100 ms. Old overview-v4.json/heatmap-v4.json
files are simply ignored (delete them with
find ~/srv/spbgit/cache -name 'overview-v4.json' -delete when convenient).
Recommended at the same time (operational, reversible)
- Memory limit: the 2.0 server idles around 250–350 MB and no longer
buffers blobs; keep
max_memory_restart: 750Min the manifest but expect zero memory restarts. Check after 24 h:pm2 describe spbgit-server | grep restarts. - Log rotation:
~/.pm2/logs/spbgit-server-out.logwas 599 MB.pm2 install pm2-logrotate && pm2 set pm2-logrotate:max_size 50M && pm2 set pm2-logrotate:retain 7, thenpm2 flush spbgit-serveronce. - AI (optional, off by default): add to the manifest env
SPBGIT_AI_PROVIDER=anthropic,SPBGIT_AI_API_KEY=…(server-side only), optionallySPBGIT_AI_MODEL(defaultclaude-opus-5-5),SPBGIT_AI_DAILY_BUDGET_TOKENS(default 500 000). Or point at a local OpenAI-compatible server:SPBGIT_AI_PROVIDER=openai-compatible,SPBGIT_AI_BASE_URL=http://<node>:<port>/v1,SPBGIT_AI_MODEL=…. Every repository is public, so no private code is involved, but enabling a remote provider still sends repository excerpts and visitor questions to it — decide explicitly.
2. Verify
curl -s https://git.spboucher.ai/healthz | jq . # version 2.0.0, features, git concurrency
curl -s -o /dev/null -w '%{http_code} %{time_total}\n' https://git.spboucher.ai/
curl -s -o /dev/null -w '%{http_code}\n' https://git.spboucher.ai/spbgit/blob/main/src/server.mjs
curl -s "https://git.spboucher.ai/api/v1/search?q=server&scope=files&limit=3" | jq .files
curl -s https://git.spboucher.ai/api/v1/stats | jq '{commits, commitsLast52Weeks}' # commits ≥ commitsLast52Weeks
git ls-remote https://git.spboucher.ai/spbgit.git | head -2 # Smart HTTP advertisement
# from the laptop: a real clone + a push to a scratch repo with a PATThen the manual Safari/iPhone checklist in docs/SPBGIT-2-PROGRESS.md (Phase 7).
3. Rollback
ssh M1M32
export PATH=/opt/homebrew/bin:$PATH
cd ~/srv/spbgit/app
git checkout 810e402 # last 1.1.0 commit (v4 "Monolith")
npm ci --omit=dev --no-audit --no-fund
pm2 restart spbgit-server --update-envData is untouched by 2.0 (bare repos, meta.json, activity.jsonl,
clones.json, tokens.json, releases). 2.0 only adds data/ai-usage.json
(when AI is enabled) and new cache file names; 1.1.0 ignores both. The 1.1.0
caches (overview-v4.json, heatmap-v4.json, search-index.json) are left in
place by 2.0, so a rollback is warm immediately. Return to 2.0 with
git checkout main.
4. What changed operationally
| Item | 1.1.0 | 2.0.0 |
|---|---|---|
/raw/* |
whole blob buffered (≤ 64 MB) | streamed from git cat-file, Content-Length from cat-file -s |
| git subprocesses | unbounded | 12 concurrent (SPBGIT_GIT_CONCURRENCY) |
| Request logging | 2 lines per request (incl. assets) | 1 line per completed request, assets only on error |
| Compression | none in-process | brotli q4 / gzip 5 for text ≥ 1 KB (@fastify/compress) |
| Overview cache | per repo, busted by hook only | validated against ref stamps (HEAD, packed-refs, refs/heads, refs/tags mtimes) |
| New routes | — | /about/metrics, /:repo/insights, /:repo/research, /api/v1/search, /api/v1/repos/:name/tree, /api/v1/ai/* (404 unless enabled) |
| Dependencies | — | @anthropic-ai/sdk, @fastify/compress; dev: axe-core |