From d27663c06b6ce4ed88287c8f329de03eed03725b Mon Sep 17 00:00:00 2001 From: sreerevanth Date: Thu, 5 Nov 2026 01:55:56 +0530 Subject: [PATCH 1/6] fix(ci): add honest CI, fix policy bots, pin deps Co-Authored-By: Claude Sonnet 5.5 --- .github/PULL_REQUEST_TEMPLATE.md | 44 +----- .github/PULL_REQUEST_TEMPLATE/perf.md | 21 +++ .github/workflows/assignment-limit.yml | 4 - .github/workflows/ci.yml | 28 ++++ .github/workflows/db-migration-check.yml | 36 +---- .github/workflows/enforce-evidence.yml | 31 +++-- .github/workflows/stale.yml | 15 +-- apps/web/package.json | 5 +- package.json | 5 +- packages/ai/package.json | 5 +- packages/ai/tsconfig.json | 11 ++ packages/db/package.json | 12 +- packages/db/tsconfig.json | 12 ++ packages/domain-analytics/package.json | 8 +- packages/domain-analytics/tsconfig.json | 11 ++ packages/github/package.json | 7 +- packages/github/tsconfig.json | 11 ++ pnpm-lock.yaml | 165 ++++------------------- turbo.json | 3 + 19 files changed, 188 insertions(+), 246 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE/perf.md create mode 100644 .github/workflows/ci.yml create mode 100644 packages/ai/tsconfig.json create mode 100644 packages/db/tsconfig.json create mode 100644 packages/domain-analytics/tsconfig.json create mode 100644 packages/github/tsconfig.json diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7092c86..e1b0bd1 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,43 +1,7 @@ -## Problem +## What -What verified problem does this solve? +## Why -Reference: -- User feedback -- Benchmark -- Incident -- Security finding +## How tested -*(If none, close this PR.)* - ---- - -## Evidence Before - -Current numbers: - -TTFV: -Memory: -Latency: -Error rate: -Queue delay: - ---- - -## Evidence After - -New numbers: - -TTFV: -Memory: -Latency: -Error rate: -Queue delay: - ---- - -## Risk - -What could this break? - -Rollback plan? +## Screenshots (if applicable) diff --git a/.github/PULL_REQUEST_TEMPLATE/perf.md b/.github/PULL_REQUEST_TEMPLATE/perf.md new file mode 100644 index 0000000..bf0e99a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/perf.md @@ -0,0 +1,21 @@ +## What and why + +## Evidence before + +TTFV: +Memory: +Latency: +Error rate: +Queue delay: + +## Evidence after + +TTFV: +Memory: +Latency: +Error rate: +Queue delay: + +## How tested + +## Risk and rollback plan diff --git a/.github/workflows/assignment-limit.yml b/.github/workflows/assignment-limit.yml index 8030308..b3f562f 100644 --- a/.github/workflows/assignment-limit.yml +++ b/.github/workflows/assignment-limit.yml @@ -25,9 +25,5 @@ jobs: if (context.eventName === 'issues') { await github.rest.issues.createComment({ ...context.repo, issue_number: context.issue.number, body: message }); - await github.rest.issues.removeAssignees({ ...context.repo, issue_number: context.issue.number, assignees: [username] }); - } else if (context.eventName === 'pull_request') { - await github.rest.issues.createComment({ ...context.repo, issue_number: context.payload.pull_request.number, body: message }); - await github.rest.pulls.update({ ...context.repo, pull_number: context.payload.pull_request.number, state: 'closed' }); } } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a675323 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: CI +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + env: + DATABASE_URL: postgres://x:x@localhost:5432/x + NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: pk_test_Zm9yZ2VsZW5zLmNvbQ== + CLERK_SECRET_KEY: sk_test_placeholder + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm lint + - run: pnpm typecheck + - run: pnpm test + - run: pnpm build diff --git a/.github/workflows/db-migration-check.yml b/.github/workflows/db-migration-check.yml index 0785a42..a08f25a 100644 --- a/.github/workflows/db-migration-check.yml +++ b/.github/workflows/db-migration-check.yml @@ -9,35 +9,13 @@ on: jobs: check-migrations: runs-on: ubuntu-latest - services: - postgres: - image: postgres:15-alpine - env: - POSTGRES_USER: user - POSTGRES_PASSWORD: password - POSTGRES_DB: forgelens - ports: - - 5432:5432 - options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 - steps: - uses: actions/checkout@v4 - - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 with: - version: 8 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'pnpm' - - - name: Install dependencies - run: pnpm install --filter db... - - # Ponytail: Just push the schema to the ephemeral DB to see if it blows up - - name: Dry Run Migration - env: - DATABASE_URL: postgresql://user:password@localhost:5432/forgelens - run: pnpm --filter db run db:push + node-version: 20 + cache: pnpm + - run: pnpm install --frozen-lockfile + - name: Check migration consistency + run: pnpm --filter @forgelens/db exec drizzle-kit check diff --git a/.github/workflows/enforce-evidence.yml b/.github/workflows/enforce-evidence.yml index 56a0194..752b812 100644 --- a/.github/workflows/enforce-evidence.yml +++ b/.github/workflows/enforce-evidence.yml @@ -1,25 +1,32 @@ name: Enforce PR Evidence on: pull_request: - types: [opened, edited, synchronize] + types: [opened, edited, synchronize, labeled, unlabeled] jobs: enforce-evidence: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - id: changed + uses: dorny/paths-filter@v3 + with: + filters: | + relevant: + - 'packages/github/**' + - 'apps/web/src/inngest/**' + - 'packages/db/src/**' - name: Check PR Body for Evidence - uses: actions/github-script@v6 + if: contains(github.event.pull_request.labels.*.name, 'perf') || steps.changed.outputs.relevant == 'true' + uses: actions/github-script@v7 with: script: | - const body = context.payload.pull_request.body; - if (!body) { - core.setFailed("PR body is empty. Please fill out the PR template."); - return; - } - // Ponytail: ultra simple regex to check if they filled out the numbers - const missingBefore = body.includes("TTFV:\nMemory:\nLatency:"); - const missingAfter = body.includes("New numbers:\n\nTTFV:\nMemory:\nLatency:"); - + const body = (context.payload.pull_request.body || '').replace(/\r\n/g, '\n'); + const emptyMetrics = /TTFV:\s*Memory:\s*Latency:\s*(?:Error rate:\s*)?(?:Queue delay:\s*)?(?:$|\n)/m; + const missingBefore = emptyMetrics.test(body); + const missingAfter = body.includes('New numbers:') && emptyMetrics.test(body); if (missingBefore || missingAfter) { - core.setFailed("Directive 002 violation: You must provide benchmark evidence in the PR template. Do not leave the metrics blank."); + core.setFailed('Performance changes must include benchmark evidence in the PR body.'); } diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 212e0ea..f8aad4c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,7 +1,7 @@ name: Manage Stale Issues and PRs on: schedule: - - cron: '0 * * * *' # Runs every hour + - cron: '0 9 * * *' # Runs daily permissions: issues: write @@ -13,15 +13,14 @@ jobs: steps: - uses: actions/stale@v8 with: - days-before-stale: 4 - days-before-close: 2 - stale-issue-message: 'This issue has been inactive for 4 days. It has been marked as stale and the assignee will be removed shortly to allow others to work on it.' - stale-pr-message: 'This PR has been inactive for 4 days. @maintainer @contributor please review or push updates, otherwise it will be closed in 2 days.' - close-issue-message: 'This issue was closed due to inactivity.' - close-pr-message: 'This PR was closed due to inactivity.' + days-before-issue-stale: 14 + days-before-issue-close: -1 + days-before-pr-stale: -1 + days-before-pr-close: -1 + stale-issue-message: 'This issue has been inactive for 14 days. It has been marked stale and its assignee removed so others can pick it up.' stale-issue-label: 'stale' - stale-pr-label: 'stale' exempt-issue-labels: 'pinned,security' exempt-pr-labels: 'do-not-merge' remove-issue-stale-when-updated: true remove-pr-stale-when-updated: true + remove-issue-assignees-when-stale: true diff --git a/apps/web/package.json b/apps/web/package.json index 72a3083..fb68adf 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -6,7 +6,8 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "eslint" + "lint": "eslint", + "typecheck": "tsc --noEmit" }, "dependencies": { "@clerk/nextjs": "^7.5.12", @@ -15,7 +16,7 @@ "@forgelens/domain-analytics": "workspace:^", "@forgelens/github": "workspace:^", "@opentelemetry/api": "^1.9.1", - "drizzle-orm": "^0.45.2", + "drizzle-orm": "0.45.2", "inngest": "^4.11.0", "next": "16.2.10", "react": "19.2.4", diff --git a/package.json b/package.json index 2023b50..4f2f57f 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,11 @@ "build": "turbo run build", "dev": "turbo run dev", "lint": "turbo run lint", - "typecheck": "turbo run typecheck" + "typecheck": "turbo run typecheck", + "test": "turbo run test" }, "devDependencies": { - "turbo": "latest" + "turbo": "2.10.2" }, "packageManager": "pnpm@9.0.0" } diff --git a/packages/ai/package.json b/packages/ai/package.json index 1dda182..9d48507 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -3,12 +3,15 @@ "version": "0.1.0", "private": true, "main": "src/index.ts", + "scripts": { + "typecheck": "tsc --noEmit" + }, "dependencies": { "ai": "^3.4.0", "@ai-sdk/openai": "^0.0.66", "zod": "^3.23.8" }, "devDependencies": { - "typescript": "latest" + "typescript": "6.0.3" } } diff --git a/packages/ai/tsconfig.json b/packages/ai/tsconfig.json new file mode 100644 index 0000000..e77b932 --- /dev/null +++ b/packages/ai/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/db/package.json b/packages/db/package.json index 7e653e5..76bf26d 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -5,14 +5,16 @@ "main": "src/index.ts", "scripts": { "generate": "drizzle-kit generate", - "push": "drizzle-kit push" + "push": "drizzle-kit push", + "typecheck": "tsc --noEmit" }, "dependencies": { - "drizzle-orm": "latest", - "postgres": "latest" + "drizzle-orm": "0.45.2", + "postgres": "3.4.9" }, "devDependencies": { - "drizzle-kit": "latest", - "typescript": "latest" + "@types/node": "20.19.43", + "drizzle-kit": "0.31.10", + "typescript": "6.0.3" } } diff --git a/packages/db/tsconfig.json b/packages/db/tsconfig.json new file mode 100644 index 0000000..9108534 --- /dev/null +++ b/packages/db/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "types": ["node"], + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src/**/*.ts", "drizzle.config.ts"] +} diff --git a/packages/domain-analytics/package.json b/packages/domain-analytics/package.json index 29f9509..50c0b19 100644 --- a/packages/domain-analytics/package.json +++ b/packages/domain-analytics/package.json @@ -1,16 +1,18 @@ { "name": "@forgelens/domain-analytics", "version": "0.1.0", + "main": "src/index.ts", "private": true, "scripts": { - "test": "vitest run" + "test": "vitest run", + "typecheck": "tsc --noEmit" }, "dependencies": { "@forgelens/db": "workspace:*", - "drizzle-orm": "^0.30.10" + "drizzle-orm": "0.45.2" }, "devDependencies": { - "typescript": "latest", + "typescript": "6.0.3", "vitest": "^4.1.9" } } diff --git a/packages/domain-analytics/tsconfig.json b/packages/domain-analytics/tsconfig.json new file mode 100644 index 0000000..e77b932 --- /dev/null +++ b/packages/domain-analytics/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/github/package.json b/packages/github/package.json index 3b9b86e..98a4e55 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -3,11 +3,14 @@ "version": "0.1.0", "private": true, "main": "src/index.ts", + "scripts": { + "typecheck": "tsc --noEmit" + }, "dependencies": { - "@octokit/plugin-throttling": "^11.0.3", + "@octokit/plugin-throttling": "10.0.0", "octokit": "^4.0.0" }, "devDependencies": { - "typescript": "latest" + "typescript": "6.0.3" } } diff --git a/packages/github/tsconfig.json b/packages/github/tsconfig.json new file mode 100644 index 0000000..e77b932 --- /dev/null +++ b/packages/github/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src/**/*.ts"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2adfec5..08a54cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,7 +9,7 @@ importers: .: devDependencies: turbo: - specifier: latest + specifier: 2.10.2 version: 2.10.2 apps/web: @@ -33,7 +33,7 @@ importers: specifier: ^1.9.1 version: 1.9.1 drizzle-orm: - specifier: ^0.45.2 + specifier: 0.45.2 version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(postgres@3.4.9) inngest: specifier: ^4.11.0 @@ -92,23 +92,26 @@ importers: version: 3.25.76 devDependencies: typescript: - specifier: latest + specifier: 6.0.3 version: 6.0.3 packages/db: dependencies: drizzle-orm: - specifier: latest - version: 0.45.2(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(postgres@3.4.9) + specifier: 0.45.2 + version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(postgres@3.4.9) postgres: - specifier: latest + specifier: 3.4.9 version: 3.4.9 devDependencies: + '@types/node': + specifier: 20.19.43 + version: 20.19.43 drizzle-kit: - specifier: latest + specifier: 0.31.10 version: 0.31.10 typescript: - specifier: latest + specifier: 6.0.3 version: 6.0.3 packages/domain-analytics: @@ -117,27 +120,27 @@ importers: specifier: workspace:* version: link:../db drizzle-orm: - specifier: ^0.30.10 - version: 0.30.10(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(@types/react@19.2.17)(postgres@3.4.9)(react@19.2.4) + specifier: 0.45.2 + version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(postgres@3.4.9) devDependencies: typescript: - specifier: latest + specifier: 6.0.3 version: 6.0.3 vitest: specifier: ^4.1.9 - version: 4.1.9(@opentelemetry/api@1.9.0)(@types/node@20.19.43)(vite@8.1.3(@types/node@20.19.43)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) + version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(vite@8.1.3(@types/node@20.19.43)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) packages/github: dependencies: '@octokit/plugin-throttling': - specifier: ^11.0.3 - version: 11.0.3(@octokit/core@6.1.6) + specifier: 10.0.0 + version: 10.0.0(@octokit/core@6.1.6) octokit: specifier: ^4.0.0 version: 4.1.4 devDependencies: typescript: - specifier: latest + specifier: 6.0.3 version: 6.0.3 packages: @@ -353,11 +356,11 @@ packages: '@esbuild-kit/core-utils@3.3.2': resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} - deprecated: 'Merged into tsx: https://tsx.is' + deprecated: 'Merged into tsx: https://tsx.hirok.io' '@esbuild-kit/esm-loader@2.6.5': resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} - deprecated: 'Merged into tsx: https://tsx.is' + deprecated: 'Merged into tsx: https://tsx.hirok.io' '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} @@ -1167,9 +1170,6 @@ packages: '@octokit/openapi-types@25.1.0': resolution: {integrity: sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==} - '@octokit/openapi-types@27.0.0': - resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==} - '@octokit/openapi-webhooks-types@11.0.0': resolution: {integrity: sha512-ZBzCFj98v3SuRM7oBas6BHZMJRadlnDoeFfvm1olVxZnYeU6Vh97FhPxyS5aLh5pN51GYv2I51l/hVUAVkGBlA==} @@ -1203,12 +1203,6 @@ packages: peerDependencies: '@octokit/core': ^6.1.3 - '@octokit/plugin-throttling@11.0.3': - resolution: {integrity: sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==} - engines: {node: '>= 20'} - peerDependencies: - '@octokit/core': ^7.0.0 - '@octokit/request-error@6.1.8': resolution: {integrity: sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==} engines: {node: '>= 18'} @@ -1220,9 +1214,6 @@ packages: '@octokit/types@14.1.0': resolution: {integrity: sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==} - '@octokit/types@16.0.0': - resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} - '@octokit/webhooks-methods@5.1.1': resolution: {integrity: sha512-NGlEHZDseJTCj8TMMFehzwa9g7On4KJMPVHDSrHxCQumL6uSQR8wIkP/qesv52fXqV1BPf4pTxwtS31ldAt9Xg==} engines: {node: '>= 18'} @@ -1629,6 +1620,7 @@ packages: '@opentelemetry/propagator-jaeger@2.9.0': resolution: {integrity: sha512-4mYGty27rYvSM0jtp1ZUOqd3LfVRCYg9H5G9OFzSx5HViYToU21MFhWfco7x1HwXr7ER8yGOiCIHZUwjPksc0Q==} engines: {node: ^18.19.0 || >=20.6.0} + deprecated: Package no longer supported. See https://github.com/open-telemetry/opentelemetry-js/pull/7077 peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -1693,12 +1685,14 @@ packages: '@opentelemetry/sdk-trace-base@2.9.0': resolution: {integrity: sha512-cp9zmTl62R8PJrpvFcmc8N2JQU/xfa0S+61q511Nji+QxCfZ8Ifvg7H27G8cANe4crg4RTrWsVvanHiXjSp6ag==} engines: {node: ^18.19.0 || >=20.6.0} + deprecated: Use @opentelemetry/sdk-trace package peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' '@opentelemetry/sdk-trace-node@2.9.0': resolution: {integrity: sha512-ec9a7ps37huy5itYk0MalaZdSLlM6AXWp/FhtEjgMpp5leEGojBDvAl/UWttQnkMZOvFHKzRESn8TD3yKTF5nQ==} engines: {node: ^18.19.0 || >=20.6.0} + deprecated: Use @opentelemetry/sdk-trace package peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -2644,86 +2638,6 @@ packages: resolution: {integrity: sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==} hasBin: true - drizzle-orm@0.30.10: - resolution: {integrity: sha512-IRy/QmMWw9lAQHpwbUh1b8fcn27S/a9zMIzqea1WNOxK9/4EB8gIo+FZWLiPXzl2n9ixGSv8BhsLZiOppWEwBw==} - peerDependencies: - '@aws-sdk/client-rds-data': '>=3' - '@cloudflare/workers-types': '>=3' - '@electric-sql/pglite': '>=0.1.1' - '@libsql/client': '*' - '@neondatabase/serverless': '>=0.1' - '@op-engineering/op-sqlite': '>=2' - '@opentelemetry/api': ^1.4.1 - '@planetscale/database': '>=1' - '@types/better-sqlite3': '*' - '@types/pg': '*' - '@types/react': '>=18' - '@types/sql.js': '*' - '@vercel/postgres': '>=0.8.0' - '@xata.io/client': '*' - better-sqlite3: '>=7' - bun-types: '*' - expo-sqlite: '>=13.2.0' - knex: '*' - kysely: '*' - mysql2: '>=2' - pg: '>=8' - postgres: '>=3' - react: '>=18' - sql.js: '>=1' - sqlite3: '>=5' - peerDependenciesMeta: - '@aws-sdk/client-rds-data': - optional: true - '@cloudflare/workers-types': - optional: true - '@electric-sql/pglite': - optional: true - '@libsql/client': - optional: true - '@neondatabase/serverless': - optional: true - '@op-engineering/op-sqlite': - optional: true - '@opentelemetry/api': - optional: true - '@planetscale/database': - optional: true - '@types/better-sqlite3': - optional: true - '@types/pg': - optional: true - '@types/react': - optional: true - '@types/sql.js': - optional: true - '@vercel/postgres': - optional: true - '@xata.io/client': - optional: true - better-sqlite3: - optional: true - bun-types: - optional: true - expo-sqlite: - optional: true - knex: - optional: true - kysely: - optional: true - mysql2: - optional: true - pg: - optional: true - postgres: - optional: true - react: - optional: true - sql.js: - optional: true - sqlite3: - optional: true - drizzle-orm@0.45.2: resolution: {integrity: sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==} peerDependencies: @@ -2995,6 +2909,7 @@ packages: eslint@9.39.4: resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true peerDependencies: jiti: '*' @@ -4104,7 +4019,7 @@ packages: serialize-error-cjs@0.1.4: resolution: {integrity: sha512-6a6dNqipzbCPlTFgztfNP2oG+IGcflMe/01zSzGrQcxGMKbIjOemBBD85pH92klWaJavAUWxAh9Z0aU28zxW6A==} - deprecated: Rolling release, please update to 0.2.0 + deprecated: Rolling release, please update to 0.3.0 server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} @@ -5500,8 +5415,6 @@ snapshots: '@octokit/openapi-types@25.1.0': {} - '@octokit/openapi-types@27.0.0': {} - '@octokit/openapi-webhooks-types@11.0.0': {} '@octokit/plugin-paginate-graphql@5.2.4(@octokit/core@6.1.6)': @@ -5531,12 +5444,6 @@ snapshots: '@octokit/types': 14.1.0 bottleneck: 2.19.5 - '@octokit/plugin-throttling@11.0.3(@octokit/core@6.1.6)': - dependencies: - '@octokit/core': 6.1.6 - '@octokit/types': 16.0.0 - bottleneck: 2.19.5 - '@octokit/request-error@6.1.8': dependencies: '@octokit/types': 14.1.0 @@ -5553,10 +5460,6 @@ snapshots: dependencies: '@octokit/openapi-types': 25.1.0 - '@octokit/types@16.0.0': - dependencies: - '@octokit/openapi-types': 27.0.0 - '@octokit/webhooks-methods@5.1.1': {} '@octokit/webhooks@13.9.1': @@ -7158,20 +7061,6 @@ snapshots: esbuild: 0.25.12 tsx: 4.23.0 - drizzle-orm@0.30.10(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(@types/react@19.2.17)(postgres@3.4.9)(react@19.2.4): - optionalDependencies: - '@opentelemetry/api': 1.9.0 - '@types/pg': 8.15.6 - '@types/react': 19.2.17 - postgres: 3.4.9 - react: 19.2.4 - - drizzle-orm@0.45.2(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(postgres@3.4.9): - optionalDependencies: - '@opentelemetry/api': 1.9.0 - '@types/pg': 8.15.6 - postgres: 3.4.9 - drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(postgres@3.4.9): optionalDependencies: '@opentelemetry/api': 1.9.1 @@ -9367,7 +9256,7 @@ snapshots: tsx: 4.23.0 yaml: 2.9.0 - vitest@4.1.9(@opentelemetry/api@1.9.0)(@types/node@20.19.43)(vite@8.1.3(@types/node@20.19.43)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)): + vitest@4.1.9(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(vite@8.1.3(@types/node@20.19.43)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.9 '@vitest/mocker': 4.1.9(vite@8.1.3(@types/node@20.19.43)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) @@ -9390,7 +9279,7 @@ snapshots: vite: 8.1.3(@types/node@20.19.43)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: - '@opentelemetry/api': 1.9.0 + '@opentelemetry/api': 1.9.1 '@types/node': 20.19.43 transitivePeerDependencies: - msw diff --git a/turbo.json b/turbo.json index a774630..7e8a47a 100644 --- a/turbo.json +++ b/turbo.json @@ -12,6 +12,9 @@ "typecheck": { "dependsOn": ["^typecheck"] }, + "test": { + "dependsOn": ["^test"] + }, "dev": { "cache": false, "persistent": true From 1697209a824239c0a1f23747c7d49173e5b0fedd Mon Sep 17 00:00:00 2001 From: sreerevanth Date: Thu, 5 Nov 2026 01:56:05 +0530 Subject: [PATCH 2/6] fix(web): resolve type errors surfaced by CI - async params for Next 16 layouts/pages - implement dashboard metrics in web action (getDashboardMetrics was removed) - add listRepositories, authorEmail, schema-correct inserts Co-Authored-By: Claude Sonnet 5.5 --- apps/web/src/actions/analytics.ts | 33 +++++++++++++++++-- apps/web/src/actions/import.ts | 6 ++-- apps/web/src/actions/sync.ts | 2 ++ apps/web/src/app/admin/import-debug/page.tsx | 3 +- apps/web/src/app/api/webhooks/github/route.ts | 23 +++++++++++-- apps/web/src/app/dashboard/page.tsx | 2 +- .../repository/[id]/contributors/page.tsx | 10 +++--- .../app/dashboard/repository/[id]/layout.tsx | 9 ++--- .../app/dashboard/repository/[id]/page.tsx | 25 +++++++------- apps/web/src/inngest/functions.ts | 21 +++++++----- packages/github/src/index.ts | 15 ++++++--- 11 files changed, 105 insertions(+), 44 deletions(-) diff --git a/apps/web/src/actions/analytics.ts b/apps/web/src/actions/analytics.ts index 2d13e91..b2ffdff 100644 --- a/apps/web/src/actions/analytics.ts +++ b/apps/web/src/actions/analytics.ts @@ -1,7 +1,8 @@ "use server"; import { auth } from "@clerk/nextjs/server"; -import { getDashboardMetrics } from "@forgelens/domain-analytics"; +import { db, repositories, commits, pullRequests } from "@forgelens/db"; +import { and, count, eq, gte } from "drizzle-orm"; export async function getDashboardAnalytics() { const { userId } = await auth(); @@ -10,6 +11,32 @@ export async function getDashboardAnalytics() { throw new Error("Unauthorized"); } - // The database and aggregation logic is safely abstracted to the Domain layer. - return await getDashboardMetrics(userId); + const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); + const owned = eq(repositories.userId, userId); + + const [[repoRow], [commitRow], [openRow], [closedRow]] = await Promise.all([ + db.select({ n: count() }).from(repositories).where(owned), + db + .select({ n: count() }) + .from(commits) + .innerJoin(repositories, eq(commits.repositoryId, repositories.id)) + .where(and(owned, gte(commits.timestamp, since))), + db + .select({ n: count() }) + .from(pullRequests) + .innerJoin(repositories, eq(pullRequests.repositoryId, repositories.id)) + .where(and(owned, eq(pullRequests.state, "open"))), + db + .select({ n: count() }) + .from(pullRequests) + .innerJoin(repositories, eq(pullRequests.repositoryId, repositories.id)) + .where(and(owned, eq(pullRequests.state, "closed"))), + ]); + + return { + totalRepos: repoRow.n, + totalCommits7d: commitRow.n, + openPrs: openRow.n, + closedPrs: closedRow.n, + }; } diff --git a/apps/web/src/actions/import.ts b/apps/web/src/actions/import.ts index 936d42d..c0cb25c 100644 --- a/apps/web/src/actions/import.ts +++ b/apps/web/src/actions/import.ts @@ -7,7 +7,7 @@ import { GitHubClient } from "@forgelens/github"; import { inngest } from "@/inngest/client"; import { revalidatePath } from "next/cache"; -export async function importRepository(prevState: any, formData: FormData) { +export async function importRepository(_prevState: unknown, formData: FormData) { const repoName = formData.get("repository")?.toString().trim(); if (!repoName) { @@ -80,8 +80,8 @@ export async function importRepository(prevState: any, formData: FormData) { revalidatePath("/dashboard"); return { success: true }; - } catch (error: any) { + } catch (error) { console.error("Import error:", error); - return { error: error.message || "Failed to import repository." }; + return { error: error instanceof Error ? error.message : "Failed to import repository." }; } } diff --git a/apps/web/src/actions/sync.ts b/apps/web/src/actions/sync.ts index 722c1ff..d69002f 100644 --- a/apps/web/src/actions/sync.ts +++ b/apps/web/src/actions/sync.ts @@ -28,6 +28,8 @@ export async function syncRepositories() { // Upsert the repository record quickly await db.insert(repositories).values({ id: repo.id.toString(), + githubId: repo.id, + name: repo.name, userId, fullName: repo.full_name, isPrivate: repo.private, diff --git a/apps/web/src/app/admin/import-debug/page.tsx b/apps/web/src/app/admin/import-debug/page.tsx index 903a435..86da166 100644 --- a/apps/web/src/app/admin/import-debug/page.tsx +++ b/apps/web/src/app/admin/import-debug/page.tsx @@ -1,5 +1,4 @@ -import { db } from "@forgelens/db"; -import { repositories } from "@forgelens/db/schemas/repositories"; +import { db, repositories } from "@forgelens/db"; import { desc } from "drizzle-orm"; export const dynamic = "force-dynamic"; diff --git a/apps/web/src/app/api/webhooks/github/route.ts b/apps/web/src/app/api/webhooks/github/route.ts index cd8c456..9735860 100644 --- a/apps/web/src/app/api/webhooks/github/route.ts +++ b/apps/web/src/app/api/webhooks/github/route.ts @@ -25,7 +25,26 @@ export async function POST(req: NextRequest) { return NextResponse.json({ error: "Invalid signature" }, { status: 401 }); } - const payload = JSON.parse(body); + const payload = JSON.parse(body) as { + repository: { id: number }; + commits: Array<{ + id: string; + message: string; + author: { name: string; email: string }; + url: string; + timestamp: string; + }>; + pull_request: { + id: number; + number: number; + title: string; + state: string; + html_url: string; + user: { login: string }; + created_at: string; + updated_at: string; + }; + }; try { if (event === "push") { @@ -39,7 +58,7 @@ export async function POST(req: NextRequest) { if (!repoExists) return NextResponse.json({ status: "Ignored" }, { status: 200 }); - const commitsToInsert = payload.commits.map((commit: any) => ({ + const commitsToInsert = payload.commits.map((commit) => ({ id: commit.id, repositoryId: repoExists.id, message: commit.message, diff --git a/apps/web/src/app/dashboard/page.tsx b/apps/web/src/app/dashboard/page.tsx index 2a230c5..c36d1ba 100644 --- a/apps/web/src/app/dashboard/page.tsx +++ b/apps/web/src/app/dashboard/page.tsx @@ -55,7 +55,7 @@ export default async function DashboardPage() { {repos.length === 0 ? (
-

