Skip to content

Commit 3ea78bb

Browse files
committed
fix(docs): add lychee link verification to build pipeline
- Default SITE_URL to localhost:4321 for local builds - Add verifyBuiltLinks() that starts local server and runs lychee - Pass BASE_PATH to remark plugin for proper link transformation - Production builds set SITE_URL via env var in CI
1 parent 5fbff6f commit 3ea78bb

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
lines changed

tools/build-docs.js

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ const archiver = require('archiver');
2121
const PROJECT_ROOT = path.dirname(__dirname);
2222
const BUILD_DIR = path.join(PROJECT_ROOT, 'build');
2323

24-
const SITE_URL = process.env.SITE_URL || 'https://bmad-code-org.github.io/BMAD-METHOD';
24+
// For local builds, use localhost. Production URL set via SITE_URL env var in CI.
25+
const SITE_URL = process.env.SITE_URL || 'http://localhost:4321';
2526
const REPO_URL = 'https://github.com/bmad-code-org/BMAD-METHOD';
2627

2728
const LLM_MAX_CHARS = 600_000;
@@ -108,15 +109,55 @@ function buildAstroSite() {
108109
runAstroBuild();
109110
copyArtifactsToSite(artifactsDir, siteDir);
110111

111-
// No longer needed: Inject AI agents banner into every HTML page
112-
// injectAgentBanner(siteDir);
112+
// Verify all links in built site
113+
verifyBuiltLinks(siteDir);
113114

114115
console.log();
115116
console.log(` \u001B[32m✓\u001B[0m Astro build complete`);
116117

117118
return siteDir;
118119
}
119120

121+
/**
122+
* Verify all internal links in the built site using lychee.
123+
* Starts a local server and crawls the site.
124+
*/
125+
function verifyBuiltLinks(siteDir) {
126+
console.log(' → Verifying links in built site...');
127+
128+
const { spawn } = require('node:child_process');
129+
const url = new URL(SITE_URL);
130+
const port = url.port || '4321';
131+
132+
// Start local server
133+
const serverProcess = spawn('npx', ['serve', siteDir, '-l', port, '--single'], {
134+
cwd: PROJECT_ROOT,
135+
stdio: 'pipe',
136+
detached: true,
137+
});
138+
139+
// Wait for server to start
140+
execSync('sleep 2');
141+
142+
try {
143+
execSync(`lychee 'http://localhost:${port}/' --no-progress`, {
144+
cwd: PROJECT_ROOT,
145+
stdio: 'inherit',
146+
});
147+
console.log(` \u001B[32m✓\u001B[0m Link verification passed`);
148+
} catch {
149+
console.error(`\n \u001B[31m✗\u001B[0m Link verification failed - fix broken links before deploying\n`);
150+
process.kill(-serverProcess.pid);
151+
process.exit(1);
152+
} finally {
153+
try {
154+
process.kill(-serverProcess.pid);
155+
} catch {
156+
// Server already stopped
157+
}
158+
}
159+
}
160+
120161
// =============================================================================
121162
// LLM File Generation
122163
/**
@@ -330,12 +371,17 @@ async function generatePromptsBundle(downloadsDir) {
330371
*/
331372
function runAstroBuild() {
332373
console.log(' → Running astro build...');
374+
// Extract base path from SITE_URL for remark plugin
375+
const url = new URL(SITE_URL);
376+
const basePath = url.pathname.replace(/\/$/, ''); // Strip trailing slash
377+
333378
execSync('npx astro build --root website', {
334379
cwd: PROJECT_ROOT,
335380
stdio: 'inherit',
336381
env: {
337382
...process.env,
338383
SITE_URL,
384+
BASE_PATH: basePath,
339385
NODE_OPTIONS: `${process.env.NODE_OPTIONS || ''} --disable-warning=MODULE_TYPELESS_PACKAGE_JSON`.trim(),
340386
},
341387
});

website/src/remark-markdown-links.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,23 @@
22
* Remark plugin to transform relative markdown links to absolute paths
33
*
44
* Uses the source file's path to resolve relative links correctly.
5+
* Prepends base path from import.meta.env.BASE_URL for proper deployment.
56
* Works with Astro's build.format: 'directory' setting.
67
*/
78

89
import { visit } from 'unist-util-visit';
910
import path from 'path';
1011

12+
// Get base path from environment, strip trailing slash for concatenation
13+
const basePath = (process.env.BASE_PATH || '').replace(/\/$/, '');
14+
1115
export default function remarkMarkdownLinks() {
1216
return (tree, file) => {
1317
const filePath = file.history?.[0] || file.path;
1418

1519
// Debug log
1620
if (!remarkMarkdownLinks._logged) {
17-
console.log('[remark-markdown-links] Plugin running, file:', filePath || 'NO PATH');
21+
console.log('[remark-markdown-links] Plugin running, basePath:', basePath || '(none)');
1822
remarkMarkdownLinks._logged = true;
1923
}
2024

@@ -55,7 +59,8 @@ export default function remarkMarkdownLinks() {
5559
resolved = '/' + resolved;
5660
}
5761

58-
node.url = resolved + suffix;
62+
// Prepend base path for deployment
63+
node.url = basePath + resolved + suffix;
5964
});
6065
};
6166
}

0 commit comments

Comments
 (0)