specweave 0.17.12 → 0.17.15
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.md +456 -0
- package/README.md +38 -1
- package/dist/src/cli/commands/cicd-monitor.d.ts +11 -0
- package/dist/src/cli/commands/cicd-monitor.d.ts.map +1 -0
- package/dist/src/cli/commands/cicd-monitor.js +154 -0
- package/dist/src/cli/commands/cicd-monitor.js.map +1 -0
- package/dist/src/cli/commands/validate-parent-repo.d.ts +8 -0
- package/dist/src/cli/commands/validate-parent-repo.d.ts.map +1 -0
- package/dist/src/cli/commands/validate-parent-repo.js +15 -0
- package/dist/src/cli/commands/validate-parent-repo.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/types.d.ts +5 -0
- package/dist/src/cli/helpers/issue-tracker/types.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/types.js.map +1 -1
- package/dist/src/core/cicd/config-loader.d.ts +21 -0
- package/dist/src/core/cicd/config-loader.d.ts.map +1 -0
- package/dist/src/core/cicd/config-loader.js +190 -0
- package/dist/src/core/cicd/config-loader.js.map +1 -0
- package/dist/src/core/cicd/index.d.ts +12 -0
- package/dist/src/core/cicd/index.d.ts.map +1 -0
- package/dist/src/core/cicd/index.js +18 -0
- package/dist/src/core/cicd/index.js.map +1 -0
- package/dist/src/core/cicd/monitor-service.d.ts +92 -0
- package/dist/src/core/cicd/monitor-service.d.ts.map +1 -0
- package/dist/src/core/cicd/monitor-service.js +132 -0
- package/dist/src/core/cicd/monitor-service.js.map +1 -0
- package/dist/src/core/cicd/notifier.d.ts +102 -0
- package/dist/src/core/cicd/notifier.d.ts.map +1 -0
- package/dist/src/core/cicd/notifier.js +184 -0
- package/dist/src/core/cicd/notifier.js.map +1 -0
- package/dist/src/core/cicd/parent-repo-validator.d.ts +42 -0
- package/dist/src/core/cicd/parent-repo-validator.d.ts.map +1 -0
- package/dist/src/core/cicd/parent-repo-validator.js +201 -0
- package/dist/src/core/cicd/parent-repo-validator.js.map +1 -0
- package/dist/src/core/cicd/state-manager.d.ts +79 -0
- package/dist/src/core/cicd/state-manager.d.ts.map +1 -0
- package/dist/src/core/cicd/state-manager.js +197 -0
- package/dist/src/core/cicd/state-manager.js.map +1 -0
- package/dist/src/core/cicd/types.d.ts +119 -0
- package/dist/src/core/cicd/types.d.ts.map +1 -0
- package/dist/src/core/cicd/types.js +18 -0
- package/dist/src/core/cicd/types.js.map +1 -0
- package/dist/src/core/cicd/workflow-monitor.d.ts +98 -0
- package/dist/src/core/cicd/workflow-monitor.d.ts.map +1 -0
- package/dist/src/core/cicd/workflow-monitor.js +215 -0
- package/dist/src/core/cicd/workflow-monitor.js.map +1 -0
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts +3 -0
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.js +5 -2
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- package/dist/src/core/spec-content-sync.d.ts.map +1 -1
- package/dist/src/core/spec-content-sync.js +14 -6
- package/dist/src/core/spec-content-sync.js.map +1 -1
- package/package.json +2 -2
- package/plugins/specweave/agents/pm/AGENT.md +170 -2
- package/plugins/specweave/hooks/post-increment-planning.sh +89 -26
- package/plugins/specweave/hooks/user-prompt-submit.sh +4 -4
- package/plugins/specweave/skills/increment-planner/SKILL.md +15 -10
- package/plugins/specweave-ado/lib/ado-project-detector.js +32 -5
- package/plugins/specweave-ado/lib/ado-project-detector.ts +44 -5
- package/src/templates/AGENTS.md.template +55 -1
- package/src/templates/CLAUDE.md.template +51 -1
- package/dist/locales/de/.gitkeep +0 -0
- package/dist/locales/de/cli.json +0 -108
- package/dist/locales/en/cli.json +0 -287
- package/dist/locales/en/errors.json +0 -7
- package/dist/locales/en/templates.json +0 -6
- package/dist/locales/es/.gitkeep +0 -0
- package/dist/locales/es/cli.json +0 -41
- package/dist/locales/fr/.gitkeep +0 -0
- package/dist/locales/fr/cli.json +0 -108
- package/dist/locales/ja/.gitkeep +0 -0
- package/dist/locales/ja/cli.json +0 -108
- package/dist/locales/ko/.gitkeep +0 -0
- package/dist/locales/ko/cli.json +0 -108
- package/dist/locales/pt/.gitkeep +0 -0
- package/dist/locales/pt/cli.json +0 -108
- package/dist/locales/ru/.gitkeep +0 -0
- package/dist/locales/ru/cli.json +0 -269
- package/dist/locales/zh/.gitkeep +0 -0
- package/dist/locales/zh/cli.json +0 -108
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI/CD Monitor State Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages persistent state for workflow monitoring, failure detection,
|
|
5
|
+
* and deduplication. Uses file-based storage with locking to prevent
|
|
6
|
+
* concurrent write corruption.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'fs-extra';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { DEFAULT_STATE } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* State file path (relative to project root)
|
|
13
|
+
*/
|
|
14
|
+
const STATE_FILE = '.specweave/state/cicd-monitor.json';
|
|
15
|
+
/**
|
|
16
|
+
* Lock file path (prevents concurrent writes)
|
|
17
|
+
*/
|
|
18
|
+
const LOCK_FILE = '.specweave/state/cicd-monitor.lock';
|
|
19
|
+
/**
|
|
20
|
+
* Lock timeout (milliseconds)
|
|
21
|
+
*/
|
|
22
|
+
const LOCK_TIMEOUT = 5000;
|
|
23
|
+
/**
|
|
24
|
+
* StateManager - Manages CI/CD monitor persistent state
|
|
25
|
+
*
|
|
26
|
+
* Features:
|
|
27
|
+
* - File-based JSON storage
|
|
28
|
+
* - File locking to prevent corruption
|
|
29
|
+
* - Automatic directory creation
|
|
30
|
+
* - State migration support
|
|
31
|
+
* - Deduplication tracking
|
|
32
|
+
*/
|
|
33
|
+
export class StateManager {
|
|
34
|
+
/**
|
|
35
|
+
* Create state manager
|
|
36
|
+
*
|
|
37
|
+
* @param rootDir - Project root directory (defaults to cwd)
|
|
38
|
+
*/
|
|
39
|
+
constructor(rootDir = process.cwd()) {
|
|
40
|
+
this.statePath = path.join(rootDir, STATE_FILE);
|
|
41
|
+
this.lockPath = path.join(rootDir, LOCK_FILE);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Load state from disk
|
|
45
|
+
*
|
|
46
|
+
* @returns Current state (or default if file doesn't exist)
|
|
47
|
+
*/
|
|
48
|
+
async loadState() {
|
|
49
|
+
try {
|
|
50
|
+
// Ensure directory exists
|
|
51
|
+
await fs.ensureDir(path.dirname(this.statePath));
|
|
52
|
+
// Check if state file exists
|
|
53
|
+
if (!(await fs.pathExists(this.statePath))) {
|
|
54
|
+
// Initialize with default state
|
|
55
|
+
await this.saveState(DEFAULT_STATE);
|
|
56
|
+
return { ...DEFAULT_STATE };
|
|
57
|
+
}
|
|
58
|
+
// Read and parse state
|
|
59
|
+
const content = await fs.readFile(this.statePath, 'utf-8');
|
|
60
|
+
const state = JSON.parse(content);
|
|
61
|
+
// Validate state structure
|
|
62
|
+
if (!state.failures || !state.version) {
|
|
63
|
+
console.warn('Invalid state file, resetting to default');
|
|
64
|
+
return { ...DEFAULT_STATE };
|
|
65
|
+
}
|
|
66
|
+
return state;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error('Error loading state:', error);
|
|
70
|
+
return { ...DEFAULT_STATE };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Save state to disk with file locking
|
|
75
|
+
*
|
|
76
|
+
* @param state - State to save
|
|
77
|
+
*/
|
|
78
|
+
async saveState(state) {
|
|
79
|
+
// Acquire lock
|
|
80
|
+
await this.acquireLock();
|
|
81
|
+
try {
|
|
82
|
+
// Ensure directory exists
|
|
83
|
+
await fs.ensureDir(path.dirname(this.statePath));
|
|
84
|
+
// Write state atomically (write to temp, then rename)
|
|
85
|
+
const tempPath = `${this.statePath}.tmp`;
|
|
86
|
+
await fs.writeFile(tempPath, JSON.stringify(state, null, 2), 'utf-8');
|
|
87
|
+
await fs.rename(tempPath, this.statePath);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
// Always release lock
|
|
91
|
+
await this.releaseLock();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Mark failure as processed (deduplication)
|
|
96
|
+
*
|
|
97
|
+
* @param runId - Workflow run ID
|
|
98
|
+
*/
|
|
99
|
+
async markProcessed(runId) {
|
|
100
|
+
const state = await this.loadState();
|
|
101
|
+
if (state.failures[runId]) {
|
|
102
|
+
state.failures[runId].processed = true;
|
|
103
|
+
state.totalProcessed++;
|
|
104
|
+
await this.saveState(state);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get last poll timestamp
|
|
109
|
+
*
|
|
110
|
+
* @returns ISO 8601 timestamp or null
|
|
111
|
+
*/
|
|
112
|
+
async getLastPoll() {
|
|
113
|
+
const state = await this.loadState();
|
|
114
|
+
return state.lastPoll;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Update last poll timestamp to now
|
|
118
|
+
*/
|
|
119
|
+
async updateLastPoll() {
|
|
120
|
+
const state = await this.loadState();
|
|
121
|
+
state.lastPoll = new Date().toISOString();
|
|
122
|
+
await this.saveState(state);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Add failure record
|
|
126
|
+
*
|
|
127
|
+
* @param failure - Failure record to add
|
|
128
|
+
*/
|
|
129
|
+
async addFailure(failure) {
|
|
130
|
+
const state = await this.loadState();
|
|
131
|
+
// Check for duplicate
|
|
132
|
+
if (state.failures[failure.runId]) {
|
|
133
|
+
console.log(`Failure ${failure.runId} already tracked, skipping`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
// Add failure
|
|
137
|
+
state.failures[failure.runId] = failure;
|
|
138
|
+
state.totalFailures++;
|
|
139
|
+
await this.saveState(state);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get all unprocessed failures
|
|
143
|
+
*
|
|
144
|
+
* @returns Array of unprocessed failure records
|
|
145
|
+
*/
|
|
146
|
+
async getUnprocessedFailures() {
|
|
147
|
+
const state = await this.loadState();
|
|
148
|
+
return Object.values(state.failures).filter((failure) => !failure.processed);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Acquire file lock (with timeout)
|
|
152
|
+
*
|
|
153
|
+
* Prevents concurrent writes from corrupting state file.
|
|
154
|
+
*/
|
|
155
|
+
async acquireLock() {
|
|
156
|
+
const startTime = Date.now();
|
|
157
|
+
while (true) {
|
|
158
|
+
try {
|
|
159
|
+
// Try to create lock file (exclusive)
|
|
160
|
+
await fs.writeFile(this.lockPath, process.pid.toString(), {
|
|
161
|
+
flag: 'wx'
|
|
162
|
+
});
|
|
163
|
+
// Lock acquired!
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
// Lock file already exists
|
|
168
|
+
if (error.code === 'EEXIST') {
|
|
169
|
+
// Check for timeout
|
|
170
|
+
if (Date.now() - startTime > LOCK_TIMEOUT) {
|
|
171
|
+
// Force release stale lock
|
|
172
|
+
console.warn('Lock timeout, forcing release');
|
|
173
|
+
await this.releaseLock();
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
// Wait and retry
|
|
177
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
// Other error
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Release file lock
|
|
187
|
+
*/
|
|
188
|
+
async releaseLock() {
|
|
189
|
+
try {
|
|
190
|
+
await fs.remove(this.lockPath);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
// Ignore errors (lock file might not exist)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=state-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-manager.js","sourceRoot":"","sources":["../../../../src/core/cicd/state-manager.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAEL,aAAa,EAGd,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,MAAM,UAAU,GAAG,oCAAoC,CAAC;AAExD;;GAEG;AACH,MAAM,SAAS,GAAG,oCAAoC,CAAC;AAEvD;;GAEG;AACH,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B;;;;;;;;;GASG;AACH,MAAM,OAAO,YAAY;IAIvB;;;;OAIG;IACH,YAAY,UAAkB,OAAO,CAAC,GAAG,EAAE;QACzC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAChD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAChD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,SAAS;QACb,IAAI,CAAC;YACH,0BAA0B;YAC1B,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;YAEjD,6BAA6B;YAC7B,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;gBAC3C,gCAAgC;gBAChC,MAAM,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;gBACpC,OAAO,EAAE,GAAG,aAAa,EAAE,CAAC;YAC9B,CAAC;YAED,uBAAuB;YACvB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAqB,CAAC;YAEtD,2BAA2B;YAC3B,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;gBACtC,OAAO,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;gBACzD,OAAO,EAAE,GAAG,aAAa,EAAE,CAAC;YAC9B,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;YAC7C,OAAO,EAAE,GAAG,aAAa,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,SAAS,CAAC,KAAuB;QACrC,eAAe;QACf,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAEzB,IAAI,CAAC;YACH,0BAA0B;YAC1B,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;YAEjD,sDAAsD;YACtD,MAAM,QAAQ,GAAG,GAAG,IAAI,CAAC,SAAS,MAAM,CAAC;YACzC,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YACtE,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5C,CAAC;gBAAS,CAAC;YACT,sBAAsB;YACtB,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,aAAa,CAAC,KAAa;QAC/B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAErC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC;YACvC,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW;QACf,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACrC,OAAO,KAAK,CAAC,QAAQ,CAAC;IACxB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc;QAClB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACrC,KAAK,CAAC,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC1C,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAU,CAAC,OAAsB;QACrC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAErC,sBAAsB;QACtB,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,WAAW,OAAO,CAAC,KAAK,4BAA4B,CAAC,CAAC;YAClE,OAAO;QACT,CAAC;QAED,cAAc;QACd,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;QACxC,KAAK,CAAC,aAAa,EAAE,CAAC;QAEtB,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,sBAAsB;QAC1B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAErC,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CACzC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAChC,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,WAAW;QACvB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,sCAAsC;gBACtC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;oBACxD,IAAI,EAAE,IAAI;iBACX,CAAC,CAAC;gBAEH,iBAAiB;gBACjB,OAAO;YACT,CAAC;YAAC,OAAO,KAAU,EAAE,CAAC;gBACpB,2BAA2B;gBAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBAC5B,oBAAoB;oBACpB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,YAAY,EAAE,CAAC;wBAC1C,2BAA2B;wBAC3B,OAAO,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;wBAC9C,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;wBACzB,SAAS;oBACX,CAAC;oBAED,iBAAiB;oBACjB,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;oBACxD,SAAS;gBACX,CAAC;gBAED,cAAc;gBACd,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,WAAW;QACvB,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,4CAA4C;QAC9C,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI/CD Monitoring Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Core types for GitHub Actions workflow monitoring, failure detection,
|
|
5
|
+
* and state management.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* GitHub Actions workflow run status
|
|
9
|
+
*/
|
|
10
|
+
export type WorkflowStatus = 'queued' | 'in_progress' | 'completed';
|
|
11
|
+
/**
|
|
12
|
+
* GitHub Actions workflow run conclusion
|
|
13
|
+
*/
|
|
14
|
+
export type WorkflowConclusion = 'success' | 'failure' | 'cancelled' | 'skipped' | 'timed_out' | 'action_required' | 'neutral';
|
|
15
|
+
/**
|
|
16
|
+
* Workflow run metadata from GitHub API
|
|
17
|
+
*/
|
|
18
|
+
export interface WorkflowRun {
|
|
19
|
+
/** Unique run ID */
|
|
20
|
+
id: number;
|
|
21
|
+
/** Workflow name (e.g., "CI", "Deploy") */
|
|
22
|
+
name: string;
|
|
23
|
+
/** Run number (sequential per workflow) */
|
|
24
|
+
run_number: number;
|
|
25
|
+
/** Git commit SHA */
|
|
26
|
+
head_sha: string;
|
|
27
|
+
/** Branch name */
|
|
28
|
+
head_branch: string;
|
|
29
|
+
/** Run status */
|
|
30
|
+
status: WorkflowStatus;
|
|
31
|
+
/** Run conclusion (only when status=completed) */
|
|
32
|
+
conclusion: WorkflowConclusion | null;
|
|
33
|
+
/** ISO 8601 timestamp */
|
|
34
|
+
created_at: string;
|
|
35
|
+
/** ISO 8601 timestamp */
|
|
36
|
+
updated_at: string;
|
|
37
|
+
/** HTML URL to view run */
|
|
38
|
+
html_url: string;
|
|
39
|
+
/** GitHub repository (owner/repo) */
|
|
40
|
+
repository: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Failure record stored in state
|
|
44
|
+
*/
|
|
45
|
+
export interface FailureRecord {
|
|
46
|
+
/** Workflow run ID */
|
|
47
|
+
runId: number;
|
|
48
|
+
/** Workflow name */
|
|
49
|
+
workflowName: string;
|
|
50
|
+
/** Git commit SHA */
|
|
51
|
+
commitSha: string;
|
|
52
|
+
/** Branch name */
|
|
53
|
+
branch: string;
|
|
54
|
+
/** Failure timestamp */
|
|
55
|
+
detectedAt: string;
|
|
56
|
+
/** Processing status */
|
|
57
|
+
processed: boolean;
|
|
58
|
+
/** Analysis status */
|
|
59
|
+
analyzed: boolean;
|
|
60
|
+
/** Fix applied status */
|
|
61
|
+
fixed: boolean;
|
|
62
|
+
/** GitHub URL */
|
|
63
|
+
url: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* CI/CD monitor state
|
|
67
|
+
*/
|
|
68
|
+
export interface CICDMonitorState {
|
|
69
|
+
/** Last poll timestamp (ISO 8601) */
|
|
70
|
+
lastPoll: string | null;
|
|
71
|
+
/** Failure records (keyed by run ID) */
|
|
72
|
+
failures: Record<number, FailureRecord>;
|
|
73
|
+
/** Total failures detected */
|
|
74
|
+
totalFailures: number;
|
|
75
|
+
/** Total failures processed */
|
|
76
|
+
totalProcessed: number;
|
|
77
|
+
/** Total failures fixed */
|
|
78
|
+
totalFixed: number;
|
|
79
|
+
/** Monitor version (for migrations) */
|
|
80
|
+
version: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* State manager interface
|
|
84
|
+
*/
|
|
85
|
+
export interface IStateManager {
|
|
86
|
+
/**
|
|
87
|
+
* Load state from disk
|
|
88
|
+
*/
|
|
89
|
+
loadState(): Promise<CICDMonitorState>;
|
|
90
|
+
/**
|
|
91
|
+
* Save state to disk
|
|
92
|
+
*/
|
|
93
|
+
saveState(state: CICDMonitorState): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Mark failure as processed (deduplication)
|
|
96
|
+
*/
|
|
97
|
+
markProcessed(runId: number): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Get last poll timestamp
|
|
100
|
+
*/
|
|
101
|
+
getLastPoll(): Promise<string | null>;
|
|
102
|
+
/**
|
|
103
|
+
* Update last poll timestamp
|
|
104
|
+
*/
|
|
105
|
+
updateLastPoll(): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Add failure record
|
|
108
|
+
*/
|
|
109
|
+
addFailure(failure: FailureRecord): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Get all unprocessed failures
|
|
112
|
+
*/
|
|
113
|
+
getUnprocessedFailures(): Promise<FailureRecord[]>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Default empty state
|
|
117
|
+
*/
|
|
118
|
+
export declare const DEFAULT_STATE: CICDMonitorState;
|
|
119
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/cicd/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,aAAa,GACb,WAAW,CAAC;AAEhB;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B,SAAS,GACT,SAAS,GACT,WAAW,GACX,SAAS,GACT,WAAW,GACX,iBAAiB,GACjB,SAAS,CAAC;AAEd;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oBAAoB;IACpB,EAAE,EAAE,MAAM,CAAC;IAEX,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IAEb,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IAEnB,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IAEjB,kBAAkB;IAClB,WAAW,EAAE,MAAM,CAAC;IAEpB,iBAAiB;IACjB,MAAM,EAAE,cAAc,CAAC;IAEvB,kDAAkD;IAClD,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAEtC,yBAAyB;IACzB,UAAU,EAAE,MAAM,CAAC;IAEnB,yBAAyB;IACzB,UAAU,EAAE,MAAM,CAAC;IAEnB,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IAEjB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IAEd,oBAAoB;IACpB,YAAY,EAAE,MAAM,CAAC;IAErB,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;IAElB,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IAEf,wBAAwB;IACxB,UAAU,EAAE,MAAM,CAAC;IAEnB,wBAAwB;IACxB,SAAS,EAAE,OAAO,CAAC;IAEnB,sBAAsB;IACtB,QAAQ,EAAE,OAAO,CAAC;IAElB,yBAAyB;IACzB,KAAK,EAAE,OAAO,CAAC;IAEf,iBAAiB;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qCAAqC;IACrC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAExC,8BAA8B;IAC9B,aAAa,EAAE,MAAM,CAAC;IAEtB,+BAA+B;IAC/B,cAAc,EAAE,MAAM,CAAC;IAEvB,2BAA2B;IAC3B,UAAU,EAAE,MAAM,CAAC;IAEnB,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,SAAS,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAEvC;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElD;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5C;;OAEG;IACH,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAEtC;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhC;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElD;;OAEG;IACH,sBAAsB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;CACpD;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,gBAO3B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI/CD Monitoring Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Core types for GitHub Actions workflow monitoring, failure detection,
|
|
5
|
+
* and state management.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Default empty state
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_STATE = {
|
|
11
|
+
lastPoll: null,
|
|
12
|
+
failures: {},
|
|
13
|
+
totalFailures: 0,
|
|
14
|
+
totalProcessed: 0,
|
|
15
|
+
totalFixed: 0,
|
|
16
|
+
version: '1.0.0'
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/core/cicd/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA2JH;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAqB;IAC7C,QAAQ,EAAE,IAAI;IACd,QAAQ,EAAE,EAAE;IACZ,aAAa,EAAE,CAAC;IAChB,cAAc,EAAE,CAAC;IACjB,UAAU,EAAE,CAAC;IACb,OAAO,EAAE,OAAO;CACjB,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI/CD Workflow Monitor
|
|
3
|
+
*
|
|
4
|
+
* Monitors GitHub Actions workflows for failures by polling the GitHub API
|
|
5
|
+
* every 60 seconds. Uses conditional requests and rate limit handling for
|
|
6
|
+
* efficient API usage.
|
|
7
|
+
*/
|
|
8
|
+
import { StateManager } from './state-manager';
|
|
9
|
+
/**
|
|
10
|
+
* Monitor configuration
|
|
11
|
+
*/
|
|
12
|
+
export interface MonitorConfig {
|
|
13
|
+
/** GitHub API token */
|
|
14
|
+
token: string;
|
|
15
|
+
/** Repository owner */
|
|
16
|
+
owner: string;
|
|
17
|
+
/** Repository name */
|
|
18
|
+
repo: string;
|
|
19
|
+
/** Poll interval (milliseconds, default: 60000 = 60s) */
|
|
20
|
+
pollInterval?: number;
|
|
21
|
+
/** Enable debug logging */
|
|
22
|
+
debug?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Poll result
|
|
26
|
+
*/
|
|
27
|
+
export interface PollResult {
|
|
28
|
+
/** Total runs checked */
|
|
29
|
+
totalRuns: number;
|
|
30
|
+
/** New failures detected */
|
|
31
|
+
newFailures: number;
|
|
32
|
+
/** Failures already tracked */
|
|
33
|
+
duplicates: number;
|
|
34
|
+
/** HTTP status code */
|
|
35
|
+
statusCode: number;
|
|
36
|
+
/** Rate limit remaining */
|
|
37
|
+
rateLimitRemaining: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* WorkflowMonitor - Polls GitHub Actions for workflow failures
|
|
41
|
+
*
|
|
42
|
+
* Features:
|
|
43
|
+
* - Polls every 60 seconds (configurable)
|
|
44
|
+
* - Detects failed workflow runs within 2 minutes
|
|
45
|
+
* - Uses conditional requests (If-Modified-Since) to reduce API calls
|
|
46
|
+
* - Handles rate limiting with exponential backoff
|
|
47
|
+
* - Deduplicates failures via StateManager
|
|
48
|
+
* - Extracts comprehensive workflow metadata
|
|
49
|
+
*/
|
|
50
|
+
export declare class WorkflowMonitor {
|
|
51
|
+
private octokit;
|
|
52
|
+
private stateManager;
|
|
53
|
+
private config;
|
|
54
|
+
private pollingTimer;
|
|
55
|
+
private lastModified;
|
|
56
|
+
private isPolling;
|
|
57
|
+
/**
|
|
58
|
+
* Create workflow monitor
|
|
59
|
+
*
|
|
60
|
+
* @param config - Monitor configuration
|
|
61
|
+
* @param stateManager - State manager instance (optional, creates new if not provided)
|
|
62
|
+
*/
|
|
63
|
+
constructor(config: MonitorConfig, stateManager?: StateManager);
|
|
64
|
+
/**
|
|
65
|
+
* Start monitoring (begins polling)
|
|
66
|
+
*/
|
|
67
|
+
start(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Stop monitoring (stops polling)
|
|
70
|
+
*/
|
|
71
|
+
stop(): void;
|
|
72
|
+
/**
|
|
73
|
+
* Check if monitor is running
|
|
74
|
+
*/
|
|
75
|
+
isRunning(): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Poll GitHub Actions API for workflow runs
|
|
78
|
+
*
|
|
79
|
+
* @returns Poll result with statistics
|
|
80
|
+
*/
|
|
81
|
+
poll(): Promise<PollResult>;
|
|
82
|
+
/**
|
|
83
|
+
* Extract failure record from GitHub workflow run
|
|
84
|
+
*
|
|
85
|
+
* @param run - GitHub workflow run data
|
|
86
|
+
* @returns Failure record for state storage
|
|
87
|
+
*/
|
|
88
|
+
private extractFailureRecord;
|
|
89
|
+
/**
|
|
90
|
+
* Sleep for specified milliseconds
|
|
91
|
+
*/
|
|
92
|
+
private sleep;
|
|
93
|
+
/**
|
|
94
|
+
* Debug logging
|
|
95
|
+
*/
|
|
96
|
+
private log;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=workflow-monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-monitor.d.ts","sourceRoot":"","sources":["../../../../src/core/cicd/workflow-monitor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAQ/C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IAEd,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IAEd,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IAEb,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAElB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IAEpB,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAC;IAEnB,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IAEnB,2BAA2B;IAC3B,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,SAAS,CAAS;IAE1B;;;;;OAKG;gBAED,MAAM,EAAE,aAAa,EACrB,YAAY,GAAE,YAAiC;IAoBjD;;OAEG;IACH,KAAK,IAAI,IAAI;IAwBb;;OAEG;IACH,IAAI,IAAI,IAAI;IAiBZ;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IA6GjC;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAc5B;;OAEG;IACH,OAAO,CAAC,KAAK;IAIb;;OAEG;IACH,OAAO,CAAC,GAAG;CAKZ"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI/CD Workflow Monitor
|
|
3
|
+
*
|
|
4
|
+
* Monitors GitHub Actions workflows for failures by polling the GitHub API
|
|
5
|
+
* every 60 seconds. Uses conditional requests and rate limit handling for
|
|
6
|
+
* efficient API usage.
|
|
7
|
+
*/
|
|
8
|
+
import { Octokit } from '@octokit/rest';
|
|
9
|
+
import { StateManager } from './state-manager';
|
|
10
|
+
/**
|
|
11
|
+
* WorkflowMonitor - Polls GitHub Actions for workflow failures
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - Polls every 60 seconds (configurable)
|
|
15
|
+
* - Detects failed workflow runs within 2 minutes
|
|
16
|
+
* - Uses conditional requests (If-Modified-Since) to reduce API calls
|
|
17
|
+
* - Handles rate limiting with exponential backoff
|
|
18
|
+
* - Deduplicates failures via StateManager
|
|
19
|
+
* - Extracts comprehensive workflow metadata
|
|
20
|
+
*/
|
|
21
|
+
export class WorkflowMonitor {
|
|
22
|
+
/**
|
|
23
|
+
* Create workflow monitor
|
|
24
|
+
*
|
|
25
|
+
* @param config - Monitor configuration
|
|
26
|
+
* @param stateManager - State manager instance (optional, creates new if not provided)
|
|
27
|
+
*/
|
|
28
|
+
constructor(config, stateManager = new StateManager()) {
|
|
29
|
+
this.pollingTimer = null;
|
|
30
|
+
this.lastModified = null;
|
|
31
|
+
this.isPolling = false;
|
|
32
|
+
// Validate config
|
|
33
|
+
if (!config.token) {
|
|
34
|
+
throw new Error('GitHub token is required');
|
|
35
|
+
}
|
|
36
|
+
if (!config.owner || !config.repo) {
|
|
37
|
+
throw new Error('Repository owner and name are required');
|
|
38
|
+
}
|
|
39
|
+
this.config = {
|
|
40
|
+
...config,
|
|
41
|
+
pollInterval: config.pollInterval ?? 60000, // Default: 60s
|
|
42
|
+
debug: config.debug ?? false
|
|
43
|
+
};
|
|
44
|
+
this.octokit = new Octokit({ auth: config.token });
|
|
45
|
+
this.stateManager = stateManager;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Start monitoring (begins polling)
|
|
49
|
+
*/
|
|
50
|
+
start() {
|
|
51
|
+
if (this.isPolling) {
|
|
52
|
+
this.log('Monitor already running');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.log('Starting monitor...');
|
|
56
|
+
this.isPolling = true;
|
|
57
|
+
// Initial poll
|
|
58
|
+
this.poll().catch((error) => {
|
|
59
|
+
console.error('Initial poll failed:', error);
|
|
60
|
+
});
|
|
61
|
+
// Schedule recurring polls
|
|
62
|
+
this.pollingTimer = setInterval(() => {
|
|
63
|
+
this.poll().catch((error) => {
|
|
64
|
+
console.error('Poll failed:', error);
|
|
65
|
+
});
|
|
66
|
+
}, this.config.pollInterval);
|
|
67
|
+
this.log(`Monitor started (polling every ${this.config.pollInterval}ms)`);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Stop monitoring (stops polling)
|
|
71
|
+
*/
|
|
72
|
+
stop() {
|
|
73
|
+
if (!this.isPolling) {
|
|
74
|
+
this.log('Monitor not running');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
this.log('Stopping monitor...');
|
|
78
|
+
if (this.pollingTimer) {
|
|
79
|
+
clearInterval(this.pollingTimer);
|
|
80
|
+
this.pollingTimer = null;
|
|
81
|
+
}
|
|
82
|
+
this.isPolling = false;
|
|
83
|
+
this.log('Monitor stopped');
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Check if monitor is running
|
|
87
|
+
*/
|
|
88
|
+
isRunning() {
|
|
89
|
+
return this.isPolling;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Poll GitHub Actions API for workflow runs
|
|
93
|
+
*
|
|
94
|
+
* @returns Poll result with statistics
|
|
95
|
+
*/
|
|
96
|
+
async poll() {
|
|
97
|
+
this.log('Polling GitHub Actions API...');
|
|
98
|
+
try {
|
|
99
|
+
// Build request headers
|
|
100
|
+
const headers = {};
|
|
101
|
+
if (this.lastModified) {
|
|
102
|
+
headers['If-Modified-Since'] = this.lastModified;
|
|
103
|
+
}
|
|
104
|
+
// Fetch workflow runs
|
|
105
|
+
const response = await this.octokit.rest.actions.listWorkflowRunsForRepo({
|
|
106
|
+
owner: this.config.owner,
|
|
107
|
+
repo: this.config.repo,
|
|
108
|
+
status: 'completed', // Only completed runs
|
|
109
|
+
per_page: 100, // Max per page
|
|
110
|
+
headers
|
|
111
|
+
});
|
|
112
|
+
// Extract rate limit info
|
|
113
|
+
const rateLimitRemaining = parseInt(response.headers['x-ratelimit-remaining'] || '0', 10);
|
|
114
|
+
this.log(`Rate limit remaining: ${rateLimitRemaining}`);
|
|
115
|
+
// Handle 304 Not Modified (no new runs)
|
|
116
|
+
// Note: Octokit types response.status as 200, but API can return 304
|
|
117
|
+
if (response.status === 304) {
|
|
118
|
+
this.log('No new workflow runs (304 Not Modified)');
|
|
119
|
+
return {
|
|
120
|
+
totalRuns: 0,
|
|
121
|
+
newFailures: 0,
|
|
122
|
+
duplicates: 0,
|
|
123
|
+
statusCode: 304,
|
|
124
|
+
rateLimitRemaining
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// Update Last-Modified header for next request
|
|
128
|
+
if (response.headers['last-modified']) {
|
|
129
|
+
this.lastModified = response.headers['last-modified'];
|
|
130
|
+
}
|
|
131
|
+
// Filter failed runs
|
|
132
|
+
const failedRuns = response.data.workflow_runs.filter((run) => run.conclusion === 'failure');
|
|
133
|
+
this.log(`Found ${failedRuns.length} failed runs (of ${response.data.workflow_runs.length} total)`);
|
|
134
|
+
// Process failures
|
|
135
|
+
let newFailures = 0;
|
|
136
|
+
let duplicates = 0;
|
|
137
|
+
for (const run of failedRuns) {
|
|
138
|
+
const failure = this.extractFailureRecord(run);
|
|
139
|
+
// Check if already tracked
|
|
140
|
+
const state = await this.stateManager.loadState();
|
|
141
|
+
if (state.failures[run.id]) {
|
|
142
|
+
duplicates++;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
// Add new failure
|
|
146
|
+
await this.stateManager.addFailure(failure);
|
|
147
|
+
newFailures++;
|
|
148
|
+
this.log(`New failure detected: ${run.name} (run #${run.run_number})`);
|
|
149
|
+
}
|
|
150
|
+
// Update last poll timestamp
|
|
151
|
+
await this.stateManager.updateLastPoll();
|
|
152
|
+
return {
|
|
153
|
+
totalRuns: response.data.workflow_runs.length,
|
|
154
|
+
newFailures,
|
|
155
|
+
duplicates,
|
|
156
|
+
statusCode: response.status,
|
|
157
|
+
rateLimitRemaining
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
// Handle rate limiting (429)
|
|
162
|
+
if (error.status === 429) {
|
|
163
|
+
const retryAfter = parseInt(error.response?.headers['retry-after'] || '60', 10);
|
|
164
|
+
console.warn(`Rate limited! Retry after ${retryAfter}s`);
|
|
165
|
+
// Exponential backoff
|
|
166
|
+
await this.sleep(retryAfter * 1000);
|
|
167
|
+
// Retry poll
|
|
168
|
+
return this.poll();
|
|
169
|
+
}
|
|
170
|
+
// Handle other errors
|
|
171
|
+
console.error('Poll error:', error.message);
|
|
172
|
+
return {
|
|
173
|
+
totalRuns: 0,
|
|
174
|
+
newFailures: 0,
|
|
175
|
+
duplicates: 0,
|
|
176
|
+
statusCode: error.status || 500,
|
|
177
|
+
rateLimitRemaining: 0
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Extract failure record from GitHub workflow run
|
|
183
|
+
*
|
|
184
|
+
* @param run - GitHub workflow run data
|
|
185
|
+
* @returns Failure record for state storage
|
|
186
|
+
*/
|
|
187
|
+
extractFailureRecord(run) {
|
|
188
|
+
return {
|
|
189
|
+
runId: run.id,
|
|
190
|
+
workflowName: run.name,
|
|
191
|
+
commitSha: run.head_sha,
|
|
192
|
+
branch: run.head_branch,
|
|
193
|
+
detectedAt: new Date().toISOString(),
|
|
194
|
+
processed: false,
|
|
195
|
+
analyzed: false,
|
|
196
|
+
fixed: false,
|
|
197
|
+
url: run.html_url
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Sleep for specified milliseconds
|
|
202
|
+
*/
|
|
203
|
+
sleep(ms) {
|
|
204
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Debug logging
|
|
208
|
+
*/
|
|
209
|
+
log(message) {
|
|
210
|
+
if (this.config.debug) {
|
|
211
|
+
console.log(`[WorkflowMonitor] ${message}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=workflow-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-monitor.js","sourceRoot":"","sources":["../../../../src/core/cicd/workflow-monitor.ts"],"names":[],"mappings":"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"}
|