You haven't synced any repositories yet.

+

You haven't synced any repositories yet.

) : (
diff --git a/apps/web/src/app/dashboard/repository/[id]/contributors/page.tsx b/apps/web/src/app/dashboard/repository/[id]/contributors/page.tsx index 715ee82..603a166 100644 --- a/apps/web/src/app/dashboard/repository/[id]/contributors/page.tsx +++ b/apps/web/src/app/dashboard/repository/[id]/contributors/page.tsx @@ -1,5 +1,4 @@ -import { db } from "@forgelens/db"; -import { contributors, repositories } from "@forgelens/db/src/schemas"; +import { db, contributors, repositories } from "@forgelens/db"; import { eq, desc } from "drizzle-orm"; import { redirect } from "next/navigation"; @@ -23,9 +22,10 @@ function getRoleBadge(role: string) { return "bg-red-100 text-red-800"; // Inactive } -export default async function ContributorsPage({ params }: { params: { id: string } }) { +export default async function ContributorsPage({ params }: { params: Promise<{ id: string }> }) { + const { id } = await params; const repo = await db.query.repositories.findFirst({ - where: eq(repositories.id, params.id), + where: eq(repositories.id, id), }); if (!repo) { @@ -33,7 +33,7 @@ export default async function ContributorsPage({ params }: { params: { id: strin } const profiles = await db.query.contributors.findMany({ - where: eq(contributors.repositoryId, params.id), + where: eq(contributors.repositoryId, id), orderBy: [desc(contributors.score)], }); diff --git a/apps/web/src/app/dashboard/repository/[id]/layout.tsx b/apps/web/src/app/dashboard/repository/[id]/layout.tsx index 35e88d8..1bf14b8 100644 --- a/apps/web/src/app/dashboard/repository/[id]/layout.tsx +++ b/apps/web/src/app/dashboard/repository/[id]/layout.tsx @@ -1,20 +1,21 @@ import { ReactNode } from "react"; import Link from "next/link"; -export default function RepositoryLayout({ +export default async function RepositoryLayout({ children, params, }: { children: ReactNode; - params: { id: string }; + params: Promise<{ id: string }>; }) { + const { id } = await params; return (
{/* Sidebar */}