feat(ops): deploy.sh — build, tag, replace, prune
One-shot deploy script that wraps the podman build + run dance: ./deploy.sh # build → replace container → prune (default) ./deploy.sh build # build + tag only ./deploy.sh run # restart from existing :latest, no rebuild ./deploy.sh prune # drop old timestamped images ./deploy.sh config # print resolved configuration Each build tags the image with both :latest AND a UTC timestamp (YYYYMMDD-HHMMSS), so rollback is a tag retag away. Prune keeps the N most recent timestamped images (KEEP_IMAGES, default 3); :latest is never touched. The matching regex is strict — only the exact YYYYMMDD-HHMMSS pattern — so a stray "dev" or hand-typed tag can't get caught. Settings come from an optional deploy.env (gitignored; example in deploy.env.example). Parser is allowlist-based: only recognised keys apply, malformed lines and command-substitution forms are ignored. Available overrides: IMAGE_NAME, CONTAINER_NAME, VOLUME_NAME, HOST_PORT, BIND_ADDR, KEEP_IMAGES, PUBLIC_BASE_URL, EXTRA_PODMAN_RUN_ARGS. HOST_PORT and KEEP_IMAGES are integer- validated before use. Uses podman run --replace per the global ops guidance (atomic, idempotent, no stop→rm→run race). BUILDAH_FORMAT=docker so the HEALTHCHECK directive in the Containerfile survives. shellcheck clean. README's Deployment section rewritten to lead with the script; manual podman snippet kept as fallback.
This commit is contained in:
parent
8ac1d8a0e6
commit
d29e1fd3d5
4 changed files with 273 additions and 11 deletions
59
README.md
59
README.md
|
|
@ -103,10 +103,53 @@ still works.
|
|||
|
||||
## Deployment
|
||||
|
||||
### Container (podman)
|
||||
### One-shot deploy: `./deploy.sh`
|
||||
|
||||
The provided `Containerfile` builds a single image that serves API + frontend
|
||||
and persists the SQLite database in `/app/data` (one volume).
|
||||
The repository ships a small bash deploy script that wraps the usual
|
||||
`podman build` + `podman run --replace` dance, tags images with both
|
||||
`:latest` and a UTC timestamp, and prunes older timestamped images so
|
||||
local disk doesn't grow forever:
|
||||
|
||||
```bash
|
||||
./deploy.sh # build → replace container → prune (default)
|
||||
./deploy.sh build # build + tag only
|
||||
./deploy.sh run # restart from existing :latest, no rebuild
|
||||
./deploy.sh prune # drop old timestamped images
|
||||
./deploy.sh config # print resolved configuration
|
||||
```
|
||||
|
||||
Settings come from `deploy.env` (gitignored). Copy the example and edit:
|
||||
|
||||
```bash
|
||||
cp deploy.env.example deploy.env
|
||||
```
|
||||
|
||||
Available overrides:
|
||||
|
||||
| Variable | Default | Notes |
|
||||
|---------------------------|-----------------------|------------------------------------------------------------------|
|
||||
| `IMAGE_NAME` | `vinterliste` | Local image-tag prefix. |
|
||||
| `CONTAINER_NAME` | `vinterliste` | `podman` container name. |
|
||||
| `VOLUME_NAME` | `vinterliste-data` | Named volume holding the SQLite file. |
|
||||
| `HOST_PORT` | `3000` | Host port mapped to the container's `:3000`. |
|
||||
| `BIND_ADDR` | `0.0.0.0` | Use `127.0.0.1` when fronting with a reverse proxy on the host. |
|
||||
| `KEEP_IMAGES` | `3` | Number of timestamped images to keep on prune. |
|
||||
| `PUBLIC_BASE_URL` | (auto) | Canonical URL injected into OpenGraph tags. |
|
||||
| `EXTRA_PODMAN_RUN_ARGS` | (empty) | Appended verbatim to `podman run`. Word-split, quote properly. |
|
||||
|
||||
The `.env` parser is allowlist-based — unknown keys log a warning and are
|
||||
ignored; lines that aren't `KEY=value` are skipped; quoted values are
|
||||
stripped of one layer of `'` or `"`. Command-substitution forms like
|
||||
`KEY=$(…)` are NOT evaluated, so a malicious `deploy.env` can't run code.
|
||||
|
||||
The container exposes `/api/health` for healthchecks and bakes the build
|
||||
date / git revision into both OCI labels and `/etc/build-info`. The
|
||||
script uses `podman run --replace` for atomicity (no `stop` → `rm` →
|
||||
`run` race).
|
||||
|
||||
### Manual `podman build` / `podman run`
|
||||
|
||||
If you don't want the script:
|
||||
|
||||
```bash
|
||||
BUILDAH_FORMAT=docker podman build \
|
||||
|
|
@ -114,10 +157,9 @@ BUILDAH_FORMAT=docker podman build \
|
|||
--build-arg GIT_REVISION="$(git describe --always --dirty 2>/dev/null || echo dev)" \
|
||||
-t vinterliste:latest .
|
||||
|
||||
# Create a named volume for the SQLite file
|
||||
podman volume create vinterliste-data
|
||||
podman volume create --ignore vinterliste-data
|
||||
|
||||
podman run --replace --name vinterliste \
|
||||
podman run --replace --name vinterliste -d \
|
||||
-p 3000:3000 \
|
||||
-v vinterliste-data:/app/data:Z \
|
||||
vinterliste:latest
|
||||
|
|
@ -125,11 +167,6 @@ podman run --replace --name vinterliste \
|
|||
# Visit http://localhost:3000
|
||||
```
|
||||
|
||||
The container exposes `/api/health` for healthchecks and bakes the build date /
|
||||
git revision into both OCI labels and `/etc/build-info`. Use `podman run
|
||||
--replace ...` for redeploys — it's atomic and avoids the "container exists"
|
||||
race.
|
||||
|
||||
### Environment variables
|
||||
|
||||
| Variable | Default | Notes |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue