fix: bulk windows apply-patch updates

This commit is contained in:
Codex
2026-06-07 00:28:38 +00:00
parent b81585bcab
commit d4b7fc95f9
5 changed files with 173 additions and 13 deletions
+40 -9
View File
@@ -63,6 +63,8 @@ export interface ApplyPatchV2FileSystem {
readBlock(path: string, blockIndex: number, blockBytes: number): Promise<Buffer>;
writeFile(path: string, content: Buffer): Promise<void>;
deleteFile(path: string): Promise<void>;
readFiles?(paths: string[]): Promise<Map<string, string>>;
applyReplacementsBulk?(paths: Iterable<string>, plans: Map<string, ApplyPatchV2BulkReplacementWritePlan>): Promise<void>;
}
export interface ApplyPatchV2FileStat {
@@ -114,15 +116,17 @@ export class ApplyPatchV2Error extends Error {
type PlannedFileState = { exists: true; content: string } | { exists: false; content: "" };
type PlannedOperation = { kind: "write"; path: string; content: string } | { kind: "delete"; path: string };
type Replacement = [start: number, oldLength: number, newLines: string[]];
interface BulkReplacementWritePlan {
export type ApplyPatchV2Replacement = [start: number, oldLength: number, newLines: string[]];
type Replacement = ApplyPatchV2Replacement;
export interface ApplyPatchV2BulkReplacementWritePlan {
path: string;
originalBytes: number;
originalSha256: string;
finalBytes: number;
finalSha256: string;
replacements: Replacement[];
replacements: ApplyPatchV2Replacement[];
}
type BulkReplacementWritePlan = ApplyPatchV2BulkReplacementWritePlan;
interface ApplyPatchV2Plan {
changed: string[];
@@ -432,12 +436,19 @@ function instrumentApplyPatchV2Executor(executor: ApplyPatchV2Executor, metrics:
}
function instrumentApplyPatchV2FileSystem(fs: ApplyPatchV2FileSystem, metrics: ApplyPatchV2RemoteMetrics): ApplyPatchV2FileSystem {
return {
const instrumented: ApplyPatchV2FileSystem = {
stat: (path) => recordApplyPatchV2FsOperation(metrics, "fs.stat", () => fs.stat(path)),
readBlock: (path, blockIndex, blockBytes) => recordApplyPatchV2FsOperation(metrics, "fs.readBlock", () => fs.readBlock(path, blockIndex, blockBytes)),
writeFile: (path, content) => recordApplyPatchV2FsOperation(metrics, "fs.writeFile", () => fs.writeFile(path, content)),
deleteFile: (path) => recordApplyPatchV2FsOperation(metrics, "fs.deleteFile", () => fs.deleteFile(path)),
};
if (fs.readFiles !== undefined) {
instrumented.readFiles = (paths) => recordApplyPatchV2FsOperation(metrics, "fs.readFiles", () => fs.readFiles!(paths));
}
if (fs.applyReplacementsBulk !== undefined) {
instrumented.applyReplacementsBulk = (paths, plans) => recordApplyPatchV2FsOperation(metrics, "fs.applyReplacementsBulk", () => fs.applyReplacementsBulk!(paths, plans));
}
return instrumented;
}
async function recordApplyPatchV2FsOperation<T>(metrics: ApplyPatchV2RemoteMetrics, operation: string, run: () => Promise<T>): Promise<T> {
@@ -621,7 +632,11 @@ function isBulkReadFailure(error: unknown): boolean {
}
function shouldUseBulkUpdatePath(executor: ApplyPatchV2Executor, hunks: PatchHunk[]): boolean {
if (!executor.run || executor.fs !== undefined) return false;
if (executor.fs !== undefined) {
if (executor.fs.readFiles === undefined || executor.fs.applyReplacementsBulk === undefined) return false;
} else if (!executor.run) {
return false;
}
const paths = new Set<string>();
for (const hunk of hunks) {
if (hunk.kind !== "update" || hunk.movePath !== null) return false;
@@ -926,13 +941,20 @@ async function readRemoteText(executor: ApplyPatchV2Executor, target: string): P
async function readRemoteTextsBulk(executor: ApplyPatchV2Executor, targets: string[]): Promise<Map<string, string>> {
if (targets.length === 0) return new Map();
if (executor.fs?.readFiles !== undefined && targets.length > 1) {
const files = await executor.fs.readFiles(targets);
for (const target of targets) {
if (!files.has(target)) throw new ApplyPatchV2Error("remote apply-patch v2 fs bulk read omitted target", { target });
}
return files;
}
if (executor.fs || targets.length === 1) {
const files = new Map<string, string>();
for (const target of targets) files.set(target, await readRemoteText(executor, target));
return files;
}
const result = await checkedRemoteV2(executor, "read-bulk-b64", targets);
return decodeRemoteBulkRead(result.stdout, targets);
return decodeApplyPatchV2BulkRead(result.stdout, targets);
}
function decodeRemoteReadBlock(stdout: string, target: string, blockIndex: number, expectedChunkBytes: number): Buffer {
@@ -979,7 +1001,7 @@ function decodeRemoteReadBlock(stdout: string, target: string, blockIndex: numbe
return decoded.length > expectedChunkBytes ? decoded.subarray(0, expectedChunkBytes) : decoded;
}
function decodeRemoteBulkRead(stdout: string, targets: string[]): Map<string, string> {
export function decodeApplyPatchV2BulkRead(stdout: string, targets: string[]): Map<string, string> {
const lines = stdout.split(/\r?\n/u);
const markerIndex = lines.findIndex((line) => line.startsWith(`${remoteBulkReadMarker} `));
if (markerIndex < 0) {
@@ -1050,8 +1072,17 @@ async function writeRemoteText(executor: ApplyPatchV2Executor, target: string, c
}
async function writeRemoteReplacementsBulk(executor: ApplyPatchV2Executor, paths: Iterable<string>, plans: Map<string, BulkReplacementWritePlan>): Promise<void> {
const targets = Array.from(paths);
const { targets, payload } = formatApplyPatchV2BulkReplacementPayload(paths, plans);
if (targets.length === 0) return;
if (executor.fs?.applyReplacementsBulk !== undefined) {
await executor.fs.applyReplacementsBulk(targets, plans);
return;
}
await checkedRemoteV2(executor, "apply-replacements-bulk-stdin", [String(targets.length)], payload);
}
export function formatApplyPatchV2BulkReplacementPayload(paths: Iterable<string>, plans: Map<string, ApplyPatchV2BulkReplacementWritePlan>): { targets: string[]; payload: string } {
const targets = Array.from(paths);
const records: string[] = [];
for (const target of targets) {
const plan = plans.get(target);
@@ -1073,7 +1104,7 @@ async function writeRemoteReplacementsBulk(executor: ApplyPatchV2Executor, paths
replacementFields.join(";"),
].join(" "));
}
await checkedRemoteV2(executor, "apply-replacements-bulk-stdin", [String(targets.length)], `${records.join("\n")}\n`);
return { targets, payload: `${records.join("\n")}\n` };
}
type RemoteV2Operation =