pipecraft 0.0.0-releaseit
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/.claude/settings.local.json +35 -0
- package/.github/actions/calculate-version/action.yml +106 -0
- package/.github/actions/create-pr/action.yml +122 -0
- package/.github/actions/create-release/action.yml +74 -0
- package/.github/actions/create-tag/action.yml +94 -0
- package/.github/actions/detect-changes/action.yml +62 -0
- package/.github/actions/manage-branch/action.yml +113 -0
- package/.github/actions/promote-branch/action.yml +352 -0
- package/.github/example/workflows/job.analyze.code.yml +26 -0
- package/.github/example/workflows/job.analyze.docker.yml +32 -0
- package/.github/example/workflows/job.app.api.deploy.yml +127 -0
- package/.github/example/workflows/job.app.api.test.yml +102 -0
- package/.github/example/workflows/job.app.docs.deploy.yml +40 -0
- package/.github/example/workflows/job.app.docs.test.yml +50 -0
- package/.github/example/workflows/job.app.web.deploy.yml +96 -0
- package/.github/example/workflows/job.app.web.test.yml +49 -0
- package/.github/example/workflows/job.changes.yml +82 -0
- package/.github/example/workflows/job.create-pr.yml +96 -0
- package/.github/example/workflows/job.env-check.yml +43 -0
- package/.github/example/workflows/job.fast-forward.yml +103 -0
- package/.github/example/workflows/job.lint.yml +64 -0
- package/.github/example/workflows/job.pr-name.yml +70 -0
- package/.github/example/workflows/job.pr-squash-name.yml +27 -0
- package/.github/example/workflows/job.tag.yml +38 -0
- package/.github/example/workflows/job.version.yml +99 -0
- package/.github/example/workflows/lib.plugin-base.publish.yml +59 -0
- package/.github/example/workflows/pipe.yml +178 -0
- package/.github/example/workflows/scripts/fastforward.sh +51 -0
- package/.github/example/workflows/scripts/pipe-cleaner.sh +70 -0
- package/.github/scripts/fastforward.sh +51 -0
- package/.github/scripts/pipe-cleaner.sh +70 -0
- package/.github/workflows/pipeline.yml +220 -0
- package/.github/workflows/pr-title-check.yml +70 -0
- package/.github/workflows/publish.yml +39 -0
- package/.pipecraftrc.json +78 -0
- package/.release-it.cjs +71 -0
- package/PIPELINE_TESTING_PLAN.md +499 -0
- package/README.md +1101 -0
- package/TRUNK_FLOW_PLAN.md +401 -0
- package/assets/logo_banner.png +0 -0
- package/assets/logo_banner.webp +0 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +364 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/generators/init.tpl.d.ts +3 -0
- package/dist/generators/init.tpl.d.ts.map +1 -0
- package/dist/generators/init.tpl.js +117 -0
- package/dist/generators/init.tpl.js.map +1 -0
- package/dist/generators/workflows.tpl.d.ts +7 -0
- package/dist/generators/workflows.tpl.d.ts.map +1 -0
- package/dist/generators/workflows.tpl.js +73 -0
- package/dist/generators/workflows.tpl.js.map +1 -0
- package/dist/templates/actions/calculate-version.yml.tpl.d.ts +3 -0
- package/dist/templates/actions/calculate-version.yml.tpl.d.ts.map +1 -0
- package/dist/templates/actions/calculate-version.yml.tpl.js +122 -0
- package/dist/templates/actions/calculate-version.yml.tpl.js.map +1 -0
- package/dist/templates/actions/create-pr.yml.tpl.d.ts +3 -0
- package/dist/templates/actions/create-pr.yml.tpl.d.ts.map +1 -0
- package/dist/templates/actions/create-pr.yml.tpl.js +145 -0
- package/dist/templates/actions/create-pr.yml.tpl.js.map +1 -0
- package/dist/templates/actions/create-tag.yml.tpl.d.ts +3 -0
- package/dist/templates/actions/create-tag.yml.tpl.d.ts.map +1 -0
- package/dist/templates/actions/create-tag.yml.tpl.js +111 -0
- package/dist/templates/actions/create-tag.yml.tpl.js.map +1 -0
- package/dist/templates/actions/detect-changes.yml.tpl.d.ts +3 -0
- package/dist/templates/actions/detect-changes.yml.tpl.d.ts.map +1 -0
- package/dist/templates/actions/detect-changes.yml.tpl.js +79 -0
- package/dist/templates/actions/detect-changes.yml.tpl.js.map +1 -0
- package/dist/templates/actions/manage-branch.yml.tpl.d.ts +3 -0
- package/dist/templates/actions/manage-branch.yml.tpl.d.ts.map +1 -0
- package/dist/templates/actions/manage-branch.yml.tpl.js +130 -0
- package/dist/templates/actions/manage-branch.yml.tpl.js.map +1 -0
- package/dist/templates/actions/promote-branch.yml.tpl.d.ts +3 -0
- package/dist/templates/actions/promote-branch.yml.tpl.d.ts.map +1 -0
- package/dist/templates/actions/promote-branch.yml.tpl.js +362 -0
- package/dist/templates/actions/promote-branch.yml.tpl.js.map +1 -0
- package/dist/templates/workflows/pipeline-path-based.yml.tpl.d.ts +16 -0
- package/dist/templates/workflows/pipeline-path-based.yml.tpl.d.ts.map +1 -0
- package/dist/templates/workflows/pipeline-path-based.yml.tpl.js +652 -0
- package/dist/templates/workflows/pipeline-path-based.yml.tpl.js.map +1 -0
- package/dist/types/index.d.ts +63 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/ast-path-operations.d.ts +287 -0
- package/dist/utils/ast-path-operations.d.ts.map +1 -0
- package/dist/utils/ast-path-operations.js +419 -0
- package/dist/utils/ast-path-operations.js.map +1 -0
- package/dist/utils/config.d.ts +3 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +49 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/github-setup.d.ts +74 -0
- package/dist/utils/github-setup.d.ts.map +1 -0
- package/dist/utils/github-setup.js +418 -0
- package/dist/utils/github-setup.js.map +1 -0
- package/dist/utils/idempotency.d.ts +47 -0
- package/dist/utils/idempotency.d.ts.map +1 -0
- package/dist/utils/idempotency.js +176 -0
- package/dist/utils/idempotency.js.map +1 -0
- package/dist/utils/preflight.d.ts +55 -0
- package/dist/utils/preflight.d.ts.map +1 -0
- package/dist/utils/preflight.js +261 -0
- package/dist/utils/preflight.js.map +1 -0
- package/dist/utils/versioning.d.ts +41 -0
- package/dist/utils/versioning.d.ts.map +1 -0
- package/dist/utils/versioning.js +219 -0
- package/dist/utils/versioning.js.map +1 -0
- package/docs/USER_JOURNEY_ERRORS.md +352 -0
- package/eslint.config.js +77 -0
- package/examples/basic-config.json +41 -0
- package/examples/monorepo-config.json +49 -0
- package/examples/usage.md +289 -0
- package/package.json +58 -0
- package/scripts/debug-workflows.sh +339 -0
- package/scripts/parse-pipeline.js +29 -0
- package/scripts/test-job-order.cjs +140 -0
- package/scripts/validate-pipeline.cjs +379 -0
- package/scripts/verify-job-order.sh +30 -0
- package/src/cli/index.ts +414 -0
- package/src/generators/init.tpl.ts +126 -0
- package/src/generators/workflows.tpl.ts +80 -0
- package/src/templates/actions/calculate-version.yml.tpl.ts +124 -0
- package/src/templates/actions/create-pr.yml.tpl.ts +147 -0
- package/src/templates/actions/create-tag.yml.tpl.ts +112 -0
- package/src/templates/actions/detect-changes.yml.tpl.ts +87 -0
- package/src/templates/actions/manage-branch.yml.tpl.ts +132 -0
- package/src/templates/actions/promote-branch.yml.tpl.ts +364 -0
- package/src/templates/workflows/pipeline-path-based.yml.tpl.ts +706 -0
- package/src/types/index.ts +64 -0
- package/src/utils/README-ast-path-operations.md +390 -0
- package/src/utils/ast-path-operations.ts +581 -0
- package/src/utils/config.ts +64 -0
- package/src/utils/github-setup.ts +558 -0
- package/src/utils/idempotency.ts +215 -0
- package/src/utils/preflight.ts +306 -0
- package/src/utils/versioning.ts +244 -0
- package/tests/README.md +229 -0
- package/tests/TEST_STRUCTURE.md +256 -0
- package/tests/act/run-act-tests.sh +345 -0
- package/tests/debugging/debug-utils.ts +538 -0
- package/tests/debugging/debug-workflow.test.ts +339 -0
- package/tests/debugging/debug-workflows.sh +339 -0
- package/tests/debugging/iterative-debug.ts +652 -0
- package/tests/debugging/run-debug-tests.sh +431 -0
- package/tests/fixtures/basic-config.json +51 -0
- package/tests/fixtures/invalid-config.json +9 -0
- package/tests/fixtures/pipeline-generated.yml +235 -0
- package/tests/fixtures/pipeline-preserve-comments.yml +245 -0
- package/tests/fixtures/pipeline-user-modified.yml +245 -0
- package/tests/fixtures/test-config.json +58 -0
- package/tests/github-live/README.md +250 -0
- package/tests/github-local/JOB_WORKFLOW_TESTS.md +296 -0
- package/tests/github-local/README.md +240 -0
- package/tests/github-local/run-all-tests.sh +422 -0
- package/tests/github-local/test-job-workflows.sh +631 -0
- package/tests/github-local/test-pipeline-workflow.sh +440 -0
- package/tests/integration/generators.test.ts +578 -0
- package/tests/integration/path-based-template.test.ts +510 -0
- package/tests/integration/simple-path-based.test.ts +415 -0
- package/tests/setup.ts +56 -0
- package/tests/unit/ast-path-operations-extended.test.ts +302 -0
- package/tests/unit/cli.test.ts +541 -0
- package/tests/unit/config-extended.test.ts +412 -0
- package/tests/unit/config.test.ts +152 -0
- package/tests/unit/github-setup.test.ts +189 -0
- package/tests/unit/idempotency-isolated.test.ts +297 -0
- package/tests/unit/job-order.test.ts +157 -0
- package/tests/unit/pipeline-path-based.test.ts +511 -0
- package/tests/unit/validate-pipeline.test.ts +384 -0
- package/tests/unit/versioning-extended.test.ts +279 -0
- package/tests/unit/versioning.test.ts +241 -0
- package/tsconfig.debug.json +17 -0
- package/tsconfig.json +28 -0
- package/vitest.config.ts +51 -0
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Iterative GitHub Actions Debugging System
|
|
3
|
+
*
|
|
4
|
+
* This module provides an interactive debugging system that can be run repeatedly
|
|
5
|
+
* to iteratively fix workflow issues. It maintains state between runs and provides
|
|
6
|
+
* intelligent suggestions for the next debugging steps.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import {
|
|
13
|
+
WorkflowDebugOrchestrator,
|
|
14
|
+
GitHubWorkflowDebugger,
|
|
15
|
+
type DebugAnalysis,
|
|
16
|
+
type LogAnalysis
|
|
17
|
+
} from './debug-utils';
|
|
18
|
+
|
|
19
|
+
export interface DebugSession {
|
|
20
|
+
sessionId: string;
|
|
21
|
+
workflowName: string;
|
|
22
|
+
branch?: string;
|
|
23
|
+
startTime: string;
|
|
24
|
+
lastRunId?: number;
|
|
25
|
+
totalRuns: number;
|
|
26
|
+
fixedIssues: string[];
|
|
27
|
+
remainingIssues: string[];
|
|
28
|
+
nextSteps: string[];
|
|
29
|
+
history: DebugHistoryEntry[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface DebugHistoryEntry {
|
|
33
|
+
timestamp: string;
|
|
34
|
+
runId: number;
|
|
35
|
+
issues: string[];
|
|
36
|
+
actions: string[];
|
|
37
|
+
outcome: 'success' | 'failure' | 'partial';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface DebuggingPlan {
|
|
41
|
+
priority: 'high' | 'medium' | 'low';
|
|
42
|
+
issue: string;
|
|
43
|
+
suggestedActions: string[];
|
|
44
|
+
estimatedTime: string;
|
|
45
|
+
dependencies: string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Interactive debugging orchestrator that maintains state between runs
|
|
50
|
+
*/
|
|
51
|
+
export class IterativeDebugger {
|
|
52
|
+
private session: DebugSession;
|
|
53
|
+
private debugger: GitHubWorkflowDebugger;
|
|
54
|
+
private sessionFile: string;
|
|
55
|
+
private outputDir: string;
|
|
56
|
+
|
|
57
|
+
constructor(
|
|
58
|
+
token: string,
|
|
59
|
+
owner: string,
|
|
60
|
+
repo: string,
|
|
61
|
+
workflowName: string,
|
|
62
|
+
branch?: string,
|
|
63
|
+
outputDir: string = './debug-sessions'
|
|
64
|
+
) {
|
|
65
|
+
this.debugger = new GitHubWorkflowDebugger(token, owner, repo);
|
|
66
|
+
this.outputDir = outputDir;
|
|
67
|
+
this.sessionFile = join(outputDir, `session-${workflowName}-${Date.now()}.json`);
|
|
68
|
+
|
|
69
|
+
// Initialize or load session
|
|
70
|
+
this.session = this.initializeSession(workflowName, branch);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Initialize or load existing debug session
|
|
75
|
+
*/
|
|
76
|
+
private initializeSession(workflowName: string, branch?: string): DebugSession {
|
|
77
|
+
if (existsSync(this.sessionFile)) {
|
|
78
|
+
try {
|
|
79
|
+
const sessionData = JSON.parse(readFileSync(this.sessionFile, 'utf-8'));
|
|
80
|
+
console.log(`๐ Loaded existing session: ${sessionData.sessionId}`);
|
|
81
|
+
return sessionData;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.warn('โ ๏ธ Could not load existing session, starting fresh');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
sessionId: `debug-${Date.now()}`,
|
|
89
|
+
workflowName,
|
|
90
|
+
branch,
|
|
91
|
+
startTime: new Date().toISOString(),
|
|
92
|
+
totalRuns: 0,
|
|
93
|
+
fixedIssues: [],
|
|
94
|
+
remainingIssues: [],
|
|
95
|
+
nextSteps: [],
|
|
96
|
+
history: []
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Save session state
|
|
102
|
+
*/
|
|
103
|
+
private saveSession(): void {
|
|
104
|
+
if (!existsSync(this.outputDir)) {
|
|
105
|
+
mkdirSync(this.outputDir, { recursive: true });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
writeFileSync(this.sessionFile, JSON.stringify(this.session, null, 2));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Run a complete debugging iteration
|
|
113
|
+
*/
|
|
114
|
+
async runDebugIteration(): Promise<DebugAnalysis | null> {
|
|
115
|
+
console.log(`๐ Starting debug iteration ${this.session.totalRuns + 1}`);
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
// Get recent workflow runs
|
|
119
|
+
const runs = await this.debugger.getWorkflowRuns(
|
|
120
|
+
this.session.workflowName,
|
|
121
|
+
this.session.branch,
|
|
122
|
+
5
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
if (runs.length === 0) {
|
|
126
|
+
console.log('โ No workflow runs found');
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Find the most recent run
|
|
131
|
+
const latestRun = runs[0];
|
|
132
|
+
this.session.lastRunId = latestRun.id;
|
|
133
|
+
this.session.totalRuns++;
|
|
134
|
+
|
|
135
|
+
console.log(`๐ Analyzing run ${latestRun.id} (${latestRun.status})`);
|
|
136
|
+
|
|
137
|
+
// Get jobs for this run
|
|
138
|
+
const jobs = await this.debugger.getWorkflowJobs(latestRun.id);
|
|
139
|
+
|
|
140
|
+
// Analyze the run
|
|
141
|
+
const analysis = this.analyzeWorkflowRun(latestRun, jobs);
|
|
142
|
+
|
|
143
|
+
// Update session with findings
|
|
144
|
+
this.updateSessionWithAnalysis(analysis);
|
|
145
|
+
|
|
146
|
+
// Generate next steps
|
|
147
|
+
this.generateNextSteps(analysis);
|
|
148
|
+
|
|
149
|
+
// Save session
|
|
150
|
+
this.saveSession();
|
|
151
|
+
|
|
152
|
+
// Generate report
|
|
153
|
+
await this.generateIterationReport(analysis);
|
|
154
|
+
|
|
155
|
+
return analysis;
|
|
156
|
+
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error(`โ Debug iteration failed: ${error}`);
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Analyze a workflow run and extract issues
|
|
165
|
+
*/
|
|
166
|
+
private analyzeWorkflowRun(run: any, jobs: any[]): DebugAnalysis {
|
|
167
|
+
const failedJobs = jobs.filter(job =>
|
|
168
|
+
job.conclusion === 'failure' || job.conclusion === 'cancelled'
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const successRate = jobs.length > 0 ?
|
|
172
|
+
((jobs.length - failedJobs.length) / jobs.length) * 100 : 0;
|
|
173
|
+
|
|
174
|
+
// Extract issues from failed jobs
|
|
175
|
+
const issues: string[] = [];
|
|
176
|
+
failedJobs.forEach(job => {
|
|
177
|
+
job.steps.forEach((step: any) => {
|
|
178
|
+
if (step.conclusion === 'failure') {
|
|
179
|
+
const issue = this.categorizeIssue(step.name, job.name);
|
|
180
|
+
if (!issues.includes(issue)) {
|
|
181
|
+
issues.push(issue);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
runId: run.id,
|
|
189
|
+
status: run.status,
|
|
190
|
+
conclusion: run.conclusion,
|
|
191
|
+
failedJobs,
|
|
192
|
+
totalJobs: jobs.length,
|
|
193
|
+
successRate,
|
|
194
|
+
commonFailurePatterns: issues,
|
|
195
|
+
recommendations: this.generateRecommendations(issues)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Categorize issues based on step and job names
|
|
201
|
+
*/
|
|
202
|
+
private categorizeIssue(stepName: string, jobName: string): string {
|
|
203
|
+
const step = stepName.toLowerCase();
|
|
204
|
+
const job = jobName.toLowerCase();
|
|
205
|
+
|
|
206
|
+
if (step.includes('test') || job.includes('test')) {
|
|
207
|
+
return 'Test Failures';
|
|
208
|
+
}
|
|
209
|
+
if (step.includes('build') || job.includes('build')) {
|
|
210
|
+
return 'Build Failures';
|
|
211
|
+
}
|
|
212
|
+
if (step.includes('lint') || job.includes('lint')) {
|
|
213
|
+
return 'Linting Issues';
|
|
214
|
+
}
|
|
215
|
+
if (step.includes('deploy') || job.includes('deploy')) {
|
|
216
|
+
return 'Deployment Issues';
|
|
217
|
+
}
|
|
218
|
+
if (step.includes('install') || step.includes('dependencies')) {
|
|
219
|
+
return 'Dependency Issues';
|
|
220
|
+
}
|
|
221
|
+
if (step.includes('docker') || job.includes('docker')) {
|
|
222
|
+
return 'Docker Issues';
|
|
223
|
+
}
|
|
224
|
+
if (step.includes('security') || job.includes('security')) {
|
|
225
|
+
return 'Security Issues';
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return 'Unknown Issues';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Generate specific recommendations based on issues
|
|
233
|
+
*/
|
|
234
|
+
private generateRecommendations(issues: string[]): string[] {
|
|
235
|
+
const recommendations: string[] = [];
|
|
236
|
+
|
|
237
|
+
if (issues.includes('Test Failures')) {
|
|
238
|
+
recommendations.push('Run tests locally to reproduce failures');
|
|
239
|
+
recommendations.push('Check for flaky tests and add retry mechanisms');
|
|
240
|
+
recommendations.push('Update test dependencies and configurations');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (issues.includes('Build Failures')) {
|
|
244
|
+
recommendations.push('Verify build environment and dependencies');
|
|
245
|
+
recommendations.push('Check for breaking changes in dependencies');
|
|
246
|
+
recommendations.push('Review build configuration files');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (issues.includes('Linting Issues')) {
|
|
250
|
+
recommendations.push('Run linting locally and fix issues');
|
|
251
|
+
recommendations.push('Configure pre-commit hooks for automatic linting');
|
|
252
|
+
recommendations.push('Update linting rules and configurations');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (issues.includes('Dependency Issues')) {
|
|
256
|
+
recommendations.push('Update package-lock.json or yarn.lock');
|
|
257
|
+
recommendations.push('Check for version conflicts in dependencies');
|
|
258
|
+
recommendations.push('Clear node_modules and reinstall dependencies');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (issues.includes('Docker Issues')) {
|
|
262
|
+
recommendations.push('Test Docker build locally');
|
|
263
|
+
recommendations.push('Check Dockerfile syntax and base images');
|
|
264
|
+
recommendations.push('Verify resource constraints in CI environment');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return recommendations;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Update session with analysis results
|
|
272
|
+
*/
|
|
273
|
+
private updateSessionWithAnalysis(analysis: DebugAnalysis): void {
|
|
274
|
+
// Track new issues
|
|
275
|
+
const newIssues = analysis.commonFailurePatterns.filter(issue =>
|
|
276
|
+
!this.session.remainingIssues.includes(issue)
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
this.session.remainingIssues = [
|
|
280
|
+
...this.session.remainingIssues,
|
|
281
|
+
...newIssues
|
|
282
|
+
];
|
|
283
|
+
|
|
284
|
+
// Remove fixed issues (issues that are no longer present)
|
|
285
|
+
const currentIssues = analysis.commonFailurePatterns;
|
|
286
|
+
const fixedIssues = this.session.remainingIssues.filter(issue =>
|
|
287
|
+
!currentIssues.includes(issue)
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
this.session.fixedIssues = [...this.session.fixedIssues, ...fixedIssues];
|
|
291
|
+
this.session.remainingIssues = this.session.remainingIssues.filter(issue =>
|
|
292
|
+
currentIssues.includes(issue)
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
// Add to history
|
|
296
|
+
this.session.history.push({
|
|
297
|
+
timestamp: new Date().toISOString(),
|
|
298
|
+
runId: analysis.runId,
|
|
299
|
+
issues: analysis.commonFailurePatterns,
|
|
300
|
+
actions: [],
|
|
301
|
+
outcome: analysis.successRate > 80 ? 'success' :
|
|
302
|
+
analysis.successRate > 50 ? 'partial' : 'failure'
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Generate next steps based on current state
|
|
308
|
+
*/
|
|
309
|
+
private generateNextSteps(analysis: DebugAnalysis): void {
|
|
310
|
+
const nextSteps: string[] = [];
|
|
311
|
+
|
|
312
|
+
if (analysis.successRate === 100) {
|
|
313
|
+
nextSteps.push('๐ All issues resolved! Workflow is now passing.');
|
|
314
|
+
nextSteps.push('Consider adding monitoring to prevent regressions.');
|
|
315
|
+
} else if (analysis.successRate > 80) {
|
|
316
|
+
nextSteps.push('โ
Significant progress made! Focus on remaining issues.');
|
|
317
|
+
nextSteps.push('Review the remaining failed jobs and their logs.');
|
|
318
|
+
} else {
|
|
319
|
+
nextSteps.push('๐ง Focus on the most critical issues first.');
|
|
320
|
+
nextSteps.push('Consider breaking down large jobs into smaller ones.');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Add specific recommendations
|
|
324
|
+
analysis.recommendations.forEach(rec => {
|
|
325
|
+
nextSteps.push(`๐ก ${rec}`);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
this.session.nextSteps = nextSteps;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Generate iteration report
|
|
333
|
+
*/
|
|
334
|
+
private async generateIterationReport(analysis: DebugAnalysis): Promise<void> {
|
|
335
|
+
const reportPath = join(this.outputDir, `iteration-${this.session.totalRuns}.md`);
|
|
336
|
+
|
|
337
|
+
const report = `# Debug Iteration ${this.session.totalRuns}
|
|
338
|
+
|
|
339
|
+
**Session ID**: ${this.session.sessionId}
|
|
340
|
+
**Timestamp**: ${new Date().toISOString()}
|
|
341
|
+
**Workflow**: ${this.session.workflowName}
|
|
342
|
+
**Run ID**: ${analysis.runId}
|
|
343
|
+
|
|
344
|
+
## Current Status
|
|
345
|
+
|
|
346
|
+
- **Success Rate**: ${analysis.successRate.toFixed(1)}%
|
|
347
|
+
- **Total Jobs**: ${analysis.totalJobs}
|
|
348
|
+
- **Failed Jobs**: ${analysis.failedJobs.length}
|
|
349
|
+
|
|
350
|
+
## Issues Found
|
|
351
|
+
|
|
352
|
+
${analysis.commonFailurePatterns.map(issue => `- ${issue}`).join('\n')}
|
|
353
|
+
|
|
354
|
+
## Recommendations
|
|
355
|
+
|
|
356
|
+
${analysis.recommendations.map(rec => `- ${rec}`).join('\n')}
|
|
357
|
+
|
|
358
|
+
## Session Progress
|
|
359
|
+
|
|
360
|
+
- **Total Iterations**: ${this.session.totalRuns}
|
|
361
|
+
- **Issues Fixed**: ${this.session.fixedIssues.length}
|
|
362
|
+
- **Remaining Issues**: ${this.session.remainingIssues.length}
|
|
363
|
+
|
|
364
|
+
### Fixed Issues
|
|
365
|
+
${this.session.fixedIssues.map(issue => `- โ
${issue}`).join('\n')}
|
|
366
|
+
|
|
367
|
+
### Remaining Issues
|
|
368
|
+
${this.session.remainingIssues.map(issue => `- ๐ง ${issue}`).join('\n')}
|
|
369
|
+
|
|
370
|
+
## Next Steps
|
|
371
|
+
|
|
372
|
+
${this.session.nextSteps.map(step => `- ${step}`).join('\n')}
|
|
373
|
+
|
|
374
|
+
## History
|
|
375
|
+
|
|
376
|
+
${this.session.history.map(entry => `
|
|
377
|
+
### ${entry.timestamp}
|
|
378
|
+
- **Run ID**: ${entry.runId}
|
|
379
|
+
- **Outcome**: ${entry.outcome}
|
|
380
|
+
- **Issues**: ${entry.issues.join(', ')}
|
|
381
|
+
`).join('\n')}
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
*Generated by Pipecraft Iterative Debugger*
|
|
385
|
+
`;
|
|
386
|
+
|
|
387
|
+
writeFileSync(reportPath, report);
|
|
388
|
+
console.log(`๐ Iteration report saved: ${reportPath}`);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Get current session status
|
|
393
|
+
*/
|
|
394
|
+
getSessionStatus(): DebugSession {
|
|
395
|
+
return { ...this.session };
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Get debugging plan with priorities
|
|
400
|
+
*/
|
|
401
|
+
getDebuggingPlan(): DebuggingPlan[] {
|
|
402
|
+
const plans: DebuggingPlan[] = [];
|
|
403
|
+
|
|
404
|
+
this.session.remainingIssues.forEach(issue => {
|
|
405
|
+
const priority = this.determinePriority(issue);
|
|
406
|
+
const actions = this.getActionsForIssue(issue);
|
|
407
|
+
|
|
408
|
+
plans.push({
|
|
409
|
+
priority,
|
|
410
|
+
issue,
|
|
411
|
+
suggestedActions: actions,
|
|
412
|
+
estimatedTime: this.estimateTime(issue),
|
|
413
|
+
dependencies: this.getDependencies(issue)
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
return plans.sort((a, b) => {
|
|
418
|
+
const priorityOrder = { high: 3, medium: 2, low: 1 };
|
|
419
|
+
return priorityOrder[b.priority] - priorityOrder[a.priority];
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Determine priority for an issue
|
|
425
|
+
*/
|
|
426
|
+
private determinePriority(issue: string): 'high' | 'medium' | 'low' {
|
|
427
|
+
const highPriorityIssues = ['Build Failures', 'Dependency Issues'];
|
|
428
|
+
const mediumPriorityIssues = ['Test Failures', 'Linting Issues'];
|
|
429
|
+
|
|
430
|
+
if (highPriorityIssues.includes(issue)) return 'high';
|
|
431
|
+
if (mediumPriorityIssues.includes(issue)) return 'medium';
|
|
432
|
+
return 'low';
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Get specific actions for an issue
|
|
437
|
+
*/
|
|
438
|
+
private getActionsForIssue(issue: string): string[] {
|
|
439
|
+
const actionMap: Record<string, string[]> = {
|
|
440
|
+
'Test Failures': [
|
|
441
|
+
'Run tests locally with same environment',
|
|
442
|
+
'Check for flaky tests and add retries',
|
|
443
|
+
'Update test dependencies',
|
|
444
|
+
'Review test configuration'
|
|
445
|
+
],
|
|
446
|
+
'Build Failures': [
|
|
447
|
+
'Test build locally',
|
|
448
|
+
'Check dependency versions',
|
|
449
|
+
'Review build configuration',
|
|
450
|
+
'Check for breaking changes'
|
|
451
|
+
],
|
|
452
|
+
'Linting Issues': [
|
|
453
|
+
'Run linting locally',
|
|
454
|
+
'Fix linting errors',
|
|
455
|
+
'Update linting configuration',
|
|
456
|
+
'Add pre-commit hooks'
|
|
457
|
+
],
|
|
458
|
+
'Dependency Issues': [
|
|
459
|
+
'Update package-lock.json',
|
|
460
|
+
'Clear node_modules and reinstall',
|
|
461
|
+
'Check for version conflicts',
|
|
462
|
+
'Update dependency versions'
|
|
463
|
+
],
|
|
464
|
+
'Docker Issues': [
|
|
465
|
+
'Test Docker build locally',
|
|
466
|
+
'Check Dockerfile syntax',
|
|
467
|
+
'Verify base image versions',
|
|
468
|
+
'Check resource constraints'
|
|
469
|
+
]
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
return actionMap[issue] || ['Investigate the issue further'];
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Estimate time to fix an issue
|
|
477
|
+
*/
|
|
478
|
+
private estimateTime(issue: string): string {
|
|
479
|
+
const timeMap: Record<string, string> = {
|
|
480
|
+
'Test Failures': '30-60 minutes',
|
|
481
|
+
'Build Failures': '15-45 minutes',
|
|
482
|
+
'Linting Issues': '10-30 minutes',
|
|
483
|
+
'Dependency Issues': '20-40 minutes',
|
|
484
|
+
'Docker Issues': '30-60 minutes'
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
return timeMap[issue] || 'Unknown';
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Get dependencies for an issue
|
|
492
|
+
*/
|
|
493
|
+
private getDependencies(issue: string): string[] {
|
|
494
|
+
const dependencyMap: Record<string, string[]> = {
|
|
495
|
+
'Test Failures': ['Build Failures'],
|
|
496
|
+
'Build Failures': ['Dependency Issues'],
|
|
497
|
+
'Linting Issues': [],
|
|
498
|
+
'Dependency Issues': [],
|
|
499
|
+
'Docker Issues': ['Build Failures']
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
return dependencyMap[issue] || [];
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Run a quick health check
|
|
507
|
+
*/
|
|
508
|
+
async runHealthCheck(): Promise<boolean> {
|
|
509
|
+
try {
|
|
510
|
+
const runs = await this.debugger.getWorkflowRuns(this.session.workflowName, this.session.branch, 1);
|
|
511
|
+
return runs.length > 0;
|
|
512
|
+
} catch (error) {
|
|
513
|
+
console.error(`โ Health check failed: ${error}`);
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Clean up old sessions
|
|
520
|
+
*/
|
|
521
|
+
static cleanupOldSessions(outputDir: string, maxAge: number = 7 * 24 * 60 * 60 * 1000): void {
|
|
522
|
+
// Implementation for cleaning up old session files
|
|
523
|
+
console.log(`๐งน Cleaning up sessions older than ${maxAge}ms`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* CLI interface for iterative debugging
|
|
529
|
+
*/
|
|
530
|
+
export class IterativeDebugCLI {
|
|
531
|
+
private debugger: IterativeDebugger;
|
|
532
|
+
|
|
533
|
+
constructor(debugger: IterativeDebugger) {
|
|
534
|
+
this.debugger = debugger;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Run interactive debugging session
|
|
539
|
+
*/
|
|
540
|
+
async runInteractiveSession(): Promise<void> {
|
|
541
|
+
console.log('๐ Starting interactive debugging session');
|
|
542
|
+
console.log(`๐ Session ID: ${this.debugger.getSessionStatus().sessionId}`);
|
|
543
|
+
|
|
544
|
+
let continueDebugging = true;
|
|
545
|
+
let iteration = 0;
|
|
546
|
+
|
|
547
|
+
while (continueDebugging && iteration < 10) { // Limit to 10 iterations
|
|
548
|
+
iteration++;
|
|
549
|
+
|
|
550
|
+
console.log(`\n๐ Iteration ${iteration}`);
|
|
551
|
+
console.log('=' * 50);
|
|
552
|
+
|
|
553
|
+
// Run debug iteration
|
|
554
|
+
const analysis = await this.debugger.runDebugIteration();
|
|
555
|
+
|
|
556
|
+
if (!analysis) {
|
|
557
|
+
console.log('โ No analysis available, stopping');
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Show current status
|
|
562
|
+
this.showCurrentStatus();
|
|
563
|
+
|
|
564
|
+
// Show debugging plan
|
|
565
|
+
this.showDebuggingPlan();
|
|
566
|
+
|
|
567
|
+
// Ask user if they want to continue
|
|
568
|
+
console.log('\nโ Continue debugging? (y/n)');
|
|
569
|
+
// In a real implementation, you'd read from stdin
|
|
570
|
+
// For now, we'll just continue automatically
|
|
571
|
+
continueDebugging = iteration < 3; // Auto-stop after 3 iterations for demo
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
console.log('\n๐ Debugging session complete');
|
|
575
|
+
this.showFinalSummary();
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Show current debugging status
|
|
580
|
+
*/
|
|
581
|
+
private showCurrentStatus(): void {
|
|
582
|
+
const status = this.debugger.getSessionStatus();
|
|
583
|
+
|
|
584
|
+
console.log('\n๐ Current Status:');
|
|
585
|
+
console.log(` Total Iterations: ${status.totalRuns}`);
|
|
586
|
+
console.log(` Issues Fixed: ${status.fixedIssues.length}`);
|
|
587
|
+
console.log(` Remaining Issues: ${status.remainingIssues.length}`);
|
|
588
|
+
|
|
589
|
+
if (status.remainingIssues.length > 0) {
|
|
590
|
+
console.log('\n๐ง Remaining Issues:');
|
|
591
|
+
status.remainingIssues.forEach(issue => {
|
|
592
|
+
console.log(` - ${issue}`);
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Show debugging plan
|
|
599
|
+
*/
|
|
600
|
+
private showDebuggingPlan(): void {
|
|
601
|
+
const plans = this.debugger.getDebuggingPlan();
|
|
602
|
+
|
|
603
|
+
if (plans.length === 0) {
|
|
604
|
+
console.log('\n๐ No remaining issues to fix!');
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
console.log('\n๐ Debugging Plan:');
|
|
609
|
+
plans.forEach((plan, index) => {
|
|
610
|
+
console.log(`\n${index + 1}. ${plan.issue} (${plan.priority} priority)`);
|
|
611
|
+
console.log(` Estimated Time: ${plan.estimatedTime}`);
|
|
612
|
+
console.log(` Actions:`);
|
|
613
|
+
plan.suggestedActions.forEach(action => {
|
|
614
|
+
console.log(` - ${action}`);
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Show final summary
|
|
621
|
+
*/
|
|
622
|
+
private showFinalSummary(): void {
|
|
623
|
+
const status = this.debugger.getSessionStatus();
|
|
624
|
+
|
|
625
|
+
console.log('\n๐ Final Summary:');
|
|
626
|
+
console.log(` Session Duration: ${this.calculateSessionDuration()}`);
|
|
627
|
+
console.log(` Total Iterations: ${status.totalRuns}`);
|
|
628
|
+
console.log(` Issues Fixed: ${status.fixedIssues.length}`);
|
|
629
|
+
console.log(` Remaining Issues: ${status.remainingIssues.length}`);
|
|
630
|
+
|
|
631
|
+
if (status.fixedIssues.length > 0) {
|
|
632
|
+
console.log('\nโ
Fixed Issues:');
|
|
633
|
+
status.fixedIssues.forEach(issue => {
|
|
634
|
+
console.log(` - ${issue}`);
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Calculate session duration
|
|
641
|
+
*/
|
|
642
|
+
private calculateSessionDuration(): string {
|
|
643
|
+
const status = this.debugger.getSessionStatus();
|
|
644
|
+
const startTime = new Date(status.startTime);
|
|
645
|
+
const duration = Date.now() - startTime.getTime();
|
|
646
|
+
|
|
647
|
+
const minutes = Math.floor(duration / 60000);
|
|
648
|
+
const seconds = Math.floor((duration % 60000) / 1000);
|
|
649
|
+
|
|
650
|
+
return `${minutes}m ${seconds}s`;
|
|
651
|
+
}
|
|
652
|
+
}
|