From debac55a8af8e4ea682696ed86cc335467a4df1f Mon Sep 17 00:00:00 2001 From: Hardik Date: Fri, 19 Jun 2026 12:49:32 +0530 Subject: [PATCH] ci: enforce PR policy (tests-present + app type-check) and PR template All changes now land via PR. New .forgejo/workflows/pr-checks.yml runs on every PR to master and (1) fails code PRs that lack a test change, (2) blocks new app-code type errors. Unit tests are advisory until the baseline is green; lint is omitted (it needs an interactive ESLint migration). PR template carries the docs/tests checklist. Also makes the autofix watcher require a test (issue-12 style) + doc updates in every fix, so its PRs satisfy the new gate. Co-Authored-By: Claude Opus 4.8 --- .forgejo/PULL_REQUEST_TEMPLATE.md | 17 ++++++++ .forgejo/workflows/pr-checks.yml | 66 ++++++++++++++++++++++++++++++ automation/README.md | 28 +++++++++++++ automation/claude-issue-watcher.sh | 19 ++++++--- 4 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 .forgejo/PULL_REQUEST_TEMPLATE.md create mode 100644 .forgejo/workflows/pr-checks.yml diff --git a/.forgejo/PULL_REQUEST_TEMPLATE.md b/.forgejo/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..475caba --- /dev/null +++ b/.forgejo/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,17 @@ + + +## What & why + + + +## Checklist + +- [ ] **Tests** added or updated for this change — or it is a docs/config/automation-only PR (tests not applicable). Model: the integration test on `claude/issue-12` (prod-mirror DB, raw-SQL inserts, prefix-isolated, cleans up after itself). +- [ ] **Docs** updated where relevant (App/README.md, App/CLAUDE.md, Docs/, automation/README.md, CHANGELOG.md). +- [ ] `pnpm type-check` shows no new errors in application code. +- [ ] Verified the change (how: unit/integration tests, or a dev server on port 3100 against the test DB). + + diff --git a/.forgejo/workflows/pr-checks.yml b/.forgejo/workflows/pr-checks.yml new file mode 100644 index 0000000..0f5fa68 --- /dev/null +++ b/.forgejo/workflows/pr-checks.yml @@ -0,0 +1,66 @@ +name: PR checks + +# Enforces the contribution policy on every PR into master: +# - code changes must ship with tests (docs/config/automation are exempt) +# - no new type errors in application code +# - unit tests are reported (advisory until the baseline is green) +# Runs on the pms1 host runner. See automation/README.md > "Contribution policy". + +on: + pull_request: + branches: [master] + +jobs: + checks: + runs-on: host + steps: + - name: Checkout PR + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Policy — code changes must include tests + run: | + set -uo pipefail + base="${GITHUB_BASE_REF:-master}" + git fetch origin "$base" --depth=200 -q + changed=$(git diff --name-only "origin/$base...HEAD") + printf 'Changed files:\n%s\n\n' "$changed" + + # "Code" = app source (pages, API routes, lib, components, hooks). + # Tests, prisma, config, docs, automation and .forgejo are exempt. + code=$(printf '%s\n' "$changed" | grep -E '^App/(app|lib|components|hooks)/' \ + | grep -vE '(\.test\.|\.spec\.|/tests/)' || true) + tests=$(printf '%s\n' "$changed" | grep -E '(\.test\.|\.spec\.|/tests/)' || true) + + if [ -n "$code" ] && [ -z "$tests" ]; then + echo "::error::Code changed but no test files changed." + echo "Every code PR must add or update tests (model: the claude/issue-12 integration test)." + echo "If a test is genuinely not applicable, say why in the PR description so a reviewer can override." + printf '\nCode files without accompanying tests:\n%s\n' "$code" + exit 1 + fi + echo "OK — test-presence policy satisfied." + + - name: No new type errors in application code + run: | + set -uo pipefail + export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh" + cd App + pnpm install --frozen-lockfile + pnpm db:generate # prisma client types (no DB needed) + pnpm type-check > /tmp/tc.txt 2>&1 || true + # Gate on application-code errors only; the test suite has a known + # pre-existing type-mismatch baseline that is tracked separately. + app_errors=$(grep -E 'error TS' /tmp/tc.txt | grep -vE '/tests/|\.test\.|\.spec\.' || true) + if [ -n "$app_errors" ]; then + echo "::error::Type errors in application code:"; printf '%s\n' "$app_errors" + exit 1 + fi + echo "OK — no type errors in application code." + + - name: Unit tests (advisory) + continue-on-error: true + run: | + export NVM_DIR="$HOME/.nvm"; . "$NVM_DIR/nvm.sh" + cd App && pnpm test diff --git a/automation/README.md b/automation/README.md index 8f938cc..54b00a9 100644 --- a/automation/README.md +++ b/automation/README.md @@ -32,6 +32,34 @@ Claude in a steered session). The triage breakdown comment is plain (no bot marker) so, for `claude-queue` issues, the fix stage reads it back as refined requirements. +## Contribution policy (all changes via PR) + +**Every change lands through a pull request — no direct pushes to `master`.** This applies +to humans and to the automated pipeline alike (the watcher already opens PRs). + +Each PR must include: + +- **Tests** for any code change. Model: the integration test on `claude/issue-12` — + it targets the prod-mirror test DB, anchors on existing rows, inserts fixtures via + raw SQL (schema-tolerant), isolates them with a unique prefix, and cleans up in + `afterEach`. Docs/config/automation-only PRs are exempt. +- **Docs** updates where relevant (`App/README.md`, `App/CLAUDE.md`, `Docs/`, + this file, `CHANGELOG.md`). + +**Enforcement** — [`.forgejo/workflows/pr-checks.yml`](../.forgejo/workflows/pr-checks.yml) +runs on every PR into `master`: + +1. **Test-presence gate (hard):** a PR touching `App/app|lib|components|hooks` with no + test change fails. Justify genuine exceptions in the PR body for a reviewer to override. +2. **App-code type-check (hard):** no new `tsc` errors in application code. (The test + suite has a known pre-existing type-mismatch baseline, tracked separately, so it is + filtered out of this gate.) +3. **Unit tests (advisory):** reported but non-blocking until the unit baseline is green + (2 known failures on `master`). `pnpm lint` is intentionally not run — it currently + requires an interactive ESLint migration. + +A [`PULL_REQUEST_TEMPLATE.md`](../.forgejo/PULL_REQUEST_TEMPLATE.md) carries the checklist. + ## Components | Piece | Where | Notes | diff --git a/automation/claude-issue-watcher.sh b/automation/claude-issue-watcher.sh index 8207aab..84f8145 100644 --- a/automation/claude-issue-watcher.sh +++ b/automation/claude-issue-watcher.sh @@ -282,13 +282,20 @@ while [ "$f" -lt "$n_fix" ]; do printf '%s\n' " NEVER use a broad 'pkill -f next' -- it would kill the production app." printf '%s\n' "- Never connect to or modify the production database or the production app." printf '%s\n' "" - printf '%s\n' "## Your job" + printf '%s\n' "## Your job (PR policy: every code change ships with tests + docs)" printf '%s\n' "1. Investigate the issue and implement a focused, minimal fix in this repository." - printf '%s\n' "2. Verify: run 'pnpm type-check' and 'pnpm lint' in App/. If behaviour is covered by unit" - printf '%s\n' " tests, run them; for DB-backed behaviour, run integration tests against the test DB above." - printf '%s\n' "3. Add or adjust tests when it makes sense." - printf '%s\n' "4. Commit ALL changes to the current branch with a conventional message ending: Fixes #$num" - printf '%s\n' "5. Do NOT push, do NOT create tags, do NOT switch branches. The supervisor handles push and PR." + printf '%s\n' "2. REQUIRED: add or update a test that fails before your fix and passes after. Model it on" + printf '%s\n' " App/tests/integration/dashboard-approved-this-month.test.ts (from issue #12): target the" + printf '%s\n' " prod-mirror test DB, anchor on existing rows (findFirstOrThrow), insert fixtures via raw" + printf '%s\n' " SQL with a unique prefix, and clean them up in afterEach. The PR check REJECTS code" + printf '%s\n' " changes under App/app|lib|components|hooks with no test change." + printf '%s\n' "3. Verify: 'pnpm type-check' (no new app-code errors) and run your test against the test DB:" + printf '%s\n' " cd App && set -a && . ./.env && set +a && pnpm test:integration" + printf '%s\n' "4. REQUIRED: update any docs the change affects (App/README.md, App/CLAUDE.md, Docs/," + printf '%s\n' " CHANGELOG.md) — skip only if nothing documented is affected." + printf '%s\n' "5. Commit ALL changes (fix + test + docs) to the current branch with a conventional message" + printf '%s\n' " ending: Fixes #$num" + printf '%s\n' "6. Do NOT push, do NOT create tags, do NOT switch branches. The supervisor handles push and PR." printf '%s\n' "If the issue is unclear, too risky (migrations, payments, permissions), or you cannot verify" printf '%s\n' "the fix, make NO commits and write a short explanation to CLAUDE_RESULT.md in the repo root." } > "$prompt_file"