mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
fix(projects): close grouped routing gaps
This commit is contained in:
@@ -22,6 +22,7 @@ describe("selectActiveGitWorkspaceProject", () => {
|
||||
it("selects the active git workspace project from checkout metadata", () => {
|
||||
expect(selectActiveGitWorkspaceProject("server-1", gitWorkspace())).toEqual({
|
||||
serverId: "server-1",
|
||||
projectId: "project-1",
|
||||
projectKey: "project-1",
|
||||
repoRoot: "/repo/main-project-1",
|
||||
});
|
||||
@@ -35,11 +36,27 @@ describe("selectActiveGitWorkspaceProject", () => {
|
||||
),
|
||||
).toEqual({
|
||||
serverId: "server-1",
|
||||
projectId: "project-1",
|
||||
projectKey: "project-1",
|
||||
repoRoot: "/repo/project-1",
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the persisted project group key for the settings route", () => {
|
||||
expect(
|
||||
selectActiveGitWorkspaceProject(
|
||||
"server-1",
|
||||
gitWorkspace({
|
||||
projectId: "prj_local",
|
||||
projectGroupKey: "remote:github.com/acme/project",
|
||||
}),
|
||||
),
|
||||
).toMatchObject({
|
||||
projectId: "prj_local",
|
||||
projectKey: "remote:github.com/acme/project",
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores non-git workspaces and blank project coordinates", () => {
|
||||
expect(
|
||||
selectActiveGitWorkspaceProject("server-1", gitWorkspace({ projectKind: "local" })),
|
||||
@@ -85,6 +102,7 @@ describe("buildWorktreeSetupCalloutPolicy", () => {
|
||||
expect(
|
||||
buildWorktreeSetupCalloutPolicy({
|
||||
serverId: "server-1",
|
||||
projectId: "project-1",
|
||||
projectKey: "project-1",
|
||||
repoRoot: "/repo/project-1",
|
||||
}),
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { PaseoConfigRaw } from "@getpaseo/protocol/messages";
|
||||
import { i18n } from "@/i18n/i18next";
|
||||
import { resolveProjectGroupKey } from "@/projects/project-group-key";
|
||||
import { buildProjectSettingsRoute } from "@/utils/host-routes";
|
||||
|
||||
export interface WorktreeSetupWorkspaceInput {
|
||||
projectId: string;
|
||||
projectGroupKey?: string | null;
|
||||
projectKind: string;
|
||||
projectRootPath: string;
|
||||
project?: {
|
||||
@@ -15,6 +17,7 @@ export interface WorktreeSetupWorkspaceInput {
|
||||
|
||||
export interface ActiveGitWorkspaceProject {
|
||||
serverId: string;
|
||||
projectId: string;
|
||||
projectKey: string;
|
||||
repoRoot: string;
|
||||
}
|
||||
@@ -43,13 +46,18 @@ export function selectActiveGitWorkspaceProject(
|
||||
return null;
|
||||
}
|
||||
|
||||
const projectKey = workspace.projectId.trim();
|
||||
const projectId = workspace.projectId.trim();
|
||||
const projectKey = resolveProjectGroupKey({
|
||||
serverId,
|
||||
projectId,
|
||||
projectGroupKey: workspace.projectGroupKey,
|
||||
});
|
||||
const repoRoot = (workspace.project?.checkout?.mainRepoRoot ?? workspace.projectRootPath).trim();
|
||||
if (!projectKey || !repoRoot) {
|
||||
if (!projectId || !repoRoot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { serverId, projectKey, repoRoot };
|
||||
return { serverId, projectId, projectKey, repoRoot };
|
||||
}
|
||||
|
||||
export function shouldShowWorktreeSetupCallout(readResult: ReadProjectConfigResult | undefined) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useRouter } from "expo-router";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { useSidebarCallouts } from "@/contexts/sidebar-callout-context";
|
||||
import { useStableEvent } from "@/hooks/use-stable-event";
|
||||
import { useProjects } from "@/hooks/use-projects";
|
||||
import { useHostRuntimeClient } from "@/runtime/host-runtime";
|
||||
import { useActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store";
|
||||
import { useWorkspaceFields } from "@/stores/session-store-hooks";
|
||||
@@ -14,11 +15,25 @@ import {
|
||||
|
||||
export function WorktreeSetupCalloutSource() {
|
||||
const selection = useActiveWorkspaceSelection();
|
||||
const activeProject = useWorkspaceFields(
|
||||
const selectedWorkspaceProject = useWorkspaceFields(
|
||||
selection?.serverId ?? null,
|
||||
selection?.workspaceId ?? null,
|
||||
(workspace) => selectActiveGitWorkspaceProject(selection?.serverId ?? "", workspace),
|
||||
);
|
||||
const { projects } = useProjects();
|
||||
const activeProject = useMemo(() => {
|
||||
if (!selectedWorkspaceProject) return null;
|
||||
const structuralProject = projects.find((project) =>
|
||||
project.hosts.some(
|
||||
(host) =>
|
||||
host.serverId === selectedWorkspaceProject.serverId &&
|
||||
host.projectId === selectedWorkspaceProject.projectId,
|
||||
),
|
||||
);
|
||||
return structuralProject
|
||||
? { ...selectedWorkspaceProject, projectKey: structuralProject.projectKey }
|
||||
: selectedWorkspaceProject;
|
||||
}, [projects, selectedWorkspaceProject]);
|
||||
const client = useHostRuntimeClient(activeProject?.serverId ?? "");
|
||||
const callouts = useSidebarCallouts();
|
||||
const router = useRouter();
|
||||
|
||||
@@ -16,6 +16,19 @@ describe("deriveProjectGroupKey", () => {
|
||||
).toBe("remote:git.example.com:8443/acme/app");
|
||||
});
|
||||
|
||||
test("accepts a root-level remote repository path", () => {
|
||||
const rootPath = path.resolve("repo");
|
||||
|
||||
expect(
|
||||
deriveProjectGroupKey({
|
||||
rootPath,
|
||||
remoteUrl: "https://git.example.com/repo.git",
|
||||
worktreeRoot: rootPath,
|
||||
mainRepoRoot: null,
|
||||
}),
|
||||
).toBe("remote:git.example.com/repo");
|
||||
});
|
||||
|
||||
test("includes the selected path within a repository", () => {
|
||||
const worktreeRoot = path.resolve("repo");
|
||||
const rootPath = path.join(worktreeRoot, "packages", "app");
|
||||
|
||||
@@ -55,6 +55,6 @@ function deriveRemoteProjectGroupKey(remoteUrl: string | null): string | null {
|
||||
if (!host || !remotePath) return null;
|
||||
let cleanedPath = remotePath.trim().replace(/^\/+/, "").replace(/\/+$/, "");
|
||||
if (cleanedPath.endsWith(".git")) cleanedPath = cleanedPath.slice(0, -4);
|
||||
if (!cleanedPath.includes("/")) return null;
|
||||
if (!cleanedPath) return null;
|
||||
return `remote:${host.toLowerCase()}/${cleanedPath}`;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { resolve } from "node:path";
|
||||
import { basename, resolve } from "node:path";
|
||||
|
||||
import type { ProjectCheckoutLitePayload } from "@getpaseo/protocol/messages";
|
||||
|
||||
@@ -46,7 +46,7 @@ export function classifyDirectoryForProjectMembership(input: {
|
||||
workspaceKind: deriveWorkspaceKind(checkout),
|
||||
workspaceDisplayName: deriveWorkspaceDisplayName({ cwd, checkout }),
|
||||
projectKey,
|
||||
projectName: deriveProjectGroupingName(projectKey),
|
||||
projectName: deriveProjectGroupingName(projectKey, cwd),
|
||||
projectRootPath: deriveProjectRootPath({ cwd, checkout }),
|
||||
projectKind: deriveProjectKind(checkout),
|
||||
};
|
||||
@@ -58,7 +58,8 @@ function deriveWorkspaceDirectoryKey(cwd: string, checkout: ProjectCheckoutLiteP
|
||||
return worktreeRoot && resolve(worktreeRoot) === selectedRoot ? worktreeRoot : selectedRoot;
|
||||
}
|
||||
|
||||
function deriveProjectGroupingName(projectKey: string): string {
|
||||
function deriveProjectGroupingName(projectKey: string, selectedRoot: string): string {
|
||||
if (projectKey.includes("#subdir:")) return basename(selectedRoot);
|
||||
if (projectKey.startsWith("remote:")) {
|
||||
const pathSegments = projectKey.slice("remote:".length).split("/").filter(Boolean).slice(1);
|
||||
if (pathSegments.length >= 2) return pathSegments.slice(-2).join("/");
|
||||
|
||||
@@ -426,9 +426,19 @@ describe("bootstrapWorkspaceRegistries", () => {
|
||||
logger,
|
||||
});
|
||||
|
||||
expect((await projectRegistry.list()).map((project) => project.projectId).sort()).toEqual([
|
||||
"remote:github.com/acme/legacy-project#subdir:packages/app",
|
||||
"remote:github.com/acme/legacy-project#subdir:packages/server",
|
||||
expect(
|
||||
(await projectRegistry.list())
|
||||
.map((project) => ({ projectId: project.projectId, displayName: project.displayName }))
|
||||
.sort((left, right) => left.projectId.localeCompare(right.projectId)),
|
||||
).toEqual([
|
||||
{
|
||||
projectId: "remote:github.com/acme/legacy-project#subdir:packages/app",
|
||||
displayName: "app",
|
||||
},
|
||||
{
|
||||
projectId: "remote:github.com/acme/legacy-project#subdir:packages/server",
|
||||
displayName: "server",
|
||||
},
|
||||
]);
|
||||
expect(
|
||||
new Set((await workspaceRegistry.list()).map((workspace) => workspace.projectId)),
|
||||
|
||||
Reference in New Issue
Block a user