diff --git a/CLAUDE.md b/CLAUDE.md index 6036e66e..03e692df 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -188,7 +188,15 @@ changemaker.lite/ ├── media-manager/ # Legacy media manager (reference) ├── nginx/ # Reverse proxy config (subdomain routing + CSP) ├── configs/ # Prometheus, Grafana, Alertmanager configs -├── scripts/ # backup.sh, legacy Cloudflare scripts +├── scripts/ # Deployment, backup, upgrade, registry scripts +│ ├── install.sh # Curl-friendly installer (downloads tarball + runs config.sh) +│ ├── build-and-push.sh # Build production images → push to Gitea registry +│ ├── build-release.sh # Package runtime files into release tarball +│ ├── mirror-images.sh # Mirror third-party images to Gitea +│ ├── upgrade.sh # 6-phase upgrade (git or release-tarball mode) +│ ├── upgrade-check.sh # Check for updates (git or Gitea API) +│ ├── upgrade-watcher.sh # Systemd bridge for admin GUI upgrades +│ └── backup.sh # PostgreSQL + Listmonk + uploads backup ├── docker-compose.yml # V2 orchestration (20+ services) ├── docker-compose.v1.yml # V1 backup (reference) ├── .env.example # All required environment variables @@ -199,7 +207,23 @@ changemaker.lite/ ## Quick Start Guide -### Initial Setup (First Time) +### Pre-built Install (Production — Recommended) + +The fastest way to deploy. No source code, no compilation: + +```bash +curl -fsSL https://gitea.bnkops.com/admin/changemaker.lite/raw/branch/v2/scripts/install.sh | bash +``` + +This downloads a ~9MB release tarball, runs the config wizard, and sets `IMAGE_TAG=latest`. Then: + +```bash +cd ~/changemaker.lite && docker compose up -d +``` + +Pre-built images are pulled from `gitea.bnkops.com/admin` (~2 min). Database migrations and seeding run automatically via the API entrypoint. Access the admin GUI at http://localhost:3000. + +### Source Install (Development) 1. **Clone repository and checkout v2 branch:** ```bash @@ -330,6 +354,36 @@ docker compose exec api npx drizzle-kit push docker compose down ``` +### Registry & Release Operations +```bash +# Build production images and push to Gitea registry +./scripts/build-and-push.sh --services api,admin,media-api,nginx +./scripts/build-and-push.sh --no-push # Build only, no push (verify) + +# Mirror third-party images to Gitea +./scripts/mirror-images.sh # Core images (postgres, redis, etc.) +./scripts/mirror-images.sh --all # Include heavy images (RC, Jitsi, n8n) + +# Build release tarball (for pre-built installs — run AFTER build-and-push) +./scripts/build-release.sh --tag v2.1.0 # Creates releases/changemaker-lite-v2.1.0.tar.gz +./scripts/build-release.sh --tag v2.1.0 --upload # Also upload to Gitea Releases API +./scripts/build-release.sh --dry-run # Preview tarball contents + +# Use registry images in upgrade (source installs) +./scripts/upgrade.sh --use-registry --force --skip-backup + +# Install from tarball (end-user one-liner) +curl -fsSL https://gitea.bnkops.com/admin/changemaker.lite/raw/branch/v2/scripts/install.sh | bash +``` + +**Two compose files:** +- `docker-compose.yml` — Development: includes `build:` blocks and `./api:/app` source mounts +- `docker-compose.prod.yml` — Production: `image:` only, no source mounts, `IMAGE_TAG:-latest` + +Release tarballs ship `docker-compose.prod.yml` as the compose file. Source installs use `docker-compose.yml`. + +**Note:** gitea.bnkops.com must use Pangolin tunnel (not Cloudflare proxy) for large image layers (>100MB). See `docs/REGISTRY_GUIDE.md`. + ### Testing & Backup ```bash # Media API tests @@ -626,6 +680,13 @@ cd api && npx tsc --noEmit && cd ../admin && npx tsc --noEmit - MkDocs port 4003 (was 4000, conflicted with API) - Media upload: requires separate RW volume mount for inbox directory (`:rw` on `/media/local/inbox`), library remains read-only - FFmpeg/FFprobe: installed in media-api container (Alpine `apk add --no-cache ffmpeg`), used for metadata extraction +- **Registry mode:** `IMAGE_TAG=local` (default) never pulls from registry; set to a commit SHA or `latest` to use pre-built images; `--use-registry` in upgrade.sh auto-sets `IMAGE_TAG` to the new commit SHA +- **Registry auth:** `docker login gitea.bnkops.com` required once per machine; `GITEA_REGISTRY_USER`/`GITEA_REGISTRY_PASS` in `.env` used by the API's registry status endpoint only (not Docker itself) +- **Cloudflare + registry:** gitea.bnkops.com must be DNS-only (no proxy) to push image layers >100MB; Cloudflare free plan blocks large blobs with 413 errors. See `docs/REGISTRY_GUIDE.md` +- **Production image size:** API Dockerfile production stage uses `npm ci --omit=dev` + `npx prisma generate` (not copying from build stage) to avoid including TypeScript devDeps in the final image +- **Release vs source installs:** Detected by `VERSION` file + absence of `.git/`. Release installs use `docker-compose.prod.yml` (no build blocks, no source mounts, `IMAGE_TAG:-latest`). Source installs use `docker-compose.yml` (build blocks + source mounts for dev). `config.sh`, `upgrade.sh`, and `upgrade-check.sh` all auto-detect the mode +- **`api/dist/` is gitignored:** Compiled output must NOT be committed. It's generated by `npm run build` (dev) or baked into Docker images (prod). If root-owned (from container builds), fix with `docker run --rm -v ./api:/api alpine chown -R $(id -u):$(id -g) /api/dist/` +- **`api/.dockerignore` excludes dist/:** Prevents stale local compiled JS from being copied into Docker build context. Without this, cached Docker layers may serve old module sets even after adding new TypeScript modules --- @@ -757,8 +818,10 @@ V1 code archived in `influence/`, `map/`, and `docker-compose.v1.yml`. Two indep ## Key Configuration Files ### Infrastructure -- `docker-compose.yml` — V2 orchestration (20+ services, monitoring profile) +- `docker-compose.yml` — Development orchestration (build blocks + source mounts, 20+ services) +- `docker-compose.prod.yml` — Production orchestration (image-only, no source mounts, `IMAGE_TAG:-latest`) - `.env` / `.env.example` — Environment variables (100+ vars) +- `config.sh` — Interactive setup wizard (14 steps, release-mode aware) ### Database - `api/prisma/schema.prisma` — Main schema (30+ Prisma models)