Skip to content
GenerateNUPublic

Latest commit

 

History

62 Commits

Folders and files

Repository files navigation

boutline

Monorepo: frontend/ is Next.js 16 + React 19 + Tailwind 4, backend/ is a Go + Fiber API with Postgres, docs/ holds design specs.

Every command below runs from the repo root. make help lists them all; backend/README.md goes deeper on the API's architecture, configuration, and testing.

Prerequisites

Tool Version Needed for
Bun 1.3.5 The only package manager for frontend/
Node.js 22 LTS Next.js tooling targets Node
Go 1.25.5+ backend/
Docker Desktop any The backend dev stack (API + Postgres)
Atlas CLI any Writing migrations
golangci-lint any make be-lint

Postgres itself is not on that list — it runs in Docker alongside the API, so there is nothing to install or start by hand.

macOS

# Bun installs to ~/.bun — add ~/.bun/bin to PATH if the installer doesn't
curl -fsSL https://bun.sh/install | bash -s "bun-v1.3.5"

brew install node@22 go golangci-lint ariga/tap/atlas
brew install --cask docker

Homebrew's node@22 is keg-only, so put it on your PATH:

echo 'export PATH="/opt/homebrew/opt/node@22/bin:$PATH"' >> ~/.zshrc
exec zsh

Linux / Windows

Bun: curl -fsSL https://bun.sh/install | bash. On Windows use WSL2 — Bun's Windows build is not what CI runs. Get the rest from your package manager or from nodejs.org, go.dev/dl, docs.docker.com/engine/install, and atlasgo.io.

The make shortcuts need make itself: on macOS run xcode-select --install, on Linux install build-essential or your distro's equivalent.

Setup

git clone https://github.com/GenerateNU/boutline.git
cd boutline
make setup   # bun install + go mod download
make dev     # API + database in Docker, then the frontend dev server

make dev starts Docker Desktop itself if it is not already running, waits for the daemon, brings the API and its Postgres up detached, applies migrations, then runs the frontend in the foreground. Stop the frontend with Ctrl-C and the stack with make be-down.

Never run npm, yarn, or pnpm in frontend/ — they write a competing lockfile. No environment variables are required: every backend value has a default (listed in backend/README.md) and the frontend reads none. .env* files are gitignored.

Running

Command What it does
make dev The whole stack, ready to work in
make test Both test suites
make check Everything CI would run, before a PR

Per stack: fe-dev, fe-build, fe-lint, fe-typecheck, fe-test, fe-check for the frontend, and be-up, be-down, be-logs, be-build, be-test, be-lint, be-check for the backend.

Frontend CI and Backend CI run the matching half of check on every PR touching that stack. The backend workflow goes further than be-check does: it also builds the Docker image and fails if the gorm models and backend/migrations have drifted apart.

One thing that looks like broken setup but isn't: fe-typecheck must run next typegen before tsc. Bare tsc reports errors CI never sees, since src/app/layout.tsx uses a LayoutProps global that typegen writes into .next/types.

Databases and migrations

The database is a container in the backend's compose stack, so make dev is all it takes to get one. Two commands cover the rest:

Command What it does
make db-shell psql shell on the dev database
make db-reset Wipe it and bring it back with migrations applied (asks first)

Its port is deliberately not published, so a Postgres already running on your machine at :5432 does not clash with it.

Atlas owns the schema, generating migrations by diffing the gorm models:

make migrate-new NAME=create_examples  # write the next migration
make migrate-status                    # what the dev database has applied

make be-up applies pending migrations before the API starts, so there is no separate apply step.

migrate-new needs the Atlas CLI and a running Docker daemon — Atlas spins up a throwaway Postgres to normalise the schema before diffing. A new feature's model must be registered in backend/cmd/atlasloader/main.go or it will never get a migration. Read the generated SQL before committing it; backend/README.md has the full workflow.

Schema

