Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
**/mlruns/
mlflow.db
**/.env
**/.env.*
!**/.env.example
.claude/
.claude-flow/
docs/k8s-evidence/
63 changes: 37 additions & 26 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ concurrency:
cancel-in-progress: true

env:
# CI uses the service-container default port 5432 (no native Postgres to clash with,
# unlike local dev which publishes the Docker DB on 5433).
# CI starts a throwaway local Docker database on 5432; local dev publishes the DB on 5433.
PG_DSN: postgresql+psycopg://second_brain:second_brain@localhost:5432/second_brain

jobs:
Expand Down Expand Up @@ -48,24 +47,27 @@ jobs:
defaults:
run:
working-directory: backend
services:
db:
image: pgvector/pgvector:pg16
env:
POSTGRES_USER: second_brain
POSTGRES_PASSWORD: second_brain
POSTGRES_DB: second_brain
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U second_brain -d second_brain"
--health-interval 5s --health-timeout 5s --health-retries 10
env:
SECOND_BRAIN_DATABASE_URL: postgresql+psycopg://second_brain:second_brain@localhost:5432/second_brain
SECOND_BRAIN_TEST_DATABASE_URL: postgresql+psycopg://second_brain:second_brain@localhost:5432/second_brain
SECOND_BRAIN_LLM_PROVIDER: fake
steps:
- uses: actions/checkout@v4
- name: Start cleaned pgvector database
run: |
docker build -f ../deploy/Dockerfile.pgvector -t second-brain-pgvector:ci ..
docker run -d --name second-brain-ci-db \
-e POSTGRES_USER=second_brain \
-e POSTGRES_PASSWORD=second_brain \
-e POSTGRES_DB=second_brain \
-p 5432:5432 \
second-brain-pgvector:ci
for i in $(seq 1 30); do
docker exec second-brain-ci-db pg_isready -U second_brain -d second_brain && exit 0
sleep 2
done
docker logs second-brain-ci-db
exit 1
- uses: actions/setup-python@v5
with:
python-version: "3.12"
Expand All @@ -81,6 +83,9 @@ jobs:
run: alembic upgrade head
- name: Run full test suite (unit + integration)
run: pytest -q
- name: Stop cleaned pgvector database
if: always()
run: docker rm -f second-brain-ci-db || true

eval-gate:
name: Eval quality gate
Expand All @@ -89,23 +94,26 @@ jobs:
defaults:
run:
working-directory: backend
services:
db:
image: pgvector/pgvector:pg16
env:
POSTGRES_USER: second_brain
POSTGRES_PASSWORD: second_brain
POSTGRES_DB: second_brain
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U second_brain -d second_brain"
--health-interval 5s --health-timeout 5s --health-retries 10
env:
SECOND_BRAIN_DATABASE_URL: postgresql+psycopg://second_brain:second_brain@localhost:5432/second_brain
SECOND_BRAIN_LLM_PROVIDER: fake
steps:
- uses: actions/checkout@v4
- name: Start cleaned pgvector database
run: |
docker build -f ../deploy/Dockerfile.pgvector -t second-brain-pgvector:ci ..
docker run -d --name second-brain-ci-db \
-e POSTGRES_USER=second_brain \
-e POSTGRES_PASSWORD=second_brain \
-e POSTGRES_DB=second_brain \
-p 5432:5432 \
second-brain-pgvector:ci
for i in $(seq 1 30); do
docker exec second-brain-ci-db pg_isready -U second_brain -d second_brain && exit 0
sleep 2
done
docker logs second-brain-ci-db
exit 1
- uses: actions/setup-python@v5
with:
python-version: "3.12"
Expand All @@ -121,3 +129,6 @@ jobs:
run: alembic upgrade head
- name: Run eval gate (fails the build on quality regression)
run: python -m app.eval.gate
- name: Stop cleaned pgvector database
if: always()
run: docker rm -f second-brain-ci-db || true
27 changes: 9 additions & 18 deletions .github/workflows/k8s.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Phase 7 — Kubernetes (kind) smoke pipeline (D8).
# Stands the full stack up on a throwaway multi-node kind cluster, applies the SAME manifests +
# Stands the core stack up on a throwaway multi-node kind cluster, applies the SAME manifests +
# add-ons used locally, waits for every rollout, smokes /health + the UI through ingress, then
# tears the cluster down (helm/kind-action deletes it in its post step — D10, nothing left running).
# This is SEPARATE from the eval-gated ci.yml (which stays untouched). HPA load-scaling is proven
Expand Down Expand Up @@ -28,10 +28,8 @@ jobs:
steps:
- uses: actions/checkout@v4

