Skip to content

Commit 5e4dd04

Browse files
AgentEnderclaude
andauthored
fix(core): only detect flaky tasks for cacheable tasks (#33994)
## Current Behavior Flaky task detection warns about all tasks that have different exit codes for the same hash, including non-cacheable tasks. This is misleading because the flaky task warning message points users to Nx Cloud's flaky task retry feature, which is only relevant for cached tasks. ## Expected Behavior Flaky task detection should only consider tasks where `task.cache === true`, making the warning more meaningful and avoiding noise for non-cacheable tasks. ## Related Issue(s) N/A - Internal improvement ## Changes Made ### `packages/nx/src/tasks-runner/life-cycles/task-history-life-cycle.ts` 1. Added `cacheable: boolean` to the `TaskRun` interface 2. In `endTasks`, now tracks `cacheable: taskResult.task.cache === true` for each task 3. In `endCommand`, filters to only check flaky tasks among cacheable tasks ### `packages/nx/src/tasks-runner/life-cycles/task-history-life-cycle-old.ts` 1. Added `cacheableHashes: Set<string>` to track which task hashes are cacheable 2. In `endTasks`, tracks cacheable tasks by adding their hash to the set 3. In `endCommand`, only checks for flaky tasks among cacheable task hashes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent 8280910 commit 5e4dd04

2 files changed

Lines changed: 52 additions & 24 deletions

File tree

packages/nx/src/tasks-runner/life-cycles/task-history-life-cycle-old.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,47 @@ export class LegacyTaskHistoryLifeCycle implements LifeCycle {
1616
private taskRuns: TaskRun[] = [];
1717
private flakyTasks: string[];
1818

19+
// hashes which could trigger flaky task detection.
20+
// set here rather than on the TaskRun to avoid storing data on
21+
// the fs
22+
private cacheableHashes: Set<string> = new Set();
23+
1924
startTasks(tasks: Task[]): void {
2025
for (let task of tasks) {
2126
this.startTimings[task.id] = new Date().getTime();
2227
}
2328
}
2429

2530
async endTasks(taskResults: TaskResult[]) {
26-
const taskRuns: TaskRun[] = taskResults.map((taskResult) => ({
27-
project: taskResult.task.target.project,
28-
target: taskResult.task.target.target,
29-
configuration: taskResult.task.target.configuration,
30-
hash: taskResult.task.hash,
31-
code: taskResult.code.toString(),
32-
status: taskResult.status,
33-
start: (
34-
taskResult.task.startTime ?? this.startTimings[taskResult.task.id]
35-
).toString(),
36-
end: (taskResult.task.endTime ?? new Date().getTime()).toString(),
37-
}));
38-
this.taskRuns.push(...taskRuns);
31+
for (const taskResult of taskResults) {
32+
// Track cacheable tasks for flaky detection
33+
if (taskResult.task.cache === true) {
34+
this.cacheableHashes.add(taskResult.task.hash);
35+
}
36+
this.taskRuns.push({
37+
project: taskResult.task.target.project,
38+
target: taskResult.task.target.target,
39+
configuration: taskResult.task.target.configuration,
40+
hash: taskResult.task.hash,
41+
code: taskResult.code.toString(),
42+
status: taskResult.status,
43+
start: (
44+
taskResult.task.startTime ?? this.startTimings[taskResult.task.id]
45+
).toString(),
46+
end: (taskResult.task.endTime ?? new Date().getTime()).toString(),
47+
});
48+
}
3949
}
4050

4151
async endCommand() {
4252
await writeTaskRunsToHistory(this.taskRuns);
43-
const history = await getHistoryForHashes(this.taskRuns.map((t) => t.hash));
53+
// Only check for flaky tasks among cacheable tasks
54+
const cacheableTaskRuns = this.taskRuns.filter((t) =>
55+
this.cacheableHashes.has(t.hash)
56+
);
57+
const history = await getHistoryForHashes(
58+
cacheableTaskRuns.map((t) => t.hash)
59+
);
4460
this.flakyTasks = [];
4561

4662
// check if any hash has different exit codes => flaky

packages/nx/src/tasks-runner/life-cycles/task-history-life-cycle.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { LegacyTaskHistoryLifeCycle } from './task-history-life-cycle-old';
1111

1212
interface TaskRun extends NativeTaskRun {
1313
target: Task['target'];
14+
cacheable: boolean;
1415
}
1516

1617
let tasksHistoryLifeCycle: TaskHistoryLifeCycle | LegacyTaskHistoryLifeCycle;
@@ -49,30 +50,41 @@ export class TaskHistoryLifeCycle implements LifeCycle {
4950
}
5051

5152
async endTasks(taskResults: TaskResult[]) {
52-
taskResults
53-
.map((taskResult) => ({
53+
for (const taskResult of taskResults) {
54+
this.taskRuns.set(taskResult.task.hash, {
5455
hash: taskResult.task.hash,
5556
target: taskResult.task.target,
5657
code: taskResult.code,
5758
status: taskResult.status,
5859
start:
5960
taskResult.task.startTime ?? this.startTimings[taskResult.task.id],
6061
end: taskResult.task.endTime ?? Date.now(),
61-
}))
62-
.forEach((taskRun) => {
63-
this.taskRuns.set(taskRun.hash, taskRun);
62+
cacheable: taskResult.task.cache === true,
6463
});
64+
}
6565
}
6666

6767
async endCommand() {
68-
const entries = Array.from(this.taskRuns);
6968
if (!this.taskHistory) {
7069
return;
7170
}
72-
await this.taskHistory.recordTaskRuns(entries.map(([_, v]) => v));
73-
this.flakyTasks = await this.taskHistory.getFlakyTasks(
74-
entries.map(([hash]) => hash)
75-
);
71+
const runs = [];
72+
73+
// Only check for flaky tasks among cacheable tasks
74+
const cacheableHashes: string[] = [];
75+
const iterator = this.taskRuns.entries();
76+
for (const [hash, run] of iterator) {
77+
runs.push(run);
78+
if (run.cacheable) {
79+
cacheableHashes.push(hash);
80+
}
81+
}
82+
await this.taskHistory.recordTaskRuns(runs);
83+
84+
this.flakyTasks =
85+
cacheableHashes.length > 0
86+
? await this.taskHistory.getFlakyTasks(cacheableHashes)
87+
: [];
7688
// Do not directly print output when using the TUI
7789
if (isTuiEnabled()) {
7890
return;

0 commit comments

Comments
 (0)