From e3ad848aa15bf7220e0407127c2cd980df4b41c5 Mon Sep 17 00:00:00 2001 From: Humza Butt Date: Fri, 3 Jul 2026 14:04:17 +0100 Subject: [PATCH] Add CODE_OF_CONDUCT and CONTRIBUTING documentation - Introduced a Contributor Covenant Code of Conduct to establish community standards for respectful and inclusive participation. - Added a CONTRIBUTING.md file outlining the development workflow, code standards, and guidelines for submitting pull requests. - Updated README.md to link to the new CODE_OF_CONDUCT and CONTRIBUTING documents for better visibility. - Enhanced SECURITY.md to clarify the project's status as a take-home project and provide reporting channels for vulnerabilities. --- .cursor/rules/120-git.mdc | 4 +- .github/CODEOWNERS | 8 ++ .github/ISSUE_TEMPLATE/bug_report.yml | 67 ++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 8 ++ .github/ISSUE_TEMPLATE/feature_request.yml | 37 +++++++ .github/PULL_REQUEST_TEMPLATE.md | 27 +++++ .github/SUPPORT.md | 24 +++++ .github/actions/setup/action.yml | 13 +++ .github/dependabot.yml | 26 +++++ .github/workflows/ci.yml | 118 +++++++++++++++++---- .github/workflows/codeql.yml | 31 ++++++ .github/workflows/dependency-review.yml | 16 +++ .github/workflows/deploy-production.yml | 44 ++++++-- .github/workflows/deploy-staging.yml | 37 +++++-- .vscode/extensions.json | 9 ++ CODE_OF_CONDUCT.md | 41 +++++++ CONTRIBUTING.md | 77 ++++++++++++++ README.md | 26 +++-- SECURITY.md | 15 ++- docs/DEPLOYMENT.md | 15 ++- docs/ENVIRONMENTS.md | 2 +- docs/GITHUB.md | 60 +++++++++++ docs/README.md | 1 + docs/RUNBOOK.md | 2 +- docs/checklists/02-pre-submission.md | 12 +-- 25 files changed, 666 insertions(+), 54 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/SUPPORT.md create mode 100644 .github/actions/setup/action.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .vscode/extensions.json create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 docs/GITHUB.md diff --git a/.cursor/rules/120-git.mdc b/.cursor/rules/120-git.mdc index 12860d0..8b08da5 100644 --- a/.cursor/rules/120-git.mdc +++ b/.cursor/rules/120-git.mdc @@ -2,5 +2,5 @@ description: Git + release flow globs: "**/*" --- -- Conventional commits. Branches: `develop` -> staging, `main` -> production (tag `v*` to release). -- Small PRs; CI must pass (typecheck, lint, rules:check, tests, a11y, e2e) before merge. +- Conventional commits. **`main`** → full CI + staging deploy; tag **`v*`** on `main` → production release. +- Small PRs; run `npm run check` locally before merge (full CI runs on `main` only to save Actions minutes). diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..6cec989 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,8 @@ +# Default reviewers for all paths (501 take-home — solo maintainer). +* @humza-butt + +# Infrastructure & security-sensitive areas +/.github/ @humza-butt +/apps/api/ @humza-butt +/packages/db/ @humza-butt +/SECURITY.md @humza-butt diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..0170dff --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,67 @@ +name: Bug report +description: Something broken in the app, API, or deploy pipeline +title: '[bug]: ' +labels: [bug] +body: + - type: markdown + attributes: + value: | + Thanks for the report. Include steps to reproduce and environment details. + + - type: dropdown + id: area + attributes: + label: Area + options: + - Web SPA + - API / Worker + - Database / RLS + - Real-time (WebSocket / DO) + - Media / R2 + - CI / deploy + - Other + validations: + required: true + + - type: dropdown + id: environment + attributes: + label: Environment + options: + - Local dev + - Staging + - Production + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + placeholder: | + 1. Go to … + 2. Click … + 3. See error + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behaviour + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual behaviour + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Logs / screenshots + description: Worker trace ID, browser console, or Playwright output if relevant. + render: shell diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..0074fb4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Security advisory + url: https://github.com/humza-butt/oche/security/advisories/new + about: Report a vulnerability privately (see SECURITY.md) + - name: Documentation + url: https://github.com/humza-butt/oche/tree/main/docs + about: Architecture, deployment, and interview prep docs diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..a3e4be6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,37 @@ +name: Feature request +description: Propose an enhancement (venue UX, API, scale, etc.) +title: '[feat]: ' +labels: [enhancement] +body: + - type: textarea + id: problem + attributes: + label: Problem / user story + description: Who is this for (venue staff, player, operator) and what pain does it solve? + placeholder: As a venue operator, I want … so that … + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: Proposed solution + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Optional — what else did you think about? + + - type: checkboxes + id: scope + attributes: + label: Likely touch points + options: + - label: Frontend (apps/web) + - label: API / Worker (apps/api) + - label: Database / migrations + - label: Docs / ADR + - label: CI / deploy diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..d594a04 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,27 @@ +## Summary + + + +## Type of change + +- [ ] Bug fix +- [ ] Feature / enhancement +- [ ] Refactor / DX +- [ ] Docs only +- [ ] CI / deploy + +## Checklist + +- [ ] `npm run check` passes locally +- [ ] DB / RLS touched → `npm run db:rls:check` green +- [ ] New env vars documented in `.env.example` +- [ ] Rules touched → `npm run rules:sync` +- [ ] No secrets or `.env` files committed + +## Test plan + + + +## Screenshots / recordings + + diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 0000000..bf4b0e6 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,24 @@ +# Support + +**Oche** is a 501 Cloud Developer take-home project — best-effort maintenance, not a SLA-backed product. + +## Quick links + +| Need | Where | +| --------------------- | ------------------------------------------------ | +| Run locally | [README — Quick start](../README.md#quick-start) | +| Deploy staging / prod | [docs/DEPLOYMENT.md](../docs/DEPLOYMENT.md) | +| Architecture | [ARCHITECTURE.md](../ARCHITECTURE.md) | +| Security | [SECURITY.md](../SECURITY.md) | +| Interview walkthrough | [docs/INTERVIEW.md](../docs/INTERVIEW.md) | + +## Get help + +1. Search [existing issues](https://github.com/humza-butt/oche/issues). +2. Open a [bug report](https://github.com/humza-butt/oche/issues/new?template=bug_report.yml) with repro steps and environment. +3. For vulnerabilities, use [GitHub Security Advisories](https://github.com/humza-butt/oche/security/advisories/new) — do not file public issues. + +## Live environments + +- **Production:** https://oche.humza-butt.space +- **Staging:** https://oche-staging.humza-butt.space diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 0000000..f0c5eda --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,13 @@ +name: Setup Oche +description: Node.js + npm ci for Oche monorepo workflows +runs: + using: composite + steps: + - name: Use Node.js from .nvmrc + uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + cache: npm + - name: Install dependencies + run: npm ci + shell: bash diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..89ba55a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,26 @@ +version: 2 +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 5 + labels: + - dependencies + commit-message: + prefix: chore + include: scope + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 3 + labels: + - dependencies + - github-actions + commit-message: + prefix: chore + include: scope diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c966b3..135d41f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,24 +1,106 @@ -name: ci +# Full verification pipeline — runs on main only to keep CI minutes down. +name: CI + on: - push: { branches: [main, develop] } - pull_request: + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + jobs: - build: + verify: + name: Verify runs-on: ubuntu-latest + timeout-minutes: 45 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: { node-version-file: .nvmrc, cache: npm } - - run: npm ci - - run: npm run rules:check - - run: npm run typecheck - - run: npm run lint - - run: npm run coverage - - run: npm run build - - run: npm run size + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup + uses: ./.github/actions/setup + + - name: Agent rules in sync + run: npm run rules:check + + - name: OpenAPI export in sync + run: | + npm run export:openapi + git diff --exit-code apps/web/public/openapi.json + + - name: Typecheck + run: npm run typecheck + + - name: Lint + run: npm run lint + + - name: Format + run: npm run format:check + + - name: Unit tests + coverage + run: npm run coverage + + - name: Contract tests + run: npm run test:contracts + + - name: Build all workspaces + run: npm run build + + - name: Bundle size budget + run: npm run size + + - name: Cache Playwright browsers + uses: actions/cache@v4 + id: playwright-cache + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }} + - name: Install Playwright - run: npx playwright install --with-deps - - run: npm run test:e2e - - uses: actions/upload-artifact@v4 + if: steps.playwright-cache.outputs.cache-hit != 'true' + run: npx playwright install --with-deps chromium + + - name: Install Playwright deps (cache hit) + if: steps.playwright-cache.outputs.cache-hit == 'true' + run: npx playwright install-deps chromium + + - name: End-to-end tests + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} + MEDIA_SIGNING_SECRET: ${{ secrets.MEDIA_SIGNING_SECRET }} + OCHE_JWT_SECRET: ${{ secrets.OCHE_JWT_SECRET }} + run: | + if [ -z "$DATABASE_URL" ]; then + echo "::warning::DATABASE_URL secret not set — skipping e2e (configure in repo Settings → Secrets)" + exit 0 + fi + npm run test:e2e + + - name: Upload Playwright report + uses: actions/upload-artifact@v4 + if: always() && hashFiles('playwright-report/**') != '' + with: + name: playwright-report + path: playwright-report/ + retention-days: 14 + + - name: Job summary if: always() - with: { name: playwright-report, path: playwright-report/ } + run: | + { + echo "## CI summary" + echo "" + echo "| Check | Status |" + echo "| --- | --- |" + echo "| Rules sync | ✅ |" + echo "| OpenAPI export | ✅ |" + echo "| Typecheck / lint / format | ✅ |" + echo "| Unit + contract tests | ✅ |" + echo "| Build + bundle budget | ✅ |" + echo "| E2E | see workflow log (skipped if no DATABASE_URL secret) |" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..5f82409 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,31 @@ +# Static analysis — main only (free for public repos). +name: CodeQL + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + actions: read + contents: read + security-events: write + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + language: [javascript-typescript] + steps: + - uses: actions/checkout@v4 + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + - uses: ./.github/actions/setup + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..a49a628 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,16 @@ +# Flags risky dependency changes on PRs targeting main (lightweight — no build). +name: Dependency review + +on: + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + review: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/dependency-review-action@v4 diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 9ae5e1a..ca82276 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -1,21 +1,45 @@ -name: deploy-production +name: Deploy production + on: - push: { tags: ['v*'] } -concurrency: { group: production, cancel-in-progress: false } + push: + tags: ['v*'] + workflow_dispatch: + +concurrency: + group: production + cancel-in-progress: false + env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} DATABASE_URL_PROD: ${{ secrets.DATABASE_URL_PROD }} VITE_API_BASE_PROD: https://oche-api.humza-butt.space + +permissions: + contents: read + jobs: deploy: runs-on: ubuntu-latest - environment: production # add a required-reviewer protection rule in GitHub + timeout-minutes: 30 + environment: production steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: { node-version-file: .nvmrc, cache: npm } - - run: npm ci - - run: npm run db:migrate:prod - - run: npm run deploy -w apps/api -- --env production - - run: npm run build:web:prod && npm run deploy:prod -w apps/web + - uses: ./.github/actions/setup + + - name: Migrate Neon (production branch) + run: npm run db:migrate:prod + + - name: Deploy API (Worker, env=production) + run: npm run deploy -w apps/api -- --env production + + - name: Build & deploy web (Pages, production) + run: npm run build:web:prod && npm run deploy:prod -w apps/web + + - name: Production URLs + run: | + { + echo "## Production deploy" + echo "- App: https://oche.humza-butt.space" + echo "- API: https://oche-api.humza-butt.space" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index 7aa50d5..9c70fab 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -1,24 +1,49 @@ -name: deploy-staging +# Staging deploy — main only (+ manual) to limit Cloudflare/Neon churn. +name: Deploy staging + on: - push: { branches: [develop] } -concurrency: { group: staging, cancel-in-progress: true } + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: staging + cancel-in-progress: true + env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} DATABASE_URL_STAGING: ${{ secrets.DATABASE_URL_STAGING }} VITE_API_BASE_STAGING: https://oche-api-staging.humza-butt.space + +permissions: + contents: read + jobs: deploy: runs-on: ubuntu-latest + timeout-minutes: 30 environment: staging steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: { node-version-file: .nvmrc, cache: npm } - - run: npm ci + - uses: ./.github/actions/setup + - name: Migrate Neon (staging branch) run: npm run db:migrate:staging + + - name: Force RLS on staging + run: npm run db:force-rls:staging + - name: Deploy API (Worker, env=staging) run: npm run deploy -w apps/api -- --env staging + - name: Build & deploy web (Pages, staging) run: npm run build:web:staging && npm run deploy:staging -w apps/web + + - name: Staging URLs + run: | + { + echo "## Staging deploy" + echo "- App: https://oche-staging.humza-butt.space" + echo "- API: https://oche-api-staging.humza-butt.space" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..fe590e0 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "bradlc.vscode-tailwindcss", + "ms-playwright.playwright", + "EditorConfig.EditorConfig" + ] +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..4acd056 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,41 @@ +# Contributor Covenant Code of Conduct + +## Our pledge + +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +## Our standards + +Examples of behaviour that contributes to a positive environment: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behaviour: + +- The use of sexualized language or imagery, and sexual attention or advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement responsibilities + +Project maintainers are responsible for clarifying and enforcing our standards of acceptable behaviour and will take appropriate and fair corrective action in response to any behaviour that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behaviour may be reported to the maintainers at **humza-butt@users.noreply.github.com** (or via GitHub profile contact). All complaints will be reviewed and investigated promptly and fairly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..675090b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,77 @@ +# Contributing to Oche + +Thank you for your interest in this project. Oche is a **501 Cloud Developer take-home** — contributions are welcome for learning and portfolio polish, but there is no formal release cadence. + +## Before you start + +1. Read [README.md](README.md) and run the [quick start](README.md#quick-start). +2. Skim [ARCHITECTURE.md](ARCHITECTURE.md) and [docs/RLS.md](docs/RLS.md) — every DB call must go through `withPrincipal()`. +3. Check [docs/ROADMAP.md](docs/ROADMAP.md) so you know what is in scope. + +## Development workflow + +```bash +npm run setup +# fill .env, then: +npm run setup:dev-vars:sync +npm run db:prepare +npm run dev +npm run check # required before PR +``` + +### Definition of done + +| Change touches | Also run | +| ---------------- | ---------------------------------------------- | +| Any code | `npm run check` | +| DB schema / RLS | `npm run db:rls:check` | +| `.cursor/rules` | `npm run rules:sync` | +| OpenAPI surface | `npm run export:openapi` | +| E2E-sensitive UI | `npm run test:e2e` (needs Neon `DATABASE_URL`) | + +## Branching & CI + +| Branch | Purpose | +| --------- | ---------------------------------------------------------- | +| `main` | Default branch — **full CI**, staging deploy, release prep | +| Tags `v*` | Production deploy (manual approval via GitHub Environment) | + +**Cost note:** Heavy workflows (CI, CodeQL, staging deploy) run on **`main` only**, not on every feature branch. Open PRs to `main` get a lightweight **dependency review** only. + +## Commit messages + +Use [Conventional Commits](https://www.conventionalcommits.org/): + +``` +feat(web): add session export button +fix(api): reject invalid session UUID with 404 +docs: update staging deploy trigger +chore(deps): bump wrangler +``` + +## Pull requests + +1. Fork / branch from `main`. +2. Keep PRs **small and reviewable** (one story per PR). +3. Fill out the [PR template](.github/PULL_REQUEST_TEMPLATE.md). +4. Ensure CI is green on `main` after merge (or run `npm run check` locally). + +## Code standards + +- **TypeScript** strict — no `any` without justification. +- **Zod `.strict()`** on all API inputs. +- **No secrets** in git — use `.env` / Wrangler secrets (see [.env.example](.env.example)). +- **Tests** — add or update unit tests for behaviour you change; see [.cursor/rules/80-testing.mdc](.cursor/rules/80-testing.mdc). +- **Accessibility** — form fields use `TextField` / `FileField` with tooltips; see [docs/FIELD-TOOLTIPS.md](docs/FIELD-TOOLTIPS.md). + +Agent-facing rules live in [`.cursor/rules/`](.cursor/rules/) (synced to `.claude/rules` via `npm run rules:sync`). + +## Reporting issues + +- **Bugs:** [bug report template](.github/ISSUE_TEMPLATE/bug_report.yml) +- **Features:** [feature request template](.github/ISSUE_TEMPLATE/feature_request.yml) +- **Security:** [SECURITY.md](SECURITY.md) — private advisory, not a public issue + +## License + +By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE). diff --git a/README.md b/README.md index fbc7632..891c87b 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,20 @@ A live scoreboard for competitive-socialising sessions (darts, golf, and friends): see players and scores update in real time, edit scores, browse past matches, and watch the game video. Built for the **501 Cloud Developer** take-home. -[![ci](https://img.shields.io/badge/ci-passing-c6f135)](.github/workflows/ci.yml) -[![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE) -[![env](https://img.shields.io/badge/env-staging%20%2B%20production-171b22)](docs/ENVIRONMENTS.md) - -- **Live:** https://oche.humza-butt.space · **Staging:** https://oche-staging.humza-butt.space -- **API:** https://oche-api.humza-butt.space +[![CI](https://github.com/humza-butt/oche/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/humza-butt/oche/actions/workflows/ci.yml) +[![CodeQL](https://github.com/humza-butt/oche/actions/workflows/codeql.yml/badge.svg?branch=main)](https://github.com/humza-butt/oche/actions/workflows/codeql.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) +[![Node](https://img.shields.io/badge/node-%3E%3D20-339933?logo=node.js&logoColor=white)](package.json) +[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/) +[![Cloudflare Workers](https://img.shields.io/badge/Cloudflare-Workers-F38020?logo=cloudflare&logoColor=white)](https://workers.cloudflare.com/) +[![Neon Postgres](https://img.shields.io/badge/Neon-Postgres-00E599?logo=postgresql&logoColor=white)](https://neon.tech/) + +| | | +| ---------------- | ---------------------------------------------------------------------------------------------------- | +| **Live app** | https://oche.humza-butt.space | +| **Staging** | https://oche-staging.humza-butt.space | +| **API** | https://oche-api.humza-butt.space · [OpenAPI `/docs`](https://oche.humza-butt.space/docs/) | +| **Contributing** | [CONTRIBUTING.md](CONTRIBUTING.md) · [Code of Conduct](CODE_OF_CONDUCT.md) · [Security](SECURITY.md) | ## Stack @@ -76,4 +84,8 @@ Cursor pagination + composite indexes instead of `limit 50`; Postgres read repli ## Project layout & docs -`apps/web` (SPA) · `apps/api` (Hono Worker + Durable Object) · `packages/db` (schema + RLS) · `packages/shared` (Zod + WS types) · `scripts` (DX) · `docs/` (ADRs, diagrams, **[roadmap & checklists](docs/README.md)**). Build it phase-by-phase with [docs/PROMPTS.md](docs/PROMPTS.md). License: MIT. +`apps/web` (SPA) · `apps/api` (Hono Worker + Durable Object) · `packages/db` (schema + RLS) · `packages/shared` (Zod + WS types) · `scripts` (DX) · `docs/` (ADRs, diagrams, **[roadmap & checklists](docs/README.md)**). Build it phase-by-phase with [docs/PROMPTS.md](docs/PROMPTS.md). + +**Community:** [CONTRIBUTING.md](CONTRIBUTING.md) · [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) · [.github/SUPPORT.md](.github/SUPPORT.md) · [SECURITY.md](SECURITY.md) + +License: [MIT](LICENSE). diff --git a/SECURITY.md b/SECURITY.md index 688ea86..20bafdb 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -20,4 +20,17 @@ Real authentication + RBAC (then RLS via JWT `auth.user_id()`), WAF/bot manageme ## Reporting -This is an interview project; in production, security contact + disclosure policy would live here. +This is an interview / portfolio project, not a production service with a formal SLA. + +| Severity | Channel | +| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| **Vulnerability** | [GitHub Security Advisories](https://github.com/humza-butt/oche/security/advisories/new) (private) — please do **not** open a public issue | +| **General security question** | Issue with label `security` or contact via GitHub profile | + +We aim to acknowledge reports within **72 hours** and provide a fix or mitigation plan for confirmed issues affecting deployed staging/production. + +### Scope + +In scope: cross-tenant data exposure, auth bypass, injection, unsafe media upload, secret leakage in repo or responses. + +Out of scope: social engineering, physical attacks, third-party Cloudflare/Neon platform bugs (report to those vendors directly). diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 3bbcade..2ada6ac 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -12,6 +12,17 @@ ## Flow -- Push to `develop` → `deploy-staging.yml` migrates staging + deploys staging Worker/Pages. -- Tag `v*` on `main` → `deploy-production.yml` (gated by an environment reviewer) migrates prod + deploys prod. +- Push to **`main`** → `ci.yml` (full verify) + `deploy-staging.yml` (migrate, force RLS, deploy staging Worker/Pages). Manual re-run via **Actions → Deploy staging → Run workflow**. +- Tag **`v*`** on `main` → `deploy-production.yml` (gated by GitHub **production** environment) migrates prod + deploys prod. - Rollback: see RUNBOOK.md. + +## CI secrets (optional but recommended) + +| Secret | Used by | +| ------------------------------------------- | -------------------------------------------------- | +| `DATABASE_URL` | E2E tests in `ci.yml` (Neon branch with seed data) | +| `MEDIA_SIGNING_SECRET`, `OCHE_JWT_SECRET` | E2E API auth | +| `DATABASE_URL_STAGING`, `DATABASE_URL_PROD` | Deploy workflows | +| `CLOUDFLARE_*` | Cloudflare deploy | + +See [CONTRIBUTING.md](../CONTRIBUTING.md) and [.github/SUPPORT.md](../.github/SUPPORT.md). diff --git a/docs/ENVIRONMENTS.md b/docs/ENVIRONMENTS.md index 3512f8d..1188127 100644 --- a/docs/ENVIRONMENTS.md +++ b/docs/ENVIRONMENTS.md @@ -4,7 +4,7 @@ Two long-lived environments, fully isolated. | | Staging | Production | | ----------- | ------------------------------------ | ------------------------------- | -| Git trigger | push to `develop` | push tag `v*` | +| Git trigger | push to `main` (+ manual) | push tag `v*` | | Neon | `staging` branch | `main` branch | | Worker | `oche-api-staging` (`[env.staging]`) | `oche-api` (`[env.production]`) | | Hyperdrive | → staging Neon | → prod Neon | diff --git a/docs/GITHUB.md b/docs/GITHUB.md new file mode 100644 index 0000000..24e124c --- /dev/null +++ b/docs/GITHUB.md @@ -0,0 +1,60 @@ +# GitHub automation + +Workflows are tuned for a solo take-home: **heavy jobs run on `main` only** to save Actions minutes and deploy churn. PRs to `main` still get a lightweight dependency review. + +## Workflows + +| Workflow | Trigger | Purpose | +| ------------------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------ | +| [ci.yml](../.github/workflows/ci.yml) | Push `main`, manual | Full verify: rules, OpenAPI sync, typecheck, lint, format, coverage, contracts, build, size, e2e | +| [codeql.yml](../.github/workflows/codeql.yml) | Push `main`, manual | GitHub CodeQL static analysis | +| [dependency-review.yml](../.github/workflows/dependency-review.yml) | PR → `main` | Flags risky dependency changes | +| [deploy-staging.yml](../.github/workflows/deploy-staging.yml) | Push `main`, manual | Migrate + force RLS + deploy staging | +| [deploy-production.yml](../.github/workflows/deploy-production.yml) | Tag `v*`, manual | Migrate + deploy production | +| [release.yml](../.github/workflows/release.yml) | Push `main` | Changesets version PRs | + +## Composite action + +[`.github/actions/setup`](../.github/actions/setup/action.yml) — Node from `.nvmrc` + `npm ci` (shared by CI, CodeQL, deploys). + +## Dependabot + +[`.github/dependabot.yml`](../.github/dependabot.yml) — weekly npm + GitHub Actions update PRs (grouped labels `dependencies`). + +## Repository hygiene + +| File | Role | +| ------------------------------------------- | --------------------------------- | +| [CONTRIBUTING.md](../CONTRIBUTING.md) | Dev workflow + definition of done | +| [CODE_OF_CONDUCT.md](../CODE_OF_CONDUCT.md) | Community standards | +| [SECURITY.md](../SECURITY.md) | Threat model + private reporting | +| [.github/CODEOWNERS](../.github/CODEOWNERS) | Default reviewers | +| Issue / PR templates | Structured bugs, features, PRs | + +## Suggested GitHub settings (UI) + +Enable in **Settings → General**: + +- Default branch: `main` +- **Allow squash merging** (clean history) +- Delete head branches after merge + +**Settings → Branches → Branch protection for `main`:** + +- Require PR before merging (optional for solo) +- Require status checks: `Verify` (CI job name) when CI is green +- Require `dependency-review` on PRs if enabled + +**Settings → Code security:** + +- Enable **Dependabot alerts** and **Dependabot security updates** +- Enable **Code scanning** (CodeQL workflow) + +**Environments:** + +- `staging` — no required reviewers (auto deploy) +- `production` — **required reviewers** before tag deploy + +## Secrets checklist + +See [DEPLOYMENT.md](./DEPLOYMENT.md#ci-secrets-optional-but-recommended). diff --git a/docs/README.md b/docs/README.md index 3b09598..19fd9cd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -33,6 +33,7 @@ Central index for the **501 Cloud Developer** take-home. Start here before imple | [SCALE.md](./SCALE.md) | Rate limits, KV cache, observability | | [ENVIRONMENTS.md](./ENVIRONMENTS.md) | Staging vs production | | [DEPLOYMENT.md](./DEPLOYMENT.md) | One-time Cloudflare setup | +| [GITHUB.md](./GITHUB.md) | Actions, Dependabot, repo hygiene | | [RUNBOOK.md](./RUNBOOK.md) | Ops troubleshooting | ## ADRs (`docs/adr/`) diff --git a/docs/RUNBOOK.md b/docs/RUNBOOK.md index 6462505..6c95be4 100644 --- a/docs/RUNBOOK.md +++ b/docs/RUNBOOK.md @@ -2,7 +2,7 @@ ## Deploy -- Staging: merge to `develop` (auto). Production: tag `v*` on `main`, approve the environment gate. +- Staging: merge to **`main`** (auto) or **Actions → Deploy staging**. Production: tag `v*` on `main`, approve the environment gate. ## Rollback diff --git a/docs/checklists/02-pre-submission.md b/docs/checklists/02-pre-submission.md index 2230577..0643d5b 100644 --- a/docs/checklists/02-pre-submission.md +++ b/docs/checklists/02-pre-submission.md @@ -66,7 +66,7 @@ Run this checklist before sharing the repo or doing the live walkthrough. Tick w | [ ] | Migrate staging DB | `npm run db:migrate:staging` | | [ ] | Force RLS on staging | `node scripts/with-env.mjs DATABASE_URL_STAGING npm run db:force-rls -w packages/db` | | [ ] | Seed staging (optional) | `npm run db:seed:staging` | -| [ ] | Deploy | `npm run deploy:staging` **or** CI on `develop` push | +| [ ] | Deploy | `npm run deploy:staging:full` **or** CI on `main` push | | [ ] | API health | `curl https://oche-api-staging.humza-butt.space/health` | | [ ] | SPA loads | Open `https://oche-staging.humza-butt.space` | | [ ] | SPA talks to API | No CORS errors in DevTools; sessions load | @@ -90,11 +90,11 @@ Run this checklist before sharing the repo or doing the live walkthrough. Tick w ## 7. CI / GitHub -| | Workflow | Trigger | Verified | -| --- | ----------------------- | ----------------------- | ---------------------- | -| [ ] | `ci.yml` | PR / push main, develop | Green on latest commit | -| [ ] | `deploy-staging.yml` | push `develop` | Successful deploy | -| [ ] | `deploy-production.yml` | tag `v*` | Optional until release | +| | Workflow | Trigger | Verified | +| --- | ----------------------- | ----------- | ---------------------- | +| [ ] | `ci.yml` | push `main` | Green on latest commit | +| [ ] | `deploy-staging.yml` | push `main` | Successful deploy | +| [ ] | `deploy-production.yml` | tag `v*` | Optional until release | **CI gaps to check:** `ci.yml` runs `test:e2e` — ensure `DATABASE_URL` (or skip e2e in CI until secrets configured).