feat: implement robust full-sitemap HTTP validation in smoke test phase
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 12s
Build & Deploy / 🧪 QA (push) Successful in 2m3s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / ♿ WCAG (push) Has been cancelled
Build & Deploy / 🛡️ Quality Gates (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 12s
Build & Deploy / 🧪 QA (push) Successful in 2m3s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / ♿ WCAG (push) Has been cancelled
Build & Deploy / 🛡️ Quality Gates (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
This commit is contained in:
@@ -396,6 +396,11 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
TEST_URL: ${{ needs.prepare.outputs.next_public_url }}
|
TEST_URL: ${{ needs.prepare.outputs.next_public_url }}
|
||||||
run: pnpm run check:og
|
run: pnpm run check:og
|
||||||
|
- name: 🌐 Run Full Sitemap HTTP Validation
|
||||||
|
env:
|
||||||
|
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_url }}
|
||||||
|
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || 'klz2026' }}
|
||||||
|
run: pnpm run check:http
|
||||||
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
# JOB 6: Lighthouse (Performance & Accessibility)
|
# JOB 6: Lighthouse (Performance & Accessibility)
|
||||||
|
|||||||
@@ -105,6 +105,7 @@
|
|||||||
"check:a11y": "pa11y-ci",
|
"check:a11y": "pa11y-ci",
|
||||||
"check:wcag": "tsx ./scripts/wcag-sitemap.ts",
|
"check:wcag": "tsx ./scripts/wcag-sitemap.ts",
|
||||||
"check:html": "tsx ./scripts/check-html.ts",
|
"check:html": "tsx ./scripts/check-html.ts",
|
||||||
|
"check:http": "tsx ./scripts/check-http.ts",
|
||||||
"check:spell": "cspell \"content/**/*.{md,mdx}\" \"app/**/*.tsx\" \"components/**/*.tsx\"",
|
"check:spell": "cspell \"content/**/*.{md,mdx}\" \"app/**/*.tsx\" \"components/**/*.tsx\"",
|
||||||
"check:security": "tsx ./scripts/check-security.ts",
|
"check:security": "tsx ./scripts/check-security.ts",
|
||||||
"check:links": "bash ./scripts/check-links.sh",
|
"check:links": "bash ./scripts/check-links.sh",
|
||||||
|
|||||||
74
scripts/check-http.ts
Normal file
74
scripts/check-http.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import * as cheerio from 'cheerio';
|
||||||
|
|
||||||
|
const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
|
||||||
|
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log(`\n🚀 Starting HTTP Sitemap Validation for: ${targetUrl}\n`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const sitemapUrl = `${targetUrl.replace(/\/$/, '')}/sitemap.xml`;
|
||||||
|
console.log(`📥 Fetching sitemap from ${sitemapUrl}...`);
|
||||||
|
|
||||||
|
const response = await axios.get(sitemapUrl, {
|
||||||
|
headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` },
|
||||||
|
validateStatus: (status) => status < 400,
|
||||||
|
});
|
||||||
|
|
||||||
|
const $ = cheerio.load(response.data, { xmlMode: true });
|
||||||
|
let urls = $('url loc')
|
||||||
|
.map((i, el) => $(el).text())
|
||||||
|
.get();
|
||||||
|
|
||||||
|
const urlPattern = /https?:\/\/[^\/]+/;
|
||||||
|
urls = [...new Set(urls)]
|
||||||
|
.filter((u) => u.startsWith('http'))
|
||||||
|
.map((u) => u.replace(urlPattern, targetUrl.replace(/\/$/, '')))
|
||||||
|
.sort();
|
||||||
|
|
||||||
|
console.log(`✅ Found ${urls.length} target URLs in sitemap.`);
|
||||||
|
|
||||||
|
if (urls.length === 0) {
|
||||||
|
console.error('❌ No URLs found in sitemap. Is the site up?');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n🔍 Verifying HTTP Status Codes (Limit: None)...`);
|
||||||
|
let hasErrors = false;
|
||||||
|
|
||||||
|
// Run fetches sequentially to avoid overwhelming the server during CI
|
||||||
|
for (let i = 0; i < urls.length; i++) {
|
||||||
|
const u = urls[i];
|
||||||
|
try {
|
||||||
|
const res = await axios.get(u, {
|
||||||
|
headers: { Cookie: `klz_gatekeeper_session=${gatekeeperPassword}` },
|
||||||
|
validateStatus: null, // Don't throw on error status
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status >= 400) {
|
||||||
|
console.error(`❌ ERROR ${res.status}: ${res.statusText} -> ${u}`);
|
||||||
|
hasErrors = true;
|
||||||
|
} else {
|
||||||
|
console.log(`✅ OK ${res.status} -> ${u}`);
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(`❌ NETWORK ERROR: ${err.message} -> ${u}`);
|
||||||
|
hasErrors = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasErrors) {
|
||||||
|
console.error(`\n❌ HTTP Sitemap Validation Failed. One or more pages returned an error.`);
|
||||||
|
process.exit(1);
|
||||||
|
} else {
|
||||||
|
console.log(`\n✨ Success: All ${urls.length} pages are healthy! (HTTP 200)`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(`\n❌ Critical Error during Sitemap Fetch:`, error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
Reference in New Issue
Block a user