Generated from backend/migrations by make db-diagram, so don't edit it by hand. Run that after adding a migration; Backend CI fails while it is stale. Relationships are labelled with their foreign key's name, so a column the code treats as a reference without a foreign key behind it does not appear as one.

erDiagram
    bouts {
      uuid id PK
      uuid tournament_id FK
      text location
      timestamptz start_time
      timestamptz started_at
      timestamptz completed_at
      uuid referee_id FK
      bigint time_limit_seconds
      bigint points_to_win
      bigint group_number
      text status
      uuid competitor_1_id FK
      uuid competitor_2_id FK
      timestamptz created_at
      timestamptz updated_at
      uuid pool_id FK
    }
    bouts }o--o| competitors : fk_bouts_competitor1
    bouts }o--o| competitors : fk_bouts_competitor2
    bouts }o--o| pools : fk_bouts_pool
    bouts }o--o| users : fk_bouts_referee
    bouts }o--o| tournaments : fk_bouts_tournament
    competitors {
      uuid id PK
      text first_name
      text last_name
      text rating
      text team
      timestamptz created_at
      timestamptz updated_at
      uuid tournament_id FK
      uuid user_id FK
    }
    competitors }o--o| tournaments : fk_competitors_tournament
    competitors }o--o| users : fk_competitors_user
    direct_eliminations {
      uuid id PK
      text status
      uuid event_id
      timestamptz started_at
      timestamptz completed_at
      timestamptz created_at
      timestamptz updated_at
    }
    events {
      uuid id PK
      uuid tournament_id FK
      text format
      text name
      text status
      timestamptz start_time
      timestamptz started_at
      timestamptz completed_at
      timestamptz created_at
      timestamptz updated_at
    }
    events }o--o| tournaments : fk_events_tournament
    pool_stages {
      uuid id PK
      uuid event_id FK
      bigint max_competitor_per_pool
      text status
      timestamptz start_time
      timestamptz started_at
      timestamptz completed_at
      timestamptz created_at
      timestamptz updated_at
    }
    pool_stages }o--o| events : fk_pool_stages_event
    pools {
      uuid id PK
      uuid pool_stage_id FK
      uuid referee_id FK
      timestamptz started_at
      timestamptz completed_at
      timestamptz created_at
      timestamptz updated_at
    }
    pools }o--o| pool_stages : fk_pools_pool_stage
    pools }o--o| users : fk_pools_referee
    scorings {
      bigserial id PK
      uuid created_by FK
      bigint points
      uuid competitor_id
      uuid bout_id FK
      timestamptz revoked_at
      uuid revoked_by FK
      timestamptz created_at
      timestamptz updated_at
    }
    scorings }o--o| bouts : fk_scorings_bout
    scorings }o--o| users : fk_scorings_creator
    scorings }o--o| users : fk_scorings_revoker
    tournament_users {
      uuid user_id PK,FK
      uuid tournament_id PK,FK
      text role
      timestamptz created_at
      timestamptz updated_at
    }
    tournament_users }o--o| tournaments : fk_tournaments_memberships
    tournament_users }o--o| users : fk_users_memberships
    tournaments {
      uuid id PK
      text name
      text visibility
      text code
      text status
      uuid created_by FK
      timestamptz start_time
      timestamptz started_at
      timestamptz completed_at
      timestamptz created_at
      timestamptz updated_at
    }
    tournaments }o--o| users : fk_users_created_tournaments
    users {
      uuid id PK
      text email
      text password
      text first_name
      text last_name
      timestamptz created_at
      timestamptz updated_at
    }
Loading

Deployment

The backend and its Postgres deploy to Generate's shared DigitalOcean droplet, and the frontend to Vercel. Publishing a GitHub Release ships production. Every pull request gets its own preview backend, preview database, and Vercel frontend preview. See DEPLOYMENT.md for day-to-day use and deploy/ for the droplet.

Releases

Packages

Contributors

Languages