mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
fix(migrate): preserve adopted workspace config
This commit is contained in:
@@ -47,6 +47,7 @@ test("imports real catalog, config, and Git worktree shapes through observable P
|
||||
},
|
||||
});
|
||||
expect(paseo.openedCheckouts).toEqual([fixture.liveWorktree]);
|
||||
expect(paseo.configs.get(fixture.liveWorktree)).toEqual(paseo.configs.get(fixture.repo));
|
||||
expect(paseo.createdCheckouts).toEqual([
|
||||
{ rootPath: fixture.repo, refName: "create-branch", directoryName: "missing-create" },
|
||||
]);
|
||||
@@ -79,7 +80,7 @@ test("imports real catalog, config, and Git worktree shapes through observable P
|
||||
output: (event) => secondEvents.push(event),
|
||||
});
|
||||
expect(second.notices.some((notice) => notice.code === "project-apply-failed")).toBe(false);
|
||||
expect(paseo.configWrites).toBe(1);
|
||||
expect(paseo.configWrites).toBe(2);
|
||||
expect(secondEvents.map((event) => event.message)).toContain(
|
||||
`Worktree ${path.join(fixture.repo, ".paseo", "missing-create")} already exists for create-branch.`,
|
||||
);
|
||||
@@ -97,7 +98,7 @@ test("reports a revision-stale config write without retrying or replacing existi
|
||||
output: () => undefined,
|
||||
});
|
||||
|
||||
expect(paseo.configWrites).toBe(1);
|
||||
expect(paseo.configWrites).toBe(2);
|
||||
expect(paseo.configs.get(fixture.repo)).toEqual({});
|
||||
expect(paseo.openedCheckouts).toEqual([fixture.liveWorktree]);
|
||||
expect(paseo.createdCheckouts).toEqual([
|
||||
|
||||
@@ -62,9 +62,14 @@ export async function migrate(input: {
|
||||
|
||||
async function migrateProject(project: MigrationProject, context: MigrationContext): Promise<void> {
|
||||
for (const notice of project.notices) context.emitNotice(notice);
|
||||
let config = project.config;
|
||||
if (context.dryRun) planProject(project, context);
|
||||
else if (!(await applyProject(project, context))) return;
|
||||
await migrateWorkspaces(project, context);
|
||||
else {
|
||||
const appliedConfig = await applyProject(project, context);
|
||||
if (!appliedConfig) return;
|
||||
config = appliedConfig;
|
||||
}
|
||||
await migrateWorkspaces(project, config, context);
|
||||
}
|
||||
|
||||
function planProject(project: MigrationProject, context: MigrationContext): void {
|
||||
@@ -80,52 +85,49 @@ function planProject(project: MigrationProject, context: MigrationContext): void
|
||||
async function applyProject(
|
||||
project: MigrationProject,
|
||||
context: MigrationContext,
|
||||
): Promise<boolean> {
|
||||
): Promise<PaseoConfigRaw | null> {
|
||||
try {
|
||||
await context.paseo.addProject(project.rootPath);
|
||||
context.output({ level: "info", message: `Registered project ${project.rootPath}.` });
|
||||
} catch (error) {
|
||||
context.emitNotice(applyFailure("project-apply-failed", project.rootPath, error));
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const current = await context.paseo.readProjectConfig(project.rootPath);
|
||||
const merged = mergeExistingConfig(project.config, current.config);
|
||||
if (isDeepStrictEqual(merged, current.config ?? {})) {
|
||||
context.output({
|
||||
level: "info",
|
||||
message: `Project config already current for ${project.rootPath}.`,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
await context.paseo.writeProjectConfig({
|
||||
rootPath: project.rootPath,
|
||||
config: merged,
|
||||
expectedRevision: current.revision,
|
||||
});
|
||||
context.stats.configs += 1;
|
||||
context.output({ level: "info", message: `Updated project config for ${project.rootPath}.` });
|
||||
return await applyConfig(project.rootPath, project.config, context);
|
||||
} catch (error) {
|
||||
context.emitNotice(applyFailure("project-config-apply-failed", project.rootPath, error));
|
||||
return project.config ?? {};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function migrateWorkspaces(
|
||||
project: MigrationProject,
|
||||
config: PaseoConfigRaw | null,
|
||||
context: MigrationContext,
|
||||
): Promise<void> {
|
||||
for (const workspace of project.workspaces) {
|
||||
for (const notice of workspace.notices) context.emitNotice(notice);
|
||||
if (context.dryRun) planWorkspace(workspace, context);
|
||||
else await applyWorkspace(project.rootPath, workspace, context);
|
||||
if (context.dryRun) planWorkspace(project, workspace, context);
|
||||
else await applyWorkspace(project, config, workspace, context);
|
||||
}
|
||||
}
|
||||
|
||||
function planWorkspace(workspace: MigrationWorkspace, context: MigrationContext): void {
|
||||
function planWorkspace(
|
||||
project: MigrationProject,
|
||||
workspace: MigrationWorkspace,
|
||||
context: MigrationContext,
|
||||
): void {
|
||||
if (workspace.disposition === "adopt" && workspace.path) {
|
||||
context.stats.adopted += 1;
|
||||
context.output({ level: "info", message: `Would adopt worktree ${workspace.path}.` });
|
||||
if (project.config) {
|
||||
context.stats.configs += 1;
|
||||
context.output({
|
||||
level: "info",
|
||||
message: `Would merge supported project config for ${workspace.path}.`,
|
||||
});
|
||||
}
|
||||
} else if (workspace.disposition === "create" && workspace.branch) {
|
||||
context.stats.created += 1;
|
||||
context.output({
|
||||
@@ -136,18 +138,20 @@ function planWorkspace(workspace: MigrationWorkspace, context: MigrationContext)
|
||||
}
|
||||
|
||||
async function applyWorkspace(
|
||||
rootPath: string,
|
||||
project: MigrationProject,
|
||||
config: PaseoConfigRaw | null,
|
||||
workspace: MigrationWorkspace,
|
||||
context: MigrationContext,
|
||||
): Promise<void> {
|
||||
try {
|
||||
if (workspace.disposition === "adopt" && workspace.path) {
|
||||
await context.paseo.openCheckout(workspace.path);
|
||||
await applyConfig(workspace.path, config, context);
|
||||
context.stats.adopted += 1;
|
||||
context.output({ level: "info", message: `Adopted worktree ${workspace.path}.` });
|
||||
} else if (workspace.disposition === "create" && workspace.branch) {
|
||||
const ensured = await context.paseo.ensureCheckout({
|
||||
rootPath,
|
||||
rootPath: project.rootPath,
|
||||
refName: workspace.branch,
|
||||
directoryName: workspace.directoryName,
|
||||
});
|
||||
@@ -164,6 +168,27 @@ async function applyWorkspace(
|
||||
}
|
||||
}
|
||||
|
||||
async function applyConfig(
|
||||
rootPath: string,
|
||||
imported: PaseoConfigRaw | null,
|
||||
context: MigrationContext,
|
||||
): Promise<PaseoConfigRaw> {
|
||||
const current = await context.paseo.readProjectConfig(rootPath);
|
||||
const merged = mergeExistingConfig(imported, current.config);
|
||||
if (isDeepStrictEqual(merged, current.config ?? {})) {
|
||||
context.output({ level: "info", message: `Project config already current for ${rootPath}.` });
|
||||
return current.config ?? {};
|
||||
}
|
||||
await context.paseo.writeProjectConfig({
|
||||
rootPath,
|
||||
config: merged,
|
||||
expectedRevision: current.revision,
|
||||
});
|
||||
context.stats.configs += 1;
|
||||
context.output({ level: "info", message: `Updated project config for ${rootPath}.` });
|
||||
return merged;
|
||||
}
|
||||
|
||||
export function mergeExistingConfig(
|
||||
imported: PaseoConfigRaw | null,
|
||||
existing: PaseoConfigRaw | null,
|
||||
|
||||
@@ -162,6 +162,7 @@ test("preserves literal or escaped Conductor names and rewrites active variables
|
||||
arithmeticBraced: { command: "serve --port $(( ${CONDUCTOR_PORT} ))" },
|
||||
arithmeticOffset: { command: "serve --port $(( CONDUCTOR_PORT + 1 ))" },
|
||||
prefixExpansion: { command: "serve --length ${#CONDUCTOR_PORT}" },
|
||||
unicodePrefix: { command: 'printf 😀 "$CONDUCTOR_PORT"' },
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -175,6 +176,7 @@ test("preserves literal or escaped Conductor names and rewrites active variables
|
||||
bare: { command: "printf CONDUCTOR_PORT" },
|
||||
escaped: { command: "printf \\$CONDUCTOR_PORT" },
|
||||
escapedDouble: { command: 'printf "\\$CONDUCTOR_PORT"' },
|
||||
unicodePrefix: { command: 'printf 😀 "$PASEO_PORT"', type: "service" },
|
||||
},
|
||||
});
|
||||
expect(inspected.notices).toContainEqual({
|
||||
|
||||
@@ -628,7 +628,7 @@ function collectConductorVariables(command: string): string[] {
|
||||
}
|
||||
|
||||
function activeShellMask(command: string): string {
|
||||
const mask = [...command];
|
||||
const mask = command.split("");
|
||||
let quote: "single" | "double" | null = null;
|
||||
for (let index = 0; index < command.length; index += 1) {
|
||||
const character = command[index];
|
||||
|
||||
Reference in New Issue
Block a user