diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..3b6f58553 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,30 @@ +{ + "name": "Ghostfolio (Ona demo)", + "image": "mcr.microsoft.com/devcontainers/javascript-node:1-22", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "moby": false, + "dockerDashComposeVersion": "v2" + }, + "ghcr.io/devcontainers/features/git:1": {} + }, + "forwardPorts": [3333, 4200, 5432, 6379], + "portsAttributes": { + "3333": { "label": "Ghostfolio API" }, + "4200": { "label": "Ghostfolio Client" }, + "5432": { "label": "PostgreSQL" }, + "6379": { "label": "Redis" } + }, + "postCreateCommand": "bash .devcontainer/post-create.sh", + "remoteUser": "node", + "customizations": { + "vscode": { + "extensions": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "Prisma.prisma", + "nrwl.angular-console" + ] + } + } +} diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh new file mode 100755 index 000000000..25f36db2c --- /dev/null +++ b/.devcontainer/post-create.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# One-shot setup for the Ghostfolio dev container inside Ona. +# Idempotent: safe to re-run. + +set -euo pipefail + +cd "$(dirname "$0")/.." + +echo "==> Seeding .env from .env.dev (if missing)" +if [ ! -f .env ]; then + cp .env.dev .env + # Fill in safe dev defaults — the upstream .env.dev has placeholders. + sed -i 's||devredispassword|g' .env + sed -i 's||devpostgrespassword|g' .env + sed -i 's||'"$(openssl rand -hex 32)"'|g' .env +fi + +echo "==> Pre-pulling Postgres + Redis images (cached into prebuild)" +docker pull postgres:15 >/dev/null 2>&1 || true +docker pull redis:7 >/dev/null 2>&1 || true + +echo "==> Installing npm dependencies" +npm ci --no-audit --no-fund + +echo "==> Done. Services will be brought up by Ona automations." diff --git a/.gitignore b/.gitignore index 0f5a1651e..349caae1f 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ testem.log # System Files .DS_Store Thumbs.db + +# Ona +.ona/.db-initialized diff --git a/.ona/README.md b/.ona/README.md new file mode 100644 index 000000000..fbc61dcee --- /dev/null +++ b/.ona/README.md @@ -0,0 +1,47 @@ +# Ona demo setup — Ghostfolio fork + +This fork is the target codebase for the **Apr 23 BlackRock deep-dive** demo. + +## What this folder contains + +- [`automations.yaml`](automations.yaml) — Ona services for Postgres, Redis, the + NestJS API, and the Angular client, plus manual `seed` and `dry-run-demo` tasks. + +## What changed vs upstream + +1. **`.devcontainer/devcontainer.json`** — new. Node 22 base image, Docker-in-Docker + feature enabled (needed so `docker compose -f docker/docker-compose.dev.yml up` + works for Postgres + Redis), ports forwarded, `postCreateCommand` wires up + `.env` and npm install. + +2. **`.devcontainer/post-create.sh`** — new. Seeds `.env` from `.env.dev`, + replaces the `` placeholders with safe dev defaults, prepulls + `postgres:15` / `redis:7` images, runs `npm ci`. Idempotent. + +3. **`.ona/automations.yaml`** — new. Starts the stack on + `postDevcontainerStart` / `postEnvironmentStart`. First boot runs + `npm run database:setup` (schema + seed), marks it with + `.ona/.db-initialized` so subsequent restarts skip re-seeding. + +## How to bring it up + +1. Open this repo in Ona. +2. Wait for the devcontainer build (DinD is heavy first time; subsequent opens + use the prebuild cache). +3. `gitpod automations service list` — all three services should go `ready`. +4. Open the forwarded port `4200` → Ghostfolio UI. First user created gets + `ADMIN`. + +## Reset before a demo + +``` +gitpod automations task start seed +``` + +## Notes for the demo day + +- Keep `5432` + `6379` forwarded but unadvertised. Port `4200` is the only UI. +- The `.env` generated by post-create uses throwaway dev passwords. DO NOT + commit a real `.env`. +- If DinD is slow to come up on first boot, `gitpod automations service logs postgres-redis` + tails the compose output. diff --git a/.ona/automations.yaml b/.ona/automations.yaml new file mode 100644 index 000000000..28522dc34 --- /dev/null +++ b/.ona/automations.yaml @@ -0,0 +1,77 @@ +# Ona automations for the Ghostfolio demo environment. +# +# Shape of the world when services are up: +# postgres (5432) + redis (6379) via docker compose on the host +# api (3333) via `npm run start:server` +# client (4200) via `npm run start:client` +# +# This is the "dev env == preprod test env" demo: the full Ghostfolio stack +# compiles, runs, and serves requests inside the same Ona VM that the +# agent (Claude Code / Cursor / Codex / Ona) operates in. + +services: + postgres-redis: + name: PostgreSQL + Redis + description: Backing stores for Ghostfolio. Uses the upstream dev compose file. + triggeredBy: + - postDevcontainerStart + - postEnvironmentStart + commands: + start: docker compose -f docker/docker-compose.dev.yml up + ready: | + docker exec gf-postgres-dev pg_isready -U user -d ghostfolio-db && \ + docker exec gf-redis-dev redis-cli --pass "$(grep ^REDIS_PASSWORD .env | cut -d= -f2)" ping | grep -q PONG + stop: docker compose -f docker/docker-compose.dev.yml down + + api: + name: Ghostfolio API + description: NestJS backend, hot-reload, port 3333. + triggeredBy: + - postDevcontainerStart + - postEnvironmentStart + commands: + start: | + # Wait for DB to be ready, then sync schema + seed on first run. + until docker exec gf-postgres-dev pg_isready -U user -d ghostfolio-db >/dev/null 2>&1; do sleep 1; done + if [ ! -f .ona/.db-initialized ]; then + npm run database:setup + touch .ona/.db-initialized + fi + npm run start:server + ready: curl -sf http://localhost:3333/api/v1/health + + client: + name: Ghostfolio Client + description: Angular frontend, hot-reload, port 4200. + triggeredBy: + - postDevcontainerStart + - postEnvironmentStart + commands: + start: npm run start:client + ready: curl -skf https://localhost:4200/en || curl -sf http://localhost:4200/en + +tasks: + seed: + name: Reset + seed demo data + description: Wipe the DB and reseed. Use before a demo run. + triggeredBy: + - manual + command: | + rm -f .ona/.db-initialized + docker compose -f docker/docker-compose.dev.yml down -v + docker compose -f docker/docker-compose.dev.yml up -d + until docker exec gf-postgres-dev pg_isready -U user -d ghostfolio-db >/dev/null 2>&1; do sleep 1; done + npm run database:setup + touch .ona/.db-initialized + echo "✅ Ghostfolio DB reset + seeded." + + dry-run-demo: + name: "Dry-run: local agent beat" + description: Sanity check that Claude Code + Ona agent can reach the API and run a test. + triggeredBy: + - manual + command: | + set -e + curl -sf http://localhost:3333/api/v1/health | jq . + npx nx test api --watch=false --passWithNoTests | tail -20 + echo "✅ Local agent beat ready."