fix: make apply-patch default to v2

This commit is contained in:
Codex
2026-05-27 02:14:17 +00:00
parent 98715ce2be
commit aa5c61e278
10 changed files with 135 additions and 78 deletions
+35 -25
View File
@@ -330,24 +330,30 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
assertCondition(JSON.stringify(remoteOptionSeparator.args) === JSON.stringify(["ssh", "D601:/tmp", "script", "--", "sed", "-n", "1,2p", "file.txt"]), "global -- must not strip nested command separators", remoteOptionSeparator);
const routeApplyPatch = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api", ["apply-patch"]);
assertCondition(routeApplyPatch.parsed.requiresStdin === true, "k3s apply-patch operation must stream local patch stdin", routeApplyPatch);
assertCondition(routeApplyPatch.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-i' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'sh' '-s' '--'", "D601:k3s:<namespace>:<workload> apply-patch must enter pod with stdin", routeApplyPatch);
assertCondition(routeApplyPatch.parsed.stdinPrefix?.includes("apply_patch") && routeApplyPatch.parsed.stdinPrefix.includes("__UNIDESK_APPLY_PATCH_PAYLOAD__"), "k3s apply-patch operation must inject pod helper before patch stdin", routeApplyPatch);
assertCondition(routeApplyPatch.parsed.stdinPrefix?.includes("#!/bin/sh"), "k3s apply-patch operation must inject the same sh helper used by host apply-patch", routeApplyPatch);
assertCondition(!routeApplyPatch.parsed.stdinPrefix?.includes("python3") && !routeApplyPatch.parsed.stdinPrefix?.includes("node "), "k3s apply-patch operation must use the sh-only pod helper", routeApplyPatch);
assertCondition(routeApplyPatch.parsed.stdinSuffix === "\n__UNIDESK_APPLY_PATCH_PAYLOAD__\n", "k3s apply-patch operation must terminate patch heredoc", routeApplyPatch);
assertCondition(routeApplyPatch.parsed.requiresStdin === true && routeApplyPatch.parsed.remoteCommand === null, "k3s apply-patch operation must use the default v2 local engine", routeApplyPatch);
const hostApplyPatchLoose = parseSshArgs(["apply-patch", "--allow-loose"]);
assertCondition(hostApplyPatchLoose.remoteCommand === "'apply_patch' '--allow-loose'", "host apply-patch must pass --allow-loose as an explicit helper argument", hostApplyPatchLoose);
assertCondition(hostApplyPatchLoose.requiredHelpers?.length === 1 && hostApplyPatchLoose.requiredHelpers.includes("apply_patch"), "host apply-patch must request only the apply_patch helper bootstrap", hostApplyPatchLoose);
const routeApplyPatchV1 = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api", ["apply-patch-v1"]);
assertCondition(routeApplyPatchV1.parsed.requiresStdin === true, "k3s apply-patch-v1 operation must stream local patch stdin", routeApplyPatchV1);
assertCondition(routeApplyPatchV1.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-i' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'sh' '-s' '--'", "D601:k3s:<namespace>:<workload> apply-patch-v1 must enter pod with stdin", routeApplyPatchV1);
assertCondition(routeApplyPatchV1.parsed.stdinPrefix?.includes("apply_patch") && routeApplyPatchV1.parsed.stdinPrefix.includes("__UNIDESK_APPLY_PATCH_PAYLOAD__"), "k3s apply-patch-v1 operation must inject pod helper before patch stdin", routeApplyPatchV1);
assertCondition(routeApplyPatchV1.parsed.stdinPrefix?.includes("#!/bin/sh"), "k3s apply-patch-v1 operation must inject the same sh helper used by host apply-patch-v1", routeApplyPatchV1);
assertCondition(!routeApplyPatchV1.parsed.stdinPrefix?.includes("python3") && !routeApplyPatchV1.parsed.stdinPrefix?.includes("node "), "k3s apply-patch-v1 operation must use the sh-only pod helper", routeApplyPatchV1);
assertCondition(routeApplyPatchV1.parsed.stdinSuffix === "\n__UNIDESK_APPLY_PATCH_PAYLOAD__\n", "k3s apply-patch-v1 operation must terminate patch heredoc", routeApplyPatchV1);
const hostApplyPatchLoose = parseSshArgs(["apply-patch-v1", "--allow-loose"]);
assertCondition(hostApplyPatchLoose.remoteCommand === "'apply_patch' '--allow-loose'", "host apply-patch-v1 must pass --allow-loose as an explicit helper argument", hostApplyPatchLoose);
assertCondition(hostApplyPatchLoose.requiredHelpers?.length === 1 && hostApplyPatchLoose.requiredHelpers.includes("apply_patch"), "host apply-patch-v1 must request only the apply_patch helper bootstrap", hostApplyPatchLoose);
assertCondition(remoteApplyPatchSource.includes("replace_once_with_perl") && remoteApplyPatchSource.includes("perl -0777"), "apply_patch helper must keep a fast path for large files", {});
const hostApplyPatchV2 = parseSshArgs(["v2"]);
assertCondition(hostApplyPatchV2.requiresStdin === true && hostApplyPatchV2.requiredHelpers === undefined && hostApplyPatchV2.remoteCommand === null, "host v2 must be a local engine operation, not a remote helper bootstrap", hostApplyPatchV2);
const hostApplyPatchV2Help = parseSshArgs(["v2", "--help"]);
assertCondition(hostApplyPatchV2Help.requiresStdin === false && hostApplyPatchV2Help.remoteCommand === null, "host v2 --help must not wait for patch stdin", hostApplyPatchV2Help);
const podApplyPatchV2 = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api/app", ["v2"]);
assertCondition(podApplyPatchV2.parsed.requiresStdin === true && podApplyPatchV2.parsed.remoteCommand === null, "pod v2 must be handled by the local v2 engine instead of injecting the legacy helper", podApplyPatchV2);
const hostApplyPatchV2 = parseSshArgs(["apply-patch"]);
assertCondition(hostApplyPatchV2.requiresStdin === true && hostApplyPatchV2.requiredHelpers === undefined && hostApplyPatchV2.remoteCommand === null, "host apply-patch must be a local v2 engine operation, not a remote helper bootstrap", hostApplyPatchV2);
const hostApplyPatchV2Help = parseSshArgs(["apply-patch", "--help"]);
assertCondition(hostApplyPatchV2Help.requiresStdin === false && hostApplyPatchV2Help.remoteCommand === null, "host apply-patch --help must not wait for patch stdin", hostApplyPatchV2Help);
const podApplyPatchV2 = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api/app", ["apply-patch"]);
assertCondition(podApplyPatchV2.parsed.requiresStdin === true && podApplyPatchV2.parsed.remoteCommand === null, "pod apply-patch must be handled by the local v2 engine instead of injecting the legacy helper", podApplyPatchV2);
assertThrows(() => parseSshArgs(["v2"]), /remote patch entrypoints/u, "v2 must not remain as an independent patch subcommand");
assertThrows(() => parseSshArgs(["patch"]), /remote patch entrypoints/u, "patch must not remain as a patch alias");
assertThrows(() => parseSshArgs(["patch-v1"]), /remote patch entrypoints/u, "patch-v1 must not remain as a legacy patch alias");
const longChinesePatch = await applyPatchV2Fixture([
"*** Begin Patch",
@@ -647,7 +653,11 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
const routeApplyPatchWorkspace = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api/app", ["apply-patch"]);
assertCondition(routeApplyPatchWorkspace.parsed.requiresStdin === true, "pod workspace apply-patch must still stream patch stdin", routeApplyPatchWorkspace);
assertCondition(routeApplyPatchWorkspace.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-i' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'sh' '-c' 'cd \"$1\" || exit; shift; exec \"$@\"' 'unidesk-cwd' '/app' 'sh' '-s' '--'", "pod workspace apply-patch must set cwd before injecting the sh helper", routeApplyPatchWorkspace);
assertCondition(routeApplyPatchWorkspace.parsed.remoteCommand === null, "pod workspace apply-patch must use the default v2 local engine", routeApplyPatchWorkspace);
const routeApplyPatchV1Workspace = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api/app", ["apply-patch-v1"]);
assertCondition(routeApplyPatchV1Workspace.parsed.requiresStdin === true, "pod workspace apply-patch-v1 must still stream patch stdin", routeApplyPatchV1Workspace);
assertCondition(routeApplyPatchV1Workspace.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-i' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'sh' '-c' 'cd \"$1\" || exit; shift; exec \"$@\"' 'unidesk-cwd' '/app' 'sh' '-s' '--'", "pod workspace apply-patch-v1 must set cwd before injecting the sh helper", routeApplyPatchV1Workspace);
const routeExecStdin = parseSshInvocation("D601:k3s:unidesk:code-queue/root/unidesk", ["exec", "--stdin", "--", "tar", "-xf", "-", "-C", "/root/unidesk"]);
assertCondition(routeExecStdin.parsed.requiresStdin === true, "pod route exec --stdin must stream local stdin into kubectl exec", routeExecStdin);
@@ -714,9 +724,9 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
assertCondition(helpText.includes("ssh G14:k3s kubectl get pipelineruns -n hwlab-ci"), "ssh help must document G14 k3s route operation", helpText);
assertCondition(helpText.includes("ssh D601:k3s:hwlab-dev:hwlab-cloud-api/app pwd"), "ssh help must document k3s pod workspace route", helpText);
assertCondition(helpText.includes("ssh D601:k3s script <<'SCRIPT'"), "ssh help must document k3s control-plane script operation", helpText);
assertCondition(helpText.includes("ssh D601:k3s:hwlab-dev:hwlab-cloud-api apply-patch <<'PATCH'"), "ssh help must document k3s pod apply-patch operation", helpText);
assertCondition(helpText.includes("ssh D601:k3s:hwlab-dev:hwlab-cloud-api/app apply-patch <<'PATCH'"), "ssh help must document k3s pod apply-patch operation", helpText);
assertCondition(helpText.includes("ssh D601:k3s:unidesk:code-queue/root/unidesk exec --stdin -- tar -xf - -C /root/unidesk"), "ssh help must document one-step stdin file streaming into pod exec", helpText);
assertCondition(helpText.includes("apply-patch [--allow-loose]") && helpText.includes("low-context update hunks"), "ssh help must document apply-patch loose-context guard", helpText);
assertCondition(helpText.includes("apply-patch-v1 [--allow-loose]") && helpText.includes("low-context update hunks"), "ssh help must document apply-patch-v1 loose-context guard", helpText);
assertCondition(helpText.includes("ssh D601:k3s:hwlab-dev:hwlab-cloud-api script <<'SCRIPT'"), "ssh help must document k3s script operation", helpText);
assertCondition(helpText.includes("UNIDESK_SSH_HINT"), "ssh help must document structured failure hint", helpText);
assertCondition(helpText.includes("UNIDESK_SSH_RUNTIME_TIMEOUT") && helpText.includes("UNIDESK_TRAN_TIMEOUT_HINT") && helpText.includes("60s") && helpText.includes("submit-and-poll"), "ssh help must document top-level runtime timeout and short polling discipline", helpText);
@@ -733,11 +743,11 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
assertCondition(!String(frontendRemoteK3sPlan.wrappedRemoteCommand ?? "").includes("UNIDESK_SSH_TOOL_DIR=/tmp/unidesk-ssh-tools"), "remote frontend ssh must not bootstrap helper tools for plain kubectl argv", frontendRemoteK3sPlan);
const frontendRemoteHostPatchPlan = remoteSshFrontendPlanForTest("D601", ["apply-patch"]);
assertCondition(String(frontendRemoteHostPatchPlan.wrappedRemoteCommand ?? "").includes("UNIDESK_SSH_TOOL_DIR=/tmp/unidesk-ssh-tools"), "host apply-patch must bootstrap the remote apply_patch helper", frontendRemoteHostPatchPlan);
assertCondition(String(frontendRemoteHostPatchPlan.wrappedRemoteCommand ?? "").includes("/apply_patch") && !String(frontendRemoteHostPatchPlan.wrappedRemoteCommand ?? "").includes("/glob") && !String(frontendRemoteHostPatchPlan.wrappedRemoteCommand ?? "").includes("/skill-discover"), "host apply-patch must not bootstrap unrelated helper tools", frontendRemoteHostPatchPlan);
assertCondition(frontendRemoteHostPatchPlan.requiresStdin === true && frontendRemoteHostPatchPlan.remoteCommand === null && !String(frontendRemoteHostPatchPlan.wrappedRemoteCommand ?? "").includes("UNIDESK_SSH_TOOL_DIR"), "frontend apply-patch plan must stay a local v2 engine operation and not bootstrap legacy helpers", frontendRemoteHostPatchPlan);
const frontendRemoteV2Plan = remoteSshFrontendPlanForTest("D601:/tmp", ["v2"]);
assertCondition(frontendRemoteV2Plan.requiresStdin === true && frontendRemoteV2Plan.remoteCommand === null && !String(frontendRemoteV2Plan.wrappedRemoteCommand ?? "").includes("UNIDESK_SSH_TOOL_DIR"), "frontend v2 plan must stay a local engine operation and not bootstrap legacy helpers", frontendRemoteV2Plan);
const frontendRemoteV1Plan = remoteSshFrontendPlanForTest("D601:/tmp", ["apply-patch-v1"]);
assertCondition(String(frontendRemoteV1Plan.wrappedRemoteCommand ?? "").includes("UNIDESK_SSH_TOOL_DIR=/tmp/unidesk-ssh-tools"), "frontend apply-patch-v1 must bootstrap the remote apply_patch helper", frontendRemoteV1Plan);
assertCondition(String(frontendRemoteV1Plan.wrappedRemoteCommand ?? "").includes("/apply_patch") && !String(frontendRemoteV1Plan.wrappedRemoteCommand ?? "").includes("/glob") && !String(frontendRemoteV1Plan.wrappedRemoteCommand ?? "").includes("/skill-discover"), "frontend apply-patch-v1 must not bootstrap unrelated helper tools", frontendRemoteV1Plan);
const frontendRemotePodArgvPlan = remoteSshFrontendPlanForTest("G14:k3s:unidesk:code-queue", ["argv", "sh", "-c", "command -v tran"]);
assertCondition(frontendRemotePodArgvPlan.providerId === "G14", "remote frontend pod route must dispatch through G14 provider", frontendRemotePodArgvPlan);
@@ -783,9 +793,9 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
"argv form is classified and quoted as the success path for non-interactive commands",
"stdin script form removes shell-command strings for host and k3s workload scripts",
"script -- single-string runs as a remote shell one-liner while multi-token form keeps dash-prefixed argv",
"pod apply-patch operation injects helper and forwards patch stdin",
"pod apply-patch operation uses the v2 local engine and apply-patch-v1 injects the legacy helper",
"pod exec --stdin streams arbitrary local stdin through workload routes without shell wrapping",
"apply-patch uses one sh helper for host and pod paths and rejects low-context hunks unless --allow-loose is explicit",
"apply-patch-v1 uses one sh helper for host and pod paths and rejects low-context hunks unless --allow-loose is explicit",
"legacy operation-in-route forms are rejected in any k3s route segment with canonical route-plus-operation guidance",
"post-provider k3s shorthand is rejected so location and operation stay separated",
"k3s route stays location-only while operations fix native kubeconfig and assemble kubectl exec as argv",
@@ -798,7 +808,7 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
"provider triage recommendedCrossChecks keeps ssh D601 argv true",
"remote frontend ssh uses the same structured route parser for host, k3s and pod argv routes",
"ssh helper bootstrap is lazy so plain argv/script commands do not transfer helper sources",
"host apply-patch bootstraps only the apply_patch helper and uses a Perl fast path for large files",
"host apply-patch-v1 bootstraps only the apply_patch helper and uses a Perl fast path for large files",
"remote frontend ssh uses authenticated /ws/ssh streaming instead of host.ssh dispatch task polling",
"Code Queue runner image installs the tran wrapper and runner tran auto-selects remote frontend transport",
"tran does not add local provider/plane directory locks and leaves coordination to k8s/Tekton/Argo/Lease",