fix(migrate): share service command semantics

This commit is contained in:
Mohamed Boudra
2026-07-19 02:01:39 +02:00
parent f2124f8cf9
commit af244d0f02
4 changed files with 44 additions and 9 deletions

View File

@@ -79,6 +79,15 @@ command = "echo $CONDUCTOR_UNSUPPORTED_VALUE"
[scripts.run.unsupported_substitution]
command = "serve --port \${CONDUCTOR_PORT/3000/3001}"
[scripts.run.comment]
command = "npm test # $CONDUCTOR_PORT"
[scripts.run.api]
command = "serve --port $CONDUCTOR_PORT"
[scripts.run.api_]
command = "serve-other --port $CONDUCTOR_PORT"
`,
);
@@ -86,6 +95,8 @@ command = "serve --port \${CONDUCTOR_PORT/3000/3001}"
expect(inspected.config).toEqual({
scripts: {
api: { command: "serve --port $PASEO_PORT", type: "service" },
comment: { command: "npm test # $CONDUCTOR_PORT" },
safe: { command: "npm test" },
service: { command: "serve --port $PASEO_PORT", type: "service" },
},
@@ -97,6 +108,7 @@ command = "serve --port \${CONDUCTOR_PORT/3000/3001}"
"scripts.escape.cwd: Absolute or escaping cwd values are not imported.",
"scripts.unknown_variable: Unsupported Conductor variables: CONDUCTOR_UNSUPPORTED_VALUE. Command was not imported.",
"scripts.unsupported_substitution: Unsupported Conductor variables: CONDUCTOR_PORT. Command was not imported.",
'scripts.api_: Service environment name collides with "api" (API).',
"settings.unknown_project_setting: Unknown Conductor setting.",
]),
);
@@ -192,6 +204,7 @@ test("skips variable commands on Windows while preserving ordinary commands", ()
run: {
service: { command: "serve --port $CONDUCTOR_PORT" },
plain: { command: "npm test" },
args: { command: "npm", args: ["run", "dev"] },
},
},
},
@@ -203,6 +216,7 @@ test("skips variable commands on Windows while preserving ordinary commands", ()
expect.arrayContaining([
"worktree.setup: Conductor variables use unsupported shell syntax on Windows: CONDUCTOR_WORKSPACE_PATH. Command was not imported.",
"scripts.service: Conductor variables use unsupported shell syntax on Windows: CONDUCTOR_PORT. Command was not imported.",
"scripts.args.args: Script arguments are not imported on Windows.",
]),
);
});

View File

@@ -1,6 +1,7 @@
import { existsSync, readFileSync } from "node:fs";
import { join, posix, relative } from "node:path";
import type { PaseoConfigRaw, PaseoScriptEntryRaw } from "@getpaseo/protocol/messages";
import { normalizeServiceEnvName } from "@getpaseo/protocol/service-env-name";
import { parse as parseToml } from "smol-toml";
import type { MigrationNotice } from "../../types.js";
@@ -294,6 +295,12 @@ function mapRunScript(
if (script.icon !== undefined) {
notices.push(unsupportedSetting(`${key}.icon`, "Script icons are not imported."));
}
if (platform === "win32" && script.args && script.args.length > 0) {
notices.push(
unsupportedSetting(`${key}.args`, "Script arguments are not imported on Windows."),
);
return;
}
let command = appendArgs(script.command, script.args ?? []);
if (script.cwd) {
@@ -324,7 +331,7 @@ function mapRunScript(
const service = containsShellVariable(command, "CONDUCTOR_PORT");
if (service) {
const environmentName = scriptId.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
const environmentName = normalizeServiceEnvName(scriptId);
const collision = serviceNames.get(environmentName);
if (collision) {
notices.push(
@@ -347,7 +354,7 @@ function mapRunScript(
);
if (!rewritten) return;
if (service) {
const environmentName = scriptId.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
const environmentName = normalizeServiceEnvName(scriptId);
serviceNames.set(environmentName, scriptId);
}
const entry: PaseoScriptEntryRaw = { command: rewritten };
@@ -632,6 +639,18 @@ function activeShellMask(command: string): string {
index += 1;
continue;
}
if (
quote === null &&
character === "#" &&
(index === 0 || /[\s;|&()<>]/.test(command[index - 1]))
) {
while (index < command.length && command[index] !== "\n") {
mask[index] = " ";
index += 1;
}
index -= 1;
continue;
}
if (quote === "double") {
if (character === '"') {
mask[index] = " ";

View File

@@ -0,0 +1,6 @@
export function normalizeServiceEnvName(scriptName: string): string {
return scriptName
.toUpperCase()
.replace(/[^A-Z0-9]+/g, "_")
.replace(/^_+|_+$/g, "");
}

View File

@@ -1,4 +1,7 @@
import { projectServiceProxyUrls } from "./service-proxy.js";
import { normalizeServiceEnvName } from "@getpaseo/protocol/service-env-name";
export { normalizeServiceEnvName } from "@getpaseo/protocol/service-env-name";
export interface WorkspaceServicePeer {
scriptName: string;
@@ -15,13 +18,6 @@ export interface BuildWorkspaceServiceEnvOptions {
peers: readonly WorkspaceServicePeer[];
}
export function normalizeServiceEnvName(scriptName: string): string {
return scriptName
.toUpperCase()
.replace(/[^A-Z0-9]+/g, "_")
.replace(/^_+|_+$/g, "");
}
export function buildWorkspaceServiceEnv(
options: BuildWorkspaceServiceEnvOptions,
): Record<string, string> {