From b9171f01358fd39cc18c62c186f8b4a55e0b5245 Mon Sep 17 00:00:00 2001 From: Harxhit Date: Sun, 24 May 2026 17:07:11 +0530 Subject: [PATCH 1/3] fix: Fixed linting issues --- apps/backend/src/app.ts | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/apps/backend/src/app.ts b/apps/backend/src/app.ts index 6a937a88..f817eb46 100644 --- a/apps/backend/src/app.ts +++ b/apps/backend/src/app.ts @@ -1,30 +1,31 @@ -import Fastify from 'fastify'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import cookie from '@fastify/cookie'; import cors from '@fastify/cors'; import helmet from '@fastify/helmet'; import jwt from '@fastify/jwt'; -import cookie from '@fastify/cookie'; import multipart from '@fastify/multipart'; -import fastifyStatic from '@fastify/static'; import rateLimit from '@fastify/rate-limit'; -import path from 'path'; -import { fileURLToPath } from 'url'; +import fastifyStatic from '@fastify/static'; +import Fastify, {type FastifyInstance} from 'fastify'; import { prismaPlugin } from './plugins/prisma.js'; import { redisPlugin } from './plugins/redis.js'; +import { analyticsRoutes } from './routes/analytics.js'; import { authRoutes } from './routes/auth.js'; -import { profileRoutes } from './routes/profiles.js'; import { cardRoutes } from './routes/cards.js'; -import { publicRoutes } from './routes/public.js'; -import { followRoutes } from './routes/follow.js'; import { connectRoutes } from './routes/connect.js'; -import { analyticsRoutes } from './routes/analytics.js'; -import { nfcRoutes } from './routes/nfc.js'; import { eventRoutes } from './routes/event.js'; +import { followRoutes } from './routes/follow.js'; +import { nfcRoutes } from './routes/nfc.js'; +import { profileRoutes } from './routes/profiles.js'; +import { publicRoutes } from './routes/public.js'; import { validateEnv } from './utils/validateEnv.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -export async function buildApp() { +export async function buildApp():Promise { // Validate all required secrets before registering any plugin. // If validation fails the process exits here — no partially-initialised // auth state can exist because Fastify is not yet instantiated. @@ -93,7 +94,7 @@ export async function buildApp() { app.decorate('authenticate', async function (request: any, reply: any) { try { await request.jwtVerify(); - } catch (err) { + } catch (_err) { reply.status(401).send({ error: 'Unauthorized' }); } }); From 6c0a36667210c605dfa886ed7d510c3d7ff0f799 Mon Sep 17 00:00:00 2001 From: Harxhit Date: Tue, 2 Jun 2026 00:49:40 +0530 Subject: [PATCH 2/3] fix(ci): improve selective checks and detailed PR comment reporting --- .github/scripts/ciScript.js | 37 +++++++++--- .github/scripts/commentResults.js | 98 ++++++++++++++++++------------- .github/workflows/ci.yml | 51 +++++++++++----- 3 files changed, 124 insertions(+), 62 deletions(-) diff --git a/.github/scripts/ciScript.js b/.github/scripts/ciScript.js index 3bc26109..810a91fb 100644 --- a/.github/scripts/ciScript.js +++ b/.github/scripts/ciScript.js @@ -40,15 +40,36 @@ module.exports = async ({ github, context, core }) => { } }); - console.log({ - backendFiles, - mobileFiles, - webFiles - }); + console.log({ + backendFiles, + mobileFiles, + webFiles + }); + + core.setOutput( + "backendFiles", + backendFiles + .map(file => file.replace("apps/backend/", "")) + .join(" ") + ) + + core.setOutput( + "mobileFiles", + mobileFiles + .map(file => file.replace("apps/mobile/", "")) + .join(" ") + ) + + core.setOutput( + "webFiles", + webFiles + .map(file => file.replace("apps/web/", "")) + .join(" ") + ) - core.setOutput("backendChanged",backendFiles.length > 0) - core.setOutput("mobileChanged",mobileFiles.length > 0) - core.setOutput("webChanged",webFiles.length > 0) + core.setOutput("backendChanged", backendFiles.length > 0) + core.setOutput("mobileChanged", mobileFiles.length > 0) + core.setOutput("webChanged", webFiles.length > 0) } catch (error) { console.error(error); diff --git a/.github/scripts/commentResults.js b/.github/scripts/commentResults.js index 6e6c3f7a..50cd1395 100644 --- a/.github/scripts/commentResults.js +++ b/.github/scripts/commentResults.js @@ -1,48 +1,65 @@ -module.exports = async ({ github, context, backend, mobile, web }) => { +module.exports = async ({ + github, + context, + backend, + mobile, + web, + backendLint, + backendTest, + backendTypecheck, + mobileLint, + mobileTest, + webCheck, + webBuild +}) => { const owner = context.repo.owner; const repo = context.repo.repo; - const pr = context.payload.pull_request; - const prNumber = pr.number; + const prNumber = context.payload.pull_request.number; - const statusEmoji = (status) => { + const emoji = (status) => { if (status === 'success') return '✅'; if (status === 'failure') return '❌'; if (status === 'skipped') return '⏭️'; return '⚪'; }; - const statusLabel = (status) => { - if (status === 'skipped') return `${statusEmoji(status)} Skipped — no changes detected`; - return `${statusEmoji(status)} ${status}`; + const label = (status) => { + if (!status) return '⚪ unknown'; + return `${emoji(status)} ${status}`; }; - const results = [backend, mobile, web]; - const allSkipped = results.every((s) => s === 'skipped'); - const anyFailure = results.some((s) => s === 'failure'); - const allPassed = results.every((s) => s === 'success' || s === 'skipped'); + const anyFailure = [ + backend, + mobile, + web + ].includes('failure'); - let title; - if (allSkipped) { - title = '⏭️ No changes detected — all checks skipped'; - } else if (anyFailure) { - title = '❌ Some checks failed'; - } else if (allPassed) { - title = '✅ All checks passed'; - } else { - title = '⚪ Checks completed'; - } + const title = anyFailure + ? '❌ Some checks failed' + : '✅ CI completed'; const timestamp = new Date().toUTCString(); const body = `## CI Results — ${title} +### 🖥️ Backend (${label(backend)}) | Check | Status | |---|---| -| 🖥️ Backend | ${statusLabel(backend)} | -| 📱 Mobile | ${statusLabel(mobile)} | -| 🌐 Web | ${statusLabel(web)} | +| Lint | ${label(backendLint)} | +| Test | ${label(backendTest)} | +| Typecheck | ${label(backendTypecheck)} | -> ⏭️ **Skipped** means no files were changed in that area — the check was not needed. +### 📱 Mobile (${label(mobile)}) +| Check | Status | +|---|---| +| Lint | ${label(mobileLint)} | +| Test | ${label(mobileTest)} | + +### 🌐 Web (${label(web)}) +| Check | Status | +|---|---| +| Check | ${label(webCheck)} | +| Build | ${label(webBuild)} | --- 🕐 Last updated: \`${timestamp}\``; @@ -50,34 +67,35 @@ module.exports = async ({ github, context, backend, mobile, web }) => { const COMMENT_MARKER = '## CI Results —'; try { - const comments = await github.paginate(github.rest.issues.listComments, { - owner, - repo, - issue_number: prNumber, - }); + const comments = await github.paginate( + github.rest.issues.listComments, + { + owner, + repo, + issue_number: prNumber + } + ); - const existingComment = comments.find( - (c) => c.body && c.body.startsWith(COMMENT_MARKER) + const existing = comments.find( + c => c.body && c.body.startsWith(COMMENT_MARKER) ); - if (existingComment) { + if (existing) { await github.rest.issues.updateComment({ owner, repo, - comment_id: existingComment.id, - body, + comment_id: existing.id, + body }); - console.log(`Updated existing comment: ${existingComment.id}`); } else { await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, - body, + body }); - console.log('Created new CI results comment'); } - } catch (error) { - console.error('Failed to post comment:', error); + } catch (err) { + console.error(err); } }; \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27c30e79..25f71019 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,14 +15,16 @@ jobs: backendChanged: ${{ steps.detect.outputs.backendChanged }} mobileChanged: ${{ steps.detect.outputs.mobileChanged }} webChanged: ${{ steps.detect.outputs.webChanged }} + backendFiles: ${{ steps.detect.outputs.backendFiles }} + mobileFiles: ${{ steps.detect.outputs.mobileFiles }} + webFiles: ${{ steps.detect.outputs.webFiles }} steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Detect changed files id: detect - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | @@ -44,9 +46,18 @@ jobs: - uses: pnpm/action-setup@v6.0.8 - run: pnpm install - - run: cd apps/backend && pnpm lint - - run: cd apps/backend && pnpm test - - run: cd apps/backend && pnpm typecheck + + - name: Backend lint + id: backend_lint + run: cd apps/backend && pnpm eslint ${{ needs.detect-changes.outputs.backendFiles }} + + - name: Backend test + id: backend_test + run: cd apps/backend && pnpm test ${{ needs.detect-changes.outputs.backendFiles }} + + - name: Backend typecheck + id: backend_typecheck + run: cd apps/backend && pnpm typecheck ${{ needs.detect-changes.outputs.backendFiles }} web-ci: needs: detect-changes @@ -63,8 +74,14 @@ jobs: - uses: pnpm/action-setup@v6.0.8 - run: pnpm install - - run: cd apps/web && pnpm check - - run: cd apps/web && pnpm build + + - name: Web check + id: web_check + run: cd apps/web && pnpm check + + - name: Web build + id: web_build + run: cd apps/web && pnpm build mobile-ci: needs: detect-changes @@ -81,8 +98,14 @@ jobs: - uses: pnpm/action-setup@v6.0.8 - run: pnpm install - - run: cd apps/mobile && pnpm lint - - run: cd apps/mobile && pnpm test + + - name: Mobile lint + id: mobile_lint + run: cd apps/mobile && pnpm eslint ${{ needs.detect-changes.outputs.mobileFiles }} + + - name: Mobile test + id: mobile_test + run: cd apps/mobile && pnpm test comment-results: needs: @@ -93,8 +116,7 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Comment results uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 @@ -102,10 +124,11 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const script = require('./.github/scripts/commentResults.js'); - await script({ + + await script({ github, context, backend: '${{ needs.backend-ci.result }}', web: '${{ needs.web-ci.result }}', mobile: '${{ needs.mobile-ci.result }}' - }); + }); \ No newline at end of file From eb142af9ffa05579c691d3687c6f38bfdbca05a3 Mon Sep 17 00:00:00 2001 From: Harxhit Date: Wed, 3 Jun 2026 16:17:37 +0530 Subject: [PATCH 3/3] fix(workflow): Runs targeted checks only on files changed in a pull request --- .github/scripts/ciScript.js | 54 ++++++++++---------- .github/scripts/commentResults.js | 69 +++++++++++++------------ .github/workflows/ci.yml | 84 ++++++++++++++++++++++++++----- 3 files changed, 131 insertions(+), 76 deletions(-) diff --git a/.github/scripts/ciScript.js b/.github/scripts/ciScript.js index 810a91fb..4e4e4792 100644 --- a/.github/scripts/ciScript.js +++ b/.github/scripts/ciScript.js @@ -1,3 +1,18 @@ +const isTestFile = (file) => /\.(test|spec)\.[jt]sx?$/.test(file); + +const deriveTestFiles = (files) => { + return files.map((file) => { + if (isTestFile(file)) return file; + + const withoutExt = file.replace(/\.[jt]sx?$/, ''); + const parts = withoutExt.split('/'); + const baseName = parts[parts.length - 1]; + const dir = parts.slice(0, -1).join('/'); + + return `${dir}/__tests__/${baseName}.test.ts`; + }); +}; + module.exports = async ({ github, context, core }) => { const owner = context.repo.owner; const repo = context.repo.repo; @@ -40,36 +55,19 @@ module.exports = async ({ github, context, core }) => { } }); - console.log({ - backendFiles, - mobileFiles, - webFiles - }); - - core.setOutput( - "backendFiles", - backendFiles - .map(file => file.replace("apps/backend/", "")) - .join(" ") - ) - - core.setOutput( - "mobileFiles", - mobileFiles - .map(file => file.replace("apps/mobile/", "")) - .join(" ") - ) + const strippedBackend = backendFiles.map(f => f.replace('apps/backend/', '')); + const strippedMobile = mobileFiles.map(f => f.replace('apps/mobile/', '')); - core.setOutput( - "webFiles", - webFiles - .map(file => file.replace("apps/web/", "")) - .join(" ") - ) + console.log({ backendFiles, mobileFiles, webFiles }); - core.setOutput("backendChanged", backendFiles.length > 0) - core.setOutput("mobileChanged", mobileFiles.length > 0) - core.setOutput("webChanged", webFiles.length > 0) + core.setOutput('backendFiles', strippedBackend.join(' ')); + core.setOutput('mobileFiles', strippedMobile.join(' ')); + core.setOutput('webFiles', webFiles.map(f => f.replace('apps/web/', '')).join(' ')); + core.setOutput('backendTestFiles', deriveTestFiles(strippedBackend).join(' ')); + core.setOutput('mobileTestFiles', deriveTestFiles(strippedMobile).join(' ')); + core.setOutput('backendChanged', backendFiles.length > 0); + core.setOutput('mobileChanged', mobileFiles.length > 0); + core.setOutput('webChanged', webFiles.length > 0); } catch (error) { console.error(error); diff --git a/.github/scripts/commentResults.js b/.github/scripts/commentResults.js index 50cd1395..a1a96089 100644 --- a/.github/scripts/commentResults.js +++ b/.github/scripts/commentResults.js @@ -10,61 +10,60 @@ module.exports = async ({ mobileLint, mobileTest, webCheck, - webBuild + webBuild, + backendLintOutput, + mobileLintOutput, }) => { const owner = context.repo.owner; const repo = context.repo.repo; const prNumber = context.payload.pull_request.number; - const emoji = (status) => { - if (status === 'success') return '✅'; - if (status === 'failure') return '❌'; - if (status === 'skipped') return '⏭️'; - return '⚪'; + const status = (s) => { + if (s === 'success') return 'PASS'; + if (s === 'failure') return 'FAIL'; + if (s === 'skipped') return 'SKIP'; + return '-'; }; - const label = (status) => { - if (!status) return '⚪ unknown'; - return `${emoji(status)} ${status}`; + const lintDetails = (output) => { + if (!output || !output.trim()) return ''; + return `\n
\nView lint errors\n\n\`\`\`\n${output.trim()}\n\`\`\`\n
`; }; - const anyFailure = [ - backend, - mobile, - web - ].includes('failure'); - - const title = anyFailure - ? '❌ Some checks failed' - : '✅ CI completed'; - + const anyFailure = [backend, mobile, web].includes('failure'); + const title = anyFailure ? 'CI — Checks Failed' : 'CI — All Checks Passed'; const timestamp = new Date().toUTCString(); - const body = `## CI Results — ${title} + const body = `## ${title} + +### Backend — ${status(backend)} -### 🖥️ Backend (${label(backend)}) -| Check | Status | +| Check | Result | |---|---| -| Lint | ${label(backendLint)} | -| Test | ${label(backendTest)} | -| Typecheck | ${label(backendTypecheck)} | +| Lint | ${status(backendLint)} | +| Test | ${status(backendTest)} | +| Typecheck | ${status(backendTypecheck)} | +${backendLint === 'failure' ? lintDetails(backendLintOutput) : ''} -### 📱 Mobile (${label(mobile)}) -| Check | Status | +### Mobile — ${status(mobile)} + +| Check | Result | |---|---| -| Lint | ${label(mobileLint)} | -| Test | ${label(mobileTest)} | +| Lint | ${status(mobileLint)} | +| Test | ${status(mobileTest)} | +${mobileLint === 'failure' ? lintDetails(mobileLintOutput) : ''} + +### Web — ${status(web)} -### 🌐 Web (${label(web)}) -| Check | Status | +| Check | Result | |---|---| -| Check | ${label(webCheck)} | -| Build | ${label(webBuild)} | +| Check | ${status(webCheck)} | +| Build | ${status(webBuild)} | --- -🕐 Last updated: \`${timestamp}\``; +Last updated: \`${timestamp}\``; - const COMMENT_MARKER = '## CI Results —'; + const COMMENT_MARKER = '## CI —'; try { const comments = await github.paginate( diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25f71019..fbb952e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,15 +12,20 @@ jobs: runs-on: ubuntu-latest outputs: - backendChanged: ${{ steps.detect.outputs.backendChanged }} - mobileChanged: ${{ steps.detect.outputs.mobileChanged }} - webChanged: ${{ steps.detect.outputs.webChanged }} - backendFiles: ${{ steps.detect.outputs.backendFiles }} - mobileFiles: ${{ steps.detect.outputs.mobileFiles }} - webFiles: ${{ steps.detect.outputs.webFiles }} + backendChanged: ${{ steps.detect.outputs.backendChanged }} + mobileChanged: ${{ steps.detect.outputs.mobileChanged }} + webChanged: ${{ steps.detect.outputs.webChanged }} + backendFiles: ${{ steps.detect.outputs.backendFiles }} + mobileFiles: ${{ steps.detect.outputs.mobileFiles }} + webFiles: ${{ steps.detect.outputs.webFiles }} + backendTestFiles: ${{ steps.detect.outputs.backendTestFiles }} + mobileTestFiles: ${{ steps.detect.outputs.mobileTestFiles }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Detect changed files id: detect @@ -36,8 +41,15 @@ jobs: if: needs.detect-changes.outputs.backendChanged == 'true' runs-on: ubuntu-latest + outputs: + lint_result: ${{ steps.backend_lint.outcome }} + test_result: ${{ steps.backend_test.outcome }} + typecheck_result: ${{ steps.backend_typecheck.outcome }} + steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ github.event.pull_request.head.sha }} - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e with: @@ -49,23 +61,40 @@ jobs: - name: Backend lint id: backend_lint + continue-on-error: true run: cd apps/backend && pnpm eslint ${{ needs.detect-changes.outputs.backendFiles }} - name: Backend test id: backend_test - run: cd apps/backend && pnpm test ${{ needs.detect-changes.outputs.backendFiles }} + if: needs.detect-changes.outputs.backendTestFiles != '' + continue-on-error: true + run: cd apps/backend && pnpm test --passWithNoTests ${{ needs.detect-changes.outputs.backendTestFiles }} - name: Backend typecheck id: backend_typecheck - run: cd apps/backend && pnpm typecheck ${{ needs.detect-changes.outputs.backendFiles }} + continue-on-error: true + run: cd apps/backend && pnpm typecheck + + - name: Fail job if any check failed + if: > + steps.backend_lint.outcome == 'failure' || + steps.backend_test.outcome == 'failure' || + steps.backend_typecheck.outcome == 'failure' + run: exit 1 web-ci: needs: detect-changes if: needs.detect-changes.outputs.webChanged == 'true' runs-on: ubuntu-latest + outputs: + check_result: ${{ steps.web_check.outcome }} + build_result: ${{ steps.web_build.outcome }} + steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ github.event.pull_request.head.sha }} - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e with: @@ -77,19 +106,33 @@ jobs: - name: Web check id: web_check + continue-on-error: true run: cd apps/web && pnpm check - name: Web build id: web_build + continue-on-error: true run: cd apps/web && pnpm build + - name: Fail job if any check failed + if: > + steps.web_check.outcome == 'failure' || + steps.web_build.outcome == 'failure' + run: exit 1 + mobile-ci: needs: detect-changes if: needs.detect-changes.outputs.mobileChanged == 'true' runs-on: ubuntu-latest + outputs: + lint_result: ${{ steps.mobile_lint.outcome }} + test_result: ${{ steps.mobile_test.outcome }} + steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + ref: ${{ github.event.pull_request.head.sha }} - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e with: @@ -101,11 +144,20 @@ jobs: - name: Mobile lint id: mobile_lint + continue-on-error: true run: cd apps/mobile && pnpm eslint ${{ needs.detect-changes.outputs.mobileFiles }} - name: Mobile test id: mobile_test - run: cd apps/mobile && pnpm test + if: needs.detect-changes.outputs.mobileTestFiles != '' + continue-on-error: true + run: cd apps/mobile && pnpm test --passWithNoTests ${{ needs.detect-changes.outputs.mobileTestFiles }} + + - name: Fail job if any check failed + if: > + steps.mobile_lint.outcome == 'failure' || + steps.mobile_test.outcome == 'failure' + run: exit 1 comment-results: needs: @@ -124,11 +176,17 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const script = require('./.github/scripts/commentResults.js'); - await script({ github, context, - backend: '${{ needs.backend-ci.result }}', - web: '${{ needs.web-ci.result }}', - mobile: '${{ needs.mobile-ci.result }}' + backend: '${{ needs.backend-ci.result }}', + web: '${{ needs.web-ci.result }}', + mobile: '${{ needs.mobile-ci.result }}', + backendLint: '${{ needs.backend-ci.outputs.lint_result }}', + backendTest: '${{ needs.backend-ci.outputs.test_result }}', + backendTypecheck: '${{ needs.backend-ci.outputs.typecheck_result }}', + webCheck: '${{ needs.web-ci.outputs.check_result }}', + webBuild: '${{ needs.web-ci.outputs.build_result }}', + mobileLint: '${{ needs.mobile-ci.outputs.lint_result }}', + mobileTest: '${{ needs.mobile-ci.outputs.test_result }}', }); \ No newline at end of file