Feat/open projects config to any (#681)
* feat(server): derive owner/repo display name for any remote host Generalize deriveProjectGroupingName so any remote:<host>/<segments...> project key returns the last two path segments (owner/repo) instead of just the trailing segment. Path-fallback for non-remote keys is unchanged. Brings GitLab, Gitea, Bitbucket, and self-hosted remotes to parity with the prior github.com-only behavior — no separate special-case needed. * feat(app): show projects from any git remote in Projects settings Remove the isSupportedProjectKey filter so workspaces with non-GitHub remotes (GitLab, Gitea, Bitbucket, self-hosted, ssh-style) appear in the Projects list and route to the project settings screen. The daemon RPCs, config schema, and registry were already host-agnostic; this lifts a client-side filter that hid them. Also remove the now-vestigial hiddenUnsupportedRemoteCount field from ProjectSummary, BuildProjectsResult, and UseProjectsResult — once the filter is gone, the count is always zero and the field is dead state. This is an internal app-package type, not a wire schema, so deletion is safe. * chore(app): simplify Projects empty state to "No projects yet" Drop the "Non-GitHub remote projects aren't supported yet" empty-state branch — it can no longer fire now that any git remote is shown. The empty state is unconditional now. * test(app): cover non-GitHub remote in projects-settings e2e Add a fixture that creates a temp git repo with origin pointing at a gitlab.com URL and exercises the same paseo.json read/edit/save flow already covered for local repos. Verifies the project surfaces with the "acme/app" display name and that the round-trip persists correctly. Extracted the remote-setup branch in createTempGitRepo into a small configureRemote helper to keep the main function under the cyclomatic complexity limit. --------- Co-authored-by: Mathias Kurz <mkurz@stamus-networks.com>
This commit is contained in:
co-authored by
Mathias Kurz
parent
2b6d7d4ec2
commit
44d13919d7
@@ -2,6 +2,7 @@ import { describe, expect, test, vi } from "vitest";
|
||||
|
||||
import {
|
||||
classifyDirectoryForProjectMembership,
|
||||
deriveProjectGroupingName,
|
||||
deriveProjectRootPath,
|
||||
deriveWorkspaceKind,
|
||||
deriveWorkspaceId,
|
||||
@@ -22,6 +23,36 @@ function createWorkspaceRecord(workspaceId: string) {
|
||||
});
|
||||
}
|
||||
|
||||
describe("deriveProjectGroupingName", () => {
|
||||
test("returns owner/repo for a github remote project key", () => {
|
||||
expect(deriveProjectGroupingName("remote:github.com/acme/app")).toBe("acme/app");
|
||||
});
|
||||
|
||||
test("returns owner/repo for a gitlab remote project key", () => {
|
||||
expect(deriveProjectGroupingName("remote:gitlab.com/acme/app")).toBe("acme/app");
|
||||
});
|
||||
|
||||
test("returns last two segments for a self-hosted remote project key", () => {
|
||||
expect(deriveProjectGroupingName("remote:git.acme.internal/platform/api")).toBe("platform/api");
|
||||
});
|
||||
|
||||
test("returns last two segments for a deeply-nested remote project key", () => {
|
||||
expect(deriveProjectGroupingName("remote:gitlab.com/group/sub/app")).toBe("sub/app");
|
||||
});
|
||||
|
||||
test("returns the lone path segment when only one segment follows the host", () => {
|
||||
expect(deriveProjectGroupingName("remote:github.com/solo")).toBe("solo");
|
||||
});
|
||||
|
||||
test("returns the trailing path segment for a non-remote project key", () => {
|
||||
expect(deriveProjectGroupingName("/repo/local")).toBe("local");
|
||||
});
|
||||
|
||||
test("returns the project key itself when no segments are present", () => {
|
||||
expect(deriveProjectGroupingName("")).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("detectStaleWorkspaces", () => {
|
||||
test("returns workspace ids whose directories no longer exist", async () => {
|
||||
const checkDirectoryExists = vi.fn(async (cwd: string) => cwd !== "/tmp/missing");
|
||||
|
||||
@@ -103,9 +103,16 @@ export function deriveProjectGroupingKey(options: {
|
||||
}
|
||||
|
||||
export function deriveProjectGroupingName(projectKey: string): string {
|
||||
const githubRemotePrefix = "remote:github.com/";
|
||||
if (projectKey.startsWith(githubRemotePrefix)) {
|
||||
return projectKey.slice(githubRemotePrefix.length) || projectKey;
|
||||
if (projectKey.startsWith("remote:")) {
|
||||
const remainder = projectKey.slice("remote:".length);
|
||||
const pathSegments = remainder.split("/").filter(Boolean).slice(1);
|
||||
if (pathSegments.length >= 2) {
|
||||
return pathSegments.slice(-2).join("/");
|
||||
}
|
||||
if (pathSegments.length === 1) {
|
||||
return pathSegments[0]!;
|
||||
}
|
||||
return projectKey;
|
||||
}
|
||||
|
||||
const segments = projectKey.split(/[\\/]/).filter(Boolean);
|
||||
|
||||
Reference in New Issue
Block a user