From 12d9aa40fdfdde03a57312285b197fed863053f7 Mon Sep 17 00:00:00 2001 From: Hermes Coding Manager Date: Mon, 7 Sep 2026 14:11:01 -0400 Subject: [PATCH] chore(komodo): switch to image-based deploys, trim CI pipeline - docker-compose.yml: replace build: sections with image: refs (git.buzzbee.dev/BuzzbeeSCD/projecte-{svc}:) - ci.yml: remove deploy + smoke jobs (Komodo owns build+deploy) - DEPLOY.md: rewrite for Komodo-era workflow, keep deploy.sh as break-glass - Gitea webhook already wired to Komodo build endpoint --- .gitea/workflows/ci.yml | 80 ---------------- DEPLOY.md | 199 +++++++--------------------------------- docker-compose.yml | 12 +-- 3 files changed, 38 insertions(+), 253 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 757def2..1a16a1e 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -149,83 +149,3 @@ jobs: if: always() shell: bash run: docker rm -f projecte-e2e-db >/dev/null 2>&1 || true - - deploy: - runs-on: projecte-runner - needs: quality - if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch' - timeout-minutes: 20 - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Fallback checkout (manual clone) - if: failure() - shell: bash - run: | - set -euo pipefail - SERVER_URL="${{ github.server_url }}" - REPO="${{ github.repository }}" - TOKEN="${{ github.token }}" - HOST="${SERVER_URL#https://}" - HOST="${HOST#http://}" - rm -rf ./* ./.git 2>/dev/null || true - echo "Manual shallow clone from ${HOST}/${REPO}" - git clone --depth 1 "https://oauth2:${TOKEN}@${HOST}/${REPO}.git" . - - - name: Deploy - shell: bash - env: - DEPLOY_DIR: ${{ vars.DEPLOY_DIR || '/home/projecte/ProjectE' }} - run: bash script/deploy.sh - - smoke: - runs-on: projecte-runner - needs: deploy - if: always() - timeout-minutes: 10 - steps: - - name: API health check - shell: bash - run: | - set -euo pipefail - BODY="$(curl -s http://localhost:3000/api/health || true)" - echo "${BODY}" - echo "${BODY}" | grep -q '"status"' || { - echo "ERROR: API health did not return the expected payload" - exit 1 - } - echo "API health: OK" - - - name: SPA root returns HTML - shell: bash - run: | - set -euo pipefail - BODY="$(curl -s http://localhost:3000/ || true)" - echo "${BODY}" | grep -qi ' \ - --name projecte-runner \ - --labels projecte-runner:host - ``` +1. Push a version tag: `git tag v1.0.0 && git push origin v1.0.0` +2. Komodo builds images and deploys automatically +3. Verify: `docker ps` on .52, health checks - The label name `projecte-runner` must match `.gitea/workflows/ci.yml`; the - executor (`host` or `docker`) is your choice — `host` is simplest for a - single colocated runner. -4. **Start it** — run `./act_runner daemon` (or install it as a systemd service - so it survives reboots). +### Break-glass: deploy.sh -**Verify:** the Runners page shows it online, then re-trigger the pipeline (a -push to `main`, or "Re-run" on the Actions tab). The `quality` job should leave -"Waiting to run". If jobs stay queued, the runner is offline or its label does -not match `runs-on: projecte-runner` — check the `act_runner` logs. - -## Secrets - -Secrets live in the host `.env` at `/home/projecte/ProjectE/.env`. This file is gitignored; never commit it. To set it up: +If Komodo is unavailable, `script/deploy.sh` still works: ```bash -cp .env.example .env +ssh projecte +cd /opt/app/ProjectE +export PROJECTE_IMAGE_TAG=v1.0.0 +bash script/deploy.sh ``` -Fill in `POSTGRES_PASSWORD`, `DATABASE_URL`, `AUTH_SECRET` (or `NEXTAUTH_SECRET`), `INITIAL_ADMIN_EMAIL`, `INITIAL_ADMIN_PASSWORD`, and, as needed, `NODE_ENV`, `PUBLIC_URL`, `COOKIE_SECURE`, and `ALLOWED_HOSTS`. `deploy.sh` sources it, and docker-compose reads the `POSTGRES_PASSWORD` and `DATABASE_URL` values from it. - -## Manual deploy - -On the deploy host: - -```bash -cd /home/projecte/ProjectE - -# Pull latest -git pull origin main - -# Install dependencies -bun install - -# Apply schema + triggers (idempotent) -bun run db:migrate - -# Build images -docker compose build - -# Restart the stack -docker compose up -d - -# Wait for API health -until curl -s http://localhost:3000/api/health | grep -q '"status"'; do sleep 2; done - -# Check status -docker compose ps -``` - -## Database migrations - -`bun run db:migrate` chains two idempotent steps: - -- `db:sync` — `drizzle-kit push --force`, which syncs the schema in `packages/db/src/schema.ts` to the database -- `db:triggers` — `script/apply-triggers.ts`, which applies the search-vector triggers from `drizzle/0005_search_vector_trigger.sql` (`CREATE OR REPLACE FUNCTION` + `DROP TRIGGER IF EXISTS`) - -Both are safe to run on every deploy. Schema changes go through `bun run db:generate` in development, then land in `drizzle/` before the next deploy. +This pulls images from the registry and redeploys. It no longer builds — that's Komodo's job. ## Rollback -Compose images are rebuilt from the checkout, so there are no pinned image tags to restore. To roll back a bad release: +1. In Komodo UI: Deployments → projecte → select previous version tag → Redeploy +2. Or via API: `POST /execute/DeployStack` with the previous image tag +3. Verify health checks pass -1. Revert the checkout to the previous good commit: `git revert ` (or `git checkout `) and push to `main` -2. Re-run the manual deploy steps (`docker compose build`, `docker compose up -d`) +## Secrets -The `project-e-pg-data` volume is untouched by deploys and rollbacks, so the database survives both. If a deploy failed, `deploy.sh` exits non-zero with the recent API logs; do not force it past a failing health check. - -## Logs - -```bash -# All services -docker compose logs --tail=50 -f - -# Specific service -docker compose logs --tail=50 -f api -docker compose logs --tail=50 -f spa -docker compose logs --tail=50 -f worker -docker compose logs --tail=50 -f db -``` - -## Debugging - -### API health (direct, :3001) - -```bash -curl http://localhost:3001/api/health -``` - -Returns `{"status":"ok", ...}` with a database ping (`database.connected`, `database.ping_ms`). - -### API health (through Caddy) - -```bash -curl http://localhost:3000/api/health -``` - -### SPA health check - -```bash -curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3000/ -``` - -### Login test - -```bash -curl -s -X POST http://localhost:3000/api/auth/credentials \ - -H "Content-Type: application/json" \ - -d '{"email":"","password":""}' -``` - -Expect HTTP 200 and a `session` cookie. - -### Container health - -```bash -docker inspect project-e-db --format '{{.State.Health.Status}}' -``` - -### Restart or rebuild one service - -```bash -docker compose restart api - -docker compose build spa -docker compose up -d --force-recreate spa -``` - -## Important notes - -- The `project-e-pg-data` Docker volume contains the live database. **Do not delete it.** Back it up (volume snapshot or `pg_dump`) before major schema work. -- Port 3000 is the SPA (Caddy); port 3001 is the API directly (for debugging). -- The MCP endpoint requires a valid API key (separate from JWT auth). -- The root `worker/` directory is legacy. The active worker is `apps/worker`. +Komodo manages runtime secrets (POSTGRES_PASSWORD, AUTH_SECRET, etc.) as Komodo variables. The host `.env` is kept as break-glass fallback only. diff --git a/docker-compose.yml b/docker-compose.yml index 52440f9..5e5021a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,9 +20,7 @@ services: restart: unless-stopped api: - build: - context: . - dockerfile: Dockerfile.api + image: git.buzzbee.dev/BuzzbeeSCD/projecte-api:${PROJECTE_IMAGE_TAG:?Set PROJECTE_IMAGE_TAG} container_name: project-e-api ports: - "3001:3000" @@ -41,9 +39,7 @@ services: restart: unless-stopped spa: - build: - context: . - dockerfile: Dockerfile.spa + image: git.buzzbee.dev/BuzzbeeSCD/projecte-spa:${PROJECTE_IMAGE_TAG:?Set PROJECTE_IMAGE_TAG} container_name: project-e-spa ports: - "3000:80" @@ -54,9 +50,7 @@ services: restart: unless-stopped worker: - build: - context: . - dockerfile: Dockerfile.worker + image: git.buzzbee.dev/BuzzbeeSCD/projecte-worker:${PROJECTE_IMAGE_TAG:?Set PROJECTE_IMAGE_TAG} container_name: project-e-worker environment: - NODE_ENV=production