mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
fix(projects): refresh worktree source project kind
This commit is contained in:
@@ -73,6 +73,50 @@ test("creates a worktree and registers it in the source workspace project withou
|
||||
expect(events).toEqual([`workspace:${result.workspace.workspaceId}`]);
|
||||
});
|
||||
|
||||
test("refreshes a source project that became Git while creating a worktree", async () => {
|
||||
const { repoDir, tempDir } = createGitRepo();
|
||||
cleanupPaths.push(tempDir);
|
||||
const deps = createDeps();
|
||||
const sourceProject = {
|
||||
...createPersistedProjectRecordForTest({
|
||||
projectId: "prj_existing-source",
|
||||
rootPath: repoDir,
|
||||
displayName: "Repository",
|
||||
}),
|
||||
kind: "non_git" as const,
|
||||
customName: "My project",
|
||||
};
|
||||
const sourceWorkspace = createPersistedWorkspaceRecordForTest({
|
||||
workspaceId: "ws-main-checkout",
|
||||
projectId: sourceProject.projectId,
|
||||
cwd: repoDir,
|
||||
kind: "local_checkout",
|
||||
displayName: "main",
|
||||
});
|
||||
deps.projects.set(sourceProject.projectId, sourceProject);
|
||||
deps.workspaces.set(sourceWorkspace.workspaceId, sourceWorkspace);
|
||||
|
||||
const result = await createPaseoWorktree(
|
||||
{
|
||||
cwd: repoDir,
|
||||
worktreeSlug: "project-became-git",
|
||||
runSetup: false,
|
||||
paseoHome: path.join(tempDir, ".paseo"),
|
||||
},
|
||||
deps,
|
||||
);
|
||||
|
||||
expect(result.workspace.projectId).toBe(sourceProject.projectId);
|
||||
expect(deps.projects.get(sourceProject.projectId)).toMatchObject({
|
||||
projectId: sourceProject.projectId,
|
||||
rootPath: repoDir,
|
||||
kind: "git",
|
||||
displayName: "Repository",
|
||||
customName: "My project",
|
||||
archivedAt: null,
|
||||
});
|
||||
});
|
||||
|
||||
test("repairs a legacy source workspace whose project record is missing", async () => {
|
||||
const { repoDir, tempDir } = createGitRepo();
|
||||
cleanupPaths.push(tempDir);
|
||||
@@ -727,7 +771,7 @@ test.skipIf(isPlatform("win32"))(
|
||||
);
|
||||
|
||||
interface TestDeps extends CreatePaseoWorktreeDeps {
|
||||
projectRegistry: Pick<ProjectRegistry, "get" | "getOrCreateActiveByRoot">;
|
||||
projectRegistry: Pick<ProjectRegistry, "get" | "getOrCreateActiveByRoot" | "upsert">;
|
||||
projects: Map<string, PersistedProjectRecord>;
|
||||
workspaces: Map<string, PersistedWorkspaceRecord>;
|
||||
}
|
||||
@@ -760,6 +804,9 @@ function createDeps(options?: {
|
||||
projects.set(project.projectId, project);
|
||||
return project;
|
||||
},
|
||||
upsert: async (project) => {
|
||||
projects.set(project.projectId, project);
|
||||
},
|
||||
},
|
||||
workspaceRegistry: {
|
||||
get: async (workspaceId) => workspaces.get(workspaceId) ?? null,
|
||||
@@ -841,15 +888,30 @@ function createWorkspaceGitServiceStub(): WorkspaceGitService {
|
||||
unsubscribe: () => {},
|
||||
}),
|
||||
peekSnapshot: (cwd) => createWorkspaceGitSnapshot(cwd),
|
||||
getCheckout: async (cwd) => ({
|
||||
cwd,
|
||||
isGit: false,
|
||||
currentBranch: null,
|
||||
remoteUrl: null,
|
||||
worktreeRoot: null,
|
||||
isPaseoOwnedWorktree: false,
|
||||
mainRepoRoot: null,
|
||||
}),
|
||||
getCheckout: async (cwd) => {
|
||||
try {
|
||||
const snapshot = createWorkspaceGitSnapshot(cwd);
|
||||
return {
|
||||
cwd,
|
||||
isGit: snapshot.git.isGit,
|
||||
currentBranch: snapshot.git.currentBranch,
|
||||
remoteUrl: snapshot.git.remoteUrl,
|
||||
worktreeRoot: snapshot.git.repoRoot,
|
||||
isPaseoOwnedWorktree: snapshot.git.isPaseoOwnedWorktree,
|
||||
mainRepoRoot: snapshot.git.mainRepoRoot,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
cwd,
|
||||
isGit: false,
|
||||
currentBranch: null,
|
||||
remoteUrl: null,
|
||||
worktreeRoot: null,
|
||||
isPaseoOwnedWorktree: false,
|
||||
mainRepoRoot: null,
|
||||
};
|
||||
}
|
||||
},
|
||||
getSnapshot: async (cwd) => createWorkspaceGitSnapshot(cwd),
|
||||
resolveRepoRoot: async (cwd) => {
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { WorkspaceGitService } from "./workspace-git-service.js";
|
||||
import { resolve } from "node:path";
|
||||
import {
|
||||
type PersistedProjectRecord,
|
||||
type PersistedWorkspaceRecord,
|
||||
type ProjectRegistry,
|
||||
type WorkspaceRegistry,
|
||||
@@ -51,7 +52,7 @@ export interface AttemptFirstAgentBranchAutoNameResult {
|
||||
}
|
||||
|
||||
export interface CreatePaseoWorktreeDeps extends CreateWorktreeCoreDeps {
|
||||
projectRegistry: Pick<ProjectRegistry, "get" | "getOrCreateActiveByRoot">;
|
||||
projectRegistry: Pick<ProjectRegistry, "get" | "getOrCreateActiveByRoot" | "upsert">;
|
||||
workspaceRegistry: Pick<WorkspaceRegistry, "get" | "list" | "upsert">;
|
||||
workspaceGitService: WorkspaceGitService;
|
||||
}
|
||||
@@ -258,14 +259,17 @@ async function resolveSourceProjectIdForWorktree(options: {
|
||||
inputCwd: string;
|
||||
projectId?: string;
|
||||
repoRoot: string;
|
||||
deps: Pick<CreatePaseoWorktreeDeps, "projectRegistry" | "workspaceRegistry">;
|
||||
deps: Pick<
|
||||
CreatePaseoWorktreeDeps,
|
||||
"projectRegistry" | "workspaceRegistry" | "workspaceGitService"
|
||||
>;
|
||||
}): Promise<string> {
|
||||
if (options.projectId) {
|
||||
const project = await options.deps.projectRegistry.get(options.projectId);
|
||||
if (!project || project.archivedAt) {
|
||||
throw new Error(`Project not found for worktree: ${options.projectId}`);
|
||||
}
|
||||
return project.projectId;
|
||||
return (await refreshProjectKind(project, options.deps)).projectId;
|
||||
}
|
||||
|
||||
const sourceWorkspace = await findWorkspaceForSource({
|
||||
@@ -276,7 +280,7 @@ async function resolveSourceProjectIdForWorktree(options: {
|
||||
|
||||
if (sourceWorkspace) {
|
||||
const sourceProject = await options.deps.projectRegistry.get(sourceWorkspace.projectId);
|
||||
if (sourceProject) return sourceProject.projectId;
|
||||
if (sourceProject) return (await refreshProjectKind(sourceProject, options.deps)).projectId;
|
||||
// COMPAT(worktreeMissingSourceProject): added in v0.1.107, remove after 2027-01-15.
|
||||
// Orphaned legacy workspace FKs fall through to exact-root allocation.
|
||||
}
|
||||
@@ -287,7 +291,20 @@ async function resolveSourceProjectIdForWorktree(options: {
|
||||
displayName: options.repoRoot.split(/[\\/]/).findLast(Boolean) ?? options.repoRoot,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
return project.projectId;
|
||||
return (await refreshProjectKind(project, options.deps)).projectId;
|
||||
}
|
||||
|
||||
async function refreshProjectKind(
|
||||
project: PersistedProjectRecord,
|
||||
deps: Pick<CreatePaseoWorktreeDeps, "projectRegistry" | "workspaceGitService">,
|
||||
): Promise<PersistedProjectRecord> {
|
||||
const checkout = await deps.workspaceGitService.getCheckout(project.rootPath);
|
||||
const kind: PersistedProjectRecord["kind"] = checkout.isGit ? "git" : "non_git";
|
||||
if (project.kind === kind) return project;
|
||||
|
||||
const refreshed = { ...project, kind, updatedAt: new Date().toISOString() };
|
||||
await deps.projectRegistry.upsert(refreshed);
|
||||
return refreshed;
|
||||
}
|
||||
|
||||
async function findWorkspaceForSource(options: {
|
||||
|
||||
Reference in New Issue
Block a user