pipecraft 0.0.0-releaseit → 0.26.0
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 +126 -108
- package/dist/cli/index.d.ts +69 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +94 -22
- package/dist/cli/index.js.map +1 -1
- package/dist/generators/init.tpl.d.ts +70 -0
- package/dist/generators/init.tpl.d.ts.map +1 -1
- package/dist/generators/init.tpl.js +100 -8
- package/dist/generators/init.tpl.js.map +1 -1
- package/dist/generators/workflows.tpl.d.ts +86 -0
- package/dist/generators/workflows.tpl.d.ts.map +1 -1
- package/dist/generators/workflows.tpl.js +101 -9
- package/dist/generators/workflows.tpl.js.map +1 -1
- package/dist/templates/actions/calculate-version.yml.tpl.d.ts +47 -0
- package/dist/templates/actions/calculate-version.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/calculate-version.yml.tpl.js +72 -3
- package/dist/templates/actions/calculate-version.yml.tpl.js.map +1 -1
- package/dist/templates/actions/create-pr.yml.tpl.d.ts +14 -0
- package/dist/templates/actions/create-pr.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/create-pr.yml.tpl.js +22 -2
- package/dist/templates/actions/create-pr.yml.tpl.js.map +1 -1
- package/dist/templates/actions/create-release.yml.tpl.d.ts +18 -0
- package/dist/templates/actions/create-release.yml.tpl.d.ts.map +1 -0
- package/dist/templates/actions/create-release.yml.tpl.js +141 -0
- package/dist/templates/actions/create-release.yml.tpl.js.map +1 -0
- package/dist/templates/actions/create-tag.yml.tpl.d.ts +14 -0
- package/dist/templates/actions/create-tag.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/create-tag.yml.tpl.js +22 -2
- package/dist/templates/actions/create-tag.yml.tpl.js.map +1 -1
- package/dist/templates/actions/detect-changes.yml.tpl.d.ts +70 -0
- package/dist/templates/actions/detect-changes.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/detect-changes.yml.tpl.js +98 -2
- package/dist/templates/actions/detect-changes.yml.tpl.js.map +1 -1
- package/dist/templates/actions/manage-branch.yml.tpl.d.ts +14 -0
- package/dist/templates/actions/manage-branch.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/manage-branch.yml.tpl.js +22 -2
- package/dist/templates/actions/manage-branch.yml.tpl.js.map +1 -1
- package/dist/templates/actions/promote-branch.yml.tpl.d.ts +14 -0
- package/dist/templates/actions/promote-branch.yml.tpl.d.ts.map +1 -1
- package/dist/templates/actions/promote-branch.yml.tpl.js +50 -7
- package/dist/templates/actions/promote-branch.yml.tpl.js.map +1 -1
- package/dist/templates/workflows/pipeline-path-based.yml.tpl.d.ts +114 -1
- package/dist/templates/workflows/pipeline-path-based.yml.tpl.d.ts.map +1 -1
- package/dist/templates/workflows/pipeline-path-based.yml.tpl.js +479 -74
- package/dist/templates/workflows/pipeline-path-based.yml.tpl.js.map +1 -1
- package/dist/types/index.d.ts +231 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +9 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/ast-path-operations.d.ts.map +1 -1
- package/dist/utils/ast-path-operations.js +24 -11
- package/dist/utils/ast-path-operations.js.map +1 -1
- package/dist/utils/config.d.ts +63 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +71 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/github-setup.d.ts +323 -2
- package/dist/utils/github-setup.d.ts.map +1 -1
- package/dist/utils/github-setup.js +551 -23
- package/dist/utils/github-setup.js.map +1 -1
- package/dist/utils/idempotency.d.ts +147 -7
- package/dist/utils/idempotency.d.ts.map +1 -1
- package/dist/utils/idempotency.js +143 -15
- package/dist/utils/idempotency.js.map +1 -1
- package/dist/utils/logger.d.ts +134 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +153 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/preflight.d.ts +223 -12
- package/dist/utils/preflight.d.ts.map +1 -1
- package/dist/utils/preflight.js +216 -16
- package/dist/utils/preflight.js.map +1 -1
- package/dist/utils/versioning.d.ts +160 -8
- package/dist/utils/versioning.d.ts.map +1 -1
- package/dist/utils/versioning.js +179 -15
- package/dist/utils/versioning.js.map +1 -1
- package/package.json +26 -5
- package/.claude/settings.local.json +0 -35
- package/.github/actions/calculate-version/action.yml +0 -106
- package/.github/actions/create-pr/action.yml +0 -122
- package/.github/actions/create-release/action.yml +0 -74
- package/.github/actions/create-tag/action.yml +0 -94
- package/.github/actions/detect-changes/action.yml +0 -62
- package/.github/actions/manage-branch/action.yml +0 -113
- package/.github/actions/promote-branch/action.yml +0 -352
- package/.github/example/workflows/job.analyze.code.yml +0 -26
- package/.github/example/workflows/job.analyze.docker.yml +0 -32
- package/.github/example/workflows/job.app.api.deploy.yml +0 -127
- package/.github/example/workflows/job.app.api.test.yml +0 -102
- package/.github/example/workflows/job.app.docs.deploy.yml +0 -40
- package/.github/example/workflows/job.app.docs.test.yml +0 -50
- package/.github/example/workflows/job.app.web.deploy.yml +0 -96
- package/.github/example/workflows/job.app.web.test.yml +0 -49
- package/.github/example/workflows/job.changes.yml +0 -82
- package/.github/example/workflows/job.create-pr.yml +0 -96
- package/.github/example/workflows/job.env-check.yml +0 -43
- package/.github/example/workflows/job.fast-forward.yml +0 -103
- package/.github/example/workflows/job.lint.yml +0 -64
- package/.github/example/workflows/job.pr-name.yml +0 -70
- package/.github/example/workflows/job.pr-squash-name.yml +0 -27
- package/.github/example/workflows/job.tag.yml +0 -38
- package/.github/example/workflows/job.version.yml +0 -99
- package/.github/example/workflows/lib.plugin-base.publish.yml +0 -59
- package/.github/example/workflows/pipe.yml +0 -178
- package/.github/example/workflows/scripts/fastforward.sh +0 -51
- package/.github/example/workflows/scripts/pipe-cleaner.sh +0 -70
- package/.github/scripts/fastforward.sh +0 -51
- package/.github/scripts/pipe-cleaner.sh +0 -70
- package/.github/workflows/pipeline.yml +0 -220
- package/.github/workflows/pr-title-check.yml +0 -70
- package/.github/workflows/publish.yml +0 -39
- package/.pipecraftrc.json +0 -78
- package/.release-it.cjs +0 -71
- package/PIPELINE_TESTING_PLAN.md +0 -499
- package/TRUNK_FLOW_PLAN.md +0 -401
- package/assets/logo_banner.png +0 -0
- package/assets/logo_banner.webp +0 -0
- package/docs/USER_JOURNEY_ERRORS.md +0 -352
- package/eslint.config.js +0 -77
- package/examples/basic-config.json +0 -41
- package/examples/monorepo-config.json +0 -49
- package/examples/usage.md +0 -289
- package/scripts/debug-workflows.sh +0 -339
- package/scripts/parse-pipeline.js +0 -29
- package/scripts/test-job-order.cjs +0 -140
- package/scripts/validate-pipeline.cjs +0 -379
- package/scripts/verify-job-order.sh +0 -30
- package/src/cli/index.ts +0 -414
- package/src/generators/init.tpl.ts +0 -126
- package/src/generators/workflows.tpl.ts +0 -80
- package/src/templates/actions/calculate-version.yml.tpl.ts +0 -124
- package/src/templates/actions/create-pr.yml.tpl.ts +0 -147
- package/src/templates/actions/create-tag.yml.tpl.ts +0 -112
- package/src/templates/actions/detect-changes.yml.tpl.ts +0 -87
- package/src/templates/actions/manage-branch.yml.tpl.ts +0 -132
- package/src/templates/actions/promote-branch.yml.tpl.ts +0 -364
- package/src/templates/workflows/pipeline-path-based.yml.tpl.ts +0 -706
- package/src/types/index.ts +0 -64
- package/src/utils/README-ast-path-operations.md +0 -390
- package/src/utils/ast-path-operations.ts +0 -581
- package/src/utils/config.ts +0 -64
- package/src/utils/github-setup.ts +0 -558
- package/src/utils/idempotency.ts +0 -215
- package/src/utils/preflight.ts +0 -306
- package/src/utils/versioning.ts +0 -244
- package/tests/README.md +0 -229
- package/tests/TEST_STRUCTURE.md +0 -256
- package/tests/act/run-act-tests.sh +0 -345
- package/tests/debugging/debug-utils.ts +0 -538
- package/tests/debugging/debug-workflow.test.ts +0 -339
- package/tests/debugging/debug-workflows.sh +0 -339
- package/tests/debugging/iterative-debug.ts +0 -652
- package/tests/debugging/run-debug-tests.sh +0 -431
- package/tests/fixtures/basic-config.json +0 -51
- package/tests/fixtures/invalid-config.json +0 -9
- package/tests/fixtures/pipeline-generated.yml +0 -235
- package/tests/fixtures/pipeline-preserve-comments.yml +0 -245
- package/tests/fixtures/pipeline-user-modified.yml +0 -245
- package/tests/fixtures/test-config.json +0 -58
- package/tests/github-live/README.md +0 -250
- package/tests/github-local/JOB_WORKFLOW_TESTS.md +0 -296
- package/tests/github-local/README.md +0 -240
- package/tests/github-local/run-all-tests.sh +0 -422
- package/tests/github-local/test-job-workflows.sh +0 -631
- package/tests/github-local/test-pipeline-workflow.sh +0 -440
- package/tests/integration/generators.test.ts +0 -578
- package/tests/integration/path-based-template.test.ts +0 -510
- package/tests/integration/simple-path-based.test.ts +0 -415
- package/tests/setup.ts +0 -56
- package/tests/unit/ast-path-operations-extended.test.ts +0 -302
- package/tests/unit/cli.test.ts +0 -541
- package/tests/unit/config-extended.test.ts +0 -412
- package/tests/unit/config.test.ts +0 -152
- package/tests/unit/github-setup.test.ts +0 -189
- package/tests/unit/idempotency-isolated.test.ts +0 -297
- package/tests/unit/job-order.test.ts +0 -157
- package/tests/unit/pipeline-path-based.test.ts +0 -511
- package/tests/unit/validate-pipeline.test.ts +0 -384
- package/tests/unit/versioning-extended.test.ts +0 -279
- package/tests/unit/versioning.test.ts +0 -241
- package/tsconfig.debug.json +0 -17
- package/tsconfig.json +0 -28
- package/vitest.config.ts +0 -51
|
@@ -1,538 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub Actions Debugging Utilities
|
|
3
|
-
*
|
|
4
|
-
* This module provides TypeScript utilities for analyzing GitHub Actions
|
|
5
|
-
* workflow failures, parsing logs, and generating debugging reports.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { execSync } from 'child_process';
|
|
9
|
-
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
10
|
-
import { join } from 'path';
|
|
11
|
-
|
|
12
|
-
export interface WorkflowRun {
|
|
13
|
-
id: number;
|
|
14
|
-
status: string;
|
|
15
|
-
conclusion: string | null;
|
|
16
|
-
created_at: string;
|
|
17
|
-
html_url: string;
|
|
18
|
-
workflow_id: number;
|
|
19
|
-
head_branch: string;
|
|
20
|
-
head_sha: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface Job {
|
|
24
|
-
id: number;
|
|
25
|
-
name: string;
|
|
26
|
-
status: string;
|
|
27
|
-
conclusion: string | null;
|
|
28
|
-
html_url: string;
|
|
29
|
-
steps: JobStep[];
|
|
30
|
-
started_at: string;
|
|
31
|
-
completed_at: string | null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface JobStep {
|
|
35
|
-
name: string;
|
|
36
|
-
status: string;
|
|
37
|
-
conclusion: string | null;
|
|
38
|
-
number: number;
|
|
39
|
-
started_at: string;
|
|
40
|
-
completed_at: string | null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface DebugAnalysis {
|
|
44
|
-
runId: number;
|
|
45
|
-
status: string;
|
|
46
|
-
conclusion: string | null;
|
|
47
|
-
failedJobs: Job[];
|
|
48
|
-
totalJobs: number;
|
|
49
|
-
successRate: number;
|
|
50
|
-
commonFailurePatterns: string[];
|
|
51
|
-
recommendations: string[];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface LogAnalysis {
|
|
55
|
-
jobId: number;
|
|
56
|
-
jobName: string;
|
|
57
|
-
errorCount: number;
|
|
58
|
-
warningCount: number;
|
|
59
|
-
criticalErrors: string[];
|
|
60
|
-
commonErrors: string[];
|
|
61
|
-
executionTime: number;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* GitHub API client for fetching workflow data
|
|
66
|
-
*/
|
|
67
|
-
export class GitHubWorkflowDebugger {
|
|
68
|
-
private token: string;
|
|
69
|
-
private owner: string;
|
|
70
|
-
private repo: string;
|
|
71
|
-
private apiBase = 'https://api.github.com';
|
|
72
|
-
|
|
73
|
-
constructor(token: string, owner: string, repo: string) {
|
|
74
|
-
this.token = token;
|
|
75
|
-
this.owner = owner;
|
|
76
|
-
this.repo = repo;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Make authenticated GitHub API request
|
|
81
|
-
*/
|
|
82
|
-
private async apiRequest(endpoint: string, method: string = 'GET'): Promise<any> {
|
|
83
|
-
const url = `${this.apiBase}/repos/${this.owner}/${this.repo}${endpoint}`;
|
|
84
|
-
|
|
85
|
-
const response = await fetch(url, {
|
|
86
|
-
method,
|
|
87
|
-
headers: {
|
|
88
|
-
'Authorization': `token ${this.token}`,
|
|
89
|
-
'Accept': 'application/vnd.github.v3+json',
|
|
90
|
-
'User-Agent': 'pipecraft-debugger'
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
if (!response.ok) {
|
|
95
|
-
throw new Error(`GitHub API request failed: ${response.status} ${response.statusText}`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return response.json();
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Get recent workflow runs
|
|
103
|
-
*/
|
|
104
|
-
async getWorkflowRuns(workflowName: string, branch?: string, limit: number = 10): Promise<WorkflowRun[]> {
|
|
105
|
-
const endpoint = `/actions/workflows/${workflowName}/runs?per_page=${limit}${branch ? `&branch=${branch}` : ''}`;
|
|
106
|
-
const response = await this.apiRequest(endpoint);
|
|
107
|
-
return response.workflow_runs;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get detailed workflow run information
|
|
112
|
-
*/
|
|
113
|
-
async getWorkflowRun(runId: number): Promise<WorkflowRun> {
|
|
114
|
-
return this.apiRequest(`/actions/runs/${runId}`);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Get jobs for a workflow run
|
|
119
|
-
*/
|
|
120
|
-
async getWorkflowJobs(runId: number): Promise<Job[]> {
|
|
121
|
-
const response = await this.apiRequest(`/actions/runs/${runId}/jobs`);
|
|
122
|
-
return response.jobs;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Download job logs
|
|
127
|
-
*/
|
|
128
|
-
async getJobLogs(runId: number, jobId: number): Promise<string> {
|
|
129
|
-
const url = `${this.apiBase}/repos/${this.owner}/${this.repo}/actions/runs/${runId}/jobs/${jobId}/logs`;
|
|
130
|
-
|
|
131
|
-
const response = await fetch(url, {
|
|
132
|
-
headers: {
|
|
133
|
-
'Authorization': `token ${this.token}`,
|
|
134
|
-
'Accept': 'application/vnd.github.v3+json',
|
|
135
|
-
'User-Agent': 'pipecraft-debugger'
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
if (!response.ok) {
|
|
140
|
-
throw new Error(`Failed to download logs: ${response.status} ${response.statusText}`);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return response.text();
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Analyze workflow failures and generate insights
|
|
149
|
-
*/
|
|
150
|
-
export class WorkflowAnalyzer {
|
|
151
|
-
/**
|
|
152
|
-
* Analyze a workflow run for failures
|
|
153
|
-
*/
|
|
154
|
-
static analyzeWorkflowRun(run: WorkflowRun, jobs: Job[]): DebugAnalysis {
|
|
155
|
-
const failedJobs = jobs.filter(job =>
|
|
156
|
-
job.conclusion === 'failure' || job.conclusion === 'cancelled'
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
const successRate = jobs.length > 0 ?
|
|
160
|
-
((jobs.length - failedJobs.length) / jobs.length) * 100 : 0;
|
|
161
|
-
|
|
162
|
-
const commonFailurePatterns = this.extractFailurePatterns(failedJobs);
|
|
163
|
-
const recommendations = this.generateRecommendations(failedJobs, commonFailurePatterns);
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
runId: run.id,
|
|
167
|
-
status: run.status,
|
|
168
|
-
conclusion: run.conclusion,
|
|
169
|
-
failedJobs,
|
|
170
|
-
totalJobs: jobs.length,
|
|
171
|
-
successRate,
|
|
172
|
-
commonFailurePatterns,
|
|
173
|
-
recommendations
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Extract common failure patterns from failed jobs
|
|
179
|
-
*/
|
|
180
|
-
private static extractFailurePatterns(failedJobs: Job[]): string[] {
|
|
181
|
-
const patterns: string[] = [];
|
|
182
|
-
const stepFailures = new Map<string, number>();
|
|
183
|
-
|
|
184
|
-
failedJobs.forEach(job => {
|
|
185
|
-
job.steps.forEach(step => {
|
|
186
|
-
if (step.conclusion === 'failure') {
|
|
187
|
-
const pattern = this.categorizeFailure(step.name);
|
|
188
|
-
stepFailures.set(pattern, (stepFailures.get(pattern) || 0) + 1);
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
// Sort by frequency and return top patterns
|
|
194
|
-
return Array.from(stepFailures.entries())
|
|
195
|
-
.sort((a, b) => b[1] - a[1])
|
|
196
|
-
.slice(0, 5)
|
|
197
|
-
.map(([pattern]) => pattern);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Categorize failure types based on step names
|
|
202
|
-
*/
|
|
203
|
-
private static categorizeFailure(stepName: string): string {
|
|
204
|
-
const name = stepName.toLowerCase();
|
|
205
|
-
|
|
206
|
-
if (name.includes('test') || name.includes('spec')) return 'Test Failures';
|
|
207
|
-
if (name.includes('build') || name.includes('compile')) return 'Build Failures';
|
|
208
|
-
if (name.includes('lint') || name.includes('format')) return 'Linting Issues';
|
|
209
|
-
if (name.includes('deploy') || name.includes('release')) return 'Deployment Issues';
|
|
210
|
-
if (name.includes('install') || name.includes('dependencies')) return 'Dependency Issues';
|
|
211
|
-
if (name.includes('docker') || name.includes('container')) return 'Docker Issues';
|
|
212
|
-
if (name.includes('security') || name.includes('scan')) return 'Security Issues';
|
|
213
|
-
|
|
214
|
-
return 'Other Failures';
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Generate recommendations based on failure analysis
|
|
219
|
-
*/
|
|
220
|
-
private static generateRecommendations(failedJobs: Job[], patterns: string[]): string[] {
|
|
221
|
-
const recommendations: string[] = [];
|
|
222
|
-
|
|
223
|
-
if (patterns.includes('Test Failures')) {
|
|
224
|
-
recommendations.push('Review test cases and ensure they are up to date');
|
|
225
|
-
recommendations.push('Check for flaky tests and add retry mechanisms');
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (patterns.includes('Build Failures')) {
|
|
229
|
-
recommendations.push('Verify build dependencies and versions');
|
|
230
|
-
recommendations.push('Check for breaking changes in dependencies');
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (patterns.includes('Linting Issues')) {
|
|
234
|
-
recommendations.push('Run linting locally before pushing');
|
|
235
|
-
recommendations.push('Configure pre-commit hooks for automatic linting');
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (patterns.includes('Dependency Issues')) {
|
|
239
|
-
recommendations.push('Update package-lock.json or yarn.lock');
|
|
240
|
-
recommendations.push('Check for version conflicts in dependencies');
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (patterns.includes('Docker Issues')) {
|
|
244
|
-
recommendations.push('Verify Dockerfile syntax and base images');
|
|
245
|
-
recommendations.push('Check for resource constraints in CI environment');
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (failedJobs.length > 3) {
|
|
249
|
-
recommendations.push('Consider breaking down large workflows into smaller, focused jobs');
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return recommendations;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Analyze job logs for patterns and errors
|
|
258
|
-
*/
|
|
259
|
-
export class LogAnalyzer {
|
|
260
|
-
/**
|
|
261
|
-
* Analyze job logs for errors and patterns
|
|
262
|
-
*/
|
|
263
|
-
static analyzeLogs(logs: string, jobName: string): LogAnalysis {
|
|
264
|
-
const lines = logs.split('\n');
|
|
265
|
-
const errorCount = lines.filter(line =>
|
|
266
|
-
line.toLowerCase().includes('error') ||
|
|
267
|
-
line.toLowerCase().includes('failed') ||
|
|
268
|
-
line.toLowerCase().includes('exception')
|
|
269
|
-
).length;
|
|
270
|
-
|
|
271
|
-
const warningCount = lines.filter(line =>
|
|
272
|
-
line.toLowerCase().includes('warning') ||
|
|
273
|
-
line.toLowerCase().includes('warn')
|
|
274
|
-
).length;
|
|
275
|
-
|
|
276
|
-
const criticalErrors = this.extractCriticalErrors(lines);
|
|
277
|
-
const commonErrors = this.extractCommonErrors(lines);
|
|
278
|
-
const executionTime = this.calculateExecutionTime(lines);
|
|
279
|
-
|
|
280
|
-
return {
|
|
281
|
-
jobId: 0, // Will be set by caller
|
|
282
|
-
jobName,
|
|
283
|
-
errorCount,
|
|
284
|
-
warningCount,
|
|
285
|
-
criticalErrors,
|
|
286
|
-
commonErrors,
|
|
287
|
-
executionTime
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Extract critical errors from logs
|
|
293
|
-
*/
|
|
294
|
-
private static extractCriticalErrors(lines: string[]): string[] {
|
|
295
|
-
const criticalPatterns = [
|
|
296
|
-
/fatal error/i,
|
|
297
|
-
/segmentation fault/i,
|
|
298
|
-
/out of memory/i,
|
|
299
|
-
/connection refused/i,
|
|
300
|
-
/permission denied/i,
|
|
301
|
-
/file not found/i,
|
|
302
|
-
/timeout/i
|
|
303
|
-
];
|
|
304
|
-
|
|
305
|
-
const errors: string[] = [];
|
|
306
|
-
lines.forEach(line => {
|
|
307
|
-
criticalPatterns.forEach(pattern => {
|
|
308
|
-
if (pattern.test(line)) {
|
|
309
|
-
errors.push(line.trim());
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
return [...new Set(errors)]; // Remove duplicates
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Extract common error patterns
|
|
319
|
-
*/
|
|
320
|
-
private static extractCommonErrors(lines: string[]): string[] {
|
|
321
|
-
const errorMap = new Map<string, number>();
|
|
322
|
-
|
|
323
|
-
lines.forEach(line => {
|
|
324
|
-
if (line.toLowerCase().includes('error')) {
|
|
325
|
-
// Extract error type (first few words after "error")
|
|
326
|
-
const match = line.match(/error[:\s]+([^:]+)/i);
|
|
327
|
-
if (match) {
|
|
328
|
-
const errorType = match[1].trim().split(' ').slice(0, 3).join(' ');
|
|
329
|
-
errorMap.set(errorType, (errorMap.get(errorType) || 0) + 1);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
return Array.from(errorMap.entries())
|
|
335
|
-
.sort((a, b) => b[1] - a[1])
|
|
336
|
-
.slice(0, 5)
|
|
337
|
-
.map(([error]) => error);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Calculate execution time from logs
|
|
342
|
-
*/
|
|
343
|
-
private static calculateExecutionTime(lines: string[]): number {
|
|
344
|
-
const timePattern = /(\d+):(\d+):(\d+)/;
|
|
345
|
-
const times: number[] = [];
|
|
346
|
-
|
|
347
|
-
lines.forEach(line => {
|
|
348
|
-
const match = line.match(timePattern);
|
|
349
|
-
if (match) {
|
|
350
|
-
const [, hours, minutes, seconds] = match;
|
|
351
|
-
const totalSeconds = parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseInt(seconds);
|
|
352
|
-
times.push(totalSeconds);
|
|
353
|
-
}
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
if (times.length < 2) return 0;
|
|
357
|
-
|
|
358
|
-
const start = Math.min(...times);
|
|
359
|
-
const end = Math.max(...times);
|
|
360
|
-
return end - start;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Generate comprehensive debug reports
|
|
366
|
-
*/
|
|
367
|
-
export class DebugReportGenerator {
|
|
368
|
-
/**
|
|
369
|
-
* Generate a comprehensive debug report
|
|
370
|
-
*/
|
|
371
|
-
static async generateReport(
|
|
372
|
-
analysis: DebugAnalysis,
|
|
373
|
-
logAnalyses: LogAnalysis[],
|
|
374
|
-
outputDir: string
|
|
375
|
-
): Promise<string> {
|
|
376
|
-
const reportPath = join(outputDir, 'debug-report.md');
|
|
377
|
-
|
|
378
|
-
if (!existsSync(outputDir)) {
|
|
379
|
-
mkdirSync(outputDir, { recursive: true });
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const report = this.buildMarkdownReport(analysis, logAnalyses);
|
|
383
|
-
writeFileSync(reportPath, report);
|
|
384
|
-
|
|
385
|
-
return reportPath;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Build markdown report content
|
|
390
|
-
*/
|
|
391
|
-
private static buildMarkdownReport(analysis: DebugAnalysis, logAnalyses: LogAnalysis[]): string {
|
|
392
|
-
const timestamp = new Date().toISOString();
|
|
393
|
-
|
|
394
|
-
return `# GitHub Actions Debug Report
|
|
395
|
-
|
|
396
|
-
Generated: ${timestamp}
|
|
397
|
-
Workflow Run: ${analysis.runId}
|
|
398
|
-
Status: ${analysis.status}
|
|
399
|
-
Conclusion: ${analysis.conclusion || 'N/A'}
|
|
400
|
-
|
|
401
|
-
## Summary
|
|
402
|
-
|
|
403
|
-
- **Total Jobs**: ${analysis.totalJobs}
|
|
404
|
-
- **Failed Jobs**: ${analysis.failedJobs.length}
|
|
405
|
-
- **Success Rate**: ${analysis.successRate.toFixed(1)}%
|
|
406
|
-
|
|
407
|
-
## Failed Jobs
|
|
408
|
-
|
|
409
|
-
${analysis.failedJobs.map(job => `
|
|
410
|
-
### ${job.name}
|
|
411
|
-
- **Status**: ${job.status}
|
|
412
|
-
- **Conclusion**: ${job.conclusion}
|
|
413
|
-
- **URL**: ${job.html_url}
|
|
414
|
-
- **Duration**: ${this.formatDuration(job.started_at, job.completed_at)}
|
|
415
|
-
|
|
416
|
-
#### Failed Steps
|
|
417
|
-
${job.steps.filter(step => step.conclusion === 'failure').map(step => `
|
|
418
|
-
- ${step.name} (${step.conclusion})
|
|
419
|
-
`).join('')}
|
|
420
|
-
`).join('')}
|
|
421
|
-
|
|
422
|
-
## Common Failure Patterns
|
|
423
|
-
|
|
424
|
-
${analysis.commonFailurePatterns.map(pattern => `- ${pattern}`).join('\n')}
|
|
425
|
-
|
|
426
|
-
## Recommendations
|
|
427
|
-
|
|
428
|
-
${analysis.recommendations.map(rec => `- ${rec}`).join('\n')}
|
|
429
|
-
|
|
430
|
-
## Log Analysis
|
|
431
|
-
|
|
432
|
-
${logAnalyses.map(log => `
|
|
433
|
-
### ${log.jobName}
|
|
434
|
-
- **Errors**: ${log.errorCount}
|
|
435
|
-
- **Warnings**: ${log.warningCount}
|
|
436
|
-
- **Execution Time**: ${log.executionTime}s
|
|
437
|
-
|
|
438
|
-
#### Critical Errors
|
|
439
|
-
${log.criticalErrors.map(error => `- ${error}`).join('\n')}
|
|
440
|
-
|
|
441
|
-
#### Common Errors
|
|
442
|
-
${log.commonErrors.map(error => `- ${error}`).join('\n')}
|
|
443
|
-
`).join('')}
|
|
444
|
-
|
|
445
|
-
## Next Steps
|
|
446
|
-
|
|
447
|
-
1. Review failed jobs and their logs
|
|
448
|
-
2. Address common failure patterns
|
|
449
|
-
3. Implement recommended improvements
|
|
450
|
-
4. Test changes locally before pushing
|
|
451
|
-
5. Consider adding more granular job dependencies
|
|
452
|
-
|
|
453
|
-
---
|
|
454
|
-
*Report generated by Pipecraft Debug Tools*
|
|
455
|
-
`;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* Format duration between two timestamps
|
|
460
|
-
*/
|
|
461
|
-
private static formatDuration(start: string, end: string | null): string {
|
|
462
|
-
if (!end) return 'In progress';
|
|
463
|
-
|
|
464
|
-
const startTime = new Date(start).getTime();
|
|
465
|
-
const endTime = new Date(end).getTime();
|
|
466
|
-
const durationMs = endTime - startTime;
|
|
467
|
-
|
|
468
|
-
const minutes = Math.floor(durationMs / 60000);
|
|
469
|
-
const seconds = Math.floor((durationMs % 60000) / 1000);
|
|
470
|
-
|
|
471
|
-
return `${minutes}m ${seconds}s`;
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* Main debugging orchestrator
|
|
477
|
-
*/
|
|
478
|
-
export class WorkflowDebugOrchestrator {
|
|
479
|
-
private debugger: GitHubWorkflowDebugger;
|
|
480
|
-
private outputDir: string;
|
|
481
|
-
|
|
482
|
-
constructor(token: string, owner: string, repo: string, outputDir: string = './debug-output') {
|
|
483
|
-
this.debugger = new GitHubWorkflowDebugger(token, owner, repo);
|
|
484
|
-
this.outputDir = outputDir;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* Run comprehensive workflow debugging
|
|
489
|
-
*/
|
|
490
|
-
async debugWorkflow(workflowName: string, branch?: string, limit: number = 10): Promise<string> {
|
|
491
|
-
console.log(`🔍 Debugging workflow: ${workflowName}`);
|
|
492
|
-
|
|
493
|
-
// Get recent runs
|
|
494
|
-
const runs = await this.debugger.getWorkflowRuns(workflowName, branch, limit);
|
|
495
|
-
|
|
496
|
-
if (runs.length === 0) {
|
|
497
|
-
throw new Error('No workflow runs found');
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// Find failed runs
|
|
501
|
-
const failedRuns = runs.filter(run =>
|
|
502
|
-
run.conclusion === 'failure' || run.conclusion === 'cancelled'
|
|
503
|
-
);
|
|
504
|
-
|
|
505
|
-
if (failedRuns.length === 0) {
|
|
506
|
-
console.log('✅ No failed runs found');
|
|
507
|
-
return '';
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
console.log(`📊 Found ${failedRuns.length} failed runs`);
|
|
511
|
-
|
|
512
|
-
// Analyze the most recent failed run
|
|
513
|
-
const latestFailedRun = failedRuns[0];
|
|
514
|
-
const jobs = await this.debugger.getWorkflowJobs(latestFailedRun.id);
|
|
515
|
-
|
|
516
|
-
// Perform analysis
|
|
517
|
-
const analysis = WorkflowAnalyzer.analyzeWorkflowRun(latestFailedRun, jobs);
|
|
518
|
-
|
|
519
|
-
// Analyze logs for failed jobs
|
|
520
|
-
const logAnalyses: LogAnalysis[] = [];
|
|
521
|
-
for (const job of analysis.failedJobs) {
|
|
522
|
-
try {
|
|
523
|
-
const logs = await this.debugger.getJobLogs(latestFailedRun.id, job.id);
|
|
524
|
-
const logAnalysis = LogAnalyzer.analyzeLogs(logs, job.name);
|
|
525
|
-
logAnalysis.jobId = job.id;
|
|
526
|
-
logAnalyses.push(logAnalysis);
|
|
527
|
-
} catch (error) {
|
|
528
|
-
console.warn(`⚠️ Could not fetch logs for job ${job.name}: ${error}`);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// Generate report
|
|
533
|
-
const reportPath = await DebugReportGenerator.generateReport(analysis, logAnalyses, this.outputDir);
|
|
534
|
-
|
|
535
|
-
console.log(`📄 Debug report generated: ${reportPath}`);
|
|
536
|
-
return reportPath;
|
|
537
|
-
}
|
|
538
|
-
}
|