# The backend image carries CUDA torch wheels (~several GB); the hosted runner's ~14 GB free
# disk overflows when `kind load` does `docker save` to /tmp. Reclaim ~20+ GB of preinstalled
# toolchains we don't use (Android SDK, .NET, GHC, CodeQL). (CPU-only torch would slim the
# image itself — deferred, see ADR-0014.)
# Docker image builds plus kind image loads need room. Reclaim preinstalled toolchains we
# don't use (Android SDK, .NET, GHC, CodeQL).
- name: Free up runner disk space
run: |
df -h / | tail -1
Expand All @@ -50,33 +48,26 @@ jobs:

- name: Build images
run: |
docker build -f deploy/Dockerfile.pgvector -t second-brain-pgvector:phase7 .
docker build -f deploy/Dockerfile.backend -t second-brain-api:phase7 .
docker build -f deploy/Dockerfile.frontend \
--build-arg NEXT_PUBLIC_API_BASE_URL=http://api.second-brain.local \
-t second-brain-web:phase7 .

- name: Load images into kind (no registry, D2)
run: |
kind load docker-image second-brain-pgvector:phase7 --name second-brain
kind load docker-image second-brain-api:phase7 --name second-brain
kind load docker-image second-brain-web:phase7 --name second-brain

- name: Namespace + Secret (throwaway CI values) + monitoring ConfigMaps
- name: Namespace + Secret (throwaway CI values)
run: |
kubectl apply -f deploy/k8s/namespace.yaml
kubectl -n second-brain create secret generic second-brain-secrets \
--from-literal=POSTGRES_PASSWORD=ci_postgres_pw \
--from-literal=SECOND_BRAIN_API_TOKEN=ci-api-token \
--from-literal=SECOND_BRAIN_ADMIN_TOKEN=ci-admin-token \
--from-literal=SECOND_BRAIN_GEMINI_API_KEY= \
--from-literal=GRAFANA_ADMIN_PASSWORD=ci-admin
kubectl -n second-brain create configmap prometheus-config \
--from-file=prometheus.yml=deploy/prometheus/prometheus.yml \
--from-file=alerts.yml=deploy/prometheus/alerts.yml --dry-run=client -o yaml | kubectl apply -f -
kubectl -n second-brain create configmap grafana-datasources \
--from-file=deploy/grafana/provisioning/datasources/datasource.yml --dry-run=client -o yaml | kubectl apply -f -
kubectl -n second-brain create configmap grafana-dashboard-provider \
--from-file=deploy/grafana/provisioning/dashboards/dashboards.yml --dry-run=client -o yaml | kubectl apply -f -
kubectl -n second-brain create configmap grafana-dashboard-json \
--from-file=deploy/grafana/dashboards/second-brain.json --dry-run=client -o yaml | kubectl apply -f -
--from-literal=SECOND_BRAIN_GEMINI_API_KEY=

- name: Install ingress-nginx + metrics-server (pinned)
run: |
Expand All @@ -102,7 +93,7 @@ jobs:
run: |
kubectl -n second-brain rollout status statefulset/db --timeout=300s
kubectl -n second-brain wait --for=condition=complete job/migrate --timeout=300s
for d in pgbouncer redis api worker frontend prometheus grafana; do
for d in redis api worker frontend; do
kubectl -n second-brain rollout status deploy/$d --timeout=300s
done

Expand Down
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ backend/.venv/

# Local env / secrets
.env
.env.*
!.env.example
!**/.env.example
backend/.env
backend/.env.*
frontend/.env
frontend/.env.*
!backend/.env.example
!frontend/.env.example
# Phase 6 production secrets — only the *.example templates are committed
deploy/.env.prod
deploy/pgbouncer/userlist.txt
# Box-specific compose override (carries the public host/IP) — commit the .example only
deploy/docker-compose.vps.yml
# Phase 7 K8s secret — only secret.example.yaml is committed (D4)
Expand Down
Loading
Loading