diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml new file mode 100644 index 00000000..bf912b57 --- /dev/null +++ b/.github/workflows/pr-checks.yml @@ -0,0 +1,47 @@ +name: PR Checks + +on: + pull_request: + branches: [dev, main] + +jobs: + link-audit: + name: Link audit & redirect enforcement + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # full history needed for git diff + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Fetch @futureagi/chat-widget + run: | + git clone --depth 1 \ + https://x-access-token:${{ secrets.GH_PAT }}@github.com/future-agi/landing-page.git .landing-tmp + cp -r .landing-tmp/docs-agent/packages/chat-widget ./chat-widget + rm -rf .landing-tmp + + - name: Patch chat-widget dependency + run: | + sed -i 's|"@futureagi/chat-widget": "workspace:\*"|"@futureagi/chat-widget": "file:./chat-widget"|' package.json + + - name: Cache npm dependencies + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json', 'package.json') }} + restore-keys: ${{ runner.os }}-npm- + + - name: Install dependencies + run: npm install + + - name: Check broken nav & content links + run: node scripts/audit-links.mjs + + - name: Check deleted pages have redirects + run: node scripts/check-deleted-pages.mjs ${{ github.base_ref }} diff --git a/scripts/check-deleted-pages.mjs b/scripts/check-deleted-pages.mjs new file mode 100644 index 00000000..5361750d --- /dev/null +++ b/scripts/check-deleted-pages.mjs @@ -0,0 +1,79 @@ +/** + * Checks that every MDX page deleted in this branch has a corresponding + * entry in src/lib/redirects.ts. Fails with exit code 1 if any are missing. + * + * Usage: node scripts/check-deleted-pages.mjs [base-branch] + * Default base branch: dev + */ +import { execSync } from 'child_process'; +import { readFileSync } from 'fs'; + +const baseBranch = process.argv[2] || 'dev'; + +// Get deleted MDX files compared to base branch +let deletedFiles; +try { + const output = execSync( + `git diff origin/${baseBranch}...HEAD --name-only --diff-filter=D`, + { encoding: 'utf-8' } + ); + deletedFiles = output.trim().split('\n').filter(Boolean); +} catch { + // If origin/base doesn't exist, try without origin/ + try { + const output = execSync( + `git diff ${baseBranch}...HEAD --name-only --diff-filter=D`, + { encoding: 'utf-8' } + ); + deletedFiles = output.trim().split('\n').filter(Boolean); + } catch { + console.error('Could not determine deleted files — failing to surface the error.'); + process.exit(1); + } +} + +// Filter to only MDX pages under src/pages/ +const deletedPages = deletedFiles.filter(f => f.startsWith('src/pages/') && f.endsWith('.mdx')); + +if (deletedPages.length === 0) { + console.log('No MDX pages deleted in this branch. ✓'); + process.exit(0); +} + +// Convert file path to URL path +function fileToPath(file) { + return file + .replace(/^src\/pages/, '') + .replace(/\.mdx$/, '') + .replace(/\/index$/, '') || '/'; +} + +// Load redirects map +const redirectsRaw = readFileSync('src/lib/redirects.ts', 'utf-8'); +const redirectEntries = [...redirectsRaw.matchAll(/["']([^"']+)["']:\s*["']([^"']+)["']/g)]; +const redirectMap = new Set(redirectEntries.map(([, from]) => from)); + +// Check each deleted page +const missing = []; +for (const file of deletedPages) { + const urlPath = fileToPath(file); + if (!redirectMap.has(urlPath)) { + missing.push({ file, urlPath }); + } +} + +if (missing.length === 0) { + console.log(`All ${deletedPages.length} deleted page(s) have redirects. ✓`); + process.exit(0); +} + +console.error(`\n✗ ${missing.length} deleted page(s) have no redirect in src/lib/redirects.ts:\n`); +for (const { file, urlPath } of missing) { + console.error(` ${urlPath}`); + console.error(` (deleted file: ${file})`); +} +console.error(` +To fix: add an entry to src/lib/redirects.ts for each path above, pointing to the closest current page. +Example: '${missing[0].urlPath}': '/docs/some-current-page', +`); +process.exit(1); diff --git a/src/components/AiChatWidget.astro b/src/components/AiChatWidget.astro index 544b518a..0ca8ec5c 100644 --- a/src/components/AiChatWidget.astro +++ b/src/components/AiChatWidget.astro @@ -88,7 +88,7 @@ const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY || ''; -

FutureAGI AI Assistant

+

FutureAGI AI Assistant

Ask me anything about the FutureAGI platform — I can search across all docs instantly.

diff --git a/src/components/GiscusComments.tsx b/src/components/GiscusComments.tsx index 2057f50a..20ec904c 100644 --- a/src/components/GiscusComments.tsx +++ b/src/components/GiscusComments.tsx @@ -28,7 +28,7 @@ export default function GiscusComments({ pagePath }: { pagePath: string }) { return (
-

Questions & Discussion

+

Questions & Discussion

); diff --git a/src/components/TableOfContents.astro b/src/components/TableOfContents.astro index 1c1cbfad..21780783 100644 --- a/src/components/TableOfContents.astro +++ b/src/components/TableOfContents.astro @@ -22,9 +22,9 @@ const feedbackUrl = `https://github.com/${GITHUB_REPO}/issues/new?title=${encode {toc.length > 0 && (
-

+

On this page -

+