Merge pull request #4192 from mastertyko/fix/4130-workflow-run-quoted-overrides

fix(gsd): preserve quoted workflow run overrides
This commit is contained in:
Jeremy McSpadden 2026-04-14 15:18:08 -05:00 committed by GitHub
commit c8d7474f57
2 changed files with 76 additions and 9 deletions

View file

@ -38,6 +38,67 @@ const WORKFLOW_USAGE = [
" resume — Resume paused custom workflow auto-mode",
].join("\n");
function splitWorkflowRunArgs(input: string): string[] {
const tokens: string[] = [];
let current = "";
let quote: '"' | "'" | null = null;
let escapeNext = false;
for (const ch of input) {
if (escapeNext) {
current += ch;
escapeNext = false;
continue;
}
if (ch === "\\") {
escapeNext = true;
continue;
}
if (quote) {
if (ch === quote) {
quote = null;
} else {
current += ch;
}
continue;
}
if (ch === '"' || ch === "'") {
quote = ch;
continue;
}
if (/\s/.test(ch)) {
if (current) {
tokens.push(current);
current = "";
}
continue;
}
current += ch;
}
if (escapeNext) current += "\\";
if (current) tokens.push(current);
return tokens;
}
export function parseWorkflowRunArgs(args: string): { defName: string; overrides: Record<string, string> } {
const parts = splitWorkflowRunArgs(args);
const defName = parts[0] ?? "";
const overrides: Record<string, string> = {};
for (let i = 1; i < parts.length; i++) {
const eqIdx = parts[i].indexOf("=");
if (eqIdx > 0) {
overrides[parts[i].slice(0, eqIdx)] = parts[i].slice(eqIdx + 1);
}
}
return { defName, overrides };
}
async function handleCustomWorkflow(
sub: string,
ctx: ExtensionCommandContext,
@ -62,15 +123,7 @@ async function handleCustomWorkflow(
ctx.ui.notify("Usage: /gsd workflow run <name> [param=value ...]", "warning");
return true;
}
const parts = args.split(/\s+/);
const defName = parts[0];
const overrides: Record<string, string> = {};
for (let i = 1; i < parts.length; i++) {
const eqIdx = parts[i].indexOf("=");
if (eqIdx > 0) {
overrides[parts[i].slice(0, eqIdx)] = parts[i].slice(eqIdx + 1);
}
}
const { defName, overrides } = parseWorkflowRunArgs(args);
try {
const base = projectRoot();
const runDir = createRun(base, defName, Object.keys(overrides).length > 0 ? overrides : undefined);

View file

@ -217,6 +217,20 @@ describe("workflow command handler", () => {
);
});
it("preserves quoted workflow run overrides (#4130)", async () => {
const { parseWorkflowRunArgs } = await import("../commands/handlers/workflow.ts");
assert.deepStrictEqual(
parseWorkflowRunArgs('demo-workflow target="multi word target" region=\'us east\''),
{
defName: "demo-workflow",
overrides: {
target: "multi word target",
region: "us east",
},
},
);
});
it("'/gsd workflow run nonexistent' shows error for missing definition", async () => {
const { handled, notifications } = await callHandler("workflow run nonexistent-def-12345");
assert.ok(handled, "should be handled");