Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e097e4f
updated docs with goose implementations
Gautam7352 Apr 5, 2026
20fc757
changed dockerfiles accordingly
Gautam7352 Apr 5, 2026
635c8ff
moved db to internals to make sure goose in compiled
Gautam7352 Apr 5, 2026
932547f
removed build.bin deprecation warning in air.toml
Gautam7352 Apr 5, 2026
65ad58d
brought back init schema implementation
Gautam7352 Apr 6, 2026
6d38a93
storing data in data directory
Gautam7352 Apr 7, 2026
d910461
implemented initschema for migration
Gautam7352 Apr 7, 2026
46f873d
unified .gitignore
Gautam7352 Apr 7, 2026
ccbd03a
implement migration and seed, they have their own files so it they bo…
Gautam7352 Apr 7, 2026
80ef5e7
implemented tests and made sure tests pass
Gautam7352 Apr 7, 2026
ad3fbd6
implemented migration sql as embedding
Gautam7352 Apr 7, 2026
1a1e355
update tests
Gautam7352 Apr 7, 2026
58fc77e
took migration to cicd
Gautam7352 Apr 7, 2026
36913e4
fixed test
Gautam7352 Apr 7, 2026
6fdae01
update docs
Gautam7352 Apr 8, 2026
b26e9e6
Merge remote-tracking branch 'origin/dev' into migration-implementaion
Gautam7352 Apr 8, 2026
e903fdc
fixed seeding from local while app is running in container
Gautam7352 Apr 8, 2026
966668d
fixed migrate path
Gautam7352 Apr 8, 2026
54fe5ad
implement separate migration
Gautam7352 Apr 13, 2026
80b96dc
implement ci stuff
Gautam7352 Apr 13, 2026
38b4bff
review 1
Gautam7352 Apr 13, 2026
d85c6b1
review 2
Gautam7352 Apr 13, 2026
f5462af
review 3
Gautam7352 Apr 13, 2026
0484e84
meaningful name
Gautam7352 Apr 13, 2026
f8b7ac5
ignoring github ci until v1 finalized
Gautam7352 Apr 13, 2026
88de475
ci should only run at dev and main branches
Gautam7352 Apr 13, 2026
c66f1ef
changed uuid to int
Gautam7352 Apr 13, 2026
f015419
cleaned comments and changed status codes
Gautam7352 Apr 13, 2026
8f407a2
separated up and down for migrate
Gautam7352 Apr 13, 2026
6c8b2e8
cleaned dependencies
Gautam7352 Apr 13, 2026
850163f
now db is not an environment variable
Gautam7352 Apr 13, 2026
4c27250
version patch
Gautam7352 Apr 13, 2026
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
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ SERVER_PORT=8080
POSTGRES_USER=capuchin_user
POSTGRES_PASSWORD=capuchin
POSTGRES_DB=capuchin_dev
POSTGRES_HOST=capuchin-db
POSTGRES_PORT=5432
# Only needed when running the backend outside Docker (e.g. `go run` or `air` directly).
# In compose, the host is hardcoded to the postgres container name (capuchin-db).
# POSTGRES_HOST=localhost

JWT_SECRET=your_jwt_secret_here
73 changes: 73 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: CI

on:
pull_request:
branches:
- main
push:
branches:
- main
- dev
# Weekly run includes migration integration tests (needs Docker via testcontainers)
schedule:
- cron: "0 3 * * 1" # Every Monday at 03:00 UTC

jobs:
backend:
name: Backend — build, vet, unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: "1.25.5"
cache-dependency-path: backend/go.sum

- name: Build
working-directory: backend
run: go build ./...

- name: Vet
working-directory: backend
run: go vet ./...

- name: Unit tests (no Docker required)
working-directory: backend
run: go test ./...

migration:
name: Migration module — build & vet
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: "1.25.5"
cache-dependency-path: backend/migration/go.sum

- name: Build
working-directory: backend/migration
run: go build ./...

- name: Vet
working-directory: backend/migration
run: go vet ./...

migration-integration:
name: Migration integration tests (Docker)
runs-on: ubuntu-latest
# Only run on schedule (weekly) — these are slow due to testcontainers
if: github.event_name == 'schedule'
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: "1.25.5"
cache-dependency-path: backend/migration/go.sum

- name: Integration tests (requires Docker)
working-directory: backend/migration
run: go test ./... -v -timeout 10m
49 changes: 49 additions & 0 deletions .github/workflows/migrate-manual.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Run Migrations (Manual)

on:
workflow_dispatch:
inputs:
environment:
description: "Target environment"
required: true
default: "production"
type: choice
options:
- production
- staging
regenerate_init_sql:
description: "Regenerate init.sql after migration"
required: true
default: true
type: boolean

jobs:
migrate:
name: Run migrations (${{ inputs.environment }})
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: "1.25.5"
cache-dependency-path: backend/migration/go.sum

- name: Run migration
working-directory: backend/migration
env:
POSTGRES_HOST: ${{ secrets.POSTGRES_HOST }}
POSTGRES_USER: ${{ secrets.POSTGRES_USER }}
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
POSTGRES_DB: ${{ secrets.POSTGRES_DB }}
POSTGRES_PORT: ${{ secrets.POSTGRES_PORT }}
run: go run ./cmd/migrate

regenerate-init-sql:
uses: ./.github/workflows/regenerate-init-sql.yml
needs: migrate
if: ${{ inputs.regenerate_init_sql }}
with:
environment: ${{ inputs.environment }}
secrets: inherit
50 changes: 50 additions & 0 deletions .github/workflows/regenerate-init-sql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Regenerate init.sql

on:
workflow_call:
inputs:
environment:
required: true
type: string

jobs:
regenerate-init-sql:
name: Regenerate init.sql
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}

- name: Install PostgreSQL client
run: sudo apt-get install -y postgresql-client

- name: Dump schema from DB
env:
PGPASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
run: |
pg_dump \
--host=${{ secrets.POSTGRES_HOST }} \
--port=${{ secrets.POSTGRES_PORT }} \
--username=${{ secrets.POSTGRES_USER }} \
--dbname=${{ secrets.POSTGRES_DB }} \
--schema-only \
--no-owner \
--no-privileges \
--exclude-table=goose_db_version \
> backend/db/init.sql

- name: Prepend auto-generated header
run: |
HEADER="-- Auto-generated schema snapshot. DO NOT EDIT MANUALLY.\n-- Regenerated by the release pipeline after each successful goose migration run.\n-- Used by Docker to bootstrap a fresh dev database container.\n-- Source of truth for schema changes remains backend/migration/db/migrations/*.sql\n"
echo -e "$HEADER$(cat backend/db/init.sql)" > backend/db/init.sql

- name: Commit updated init.sql
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add backend/db/init.sql
git diff --staged --quiet || git commit -m "chore: regenerate init.sql after migration [${{ inputs.environment }}] [skip ci]"
git push
55 changes: 55 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Release

on:
push:
branches:
- main
- dev
tags:
- "v*.*.*"

jobs:
migrate:
name: Run migrations
runs-on: ubuntu-latest
environment: production
outputs:
migration_success: ${{ steps.run_migration.outputs.success }}
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: "1.25.5"
cache-dependency-path: backend/migration/go.sum

- name: Run migration
id: run_migration
working-directory: backend/migration
env:
POSTGRES_HOST: ${{ secrets.POSTGRES_HOST }}
POSTGRES_USER: ${{ secrets.POSTGRES_USER }}
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
POSTGRES_DB: ${{ secrets.POSTGRES_DB }}
POSTGRES_PORT: ${{ secrets.POSTGRES_PORT }}
run: |
go run ./cmd/migrate
echo "success=true" >> $GITHUB_OUTPUT

regenerate-init-sql:
uses: ./.github/workflows/regenerate-init-sql.yml
needs: migrate
with:
environment: production
secrets: inherit

deploy:
name: Deploy backend
runs-on: ubuntu-latest
needs: migrate
environment: production
steps:
- uses: actions/checkout@v4

- name: Deployment placeholder
run: echo "Deploy capuchin-backend:${{ github.sha }} to production"
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ dist-ssr/
# Test binary, built with `go test -c`
*.test
server
test.sh

# Go workspace file
go.work
Expand Down Expand Up @@ -97,3 +98,8 @@ crash.*.log
# personal
docs/ideas.md
backup/
.kiro


# removing for now
.github/
48 changes: 35 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,56 @@ ifneq (, $(shell command -v docker 2> /dev/null))
CONTAINER_RUNTIME := docker
endif

# Docker Dev Mode (Hot Reload)
dev:
.DEFAULT_GOAL := help

help: ## Show available targets
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'

# ── Dev (hot reload via Docker) ───────────────────────────────────────────────

dev: ## Start all services in dev mode (hot reload)
$(CONTAINER_RUNTIME) compose --env-file .env.example -f compose-dev.yml up --build -d

dev-logs:
$(CONTAINER_RUNTIME) compose -f compose-dev.yml logs
dev-logs: ## Tail dev logs
$(CONTAINER_RUNTIME) compose -f compose-dev.yml logs -f

dev-down:
dev-down: ## Stop dev services
$(CONTAINER_RUNTIME) compose -f compose-dev.yml down
clean:

clean: ## Stop dev services and remove volumes, images, orphans
$(CONTAINER_RUNTIME) compose -f compose-dev.yml down --volumes --remove-orphans --rmi all

# ── Prod ──────────────────────────────────────────────────────────────────────

prod:
$(CONTAINER_RUNTIME) compose --env-file .env -f compose.yml up
prod: ## Start all services in prod mode (detached)
$(CONTAINER_RUNTIME) compose --env-file .env -f compose.yml up -d

logs:
logs: ## Tail prod logs
$(CONTAINER_RUNTIME) compose -f compose.yml logs -f

down:
down: ## Stop prod services
$(CONTAINER_RUNTIME) compose -f compose.yml down

# ── Local dev (outside Docker) ────────────────────────────────────────────────

frontend:
frontend: ## Start frontend dev server
cd frontend && npm run dev

backend:
backend: ## Start backend with hot reload (requires air: go install github.com/air-verse/air@v1.61.7)
cd backend && air

.PHONY: dev dev-logs dev-down prod logs down
# ── Database ──────────────────────────────────────────────────────────────────

migrate: ## Run migrations against localhost DB (reads .env for credentials)
@set -a && . ./.env.example && set +a && export POSTGRES_HOST=localhost && cd backend/migration && go run ./cmd/migrate up

migrate-down: ## Roll back the last migration against localhost DB
@set -a && . ./.env.example && set +a && export POSTGRES_HOST=localhost && cd backend/migration && go run ./cmd/migrate down

seed: ## Seed dev database with sample data (reads .env.example for credentials)
@set -a && . ./.env.example && set +a && export POSTGRES_HOST=localhost && cd backend && go run ./cmd/seed

migrate-build: ## Build migration Docker image
docker build -f backend/migration/Dockerfile -t capuchin-migration ./backend

.PHONY: help dev dev-logs dev-down clean prod logs down frontend backend migrate migrate-down seed migrate-build
1 change: 1 addition & 0 deletions backend/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
vendor
bin
server
tmp
*.exe
*.exe~
*.dll
Expand Down
1 change: 0 additions & 1 deletion backend/.gitignore

This file was deleted.

22 changes: 9 additions & 13 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
FROM golang:1.25.5-alpine AS deps
FROM golang:1.26-alpine AS deps

WORKDIR /app

# Download dependencies
COPY go.mod go.sum ./
RUN go mod download

# Build Stage
FROM deps AS builder

# Copy source code
COPY . .

# Build the application
# CGO_ENABLED=0 ensures a statically linked binary
RUN CGO_ENABLED=0 GOOS=linux go build -o server cmd/server/main.go

# Development Stage
# Development Stage - pinned air version for reproducible dev builds
FROM deps AS dev

RUN go install github.com/air-verse/air@latest
RUN go install github.com/air-verse/air@v1.61.7

CMD ["air", "-c", "air.toml"]


# Final Stage
# Final Stage - minimal image, non-root user for security
FROM scratch

# Set working directory to the app root
WORKDIR /app

# Copy the binary from the builder stage
# Copy passwd so the non-root user exists in scratch
COPY --from=builder /etc/passwd /etc/passwd

COPY --from=builder /app/server ./

# Expose the application port
EXPOSE 8080

# Run the application
CMD ["./server"]
USER nobody

CMD ["./server"]
Loading