veza/veza-docs/scripts/verify_integrity.ts

302 lines
9.4 KiB
TypeScript

#!/usr/bin/env node
import * as fs from 'fs';
import * as path from 'path';
import { glob } from 'glob';
interface VerificationResult {
check: string;
status: 'PASS' | 'FAIL' | 'WARN';
message: string;
details?: any;
}
class IntegrityVerifier {
private vezaDocsRoot: string;
private results: VerificationResult[] = [];
constructor(vezaDocsRoot: string) {
this.vezaDocsRoot = vezaDocsRoot;
}
async verifyAll(): Promise<void> {
console.log('🔍 Vérification de l\'intégrité de la documentation...');
await this.verifyBuild();
await this.verifySidebars();
await this.verifyLinks();
await this.verifySearch();
await this.verifySitemap();
this.generateReport();
}
private async verifyBuild(): Promise<void> {
console.log('📦 Vérification du build...');
try {
const buildDir = path.join(this.vezaDocsRoot, 'build');
if (fs.existsSync(buildDir)) {
this.results.push({
check: 'Build Docusaurus',
status: 'PASS',
message: 'Build réussi, dossier build/ existe'
});
} else {
this.results.push({
check: 'Build Docusaurus',
status: 'FAIL',
message: 'Build échoué, dossier build/ manquant'
});
}
} catch (error) {
this.results.push({
check: 'Build Docusaurus',
status: 'FAIL',
message: `Erreur lors de la vérification du build: ${error}`
});
}
}
private async verifySidebars(): Promise<void> {
console.log('📋 Vérification des sidebars...');
const tracks = ['current', 'vision'];
let allPagesReferenced = true;
const unreferencedPages: string[] = [];
for (const track of tracks) {
const sidebarPath = path.join(this.vezaDocsRoot, track, 'sidebars.js');
if (!fs.existsSync(sidebarPath)) {
this.results.push({
check: `Sidebar ${track}`,
status: 'FAIL',
message: `Fichier sidebars.js manquant pour ${track}`
});
continue;
}
// Lire le contenu du sidebar
const sidebarContent = fs.readFileSync(sidebarPath, 'utf-8');
// Trouver toutes les pages dans le track
const pagesPattern = `${track}/**/*.md`;
const pages = await glob(pagesPattern, { cwd: this.vezaDocsRoot });
// Vérifier que chaque page est référencée dans le sidebar
for (const page of pages) {
const pageId = path.basename(page, '.md');
if (!sidebarContent.includes(`'${pageId}'`) && !sidebarContent.includes(`"${pageId}"`)) {
unreferencedPages.push(page);
allPagesReferenced = false;
}
}
}
if (allPagesReferenced) {
this.results.push({
check: 'Couverture des sidebars',
status: 'PASS',
message: 'Toutes les pages sont référencées dans les sidebars'
});
} else {
this.results.push({
check: 'Couverture des sidebars',
status: 'WARN',
message: `${unreferencedPages.length} pages non référencées dans les sidebars`,
details: unreferencedPages
});
}
}
private async verifyLinks(): Promise<void> {
console.log('🔗 Vérification des liens...');
const buildDir = path.join(this.vezaDocsRoot, 'build');
if (!fs.existsSync(buildDir)) {
this.results.push({
check: 'Vérification des liens',
status: 'FAIL',
message: 'Impossible de vérifier les liens, build manquant'
});
return;
}
// Compter les fichiers HTML générés
const htmlFiles = await glob('**/*.html', { cwd: buildDir });
this.results.push({
check: 'Génération des pages',
status: 'PASS',
message: `${htmlFiles.length} pages HTML générées`,
details: { htmlFiles: htmlFiles.length }
});
// Vérifier la présence des pages principales
const mainPages = [
'current/overview/index.html',
'vision/overview/index.html'
];
const missingPages = mainPages.filter(page => !fs.existsSync(path.join(buildDir, page)));
if (missingPages.length === 0) {
this.results.push({
check: 'Pages principales',
status: 'PASS',
message: 'Toutes les pages principales sont présentes'
});
} else {
this.results.push({
check: 'Pages principales',
status: 'FAIL',
message: `Pages manquantes: ${missingPages.join(', ')}`
});
}
}
private async verifySearch(): Promise<void> {
console.log('🔍 Vérification de la recherche...');
const buildDir = path.join(this.vezaDocsRoot, 'build');
const searchIndexPath = path.join(buildDir, 'search-index.json');
if (fs.existsSync(searchIndexPath)) {
const searchIndex = JSON.parse(fs.readFileSync(searchIndexPath, 'utf-8'));
const indexedPages = searchIndex.length || 0;
this.results.push({
check: 'Index de recherche',
status: 'PASS',
message: `${indexedPages} pages indexées pour la recherche`,
details: { indexedPages }
});
} else {
this.results.push({
check: 'Index de recherche',
status: 'WARN',
message: 'Index de recherche manquant (recherche locale non configurée)'
});
}
}
private async verifySitemap(): Promise<void> {
console.log('🗺️ Vérification du sitemap...');
const buildDir = path.join(this.vezaDocsRoot, 'build');
const sitemapPath = path.join(buildDir, 'sitemap.xml');
if (fs.existsSync(sitemapPath)) {
const sitemapContent = fs.readFileSync(sitemapPath, 'utf-8');
const urlCount = (sitemapContent.match(/<url>/g) || []).length;
this.results.push({
check: 'Sitemap',
status: 'PASS',
message: `Sitemap généré avec ${urlCount} URLs`,
details: { urlCount }
});
} else {
this.results.push({
check: 'Sitemap',
status: 'WARN',
message: 'Sitemap manquant'
});
}
}
private generateReport(): void {
console.log('📊 Génération du rapport de vérification...');
const reportsDir = path.join(this.vezaDocsRoot, '_reports');
if (!fs.existsSync(reportsDir)) {
fs.mkdirSync(reportsDir, { recursive: true });
}
// Rapport Markdown
let md = `# 🔍 Rapport de Vérification - Documentation Veza\n\n`;
md += `**Date** : ${new Date().toLocaleDateString('fr-FR')}\n\n`;
// Résumé
const passCount = this.results.filter(r => r.status === 'PASS').length;
const failCount = this.results.filter(r => r.status === 'FAIL').length;
const warnCount = this.results.filter(r => r.status === 'WARN').length;
md += `## 📈 Résumé\n\n`;
md += `- ✅ **Succès** : ${passCount}\n`;
md += `- ❌ **Échecs** : ${failCount}\n`;
md += `- ⚠️ **Avertissements** : ${warnCount}\n\n`;
// Détail des vérifications
md += `## 🔍 Détail des Vérifications\n\n`;
md += `| Vérification | Statut | Message |\n`;
md += `|--------------|--------|----------|\n`;
this.results.forEach(result => {
const statusIcon = result.status === 'PASS' ? '✅' : result.status === 'FAIL' ? '❌' : '⚠️';
md += `| ${result.check} | ${statusIcon} ${result.status} | ${result.message} |\n`;
});
md += `\n## 📋 Critères de Prêt au Nettoyage\n\n`;
const isReadyForCleanup = failCount === 0 && warnCount <= 2;
md += `- **Build réussi** : ${this.results.find(r => r.check === 'Build Docusaurus')?.status === 'PASS' ? '✅' : '❌'}\n`;
md += `- **Liens fonctionnels** : ${this.results.find(r => r.check === 'Vérification des liens')?.status === 'PASS' ? '✅' : '❌'}\n`;
md += `- **Sidebars complets** : ${this.results.find(r => r.check === 'Couverture des sidebars')?.status === 'PASS' ? '✅' : '⚠️'}\n`;
md += `- **Pages principales** : ${this.results.find(r => r.check === 'Pages principales')?.status === 'PASS' ? '✅' : '❌'}\n\n`;
if (isReadyForCleanup) {
md += `## ✅ **PRÊT POUR LE NETTOYAGE**\n\n`;
md += `Tous les critères sont respectés. Le nettoyage des anciens dossiers peut être effectué en toute sécurité.\n`;
} else {
md += `## ❌ **NON PRÊT POUR LE NETTOYAGE**\n\n`;
md += `Des problèmes doivent être résolus avant de procéder au nettoyage.\n`;
}
fs.writeFileSync(
path.join(reportsDir, 'verification_summary.md'),
md
);
// Rapport JSON
const jsonReport = {
scanDate: new Date().toISOString(),
summary: {
pass: passCount,
fail: failCount,
warn: warnCount,
total: this.results.length
},
isReadyForCleanup,
results: this.results
};
fs.writeFileSync(
path.join(reportsDir, 'verification_summary.json'),
JSON.stringify(jsonReport, null, 2)
);
console.log('✅ Rapport de vérification généré');
console.log(`📊 Résumé: ${passCount} ✅, ${failCount} ❌, ${warnCount} ⚠️`);
console.log(`🧹 Prêt pour le nettoyage: ${isReadyForCleanup ? 'OUI' : 'NON'}`);
}
async run(): Promise<void> {
try {
await this.verifyAll();
} catch (error) {
console.error('❌ Erreur lors de la vérification:', error);
process.exit(1);
}
}
}
// Exécution du script
if (require.main === module) {
const vezaDocsRoot = process.argv[2] || path.join(__dirname, '..');
const verifier = new IntegrityVerifier(vezaDocsRoot);
verifier.run();
}
export { IntegrityVerifier };