Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
@@ -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 }}
79 changes: 79 additions & 0 deletions scripts/check-deleted-pages.mjs
Original file line number Diff line number Diff line change
@@ -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.log('Could not determine deleted files — skipping check.');
process.exit(0);
}
}

// 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);
2 changes: 1 addition & 1 deletion src/components/AiChatWidget.astro
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY || '';
<path d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
</svg>
</div>
<h3 class="text-sm font-semibold text-[var(--color-text-primary)] mb-1">FutureAGI AI Assistant</h3>
<p class="text-sm font-semibold text-[var(--color-text-primary)] mb-1">FutureAGI AI Assistant</p>
<p class="text-xs text-[var(--color-text-muted)] mb-4 leading-relaxed max-w-[240px]">
Ask me anything about the FutureAGI platform — I can search across all docs instantly.
</p>
Expand Down
2 changes: 1 addition & 1 deletion src/components/GiscusComments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default function GiscusComments({ pagePath }: { pagePath: string }) {

return (
<div>
<h3 className="text-lg font-semibold text-[var(--color-text-primary)] mb-4">Questions & Discussion</h3>
<p className="text-lg font-semibold text-[var(--color-text-primary)] mb-4">Questions & Discussion</p>
<div ref={ref} />
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions src/components/TableOfContents.astro
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ const feedbackUrl = `https://github.com/${GITHUB_REPO}/issues/new?title=${encode

{toc.length > 0 && (
<div class="space-y-4">
<h4 class="text-xs font-semibold uppercase tracking-wider text-[var(--color-text-tertiary)]">
<p class="text-xs font-semibold uppercase tracking-wider text-[var(--color-text-tertiary)]">
On this page
</h4>
</p>
<nav class="space-y-1">
{toc.map((heading) => (
<a
Expand Down
4 changes: 2 additions & 2 deletions src/components/docs/Card.astro
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const Tag = href ? 'a' : 'div';
</div>
)}

<h3 class:list={[
<p class:list={[
"font-semibold text-[var(--color-text-primary)] mb-2",
href && "group-hover:text-[var(--color-accent-primary)] transition-colors"
]}>
Expand All @@ -96,7 +96,7 @@ const Tag = href ? 'a' : 'div';
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
)}
</h3>
</p>

<div class="text-sm text-[var(--color-text-secondary)]">
<slot />
Expand Down
Loading
Loading