github-mcp-auto-git
Version:
GitHub MCP Auto Git v3.0 - メモリ効率化・統合MCP・モジュール化完了の完全自動Git操作システム
493 lines • 19.9 kB
JavaScript
/**
* Fail Fast Checker
* エラーハンドリング、早期バリデーション、異常状態検出
*/
import { promises as fs } from 'fs';
import { join } from 'path';
export class FailFastChecker {
constructor(workingDir, config) {
this.workingDir = workingDir;
this.config = config;
}
/**
* Fail Fast原則の包括的チェック
*/
async performFullCheck(context) {
if (!this.config.enabled) {
return this.getDisabledResult();
}
try {
console.log('⚡ Fail Fast Checker 実行中...');
// 並列でチェック実行
const [validation, anomalies, coverage, boundaries] = await Promise.all([
this.validateEarly(context?.operation || 'general', context),
this.detectAnomalies(await this.getSystemState()),
this.verifyErrorHandling(context?.files || []),
this.validateSecurityBoundaries()
]);
const violations = this.analyzeResults(validation, anomalies, coverage, boundaries);
const score = this.calculateFailFastScore(validation, anomalies, coverage, boundaries);
const recommendations = this.generateRecommendations(violations);
console.log(`✅ Fail Fast チェック完了 (スコア: ${score}/100)`);
return {
score,
violations,
recommendations,
details: {
validation,
anomalies,
coverage,
boundaries
}
};
}
catch (error) {
console.error('❌ Fail Fast Checker エラー:', error);
return this.getErrorResult(error);
}
}
/**
* 早期バリデーション
*/
async validateEarly(operation, input) {
const startTime = Date.now();
const errors = [];
const warnings = [];
try {
// 入力値の基本検証
if (!operation) {
errors.push({
code: 'MISSING_OPERATION',
message: '操作名が必要です',
severity: 'high'
});
}
// ファイルパス検証
if (input?.files) {
for (const file of input.files) {
if (!file || typeof file !== 'string') {
errors.push({
code: 'INVALID_FILE_PATH',
message: `無効なファイルパス: ${file}`,
severity: 'medium'
});
}
else if (file.includes('..')) {
errors.push({
code: 'PATH_TRAVERSAL_RISK',
message: `パストラバーサル攻撃のリスク: ${file}`,
filePath: file,
severity: 'critical'
});
}
}
}
// メタデータ検証
if (input?.metadata) {
if (typeof input.metadata !== 'object') {
errors.push({
code: 'INVALID_METADATA',
message: 'メタデータはオブジェクトである必要があります',
severity: 'medium'
});
}
}
// 環境変数検証
if (operation.includes('github') && !process.env.GITHUB_TOKEN) {
warnings.push('GITHUB_TOKENが設定されていません - GitHub操作が失敗する可能性があります');
}
return {
isValid: errors.length === 0,
errors,
warnings,
executionTime: Date.now() - startTime
};
}
catch (error) {
errors.push({
code: 'VALIDATION_ERROR',
message: `バリデーション失敗: ${error instanceof Error ? error.message : '不明なエラー'}`,
severity: 'critical'
});
return {
isValid: false,
errors,
warnings,
executionTime: Date.now() - startTime
};
}
}
/**
* 異常状態検出
*/
async detectAnomalies(systemState) {
const anomalies = [];
// メモリ使用量チェック
if (systemState.memory.percentage > 90) {
anomalies.push({
type: 'memory',
description: `高メモリ使用量: ${systemState.memory.percentage}%`,
severity: 'critical',
detectedAt: new Date(),
context: { memoryUsage: systemState.memory }
});
}
else if (systemState.memory.percentage > 75) {
anomalies.push({
type: 'memory',
description: `メモリ使用量上昇: ${systemState.memory.percentage}%`,
severity: 'medium',
detectedAt: new Date(),
context: { memoryUsage: systemState.memory }
});
}
// CPU使用量チェック
if (systemState.cpu.usage > 95) {
anomalies.push({
type: 'performance',
description: `非常に高いCPU使用量: ${systemState.cpu.usage}%`,
severity: 'critical',
detectedAt: new Date(),
context: { cpuUsage: systemState.cpu }
});
}
// エラー頻度チェック
if (systemState.errors.recent > 10) {
anomalies.push({
type: 'error',
description: `高エラー頻度: ${systemState.errors.recent}件の最近のエラー`,
severity: 'high',
detectedAt: new Date(),
context: { errorStats: systemState.errors }
});
}
if (systemState.errors.critical > 0) {
anomalies.push({
type: 'error',
description: `クリティカルエラー検出: ${systemState.errors.critical}件`,
severity: 'critical',
detectedAt: new Date(),
context: { errorStats: systemState.errors }
});
}
// パフォーマンス異常チェック
if (systemState.performance.averageResponseTime > 5000) {
anomalies.push({
type: 'performance',
description: `応答時間遅延: ${systemState.performance.averageResponseTime}ms`,
severity: 'medium',
detectedAt: new Date(),
context: { performance: systemState.performance }
});
}
const systemHealth = anomalies.some(a => a.severity === 'critical') ? 'critical' :
anomalies.some(a => a.severity === 'high') ? 'warning' : 'healthy';
const recommendations = anomalies.map(anomaly => {
switch (anomaly.type) {
case 'memory':
return 'メモリ割り当てを増やすか、メモリ使用量を最適化することを検討してください';
case 'performance':
return 'パフォーマンスボトルネックを調査し、クリティカルパスを最適化してください';
case 'error':
return '繰り返し発生するエラーを確認・修正して、システム安定性を向上させてください';
default:
return 'システムを注意深く監視し、予防措置を検討してください';
}
});
return {
anomalies,
systemHealth,
recommendations: [...new Set(recommendations)]
};
}
/**
* エラーハンドリング完全性の検証
*/
async verifyErrorHandling(files) {
let totalFunctions = 0;
let errorHandledFunctions = 0;
const missingErrorHandling = [];
try {
// TypeScriptファイルを対象に分析
const tsFiles = files.filter(f => f.endsWith('.ts') || f.endsWith('.tsx'));
for (const file of tsFiles) {
try {
const fullPath = join(this.workingDir, file);
const content = await fs.readFile(fullPath, 'utf-8');
// 関数定義を検索
const functionMatches = content.match(/(async\s+)?function\s+\w+|(\w+\s*:\s*)?async\s*\([^)]*\)\s*=>|(async\s+)?\w+\s*\([^)]*\)\s*\{/g) || [];
totalFunctions += functionMatches.length;
// try-catch文の存在をチェック
const tryCatchMatches = content.match(/try\s*\{[^}]*\}\s*catch/g) || [];
const errorHandlingMatches = content.match(/\.catch\(|catch\s*\(|throw\s+/g) || [];
const hasErrorHandling = tryCatchMatches.length > 0 || errorHandlingMatches.length > 0;
if (hasErrorHandling) {
errorHandledFunctions += Math.min(functionMatches.length, tryCatchMatches.length + errorHandlingMatches.length);
}
else if (functionMatches.length > 0) {
missingErrorHandling.push(file);
}
}
catch (error) {
missingErrorHandling.push(`${file} (読み込みエラー)`);
}
}
const coveragePercentage = totalFunctions > 0 ? Math.round((errorHandledFunctions / totalFunctions) * 100) : 100;
const recommendations = [
...(coveragePercentage < 80 ? ['エラーハンドリングの網羅性を向上させてください'] : []),
...(missingErrorHandling.length > 0 ? ['以下のファイルにエラーハンドリングを追加してください'] : [])
];
return {
module: 'error-handling',
totalFunctions,
errorHandledFunctions,
coveragePercentage,
missingErrorHandling,
recommendations
};
}
catch (error) {
return {
module: 'error-handling',
totalFunctions: 0,
errorHandledFunctions: 0,
coveragePercentage: 0,
missingErrorHandling: ['分析エラーが発生しました'],
recommendations: ['エラーハンドリング分析機能を確認してください']
};
}
}
/**
* セキュリティ境界の検証
*/
async validateSecurityBoundaries() {
const boundaries = [];
const violations = [];
// 入力検証境界
boundaries.push({
name: 'user-input',
type: 'input',
validated: true, // 既存のSecurityManagerで実装済み
sanitized: true
});
// ファイルアクセス境界
boundaries.push({
name: 'file-access',
type: 'file',
validated: true,
sanitized: true
});
// API境界
boundaries.push({
name: 'github-api',
type: 'api',
validated: !!process.env.GITHUB_TOKEN,
sanitized: true
});
// ネットワーク境界
boundaries.push({
name: 'network',
type: 'network',
validated: true,
sanitized: true
});
// 出力境界
boundaries.push({
name: 'output',
type: 'output',
validated: true,
sanitized: true
});
// 境界違反のチェック
for (const boundary of boundaries) {
if (!boundary.validated) {
violations.push({
boundary: boundary.name,
issue: 'バリデーションが実装されていません',
severity: 'high',
recommendation: `${boundary.name}境界のバリデーションを実装してください`
});
}
if (!boundary.sanitized) {
violations.push({
boundary: boundary.name,
issue: 'サニタイゼーションが実装されていません',
severity: 'medium',
recommendation: `${boundary.name}境界のサニタイゼーションを実装してください`
});
}
}
const score = Math.round(((boundaries.length - violations.length) / boundaries.length) * 100);
return {
boundaries,
violations,
score
};
}
/**
* Fail Fastスコア計算
*/
calculateFailFastScore(validation, anomalies, coverage, boundaries) {
// 各要素の重み付きスコア計算
const validationScore = validation.isValid ? 100 : Math.max(0, 100 - (validation.errors.length * 20));
const anomalyScore = anomalies.systemHealth === 'healthy' ? 100 :
anomalies.systemHealth === 'warning' ? 70 : 40;
const coverageScore = coverage.coveragePercentage;
const boundaryScore = boundaries.score;
// 重み付き平均(バリデーション30%、異常検出20%、カバレッジ30%、境界20%)
return Math.round(validationScore * 0.3 +
anomalyScore * 0.2 +
coverageScore * 0.3 +
boundaryScore * 0.2);
}
/**
* 結果分析と違反抽出
*/
analyzeResults(validation, anomalies, coverage, boundaries) {
const violations = [];
// バリデーションエラーを違反に変換
validation.errors.forEach((error, index) => {
violations.push({
id: `validation-${index}`,
principle: 'fail-fast',
severity: error.severity,
description: error.message,
filePath: error.filePath,
lineNumber: error.lineNumber,
autoFixable: error.severity !== 'critical',
recommendation: `Fix validation error: ${error.code}`
});
});
// 異常状態を違反に変換
anomalies.anomalies.forEach((anomaly, index) => {
violations.push({
id: `anomaly-${index}`,
principle: 'fail-fast',
severity: anomaly.severity,
description: anomaly.description,
autoFixable: false,
recommendation: `Address system anomaly: ${anomaly.type}`
});
});
// カバレッジ不足を違反に変換
if (coverage.coveragePercentage < 80) {
violations.push({
id: 'coverage-insufficient',
principle: 'fail-fast',
severity: coverage.coveragePercentage < 50 ? 'high' : 'medium',
description: `Error handling coverage is ${coverage.coveragePercentage}% (target: 80%+)`,
autoFixable: false,
recommendation: 'Improve error handling coverage in missing functions'
});
}
// 境界違反を変換
boundaries.violations.forEach((violation, index) => {
violations.push({
id: `boundary-${index}`,
principle: 'fail-fast',
severity: violation.severity,
description: `Security boundary violation: ${violation.issue}`,
autoFixable: violation.severity !== 'critical',
recommendation: violation.recommendation
});
});
return violations;
}
/**
* 推奨事項生成
*/
generateRecommendations(violations) {
const recommendations = new Set();
violations.forEach(violation => {
recommendations.add(violation.recommendation);
});
// 一般的な推奨事項
if (violations.some(v => v.severity === 'critical')) {
recommendations.add('Critical issues detected - immediate attention required');
}
if (violations.length > 10) {
recommendations.add('Consider systematic improvement of error handling practices');
}
if (violations.some(v => v.principle === 'fail-fast' && v.description.includes('validation'))) {
recommendations.add('Implement comprehensive input validation for all external inputs');
}
return Array.from(recommendations);
}
/**
* システム状態取得
*/
async getSystemState() {
const process = await import('process');
const memUsage = process.memoryUsage();
return {
memory: {
used: Math.round(memUsage.heapUsed / 1024 / 1024),
total: Math.round(memUsage.heapTotal / 1024 / 1024),
percentage: Math.round((memUsage.heapUsed / memUsage.heapTotal) * 100)
},
cpu: {
usage: Math.round(process.cpuUsage().user / 1000),
processes: 1
},
disk: {
used: 0,
total: 0,
percentage: 0
},
errors: {
recent: 0, // ErrorRecoverySystemから取得する場合は要修正
critical: 0
},
performance: {
averageResponseTime: 0,
throughput: 0
}
};
}
/**
* 設定更新
*/
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
}
/**
* 無効時の結果
*/
getDisabledResult() {
return {
score: 0,
violations: [],
recommendations: ['Fail Fast checker is disabled'],
details: {
validation: { isValid: true, errors: [], warnings: [], executionTime: 0 },
anomalies: { anomalies: [], systemHealth: 'healthy', recommendations: [] },
coverage: { module: 'disabled', totalFunctions: 0, errorHandledFunctions: 0, coveragePercentage: 0, missingErrorHandling: [], recommendations: [] },
boundaries: { boundaries: [], violations: [], score: 0 }
}
};
}
/**
* エラー時の結果
*/
getErrorResult(error) {
return {
score: 0,
violations: [{
id: 'checker-error',
principle: 'fail-fast',
severity: 'critical',
description: `Fail Fast checker error: ${error instanceof Error ? error.message : 'Unknown error'}`,
autoFixable: false,
recommendation: 'Check Fail Fast checker configuration and dependencies'
}],
recommendations: ['Fix Fail Fast checker error'],
details: {
validation: { isValid: false, errors: [], warnings: [], executionTime: 0 },
anomalies: { anomalies: [], systemHealth: 'critical', recommendations: [] },
coverage: { module: 'error', totalFunctions: 0, errorHandledFunctions: 0, coveragePercentage: 0, missingErrorHandling: [], recommendations: [] },
boundaries: { boundaries: [], violations: [], score: 0 }
}
};
}
}
//# sourceMappingURL=fail-fast-checker.js.map