diff --git a/packages/migrate/src/sources/conductor/project-config.test.ts b/packages/migrate/src/sources/conductor/project-config.test.ts index a62e86426e..151abe6a31 100644 --- a/packages/migrate/src/sources/conductor/project-config.test.ts +++ b/packages/migrate/src/sources/conductor/project-config.test.ts @@ -163,6 +163,7 @@ test("preserves literal or escaped Conductor names and rewrites active variables arithmeticOffset: { command: "serve --port $(( CONDUCTOR_PORT + 1 ))" }, prefixExpansion: { command: "serve --length ${#CONDUCTOR_PORT}" }, unicodePrefix: { command: 'printf 😀 "$CONDUCTOR_PORT"' }, + commandSubstitution: { command: "printf \"$(printf '$CONDUCTOR_PORT')\"" }, }, }, }); @@ -196,6 +197,11 @@ test("preserves literal or escaped Conductor names and rewrites active variables message: "scripts.prefixExpansion: Unsupported Conductor variables: CONDUCTOR_PORT. Command was not imported.", }); + expect(inspected.notices).toContainEqual({ + code: "conductor-setting-unsupported", + level: "warning", + message: "scripts.commandSubstitution: Command substitutions are not imported.", + }); }); test("skips cwd scripts on Windows instead of emitting POSIX shell syntax", () => { diff --git a/packages/migrate/src/sources/conductor/project-config.ts b/packages/migrate/src/sources/conductor/project-config.ts index 599060cd4c..2b5989c0be 100644 --- a/packages/migrate/src/sources/conductor/project-config.ts +++ b/packages/migrate/src/sources/conductor/project-config.ts @@ -369,6 +369,10 @@ function rewriteExactCommand( notices: MigrationNotice[], platform: NodeJS.Platform, ): string | null { + if (containsActiveCommandSubstitution(command)) { + notices.push(unsupportedSetting(key, "Command substitutions are not imported.")); + return null; + } if (containsActiveHereDocument(command)) { notices.push(unsupportedSetting(key, "Here-document commands are not imported.")); return null; @@ -727,6 +731,17 @@ function containsActiveHereDocument(command: string): boolean { return false; } +function containsActiveCommandSubstitution(command: string): boolean { + const mask = activeShellMask(command); + for (const match of command.matchAll(/\$\((?!\()/g)) { + if (mask.slice(match.index, match.index + 2) === "$(") return true; + } + for (const match of command.matchAll(/`/g)) { + if (mask[match.index] === "`") return true; + } + return false; +} + function appendArgs(command: string, args: string[]): string { return args.length === 0 ? command : `${command} ${args.map(shellQuoteArgument).join(" ")}`; }