fix(projects): stabilize legacy subproject grouping

This commit is contained in:
Mohamed Boudra
2026-07-28 22:40:04 +00:00
parent 07bb8eeaa6
commit ec08367d93
4 changed files with 45 additions and 10 deletions

View File

@@ -29,6 +29,19 @@ describe("deriveProjectGroupKey", () => {
).toBe("remote:git.example.com/repo");
});
test("accepts an SCP-style remote without a username", () => {
const rootPath = path.resolve("repo");
expect(
deriveProjectGroupKey({
rootPath,
remoteUrl: "github.com:getpaseo/paseo.git",
worktreeRoot: rootPath,
mainRepoRoot: null,
}),
).toBe("remote:github.com/getpaseo/paseo");
});
test("includes the selected path within a repository", () => {
const worktreeRoot = path.resolve("repo");
const rootPath = path.join(worktreeRoot, "packages", "app");

View File

@@ -38,7 +38,10 @@ function deriveRemoteProjectGroupKey(remoteUrl: string | null): string | null {
let host: string | null = null;
let remotePath: string | null = null;
const scpLike = trimmed.match(/^[^@]+@([^:]+):(.+)$/);
const scpLike =
!trimmed.includes("://") && !/^[A-Za-z]:[\\/]/.test(trimmed)
? trimmed.match(/^(?:[^@/:]+@)?([^/:]+):(.+)$/)
: null;
if (scpLike) {
host = scpLike[1] ?? null;
remotePath = scpLike[2] ?? null;

View File

@@ -1,4 +1,4 @@
import { basename, resolve } from "node:path";
import { basename, isAbsolute, relative, resolve, sep } from "node:path";
import type { ProjectCheckoutLitePayload } from "@getpaseo/protocol/messages";
@@ -75,7 +75,20 @@ function deriveProjectRootPath(input: {
cwd: string;
checkout: ProjectCheckoutLitePayload;
}): string {
return input.checkout.isGit && input.checkout.mainRepoRoot
? input.checkout.mainRepoRoot
: input.cwd;
if (!input.checkout.isGit || !input.checkout.mainRepoRoot) return input.cwd;
const worktreeRoot = input.checkout.worktreeRoot
? parseGitRevParsePath(input.checkout.worktreeRoot)
: null;
if (!worktreeRoot) return input.checkout.mainRepoRoot;
const selectedPath = relative(resolve(worktreeRoot), resolve(input.cwd));
if (
!selectedPath ||
selectedPath === "." ||
selectedPath === ".." ||
selectedPath.startsWith(`..${sep}`) ||
isAbsolute(selectedPath)
) {
return input.checkout.mainRepoRoot;
}
return resolve(input.checkout.mainRepoRoot, selectedPath);
}

View File

@@ -378,8 +378,8 @@ describe("bootstrapWorkspaceRegistries", () => {
});
test("keeps legacy agents in different monorepo subprojects separate", async () => {
const appProject = path.join(GIT_PROJECT, "packages", "app");
const serverProject = path.join(GIT_PROJECT, "packages", "server");
const appProject = path.join(GIT_WORKTREE, "packages", "app");
const serverProject = path.join(GIT_WORKTREE, "packages", "server");
mkdirSync(appProject, { recursive: true });
mkdirSync(serverProject, { recursive: true });
workspaceGitService = createNoopWorkspaceGitService({
@@ -388,9 +388,9 @@ describe("bootstrapWorkspaceRegistries", () => {
isGit: true,
currentBranch: "main",
remoteUrl: "git@github.com:acme/legacy-project.git",
worktreeRoot: GIT_PROJECT,
worktreeRoot: GIT_WORKTREE,
isPaseoOwnedWorktree: false,
mainRepoRoot: null,
mainRepoRoot: GIT_PROJECT,
}),
});
await agentStorage.initialize();
@@ -428,16 +428,22 @@ describe("bootstrapWorkspaceRegistries", () => {
expect(
(await projectRegistry.list())
.map((project) => ({ projectId: project.projectId, displayName: project.displayName }))
.map((project) => ({
projectId: project.projectId,
displayName: project.displayName,
rootPath: project.rootPath,
}))
.sort((left, right) => left.projectId.localeCompare(right.projectId)),
).toEqual([
{
projectId: "remote:github.com/acme/legacy-project#subdir:packages/app",
displayName: "app",
rootPath: path.join(GIT_PROJECT, "packages", "app"),
},
{
projectId: "remote:github.com/acme/legacy-project#subdir:packages/server",
displayName: "server",
rootPath: path.join(GIT_PROJECT, "packages", "server"),
},
]);
expect(