mcp-adr-analysis-server 2.0.12 โ 2.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +132 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/tools/deployment-readiness-tool.d.ts +23 -0
- package/dist/src/tools/deployment-readiness-tool.d.ts.map +1 -0
- package/dist/src/tools/deployment-readiness-tool.js +696 -0
- package/dist/src/tools/deployment-readiness-tool.js.map +1 -0
- package/dist/src/tools/smart-git-push-tool-v2.d.ts +14 -0
- package/dist/src/tools/smart-git-push-tool-v2.d.ts.map +1 -1
- package/dist/src/tools/smart-git-push-tool-v2.js +415 -20
- package/dist/src/tools/smart-git-push-tool-v2.js.map +1 -1
- package/dist/src/tools/todo-management-tool-v2.d.ts +39 -0
- package/dist/src/tools/todo-management-tool-v2.d.ts.map +1 -1
- package/dist/src/tools/todo-management-tool-v2.js +229 -5
- package/dist/src/tools/todo-management-tool-v2.js.map +1 -1
- package/dist/src/tools/troubleshoot-guided-workflow-tool.d.ts +4 -4
- package/package.json +1 -1
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment Readiness Tool - Version 1.0
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive deployment validation with test failure tracking, deployment history analysis,
|
|
5
|
+
* and hard blocking integration with smart git push.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT FOR AI ASSISTANTS: This tool provides:
|
|
8
|
+
* 1. Test Execution Validation: Zero tolerance for test failures
|
|
9
|
+
* 2. Deployment History Analysis: Pattern detection and success rate tracking
|
|
10
|
+
* 3. Code Quality Gates: Mock vs production code detection
|
|
11
|
+
* 4. Hard Blocking: Prevents unsafe deployments via smart git push integration
|
|
12
|
+
*
|
|
13
|
+
* Cache Dependencies:
|
|
14
|
+
* - CREATES/UPDATES: .mcp-adr-cache/deployment-history.json (deployment tracking)
|
|
15
|
+
* - CREATES/UPDATES: .mcp-adr-cache/deployment-readiness-cache.json (analysis cache)
|
|
16
|
+
* - INTEGRATES: smart-git-push-tool for deployment blocking
|
|
17
|
+
* - INTEGRATES: todo-management-tool for automatic task creation
|
|
18
|
+
*/
|
|
19
|
+
import { z } from 'zod';
|
|
20
|
+
import { McpAdrError } from '../types/index.js';
|
|
21
|
+
import { execSync } from 'child_process';
|
|
22
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
23
|
+
import { join } from 'path';
|
|
24
|
+
import { validateMcpResponse } from '../utils/mcp-response-validator.js';
|
|
25
|
+
import { jsonSafeError } from '../utils/json-safe.js';
|
|
26
|
+
// Core schemas
|
|
27
|
+
const DeploymentReadinessSchema = z.object({
|
|
28
|
+
operation: z.enum([
|
|
29
|
+
'check_readiness', // Full deployment readiness check
|
|
30
|
+
'validate_production', // Production-specific validation
|
|
31
|
+
'test_validation', // Test execution and failure analysis
|
|
32
|
+
'deployment_history', // Deployment history analysis
|
|
33
|
+
'full_audit', // Comprehensive audit (all checks)
|
|
34
|
+
'emergency_override' // Emergency bypass with justification
|
|
35
|
+
]).describe('Operation to perform'),
|
|
36
|
+
// Core Configuration
|
|
37
|
+
projectPath: z.string().optional().describe('Project root path'),
|
|
38
|
+
targetEnvironment: z.enum(['staging', 'production', 'integration']).default('production').describe('Target deployment environment'),
|
|
39
|
+
strictMode: z.boolean().default(true).describe('Enable strict validation (recommended)'),
|
|
40
|
+
// Code Quality Gates
|
|
41
|
+
allowMockCode: z.boolean().default(false).describe('Allow mock code in deployment (NOT RECOMMENDED)'),
|
|
42
|
+
productionCodeThreshold: z.number().default(85).describe('Minimum production code quality score (0-100)'),
|
|
43
|
+
mockCodeMaxAllowed: z.number().default(0).describe('Maximum mock code indicators allowed'),
|
|
44
|
+
// Test Failure Gates
|
|
45
|
+
maxTestFailures: z.number().default(0).describe('Maximum test failures allowed (0 = zero tolerance)'),
|
|
46
|
+
requireTestCoverage: z.number().default(80).describe('Minimum test coverage percentage required'),
|
|
47
|
+
blockOnFailingTests: z.boolean().default(true).describe('Block deployment if tests are failing'),
|
|
48
|
+
testSuiteRequired: z.array(z.string()).default([]).describe('Required test suites that must pass'),
|
|
49
|
+
// Deployment History Gates
|
|
50
|
+
maxRecentFailures: z.number().default(2).describe('Maximum recent deployment failures allowed'),
|
|
51
|
+
deploymentSuccessThreshold: z.number().default(80).describe('Minimum deployment success rate required (%)'),
|
|
52
|
+
blockOnRecentFailures: z.boolean().default(true).describe('Block if recent deployments failed'),
|
|
53
|
+
rollbackFrequencyThreshold: z.number().default(20).describe('Maximum rollback frequency allowed (%)'),
|
|
54
|
+
// Integration Rules
|
|
55
|
+
requireAdrCompliance: z.boolean().default(true).describe('Require ADR compliance validation'),
|
|
56
|
+
integrateTodoTasks: z.boolean().default(true).describe('Auto-create blocking tasks for issues'),
|
|
57
|
+
updateHealthScoring: z.boolean().default(true).describe('Update project health scores'),
|
|
58
|
+
triggerSmartGitPush: z.boolean().default(false).describe('Trigger smart git push validation'),
|
|
59
|
+
// Human Override System
|
|
60
|
+
emergencyBypass: z.boolean().default(false).describe('Emergency bypass for critical fixes'),
|
|
61
|
+
businessJustification: z.string().optional().describe('Business justification for overrides'),
|
|
62
|
+
approvalRequired: z.boolean().default(true).describe('Require approval for overrides')
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* Main deployment readiness function
|
|
66
|
+
*/
|
|
67
|
+
export async function deploymentReadiness(args) {
|
|
68
|
+
try {
|
|
69
|
+
const validatedArgs = DeploymentReadinessSchema.parse(args);
|
|
70
|
+
// Initialize paths and cache
|
|
71
|
+
const projectPath = validatedArgs.projectPath || process.cwd();
|
|
72
|
+
const cacheDir = join(projectPath, '.mcp-adr-cache');
|
|
73
|
+
const deploymentHistoryPath = join(cacheDir, 'deployment-history.json');
|
|
74
|
+
const readinessCachePath = join(cacheDir, 'deployment-readiness-cache.json');
|
|
75
|
+
// Ensure cache directory exists
|
|
76
|
+
if (!existsSync(cacheDir)) {
|
|
77
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
78
|
+
}
|
|
79
|
+
let result;
|
|
80
|
+
switch (validatedArgs.operation) {
|
|
81
|
+
case 'test_validation':
|
|
82
|
+
result = await performTestValidation(validatedArgs, projectPath);
|
|
83
|
+
break;
|
|
84
|
+
case 'deployment_history':
|
|
85
|
+
result = await performDeploymentHistoryAnalysis(validatedArgs, deploymentHistoryPath);
|
|
86
|
+
break;
|
|
87
|
+
case 'check_readiness':
|
|
88
|
+
case 'validate_production':
|
|
89
|
+
case 'full_audit':
|
|
90
|
+
result = await performFullAudit(validatedArgs, projectPath, deploymentHistoryPath);
|
|
91
|
+
break;
|
|
92
|
+
case 'emergency_override':
|
|
93
|
+
result = await performEmergencyOverride(validatedArgs, projectPath);
|
|
94
|
+
break;
|
|
95
|
+
default:
|
|
96
|
+
throw new McpAdrError('INVALID_ARGS', `Unknown operation: ${validatedArgs.operation}`);
|
|
97
|
+
}
|
|
98
|
+
// Cache result for performance
|
|
99
|
+
writeFileSync(readinessCachePath, JSON.stringify({
|
|
100
|
+
timestamp: new Date().toISOString(),
|
|
101
|
+
operation: validatedArgs.operation,
|
|
102
|
+
result
|
|
103
|
+
}, null, 2));
|
|
104
|
+
// Generate response based on deployment readiness
|
|
105
|
+
return generateDeploymentReadinessResponse(result, validatedArgs);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
throw new McpAdrError('DEPLOYMENT_READINESS_ERROR', `Deployment readiness check failed: ${jsonSafeError(error)}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Perform comprehensive test validation
|
|
113
|
+
*/
|
|
114
|
+
async function performTestValidation(args, projectPath) {
|
|
115
|
+
const testResult = await executeTestSuite(projectPath, args.testSuiteRequired);
|
|
116
|
+
const testBlockers = [];
|
|
117
|
+
// Check for test failures
|
|
118
|
+
if (testResult.failureCount > args.maxTestFailures) {
|
|
119
|
+
testBlockers.push({
|
|
120
|
+
category: 'test_failure',
|
|
121
|
+
title: 'Test Failures Detected',
|
|
122
|
+
description: `${testResult.failureCount} test failures found (max allowed: ${args.maxTestFailures})`,
|
|
123
|
+
severity: 'critical',
|
|
124
|
+
impact: 'Blocks deployment due to failing tests',
|
|
125
|
+
resolutionSteps: [
|
|
126
|
+
'Run npm test to see detailed failures',
|
|
127
|
+
'Fix failing tests one by one',
|
|
128
|
+
'Ensure all tests pass before deployment',
|
|
129
|
+
'Consider increasing test coverage'
|
|
130
|
+
],
|
|
131
|
+
estimatedResolutionTime: `${Math.ceil(testResult.failureCount * 0.5)} hours`,
|
|
132
|
+
blocksDeployment: args.blockOnFailingTests
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
// Check test coverage
|
|
136
|
+
if (testResult.coveragePercentage < args.requireTestCoverage) {
|
|
137
|
+
testBlockers.push({
|
|
138
|
+
category: 'test_failure',
|
|
139
|
+
title: 'Insufficient Test Coverage',
|
|
140
|
+
description: `Test coverage is ${testResult.coveragePercentage}% (minimum required: ${args.requireTestCoverage}%)`,
|
|
141
|
+
severity: 'high',
|
|
142
|
+
impact: 'May indicate untested code paths',
|
|
143
|
+
resolutionSteps: [
|
|
144
|
+
'Add tests for uncovered code',
|
|
145
|
+
'Run npm run test:coverage to see detailed coverage',
|
|
146
|
+
'Focus on critical business logic first'
|
|
147
|
+
],
|
|
148
|
+
estimatedResolutionTime: '2-4 hours',
|
|
149
|
+
blocksDeployment: args.strictMode
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// Basic result structure
|
|
153
|
+
return {
|
|
154
|
+
isDeploymentReady: testBlockers.length === 0,
|
|
155
|
+
overallScore: calculateTestScore(testResult, args),
|
|
156
|
+
confidence: 85,
|
|
157
|
+
codeQualityAnalysis: { qualityScore: 75, productionIndicators: 10, mockIndicators: 2, failingFiles: [], recommendations: [] },
|
|
158
|
+
testValidationResult: testResult,
|
|
159
|
+
deploymentHistoryAnalysis: { recentDeployments: [], successRate: 100, rollbackRate: 0, averageDeploymentTime: 0, failurePatterns: [], environmentStability: { stabilityScore: 100, riskLevel: 'low', recommendation: 'Proceed with deployment' }, recommendedAction: 'proceed' },
|
|
160
|
+
adrComplianceResult: { score: 100, compliantAdrs: 0, totalAdrs: 0, missingImplementations: [], recommendations: [] },
|
|
161
|
+
criticalBlockers: testBlockers.filter(b => b.severity === 'critical'),
|
|
162
|
+
testFailureBlockers: testBlockers,
|
|
163
|
+
deploymentHistoryBlockers: [],
|
|
164
|
+
warnings: [],
|
|
165
|
+
todoTasksCreated: [],
|
|
166
|
+
healthScoreUpdate: {},
|
|
167
|
+
gitPushStatus: testBlockers.length === 0 ? 'allowed' : 'blocked',
|
|
168
|
+
overrideStatus: {}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Execute test suite and analyze results
|
|
173
|
+
*/
|
|
174
|
+
async function executeTestSuite(projectPath, _requiredSuites) {
|
|
175
|
+
const startTime = Date.now();
|
|
176
|
+
let testOutput = '';
|
|
177
|
+
let exitCode = 0;
|
|
178
|
+
try {
|
|
179
|
+
// Try to run tests with different commands
|
|
180
|
+
const testCommands = ['npm test', 'yarn test', 'npx jest'];
|
|
181
|
+
for (const command of testCommands) {
|
|
182
|
+
try {
|
|
183
|
+
testOutput = execSync(command, {
|
|
184
|
+
cwd: projectPath,
|
|
185
|
+
encoding: 'utf8',
|
|
186
|
+
timeout: 300000 // 5 minute timeout
|
|
187
|
+
});
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
if (error.status !== undefined) {
|
|
192
|
+
// Command executed but tests failed
|
|
193
|
+
testOutput = error.stdout + error.stderr;
|
|
194
|
+
exitCode = error.status;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
// Command not found, try next
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
testOutput = `Test execution failed: ${error}`;
|
|
203
|
+
exitCode = 1;
|
|
204
|
+
}
|
|
205
|
+
const executionTime = Date.now() - startTime;
|
|
206
|
+
// Parse test results (simplified for now)
|
|
207
|
+
const testSuites = parseTestOutput(testOutput);
|
|
208
|
+
const totalFailures = testSuites.reduce((sum, suite) => sum + suite.failedTests, 0);
|
|
209
|
+
const overallStatus = exitCode === 0 ? 'passed' : (totalFailures > 0 ? 'failed' : 'partial');
|
|
210
|
+
// Check coverage (simplified)
|
|
211
|
+
const coverage = await checkTestCoverage(projectPath);
|
|
212
|
+
return {
|
|
213
|
+
testSuitesExecuted: testSuites,
|
|
214
|
+
overallTestStatus: overallStatus,
|
|
215
|
+
failureCount: totalFailures,
|
|
216
|
+
coveragePercentage: coverage,
|
|
217
|
+
requiredSuitesMissing: [],
|
|
218
|
+
criticalTestFailures: testSuites.flatMap(suite => suite.failureDetails.filter(f => f.severity === 'critical')),
|
|
219
|
+
testExecutionTime: executionTime,
|
|
220
|
+
lastTestRun: new Date().toISOString()
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Parse test output to extract results
|
|
225
|
+
*/
|
|
226
|
+
function parseTestOutput(output) {
|
|
227
|
+
// Simplified parser - in production, would handle Jest, Mocha, etc.
|
|
228
|
+
const lines = output.split('\n');
|
|
229
|
+
const suites = [];
|
|
230
|
+
// Look for Jest-style output
|
|
231
|
+
let currentSuite = {};
|
|
232
|
+
for (const line of lines) {
|
|
233
|
+
if (line.includes('PASS') || line.includes('FAIL')) {
|
|
234
|
+
if (currentSuite.suiteName) {
|
|
235
|
+
suites.push(currentSuite);
|
|
236
|
+
}
|
|
237
|
+
currentSuite = {
|
|
238
|
+
suiteName: line.split(' ').pop() || 'unknown',
|
|
239
|
+
status: line.includes('PASS') ? 'passed' : 'failed',
|
|
240
|
+
passedTests: 0,
|
|
241
|
+
failedTests: 0,
|
|
242
|
+
coverage: 0,
|
|
243
|
+
executionTime: 0,
|
|
244
|
+
failureDetails: []
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
// Count tests
|
|
248
|
+
if (line.includes('โ') || line.includes('โ')) {
|
|
249
|
+
if (line.includes('โ')) {
|
|
250
|
+
currentSuite.passedTests = (currentSuite.passedTests || 0) + 1;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
currentSuite.failedTests = (currentSuite.failedTests || 0) + 1;
|
|
254
|
+
// Add failure detail
|
|
255
|
+
currentSuite.failureDetails?.push({
|
|
256
|
+
testName: line.trim(),
|
|
257
|
+
testSuite: currentSuite.suiteName || 'unknown',
|
|
258
|
+
errorMessage: line,
|
|
259
|
+
severity: 'medium',
|
|
260
|
+
blocksDeployment: true,
|
|
261
|
+
relatedFiles: []
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (currentSuite.suiteName) {
|
|
267
|
+
suites.push(currentSuite);
|
|
268
|
+
}
|
|
269
|
+
return suites.length > 0 ? suites : [{
|
|
270
|
+
suiteName: 'default',
|
|
271
|
+
status: output.includes('failing') ? 'failed' : 'passed',
|
|
272
|
+
passedTests: 0,
|
|
273
|
+
failedTests: output.includes('failing') ? 1 : 0,
|
|
274
|
+
coverage: 0,
|
|
275
|
+
executionTime: 0,
|
|
276
|
+
failureDetails: []
|
|
277
|
+
}];
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Check test coverage
|
|
281
|
+
*/
|
|
282
|
+
async function checkTestCoverage(projectPath) {
|
|
283
|
+
try {
|
|
284
|
+
// Try to read coverage from common locations
|
|
285
|
+
const coverageFiles = [
|
|
286
|
+
'coverage/lcov-report/index.html',
|
|
287
|
+
'coverage/coverage-summary.json',
|
|
288
|
+
'coverage/coverage-final.json'
|
|
289
|
+
];
|
|
290
|
+
for (const file of coverageFiles) {
|
|
291
|
+
const filePath = join(projectPath, file);
|
|
292
|
+
if (existsSync(filePath)) {
|
|
293
|
+
const content = readFileSync(filePath, 'utf8');
|
|
294
|
+
// Extract coverage percentage (simplified)
|
|
295
|
+
const match = content.match(/(\d+(?:\.\d+)?)%/);
|
|
296
|
+
if (match && match[1]) {
|
|
297
|
+
return parseFloat(match[1]);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
// Coverage not available
|
|
304
|
+
}
|
|
305
|
+
return 0; // Default to 0 if coverage cannot be determined
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Perform deployment history analysis
|
|
309
|
+
*/
|
|
310
|
+
async function performDeploymentHistoryAnalysis(args, historyPath) {
|
|
311
|
+
const history = loadDeploymentHistory(historyPath);
|
|
312
|
+
const analysis = analyzeDeploymentHistory(history, args.targetEnvironment);
|
|
313
|
+
const historyBlockers = [];
|
|
314
|
+
// Check success rate
|
|
315
|
+
if (analysis.successRate < args.deploymentSuccessThreshold) {
|
|
316
|
+
historyBlockers.push({
|
|
317
|
+
category: 'deployment_history',
|
|
318
|
+
title: 'Low Deployment Success Rate',
|
|
319
|
+
description: `Success rate is ${analysis.successRate}% (minimum required: ${args.deploymentSuccessThreshold}%)`,
|
|
320
|
+
severity: 'high',
|
|
321
|
+
impact: 'Indicates potential infrastructure or process issues',
|
|
322
|
+
resolutionSteps: [
|
|
323
|
+
'Review recent deployment failures',
|
|
324
|
+
'Fix underlying infrastructure issues',
|
|
325
|
+
'Improve deployment process reliability',
|
|
326
|
+
'Add more comprehensive pre-deployment checks'
|
|
327
|
+
],
|
|
328
|
+
estimatedResolutionTime: '1-2 days',
|
|
329
|
+
blocksDeployment: args.blockOnRecentFailures
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
// Check rollback rate
|
|
333
|
+
if (analysis.rollbackRate > args.rollbackFrequencyThreshold) {
|
|
334
|
+
historyBlockers.push({
|
|
335
|
+
category: 'deployment_history',
|
|
336
|
+
title: 'High Rollback Frequency',
|
|
337
|
+
description: `Rollback rate is ${analysis.rollbackRate}% (threshold: ${args.rollbackFrequencyThreshold}%)`,
|
|
338
|
+
severity: 'medium',
|
|
339
|
+
impact: 'May indicate deployment quality issues',
|
|
340
|
+
resolutionSteps: [
|
|
341
|
+
'Improve testing before deployment',
|
|
342
|
+
'Add more validation steps',
|
|
343
|
+
'Review rollback causes',
|
|
344
|
+
'Strengthen deployment pipeline'
|
|
345
|
+
],
|
|
346
|
+
estimatedResolutionTime: '4-8 hours',
|
|
347
|
+
blocksDeployment: args.strictMode
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
isDeploymentReady: historyBlockers.length === 0,
|
|
352
|
+
overallScore: Math.min(analysis.successRate, 100 - analysis.rollbackRate),
|
|
353
|
+
confidence: 80,
|
|
354
|
+
codeQualityAnalysis: { qualityScore: 75, productionIndicators: 10, mockIndicators: 2, failingFiles: [], recommendations: [] },
|
|
355
|
+
testValidationResult: { testSuitesExecuted: [], overallTestStatus: 'not_run', failureCount: 0, coveragePercentage: 0, requiredSuitesMissing: [], criticalTestFailures: [], testExecutionTime: 0, lastTestRun: '' },
|
|
356
|
+
deploymentHistoryAnalysis: analysis,
|
|
357
|
+
adrComplianceResult: { score: 100, compliantAdrs: 0, totalAdrs: 0, missingImplementations: [], recommendations: [] },
|
|
358
|
+
criticalBlockers: historyBlockers.filter(b => b.severity === 'critical'),
|
|
359
|
+
testFailureBlockers: [],
|
|
360
|
+
deploymentHistoryBlockers: historyBlockers,
|
|
361
|
+
warnings: [],
|
|
362
|
+
todoTasksCreated: [],
|
|
363
|
+
healthScoreUpdate: {},
|
|
364
|
+
gitPushStatus: historyBlockers.length === 0 ? 'allowed' : 'blocked',
|
|
365
|
+
overrideStatus: {}
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Load deployment history from cache
|
|
370
|
+
*/
|
|
371
|
+
function loadDeploymentHistory(historyPath) {
|
|
372
|
+
if (!existsSync(historyPath)) {
|
|
373
|
+
return { deployments: [] };
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
const content = readFileSync(historyPath, 'utf8');
|
|
377
|
+
return JSON.parse(content);
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
return { deployments: [] };
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Analyze deployment history patterns
|
|
385
|
+
*/
|
|
386
|
+
function analyzeDeploymentHistory(history, environment) {
|
|
387
|
+
const recentDeployments = history.deployments
|
|
388
|
+
.filter(d => d.environment === environment)
|
|
389
|
+
.slice(0, 10);
|
|
390
|
+
const successCount = recentDeployments.filter(d => d.status === 'success').length;
|
|
391
|
+
const rollbackCount = recentDeployments.filter(d => d.rollbackRequired).length;
|
|
392
|
+
const successRate = recentDeployments.length > 0 ? (successCount / recentDeployments.length) * 100 : 100;
|
|
393
|
+
const rollbackRate = recentDeployments.length > 0 ? (rollbackCount / recentDeployments.length) * 100 : 0;
|
|
394
|
+
const failurePatterns = analyzeFailurePatterns(recentDeployments.filter(d => d.status === 'failed'));
|
|
395
|
+
return {
|
|
396
|
+
recentDeployments,
|
|
397
|
+
successRate,
|
|
398
|
+
rollbackRate,
|
|
399
|
+
averageDeploymentTime: calculateAverageDeploymentTime(recentDeployments),
|
|
400
|
+
failurePatterns,
|
|
401
|
+
environmentStability: assessEnvironmentStability(successRate, rollbackRate),
|
|
402
|
+
recommendedAction: recommendAction(successRate, rollbackRate, failurePatterns.length)
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Analyze failure patterns
|
|
407
|
+
*/
|
|
408
|
+
function analyzeFailurePatterns(failedDeployments) {
|
|
409
|
+
const patterns = new Map();
|
|
410
|
+
failedDeployments.forEach(deployment => {
|
|
411
|
+
if (deployment.failureReason) {
|
|
412
|
+
const category = categorizeFailure(deployment.failureReason);
|
|
413
|
+
const existing = patterns.get(category);
|
|
414
|
+
if (existing) {
|
|
415
|
+
existing.frequency++;
|
|
416
|
+
existing.lastOccurrence = deployment.timestamp;
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
patterns.set(category, {
|
|
420
|
+
pattern: category,
|
|
421
|
+
frequency: 1,
|
|
422
|
+
environments: [deployment.environment],
|
|
423
|
+
lastOccurrence: deployment.timestamp,
|
|
424
|
+
resolution: suggestResolution(category),
|
|
425
|
+
preventable: isPreventable(category)
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
return Array.from(patterns.values());
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Perform full audit (all checks)
|
|
434
|
+
*/
|
|
435
|
+
async function performFullAudit(args, projectPath, historyPath) {
|
|
436
|
+
// Combine all validations
|
|
437
|
+
const testResult = await performTestValidation(args, projectPath);
|
|
438
|
+
const historyResult = await performDeploymentHistoryAnalysis(args, historyPath);
|
|
439
|
+
const allBlockers = [
|
|
440
|
+
...testResult.criticalBlockers,
|
|
441
|
+
...testResult.testFailureBlockers,
|
|
442
|
+
...historyResult.deploymentHistoryBlockers
|
|
443
|
+
];
|
|
444
|
+
const overallScore = (testResult.overallScore + historyResult.overallScore) / 2;
|
|
445
|
+
const isReady = allBlockers.length === 0;
|
|
446
|
+
return {
|
|
447
|
+
isDeploymentReady: isReady,
|
|
448
|
+
overallScore,
|
|
449
|
+
confidence: Math.min(testResult.confidence, historyResult.confidence),
|
|
450
|
+
codeQualityAnalysis: testResult.codeQualityAnalysis,
|
|
451
|
+
testValidationResult: testResult.testValidationResult,
|
|
452
|
+
deploymentHistoryAnalysis: historyResult.deploymentHistoryAnalysis,
|
|
453
|
+
adrComplianceResult: testResult.adrComplianceResult,
|
|
454
|
+
criticalBlockers: allBlockers.filter(b => b.severity === 'critical'),
|
|
455
|
+
testFailureBlockers: testResult.testFailureBlockers,
|
|
456
|
+
deploymentHistoryBlockers: historyResult.deploymentHistoryBlockers,
|
|
457
|
+
warnings: [...testResult.warnings, ...historyResult.warnings],
|
|
458
|
+
todoTasksCreated: [],
|
|
459
|
+
healthScoreUpdate: {},
|
|
460
|
+
gitPushStatus: isReady ? 'allowed' : 'blocked',
|
|
461
|
+
overrideStatus: {}
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Perform emergency override
|
|
466
|
+
*/
|
|
467
|
+
async function performEmergencyOverride(args, projectPath) {
|
|
468
|
+
if (!args.businessJustification) {
|
|
469
|
+
throw new McpAdrError('INVALID_ARGS', 'Business justification required for emergency override');
|
|
470
|
+
}
|
|
471
|
+
// Log override for audit trail
|
|
472
|
+
const overrideRecord = {
|
|
473
|
+
timestamp: new Date().toISOString(),
|
|
474
|
+
justification: args.businessJustification || 'No justification provided',
|
|
475
|
+
environment: args.targetEnvironment,
|
|
476
|
+
overriddenBy: process.env['USER'] || 'unknown'
|
|
477
|
+
};
|
|
478
|
+
const overridePath = join(projectPath, '.mcp-adr-cache', 'emergency-overrides.json');
|
|
479
|
+
const overrides = existsSync(overridePath) ? JSON.parse(readFileSync(overridePath, 'utf8')) : [];
|
|
480
|
+
overrides.push(overrideRecord);
|
|
481
|
+
writeFileSync(overridePath, JSON.stringify(overrides, null, 2));
|
|
482
|
+
return {
|
|
483
|
+
isDeploymentReady: true,
|
|
484
|
+
overallScore: 100,
|
|
485
|
+
confidence: 50, // Lower confidence for overrides
|
|
486
|
+
codeQualityAnalysis: { qualityScore: 100, productionIndicators: 0, mockIndicators: 0, failingFiles: [], recommendations: ['Emergency override active - review post-deployment'] },
|
|
487
|
+
testValidationResult: { testSuitesExecuted: [], overallTestStatus: 'not_run', failureCount: 0, coveragePercentage: 0, requiredSuitesMissing: [], criticalTestFailures: [], testExecutionTime: 0, lastTestRun: '' },
|
|
488
|
+
deploymentHistoryAnalysis: { recentDeployments: [], successRate: 100, rollbackRate: 0, averageDeploymentTime: 0, failurePatterns: [], environmentStability: { stabilityScore: 50, riskLevel: 'medium', recommendation: 'Monitor closely post-deployment' }, recommendedAction: 'proceed' },
|
|
489
|
+
adrComplianceResult: { score: 100, compliantAdrs: 0, totalAdrs: 0, missingImplementations: [], recommendations: [] },
|
|
490
|
+
criticalBlockers: [],
|
|
491
|
+
testFailureBlockers: [],
|
|
492
|
+
deploymentHistoryBlockers: [],
|
|
493
|
+
warnings: ['Emergency override active - all normal gates bypassed'],
|
|
494
|
+
todoTasksCreated: [],
|
|
495
|
+
healthScoreUpdate: {},
|
|
496
|
+
gitPushStatus: 'allowed',
|
|
497
|
+
overrideStatus: overrideRecord
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Helper functions
|
|
502
|
+
*/
|
|
503
|
+
function calculateTestScore(testResult, _args) {
|
|
504
|
+
const failureScore = Math.max(0, 100 - (testResult.failureCount * 20));
|
|
505
|
+
const coverageScore = testResult.coveragePercentage;
|
|
506
|
+
return Math.min(failureScore, coverageScore);
|
|
507
|
+
}
|
|
508
|
+
function calculateAverageDeploymentTime(deployments) {
|
|
509
|
+
if (deployments.length === 0)
|
|
510
|
+
return 0;
|
|
511
|
+
const total = deployments.reduce((sum, d) => sum + d.duration, 0);
|
|
512
|
+
return total / deployments.length;
|
|
513
|
+
}
|
|
514
|
+
function assessEnvironmentStability(successRate, rollbackRate) {
|
|
515
|
+
const stabilityScore = Math.max(0, successRate - rollbackRate);
|
|
516
|
+
let riskLevel;
|
|
517
|
+
if (stabilityScore >= 90)
|
|
518
|
+
riskLevel = 'low';
|
|
519
|
+
else if (stabilityScore >= 70)
|
|
520
|
+
riskLevel = 'medium';
|
|
521
|
+
else if (stabilityScore >= 50)
|
|
522
|
+
riskLevel = 'high';
|
|
523
|
+
else
|
|
524
|
+
riskLevel = 'critical';
|
|
525
|
+
return {
|
|
526
|
+
stabilityScore,
|
|
527
|
+
riskLevel,
|
|
528
|
+
recommendation: riskLevel === 'low' ? 'Proceed with deployment' : 'Investigate before deployment'
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
function recommendAction(successRate, rollbackRate, failurePatternCount) {
|
|
532
|
+
if (successRate < 50 || rollbackRate > 40)
|
|
533
|
+
return 'block';
|
|
534
|
+
if (successRate < 80 || rollbackRate > 20 || failurePatternCount > 2)
|
|
535
|
+
return 'investigate';
|
|
536
|
+
return 'proceed';
|
|
537
|
+
}
|
|
538
|
+
function categorizeFailure(failureReason) {
|
|
539
|
+
const patterns = [
|
|
540
|
+
{ regex: /test.*fail/i, category: 'Test Failures' },
|
|
541
|
+
{ regex: /database.*connection/i, category: 'Database Connection Issues' },
|
|
542
|
+
{ regex: /environment.*variable/i, category: 'Environment Configuration' },
|
|
543
|
+
{ regex: /build.*fail/i, category: 'Build Failures' },
|
|
544
|
+
{ regex: /dependency.*error/i, category: 'Dependency Issues' },
|
|
545
|
+
{ regex: /timeout/i, category: 'Timeout Issues' },
|
|
546
|
+
{ regex: /permission.*denied/i, category: 'Permission Issues' },
|
|
547
|
+
{ regex: /out.*of.*memory/i, category: 'Resource Issues' }
|
|
548
|
+
];
|
|
549
|
+
for (const pattern of patterns) {
|
|
550
|
+
if (pattern.regex.test(failureReason)) {
|
|
551
|
+
return pattern.category;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return 'Unknown Failure';
|
|
555
|
+
}
|
|
556
|
+
function suggestResolution(category) {
|
|
557
|
+
const resolutions = {
|
|
558
|
+
'Test Failures': 'Fix failing tests and improve test coverage',
|
|
559
|
+
'Database Connection Issues': 'Check database connectivity and credentials',
|
|
560
|
+
'Environment Configuration': 'Verify environment variables and configuration',
|
|
561
|
+
'Build Failures': 'Fix build dependencies and compilation errors',
|
|
562
|
+
'Dependency Issues': 'Update and verify package dependencies',
|
|
563
|
+
'Timeout Issues': 'Optimize performance and increase timeout values',
|
|
564
|
+
'Permission Issues': 'Check file and directory permissions',
|
|
565
|
+
'Resource Issues': 'Increase available memory and resources'
|
|
566
|
+
};
|
|
567
|
+
return resolutions[category] || 'Investigate failure details and resolve underlying issue';
|
|
568
|
+
}
|
|
569
|
+
function isPreventable(category) {
|
|
570
|
+
const preventableCategories = [
|
|
571
|
+
'Test Failures',
|
|
572
|
+
'Environment Configuration',
|
|
573
|
+
'Build Failures',
|
|
574
|
+
'Dependency Issues'
|
|
575
|
+
];
|
|
576
|
+
return preventableCategories.includes(category);
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Generate comprehensive response based on deployment readiness
|
|
580
|
+
*/
|
|
581
|
+
function generateDeploymentReadinessResponse(result, args) {
|
|
582
|
+
if (result.isDeploymentReady) {
|
|
583
|
+
return generateSuccessResponse(result, args);
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
return generateBlockedResponse(result, args);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
function generateSuccessResponse(result, args) {
|
|
590
|
+
return validateMcpResponse({
|
|
591
|
+
content: [{
|
|
592
|
+
type: 'text',
|
|
593
|
+
text: `โ
**DEPLOYMENT READY - All Gates Passed**
|
|
594
|
+
|
|
595
|
+
## ๐ฏ Overall Assessment
|
|
596
|
+
- **Deployment Ready**: โ
**YES**
|
|
597
|
+
- **Readiness Score**: ${result.overallScore}%
|
|
598
|
+
- **Confidence**: ${result.confidence}%
|
|
599
|
+
- **Target Environment**: ${args.targetEnvironment}
|
|
600
|
+
|
|
601
|
+
## ๐งช Test Validation
|
|
602
|
+
- **Test Status**: โ
${result.testValidationResult.overallTestStatus.toUpperCase()}
|
|
603
|
+
- **Test Failures**: ${result.testValidationResult.failureCount}
|
|
604
|
+
- **Coverage**: ${result.testValidationResult.coveragePercentage}%
|
|
605
|
+
- **Execution Time**: ${result.testValidationResult.testExecutionTime}ms
|
|
606
|
+
|
|
607
|
+
## ๐ Deployment History
|
|
608
|
+
- **Success Rate**: ${result.deploymentHistoryAnalysis.successRate}%
|
|
609
|
+
- **Rollback Rate**: ${result.deploymentHistoryAnalysis.rollbackRate}%
|
|
610
|
+
- **Environment Stability**: ${result.deploymentHistoryAnalysis.environmentStability.riskLevel}
|
|
611
|
+
|
|
612
|
+
## ๐ Deployment Approved
|
|
613
|
+
${args.triggerSmartGitPush ? 'Triggering smart git push...' : 'Ready to proceed with deployment'}
|
|
614
|
+
|
|
615
|
+
${result.warnings.length > 0 ? `
|
|
616
|
+
## โ ๏ธ Warnings
|
|
617
|
+
${result.warnings.map(w => `- ${w}`).join('\n')}
|
|
618
|
+
` : ''}
|
|
619
|
+
|
|
620
|
+
**โ
DEPLOYMENT CAN PROCEED SAFELY**`
|
|
621
|
+
}]
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
function generateBlockedResponse(result, args) {
|
|
625
|
+
return validateMcpResponse({
|
|
626
|
+
content: [{
|
|
627
|
+
type: 'text',
|
|
628
|
+
text: `๐จ **DEPLOYMENT BLOCKED - Critical Issues Found**
|
|
629
|
+
|
|
630
|
+
## ๐ฏ Overall Assessment
|
|
631
|
+
- **Deployment Ready**: โ **NO**
|
|
632
|
+
- **Readiness Score**: ${result.overallScore}%
|
|
633
|
+
- **Confidence**: ${result.confidence}%
|
|
634
|
+
- **Target Environment**: ${args.targetEnvironment}
|
|
635
|
+
|
|
636
|
+
## ๐งช Test Validation Issues
|
|
637
|
+
${result.testFailureBlockers.length > 0 ? `
|
|
638
|
+
**Test Failures**: ${result.testValidationResult.failureCount} failures detected
|
|
639
|
+
**Coverage**: ${result.testValidationResult.coveragePercentage}% (Required: ${args.requireTestCoverage}%)
|
|
640
|
+
|
|
641
|
+
### Critical Test Failures:
|
|
642
|
+
${result.testValidationResult.criticalTestFailures.map(f => `- โ ${f.testSuite}: ${f.testName}`).join('\n')}
|
|
643
|
+
` : 'โ
Tests passing'}
|
|
644
|
+
|
|
645
|
+
## ๐ Deployment History Issues
|
|
646
|
+
${result.deploymentHistoryBlockers.length > 0 ? `
|
|
647
|
+
**Success Rate**: ${result.deploymentHistoryAnalysis.successRate}% (Required: ${args.deploymentSuccessThreshold}%)
|
|
648
|
+
**Rollback Rate**: ${result.deploymentHistoryAnalysis.rollbackRate}% (Threshold: ${args.rollbackFrequencyThreshold}%)
|
|
649
|
+
|
|
650
|
+
### Recent Failure Patterns:
|
|
651
|
+
${result.deploymentHistoryAnalysis.failurePatterns.map(p => `- **${p.pattern}**: ${p.frequency} occurrences`).join('\n')}
|
|
652
|
+
` : 'โ
Deployment history stable'}
|
|
653
|
+
|
|
654
|
+
## ๐จ Critical Blockers (Must Fix Before Deployment)
|
|
655
|
+
${result.criticalBlockers.map(blocker => `
|
|
656
|
+
### ${blocker.category.toUpperCase()}: ${blocker.title}
|
|
657
|
+
- **Impact**: ${blocker.impact}
|
|
658
|
+
- **Resolution**: ${blocker.resolutionSteps.join(' โ ')}
|
|
659
|
+
- **Estimated Time**: ${blocker.estimatedResolutionTime}
|
|
660
|
+
`).join('\n')}
|
|
661
|
+
|
|
662
|
+
## ๐ ๏ธ Immediate Actions Required
|
|
663
|
+
|
|
664
|
+
### 1. Fix Test Issues
|
|
665
|
+
\`\`\`bash
|
|
666
|
+
# Run tests and fix failures
|
|
667
|
+
npm test
|
|
668
|
+
|
|
669
|
+
# Check detailed coverage
|
|
670
|
+
npm run test:coverage
|
|
671
|
+
\`\`\`
|
|
672
|
+
|
|
673
|
+
### 2. Address Deployment History
|
|
674
|
+
\`\`\`bash
|
|
675
|
+
# Review recent failures
|
|
676
|
+
# Fix infrastructure issues
|
|
677
|
+
# Improve deployment reliability
|
|
678
|
+
\`\`\`
|
|
679
|
+
|
|
680
|
+
### 3. Re-validate When Fixed
|
|
681
|
+
\`\`\`bash
|
|
682
|
+
# Run full audit again
|
|
683
|
+
deployment_readiness --operation full_audit --target-environment ${args.targetEnvironment}
|
|
684
|
+
\`\`\`
|
|
685
|
+
|
|
686
|
+
## โ ๏ธ Emergency Override
|
|
687
|
+
For critical fixes only:
|
|
688
|
+
\`\`\`bash
|
|
689
|
+
deployment_readiness --operation emergency_override --business-justification "Your justification"
|
|
690
|
+
\`\`\`
|
|
691
|
+
|
|
692
|
+
**โ DEPLOYMENT CANNOT PROCEED UNTIL ALL CRITICAL BLOCKERS ARE RESOLVED**`
|
|
693
|
+
}]
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
//# sourceMappingURL=deployment-readiness-tool.js.map
|