Files
gh-allex-znews-cc-workflow-…/hooks/post-docs-check.js
2025-11-29 17:52:09 +08:00

182 lines
5.2 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* ドキュメント作成後のチェックフック
*
* このフックは /create-docs コマンド実行後に自動的に実行され、
* 作成されたドキュメントの完全性と品質をチェックします。
*/
const fs = require('fs');
const path = require('path');
// ドキュメントの必須セクション
const REQUIRED_SECTIONS = [
'概要',
'機能要件',
'技術仕様',
'非機能要件',
'テスト',
];
// ドキュメントファイルを探す
function findDocFiles() {
const possiblePaths = [
'docs/features',
'docs/specs',
'docs',
'.',
];
const docFiles = [];
for (const dir of possiblePaths) {
if (!fs.existsSync(dir)) continue;
const files = fs.readdirSync(dir)
.filter(f => f.endsWith('.md') && !f.startsWith('README'))
.map(f => path.join(dir, f));
docFiles.push(...files);
}
return docFiles;
}
// ドキュメントの内容をチェック
function checkDocument(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
const issues = [];
// 必須セクションのチェック
const missingSection = REQUIRED_SECTIONS.filter(section => {
const regex = new RegExp(`##?\\s+${section}`, 'i');
return !regex.test(content);
});
if (missingSections.length > 0) {
issues.push({
type: 'missing_sections',
message: `必須セクションが不足しています: ${missingSections.join(', ')}`,
});
}
// 最小文字数チェック(簡易的な品質チェック)
if (content.length < 500) {
issues.push({
type: 'too_short',
message: 'ドキュメントが短すぎます。より詳細な説明が必要です。',
});
}
// TODOやFIXMEが残っていないかチェック
const todoMatches = content.match(/TODO|FIXME|XXX/g);
if (todoMatches && todoMatches.length > 0) {
issues.push({
type: 'incomplete',
message: `未完了の項目が ${todoMatches.length} 件ありますTODO/FIXME`,
});
}
// コードブロックがあるかチェック(技術仕様には必要)
if (!content.includes('```')) {
issues.push({
type: 'no_code_examples',
message: 'コード例やデータ構造の記載がありません',
severity: 'warning',
});
}
return {
filePath,
issues,
passed: issues.filter(i => i.severity !== 'warning').length === 0,
};
}
// メイン処理
function main() {
console.log('📄 ドキュメントの品質チェックを実行中...\n');
const docFiles = findDocFiles();
if (docFiles.length === 0) {
console.error('❌ ドキュメントファイルが見つかりません');
console.error(' 以下のディレクトリにMarkdownファイルを作成してください:');
console.error(' - docs/features/');
console.error(' - docs/specs/');
process.exit(1);
}
console.log(`検出されたドキュメント: ${docFiles.length}\n`);
const results = docFiles.map(checkDocument);
const failedDocs = results.filter(r => !r.passed);
// 結果の表示
results.forEach(result => {
if (result.passed) {
console.log(`${result.filePath}`);
} else {
console.log(`${result.filePath}`);
result.issues.forEach(issue => {
const icon = issue.severity === 'warning' ? '⚠️' : '❌';
console.log(` ${icon} ${issue.message}`);
});
}
console.log();
});
// サマリー
console.log('─'.repeat(50));
console.log(`合計: ${results.length}`);
console.log(`成功: ${results.length - failedDocs.length}`);
console.log(`失敗: ${failedDocs.length}`);
if (failedDocs.length > 0) {
console.log('\n⚠ ドキュメントに問題があります。修正してから次のステップに進んでください。');
process.exit(1);
}
console.log('\n✅ ドキュメントの品質チェックが完了しました!');
console.log(' 次のステップ: /create-tests を実行してテストケースを作成');
// タスク状態の自動更新
updateTaskStatus(2, 'completed');
}
// タスク状態を更新する関数
function updateTaskStatus(taskId, status) {
const tasksFile = '.tasks.json';
if (!fs.existsSync(tasksFile)) {
console.log('\n⚠ タスクファイルが見つかりません。タスク状態は更新されませんでした。');
return;
}
try {
const data = JSON.parse(fs.readFileSync(tasksFile, 'utf-8'));
const task = data.tasks.find(t => t.id === taskId);
if (!task) {
console.log(`\n⚠️ タスク #${taskId} が見つかりません。`);
return;
}
task.status = status;
task.completedAt = new Date().toISOString();
data.updatedAt = new Date().toISOString();
fs.writeFileSync(tasksFile, JSON.stringify(data, null, 2));
console.log(`\n📋 タスク #${taskId} (${task.name}) を完了としてマークしました`);
} catch (error) {
console.log(`\n⚠️ タスク状態の更新に失敗しました: ${error.message}`);
}
}
// エラーハンドリング
try {
main();
} catch (error) {
console.error('❌ チェック中にエラーが発生しました:', error.message);
process.exit(1);
}