Files
paseo/packages/protocol/src/git-remote.test.ts
Matt Cowger 218097b7cc feat: clone GitHub repo into a workspace (#1331)
* feat: clone GitHub repo into a workspace

Add an end-to-end "clone a GitHub repo and register it as a Paseo
workspace" flow: a new workspace.github.clone RPC, daemon handler,
client method, CLI `paseo clone` command, and a GitHub-repo mode in
the project picker modal. Gated behind the workspaceGithubClone
server capability flag.

- protocol: workspace.github.clone request/response schemas + feature flag
- server: handleWorkspaceGithubCloneRequest, normalizeCloneRepository
- client: DaemonClient.cloneGithubWorkspace
- cli: `paseo clone <repo> --dir <path> [--protocol https|ssh]`
- app: GitHub-repo mode, clone-protocol picker, error surfacing

Review fixes:
- CLI clone now checks the workspaceGithubClone capability and fails
  fast with a clear "update the host" error instead of hanging for the
  full request timeout against an older daemon.
- Replace the duplicated client-side URL-detection regex (app + CLI)
  with a shared isCompleteGitRemote() in @getpaseo/protocol/git-remote,
  backed by parseGitRemoteLocation so clients classify remotes
  identically to the daemon (fixes confusing errors for git://, ftp://,
  file:// inputs).
- Add git-remote.test.ts covering the shared classifier.

* Fix GitHub clone failure handling

---------

Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-07-13 11:15:40 +00:00

30 lines
1.5 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { isCompleteGitRemote, parseGitRemoteLocation } from "./git-remote.js";
describe("isCompleteGitRemote", () => {
it("treats supported URLs and scp-like addresses as complete remotes", () => {
expect(isCompleteGitRemote("https://github.com/owner/repo")).toBe(true);
expect(isCompleteGitRemote("http://internal/owner/repo.git")).toBe(true);
expect(isCompleteGitRemote("ssh://git@github.com/owner/repo")).toBe(true);
expect(isCompleteGitRemote("git@github.com:owner/repo.git")).toBe(true);
expect(isCompleteGitRemote(" https://github.com/owner/repo ")).toBe(true);
});
it("treats owner/repo shorthand as incomplete (needs a clone protocol)", () => {
expect(isCompleteGitRemote("owner/repo")).toBe(false);
expect(isCompleteGitRemote("owner/repo.git")).toBe(false);
expect(isCompleteGitRemote("")).toBe(false);
});
it("rejects schemes the daemon's parser does not accept, so clients agree with the server", () => {
// The old client-side regex matched any `scheme://`, classifying these as
// complete URLs while the daemon (parseGitRemoteLocation) rejected them —
// producing a confusing "use owner/repo format" error. The shared helper
// must classify them identically to the daemon.
for (const repo of ["git://github.com/owner/repo", "ftp://host/repo", "file:///tmp/repo"]) {
expect(isCompleteGitRemote(repo)).toBe(false);
expect(parseGitRemoteLocation(repo)).toBeNull();
}
});
});