Add /api/version and bin/wait-deploy so deploys are observable from any machine

This commit is contained in:
Chris Amow 2026-08-10 01:04:58 +00:00
parent 3cc682d63a
commit 49b180e797
3 changed files with 73 additions and 3 deletions

View file

@ -33,17 +33,34 @@ uvicorn main:app --reload
| `static/` | `index.html`, `app.js`, `style.css` — Vue 3 loaded from unpkg |
| `requirements.txt` | Python deps |
| `Procfile` | Start command; **nixpacks needs this** or the deploy has nothing to run |
| `bin/wait-deploy` | Blocks until the live site serves your latest commit |
| `Dockerfile.dev`, `docker-compose.yml` | Local dev only — production does not use them |
## Deployment
Push to `main` → Forgejo webhook → Coolify rebuilds with nixpacks → live in
roughly 25 seconds.
Push to `main` → Forgejo webhook → Coolify rebuilds with nixpacks → live.
A nixpacks rebuild takes **90 seconds to 2 minutes** — longer than the static
sites, which go in ~25s. Nothing changes on the site until the new container
swaps in at the very end.
To know when your commit is actually live, rather than guessing:
```bash
git push
git push && bin/wait-deploy
```
It polls `/api/version` (which returns the `SOURCE_COMMIT` Coolify bakes into
the container) until it matches your local `HEAD`, then exits. Runs from any
machine — no Coolify token, no SSH tunnel. Check by hand any time with:
```bash
curl -s https://chart.amow.com/api/version
```
One more trap worth naming: if your change only touches an `/api` endpoint, the
HTML is byte-identical and a browser refresh looks like nothing happened even
after a successful deploy. Verify the endpoint, not the page.
Two things to know before changing the deploy config:
- The Coolify domain is registered as `https://chart.amow.com:8000`. The port

43
bin/wait-deploy Executable file
View file

@ -0,0 +1,43 @@
#!/usr/bin/env bash
# Block until the deployed site is serving the commit at local HEAD.
#
# git push && bin/wait-deploy
#
# Works from any machine — it only reads the public /api/version endpoint, so
# no Coolify token and no SSH tunnel are needed.
set -uo pipefail
URL=${URL:-https://chart.amow.com}
TIMEOUT=${TIMEOUT:-300}
want=$(git rev-parse HEAD) || exit 1
short=${want:0:8}
echo "waiting for $URL to serve $short (timeout ${TIMEOUT}s)"
start=$(date +%s)
last=""
while :; do
now=$(date +%s)
elapsed=$(( now - start ))
if [ "$elapsed" -ge "$TIMEOUT" ]; then
echo "TIMEOUT after ${elapsed}s — still serving ${last:-unknown}"
echo "check the build log: https://chart.amow.com is fine but Coolify may have failed"
exit 1
fi
# 502 during the rolling container swap is normal; keep polling.
got=$(curl -fsS -m 5 "$URL/api/version" 2>/dev/null \
| sed -n 's/.*"commit"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
if [ "$got" = "$want" ]; then
echo "deployed ${short} after ${elapsed}s"
exit 0
fi
if [ "$got" != "$last" ]; then
echo " ${elapsed}s: serving ${got:-unreachable}"
last="$got"
fi
sleep 3
done

10
main.py
View file

@ -4,6 +4,7 @@ Placeholder app: serves the Vue 3 single-page frontend from static/ and a
couple of JSON endpoints under /api. Replace the endpoints as the real app
takes shape; the serving/deploy wiring below does not need to change.
"""
import os
from pathlib import Path
from fastapi import FastAPI
@ -13,6 +14,9 @@ from fastapi.staticfiles import StaticFiles
BASE_DIR = Path(__file__).parent
STATIC_DIR = BASE_DIR / "static"
# Coolify injects the deployed commit; absent when running locally.
SOURCE_COMMIT = os.environ.get("SOURCE_COMMIT", "dev")
app = FastAPI(title="chart")
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
@ -23,6 +27,12 @@ def health():
return {"status": "ok", "service": "chart"}
@app.get("/api/version")
def version():
"""Which commit is actually serving. `bin/wait-deploy` polls this."""
return {"commit": SOURCE_COMMIT}
@app.get("/api/hello")
def hello():
return {"msg": "this is fing awesome"}