diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..bbcac6c --- /dev/null +++ b/.env.example @@ -0,0 +1,13 @@ +# Copy to .env at the repo root to override any of these. + +# Matches the credentials in docker-compose.yml. +DATABASE_URL=postgresql://acmfeup:acmfeup@localhost:5432/acmfeup + +# Port the NestJS API listens on. +API_PORT=4000 + +# Comma separated list of origins the API accepts requests from. +CORS_ORIGIN=http://localhost:3000 + +# Where the web app looks for the API. Inlined into the browser bundle. +NEXT_PUBLIC_API_URL=http://localhost:4000 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..7f000f8 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Every change needs a review from the owner below. +* @Bebaz0 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..b9ed061 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,48 @@ +name: Bug +description: Something is broken or behaves differently from what it should +labels: [bug] +body: + - type: textarea + id: what-happened + attributes: + label: What happened + validations: + required: true + + - type: textarea + id: expected + attributes: + label: What you expected instead + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + placeholder: | + 1. Run pnpm dev + 2. Open http://localhost:3000/... + 3. ... + validations: + required: true + + - type: dropdown + id: area + attributes: + label: Area + multiple: true + options: + - web + - api + - database + - tooling / CI + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Logs or screenshots + description: Paste terminal output or the browser console. Remove any tokens or passwords first. + render: shell diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 0000000..84b5547 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,49 @@ +name: Feature +description: Propose something new for the site or the platform +labels: [enhancement] +body: + - type: textarea + id: problem + attributes: + label: Problem + description: What is missing or painful today, and for whom? + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: Proposal + description: What should exist once this is done? Sketches and links welcome. + validations: + required: true + + - type: textarea + id: done-when + attributes: + label: Done when + description: Outcomes a reviewer can check one by one. This is what makes the issue Ready. + placeholder: | + - [ ] A member can ... + - [ ] The API returns ... + validations: + required: true + + - type: dropdown + id: area + attributes: + label: Area + multiple: true + options: + - web + - api + - database + - tooling / CI + validations: + required: true + + - type: textarea + id: open-questions + attributes: + label: Open questions + description: Anything still undecided. An issue with open questions is not Ready yet. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..bf4c8fc --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,21 @@ +## What changes + + + +Closes # + +## How to test + + + +1. + +## Checklist + + + +- [ ] I can explain every change in this PR during review, including any code an agent wrote +- [ ] Tests cover the logic I added or changed +- [ ] Migrations are backwards compatible: the code running in production still works after they apply +- [ ] I did not edit a migration that is already merged, and new `.sql` files come from `pnpm db:generate` +- [ ] No secrets, tokens or real credentials anywhere in the diff diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml deleted file mode 100644 index f050782..0000000 --- a/.github/workflows/linter.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Code quality - -on: - push: - pull_request: - -jobs: - quality: - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Checkout - uses: actions/checkout@v5 - with: - persist-credentials: false - - name: Setup Biome - uses: biomejs/setup-biome@v2 - with: - version: 2.2.4 - - name: Run Biome - run: biome ci . diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..e97a875 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,112 @@ +name: PR checks + +on: + pull_request: + push: + branches: [main] + +# A new push to a PR cancels the run still going for its previous commit. +# Runs on main are never cancelled, so every merge gets a full result. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + +jobs: + checks: + name: ${{ matrix.task }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + task: [lint, typecheck, test, build] + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + + # Reads the pnpm version from packageManager in package.json. + - uses: pnpm/action-setup@v6 + + - uses: actions/setup-node@v7 + with: + node-version-file: .nvmrc + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - uses: actions/cache@v6 + with: + path: .turbo/cache + key: turbo-${{ matrix.task }}-${{ github.sha }} + restore-keys: turbo-${{ matrix.task }}- + + - run: pnpm ${{ matrix.task }} + + migrations: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:18-alpine + env: + POSTGRES_USER: acmfeup + POSTGRES_PASSWORD: acmfeup + POSTGRES_DB: acmfeup + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U acmfeup -d acmfeup" + --health-interval 5s + --health-timeout 5s + --health-retries 10 + env: + # Throwaway database that only lives for this job. + DATABASE_URL: postgresql://acmfeup:acmfeup@localhost:5432/acmfeup + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + + - uses: pnpm/action-setup@v6 + + - uses: actions/setup-node@v7 + with: + node-version-file: .nvmrc + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - name: Apply every migration to an empty database + run: pnpm db:migrate + + - name: Check schema.ts and the migrations are in sync + run: | + pnpm db:generate + drift="$(git status --porcelain apps/api/drizzle)" + if [ -n "$drift" ]; then + echo "$drift" + echo "::error::schema.ts has changes with no migration. Run pnpm db:generate and commit apps/api/drizzle." + exit 1 + fi + + gitleaks: + runs-on: ubuntu-latest + permissions: + contents: read + # Lets the action comment the finding on the offending PR line. + pull-requests: write + steps: + - uses: actions/checkout@v7 + with: + # Full history, so every commit in the PR gets scanned, not just the tip. + fetch-depth: 0 + persist-credentials: false + + - uses: gitleaks/gitleaks-action@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # acmfeup is an organisation, so the action needs a (free) license. + GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} + GITLEAKS_VERSION: 8.30.1 diff --git a/.gitignore b/.gitignore index c6f8d7f..8433fd5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,43 +1,35 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - # dependencies -/node_modules -/.pnp +node_modules/ +.pnp .pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions -# testing -/coverage +# builds +.next/ +out/ +dist/ +build/ +.turbo/ +*.tsbuildinfo +next-env.d.ts -# next.js -/.next/ -/out/ +# testing +coverage/ -# production -/build +# env — everything except the template +.env* +!.env.example # misc .DS_Store *.pem - -# debug npm-debug.log* yarn-debug.log* yarn-error.log* .pnpm-debug.log* -# env files (can opt-in for committing if needed) -.env* - -# vercel +# deployment .vercel -# typescript -*.tsbuildinfo -next-env.d.ts - +# editors / nix +.idea/ .direnv/ diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3475725 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,81 @@ +# AGENTS.md + +Conventions for any coding agent working in this repo (Claude Code, Codex, Cursor, Copilot and the like). Humans: [README.md](README.md) and [CONTRIBUTING.md](CONTRIBUTING.md) cover the same ground in more detail. + +## The one rule + +Whoever opens the PR has to be able to explain every change in it during review. So: + +- Keep diffs small and on topic. One issue, one PR. No drive-by refactors. +- Say in the PR description why you made each non-obvious choice. "The agent did it" is not an explanation. +- If you can't tell why a piece of existing code is there, ask before changing it. + +## Layout + +``` +apps/web Next.js 15 site (App Router, src/, Tailwind 4, shadcn-style components) +apps/api NestJS 12 API, Drizzle ORM on Postgres 18 + src/db/ schema.ts (source of truth), client.ts (pool + db), seed.ts + drizzle/ generated migrations: never edit by hand +packages/config shared tsconfig.base.json and biome.json +``` + +## Commands + +Run from the repo root. Local setup is in the README: `pnpm install`, `docker compose up -d --wait`, `pnpm db:migrate`, `pnpm dev`. + +```bash +pnpm lint && pnpm typecheck && pnpm test && pnpm build # what CI runs +pnpm --filter @acmfeup/api