deflake 1.2.37 ā 1.2.38
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/cli.js +62 -10
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -46,15 +46,63 @@ async function main() {
|
|
|
46
46
|
console.log(`š Detected Framework: ${C.GREEN}${fw.toUpperCase()}${C.RESET}`);
|
|
47
47
|
|
|
48
48
|
if (commandToRun) {
|
|
49
|
-
const
|
|
50
|
-
|
|
49
|
+
const MAX_ATTEMPTS = 5;
|
|
50
|
+
let { code, output } = await runNative(commandToRun);
|
|
51
|
+
|
|
52
|
+
if (code !== 0 && argv.fix) {
|
|
51
53
|
console.log(`\nš“ Command failed with code ${code}. Activating DeFlake...`);
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
|
|
55
|
+
// Save backups of all source files before any modifications
|
|
56
|
+
const backups = new Map(); // path -> original content
|
|
57
|
+
let prevFailCount = Infinity;
|
|
58
|
+
|
|
59
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
60
|
+
console.log(`\n${C.BRIGHT}${C.CYAN}š Fix attempt ${attempt}/${MAX_ATTEMPTS}${C.RESET}`);
|
|
61
|
+
|
|
62
|
+
const artifacts = detectFailures();
|
|
63
|
+
const currentFailCount = artifacts.length;
|
|
64
|
+
|
|
65
|
+
if (currentFailCount === 0) {
|
|
66
|
+
console.log(`${C.GREEN}š All tests passing! No failures detected.${C.RESET}`);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// If failures INCREASED since last attempt, rollback all changes
|
|
71
|
+
if (attempt > 1 && currentFailCount > prevFailCount) {
|
|
72
|
+
console.log(`${C.RED}ā ļø Failures increased (${prevFailCount} ā ${currentFailCount}). Rolling back all changes...${C.RESET}`);
|
|
73
|
+
for (const [filePath, originalContent] of backups) {
|
|
74
|
+
fs.writeFileSync(filePath, originalContent);
|
|
75
|
+
console.log(` ${C.YELLOW}ā©ļø Restored: ${path.basename(filePath)}${C.RESET}`);
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
prevFailCount = currentFailCount;
|
|
80
|
+
|
|
81
|
+
const applied = await analyzeAndFix(artifacts, client, argv, output, backups);
|
|
82
|
+
|
|
83
|
+
if (applied === 0) {
|
|
84
|
+
console.log(`${C.YELLOW}ā ļø No fixes could be applied. Stopping retry loop.${C.RESET}`);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
console.log(`\n${C.BRIGHT}š ${applied} fix(es) applied. Re-running tests to verify (attempt ${attempt})...${C.RESET}`);
|
|
90
|
+
const rerun = await runNative(commandToRun);
|
|
91
|
+
output = rerun.output; // Use new output for next iteration's AI context
|
|
92
|
+
|
|
93
|
+
if (rerun.code === 0) {
|
|
94
|
+
console.log(`\n${C.GREEN}${C.BRIGHT}š All tests passing after attempt ${attempt}!${C.RESET}`);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (attempt === MAX_ATTEMPTS) {
|
|
99
|
+
console.log(`\n${C.YELLOW}ā ļø Max attempts (${MAX_ATTEMPTS}) reached. Some failures may persist.${C.RESET}`);
|
|
100
|
+
}
|
|
57
101
|
}
|
|
102
|
+
} else if (code !== 0) {
|
|
103
|
+
console.log(`\nš“ Command failed with code ${code}. Activating DeFlake...`);
|
|
104
|
+
const artifacts = detectFailures();
|
|
105
|
+
await analyzeAndFix(artifacts, client, argv, output, new Map());
|
|
58
106
|
}
|
|
59
107
|
}
|
|
60
108
|
if (argv.report) showReport();
|
|
@@ -110,7 +158,7 @@ function detectFailures() {
|
|
|
110
158
|
return results;
|
|
111
159
|
}
|
|
112
160
|
|
|
113
|
-
async function analyzeAndFix(artifacts, client, argv, capturedOutput = '') {
|
|
161
|
+
async function analyzeAndFix(artifacts, client, argv, capturedOutput = '', backups = new Map()) {
|
|
114
162
|
if (artifacts.length === 0) {
|
|
115
163
|
console.log(` ${C.YELLOW}ā ļø No failure artifacts detected. Run 'npx deflake doctor' to check permissions.${C.RESET}`);
|
|
116
164
|
return 0;
|
|
@@ -186,7 +234,7 @@ async function analyzeAndFix(artifacts, client, argv, capturedOutput = '') {
|
|
|
186
234
|
}
|
|
187
235
|
if (fixLoc) {
|
|
188
236
|
console.log(` ${C.BRIGHT}š Applying patches...${C.RESET}`);
|
|
189
|
-
if (await applyFix(res, fixLoc)) {
|
|
237
|
+
if (await applyFix(res, fixLoc, backups)) {
|
|
190
238
|
count++;
|
|
191
239
|
console.log(` ${C.GREEN}${C.BRIGHT}ā
Fix applied to ${path.basename(fixLoc.path)}:${fixLoc.line}${C.RESET}`);
|
|
192
240
|
} else {
|
|
@@ -239,7 +287,7 @@ function checkBalance(code) {
|
|
|
239
287
|
return { balanced: true, detail: 'ok' };
|
|
240
288
|
}
|
|
241
289
|
|
|
242
|
-
async function applyFix(res, loc) {
|
|
290
|
+
async function applyFix(res, loc, backups = new Map()) {
|
|
243
291
|
if (!loc?.path) {
|
|
244
292
|
console.log(` ${C.YELLOW}ā ļø Cannot apply fix: failure location not detected.${C.RESET}`);
|
|
245
293
|
return false;
|
|
@@ -251,6 +299,10 @@ async function applyFix(res, loc) {
|
|
|
251
299
|
return false;
|
|
252
300
|
}
|
|
253
301
|
const original = fs.readFileSync(loc.path, 'utf8');
|
|
302
|
+
// Save backup before first modification
|
|
303
|
+
if (!backups.has(loc.path)) {
|
|
304
|
+
backups.set(loc.path, original);
|
|
305
|
+
}
|
|
254
306
|
let content = original;
|
|
255
307
|
let appliedCount = 0;
|
|
256
308
|
for (const p of patches) {
|