patchrelay 0.41.5 → 0.41.7
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/dist/build-info.json
CHANGED
|
@@ -101,7 +101,7 @@ function buildFacts(issue, selected) {
|
|
|
101
101
|
facts.push({ text: "awaiting review", color: "yellow" });
|
|
102
102
|
}
|
|
103
103
|
if (issue.factoryState === "awaiting_queue") {
|
|
104
|
-
facts.push({ text: "
|
|
104
|
+
facts.push({ text: "downstream ready", color: "cyan" });
|
|
105
105
|
}
|
|
106
106
|
// Check status — compact
|
|
107
107
|
const checksFact = prChecksFact(issue);
|
|
@@ -401,7 +401,7 @@ function buildFactSegments(issue, issueContext) {
|
|
|
401
401
|
&& (isAwaitingReviewState(issue.prReviewState) || (!issue.prReviewState && issue.factoryState === "pr_open")))
|
|
402
402
|
facts.push([{ text: "awaiting review", color: "yellow" }]);
|
|
403
403
|
if (issue.factoryState === "awaiting_queue")
|
|
404
|
-
facts.push([{ text: "
|
|
404
|
+
facts.push([{ text: "downstream ready", color: "cyan" }]);
|
|
405
405
|
if (issue.waitingReason && issue.sessionState === "waiting_input")
|
|
406
406
|
facts.push([{ text: issue.waitingReason, color: "yellow" }]);
|
|
407
407
|
const checks = prChecksFact({
|
package/dist/worktree-manager.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { existsSync, lstatSync, realpathSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { ensureDir, execCommand } from "./utils.js";
|
|
4
|
+
const FETCH_RETRY_DELAYS_MS = [0, 1_000];
|
|
5
|
+
const TRANSIENT_FETCH_ERROR_PATTERNS = [
|
|
6
|
+
/connection reset by peer/i,
|
|
7
|
+
/recv failure/i,
|
|
8
|
+
/tls handshake timeout/i,
|
|
9
|
+
/connection timed out/i,
|
|
10
|
+
/operation timed out/i,
|
|
11
|
+
/timed out after \d+ms/i,
|
|
12
|
+
/remote end hung up unexpectedly/i,
|
|
13
|
+
/unexpected disconnect while reading sideband packet/i,
|
|
14
|
+
];
|
|
4
15
|
export class WorktreeManager {
|
|
5
16
|
config;
|
|
6
17
|
constructor(config) {
|
|
@@ -84,9 +95,7 @@ export class WorktreeManager {
|
|
|
84
95
|
// Fetch latest main so the branch forks from a clean, up-to-date base.
|
|
85
96
|
// This prevents branch contamination when local HEAD has drifted.
|
|
86
97
|
// freshenWorktree in run-orchestrator acts as a secondary safety net.
|
|
87
|
-
const fetchResult = await
|
|
88
|
-
timeoutMs: 60_000,
|
|
89
|
-
});
|
|
98
|
+
const fetchResult = await this.fetchWithTransientRetry(repoPath, "main");
|
|
90
99
|
if (fetchResult.exitCode !== 0) {
|
|
91
100
|
throw new Error(`Failed to fetch origin/main before creating issue worktree: ${fetchResult.stderr?.slice(0, 300) ?? "git fetch failed"}`);
|
|
92
101
|
}
|
|
@@ -95,6 +104,34 @@ export class WorktreeManager {
|
|
|
95
104
|
throw new Error(`Failed to create issue worktree at ${worktreePath}: ${addResult.stderr?.slice(0, 300) ?? "git worktree add failed"}`);
|
|
96
105
|
}
|
|
97
106
|
}
|
|
107
|
+
async fetchWithTransientRetry(repoPath, branchName) {
|
|
108
|
+
let lastResult;
|
|
109
|
+
let lastError;
|
|
110
|
+
for (let attempt = 0; attempt <= FETCH_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
111
|
+
if (attempt > 0) {
|
|
112
|
+
await delay(FETCH_RETRY_DELAYS_MS[attempt - 1] ?? 0);
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const result = await execCommand(this.config.runner.gitBin, ["-C", repoPath, "fetch", "origin", branchName], {
|
|
116
|
+
timeoutMs: 60_000,
|
|
117
|
+
});
|
|
118
|
+
if (result.exitCode === 0 || !isTransientFetchFailure(result.stderr)) {
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
lastResult = result;
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (!isTransientFetchFailure(error instanceof Error ? error.message : String(error))) {
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (lastResult) {
|
|
131
|
+
return lastResult;
|
|
132
|
+
}
|
|
133
|
+
throw lastError ?? new Error(`Failed to fetch origin/${branchName}`);
|
|
134
|
+
}
|
|
98
135
|
async assertTrustedExistingWorktree(repoPath, worktreeRoot, worktreePath, options) {
|
|
99
136
|
const worktreeStats = lstatSync(worktreePath);
|
|
100
137
|
if (worktreeStats.isSymbolicLink()) {
|
|
@@ -145,3 +182,15 @@ function isPathWithinRoot(rootPath, candidatePath) {
|
|
|
145
182
|
const relative = path.relative(rootPath, candidatePath);
|
|
146
183
|
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
147
184
|
}
|
|
185
|
+
function isTransientFetchFailure(message) {
|
|
186
|
+
if (!message) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
return TRANSIENT_FETCH_ERROR_PATTERNS.some((pattern) => pattern.test(message));
|
|
190
|
+
}
|
|
191
|
+
function delay(delayMs) {
|
|
192
|
+
if (delayMs <= 0) {
|
|
193
|
+
return Promise.resolve();
|
|
194
|
+
}
|
|
195
|
+
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
196
|
+
}
|