Support repository search on older GitHub CLI versions (#2611)
* fix(github): support repository lists from older gh versions GitHub CLI versions before 2.28 reject the visibility JSON field before returning repository data. Retry that specific failure with the older isPrivate field while preserving exact visibility on newer clients. * fix(github): stop requesting repository visibility Repository cloning does not use visibility. Leave the released response field optional and keep repository discovery on JSON fields supported by older GitHub CLI versions. * fix(github): keep the visibility enum on the wire, derive it from isPrivate Restores the required visibility enum in the search-repositories response schema. Omitting it broke old clients, which reject the whole response. The daemon still never asks gh for visibility -- that JSON field does not exist before gh 2.28 and Debian Bookworm ships 2.23. It asks for isPrivate, supported on every version, and maps it to public/private. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com> Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Mohamed Boudra
Claude Opus 5
parent
891dd5a813
commit
ee3a9b013b
@@ -14,7 +14,6 @@ export interface GithubRepositoryChoice {
|
||||
cloneUrl: string;
|
||||
cloneProtocol?: "https" | "ssh";
|
||||
description: string | null;
|
||||
visibility: string | null;
|
||||
updatedAt: string | null;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,6 @@ export function buildManualGithubRepositoryChoices(query: string): GithubReposit
|
||||
nameWithOwner: identity?.repo ?? remoteName,
|
||||
cloneUrl: repo,
|
||||
description: "Clone this repository URL",
|
||||
visibility: null,
|
||||
updatedAt: null,
|
||||
},
|
||||
];
|
||||
@@ -116,7 +115,6 @@ export function buildManualGithubRepositoryChoices(query: string): GithubReposit
|
||||
cloneUrl: nameWithOwner,
|
||||
cloneProtocol,
|
||||
description: `Clone owner/repo via ${cloneProtocol.toUpperCase()}`,
|
||||
visibility: null,
|
||||
updatedAt: null,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -644,7 +644,7 @@ export function AddProjectFlow({ request, onClose }: AddProjectFlowProps) {
|
||||
title: repository.cloneProtocol
|
||||
? `${repository.nameWithOwner} via ${repository.cloneProtocol.toUpperCase()}`
|
||||
: repository.nameWithOwner,
|
||||
subtitle: repository.description ?? repository.visibility,
|
||||
subtitle: repository.description,
|
||||
icon: Github,
|
||||
testID: `add-project-flow-repository-${repository.id}`,
|
||||
select: () =>
|
||||
|
||||
@@ -30,7 +30,7 @@ describe("GitHub repository search", () => {
|
||||
name: " paseo ",
|
||||
nameWithOwner: " getpaseo/paseo ",
|
||||
description: null,
|
||||
visibility: "PUBLIC",
|
||||
isPrivate: false,
|
||||
updatedAt: "2026-07-15T12:00:00Z",
|
||||
sshUrl: " git@github.com:getpaseo/paseo.git ",
|
||||
url: "https://github.com/getpaseo/paseo",
|
||||
@@ -62,7 +62,7 @@ describe("GitHub repository search", () => {
|
||||
"repo",
|
||||
"list",
|
||||
"--json",
|
||||
"id,name,nameWithOwner,description,visibility,updatedAt,sshUrl,url",
|
||||
"id,name,nameWithOwner,description,isPrivate,updatedAt,sshUrl,url",
|
||||
"--limit",
|
||||
"8",
|
||||
],
|
||||
@@ -83,7 +83,7 @@ describe("GitHub repository search", () => {
|
||||
name: "private-repo",
|
||||
fullName: "octo/private-repo",
|
||||
description: "Private project",
|
||||
visibility: "PRIVATE",
|
||||
isPrivate: true,
|
||||
updatedAt: "2026-07-14T08:00:00Z",
|
||||
url: "https://github.com/octo/private-repo",
|
||||
},
|
||||
@@ -115,7 +115,7 @@ describe("GitHub repository search", () => {
|
||||
"repos",
|
||||
"private project",
|
||||
"--json",
|
||||
"id,name,fullName,description,visibility,updatedAt,url",
|
||||
"id,name,fullName,description,isPrivate,updatedAt,url",
|
||||
"--sort",
|
||||
"updated",
|
||||
"--order",
|
||||
|
||||
@@ -147,7 +147,7 @@ const GitHubRepositoryListItemSchema = z.object({
|
||||
name: z.string(),
|
||||
nameWithOwner: z.string(),
|
||||
description: z.string().nullable().optional(),
|
||||
visibility: z.string(),
|
||||
isPrivate: z.boolean(),
|
||||
updatedAt: z.string(),
|
||||
sshUrl: z.string(),
|
||||
url: z.string(),
|
||||
@@ -158,7 +158,7 @@ const GitHubRepositorySearchItemSchema = z.object({
|
||||
name: z.string(),
|
||||
fullName: z.string(),
|
||||
description: z.string().nullable().optional(),
|
||||
visibility: z.string(),
|
||||
isPrivate: z.boolean(),
|
||||
updatedAt: z.string(),
|
||||
url: z.string(),
|
||||
});
|
||||
@@ -1287,7 +1287,7 @@ export function createGitHubService(options: CreateGitHubServiceOptions = {}): G
|
||||
"repo",
|
||||
"list",
|
||||
"--json",
|
||||
"id,name,nameWithOwner,description,visibility,updatedAt,sshUrl,url",
|
||||
"id,name,nameWithOwner,description,isPrivate,updatedAt,sshUrl,url",
|
||||
"--limit",
|
||||
String(limit),
|
||||
],
|
||||
@@ -1305,7 +1305,7 @@ export function createGitHubService(options: CreateGitHubServiceOptions = {}): G
|
||||
"repos",
|
||||
query,
|
||||
"--json",
|
||||
"id,name,fullName,description,visibility,updatedAt,url",
|
||||
"id,name,fullName,description,isPrivate,updatedAt,url",
|
||||
"--sort",
|
||||
"updated",
|
||||
"--order",
|
||||
@@ -2315,7 +2315,7 @@ function normalizeRepositorySummary(repository: {
|
||||
name: string;
|
||||
nameWithOwner: string;
|
||||
description?: string | null;
|
||||
visibility: string;
|
||||
isPrivate: boolean;
|
||||
updatedAt: string;
|
||||
cloneUrl: string;
|
||||
}): GitHubRepositorySummary {
|
||||
@@ -2328,20 +2328,15 @@ function normalizeRepositorySummary(repository: {
|
||||
name: repository.name.trim(),
|
||||
nameWithOwner,
|
||||
description: repository.description ?? null,
|
||||
visibility: normalizeRepositoryVisibility(repository.visibility),
|
||||
// We query isPrivate, not visibility: `gh repo list --json visibility` is unsupported before
|
||||
// gh 2.28 (Debian Bookworm ships 2.23), while isPrivate works on every version. The wire schema
|
||||
// requires the visibility enum, so old clients still get a value they can parse.
|
||||
visibility: repository.isPrivate ? "private" : "public",
|
||||
updatedAt: repository.updatedAt,
|
||||
cloneUrl: repository.cloneUrl.trim(),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeRepositoryVisibility(visibility: string): GitHubRepositorySummary["visibility"] {
|
||||
const normalized = visibility.toLowerCase();
|
||||
if (normalized === "public" || normalized === "private" || normalized === "internal") {
|
||||
return normalized;
|
||||
}
|
||||
throw new Error(`Unknown GitHub repository visibility: ${visibility}`);
|
||||
}
|
||||
|
||||
function toPullRequestCheckoutTarget(
|
||||
parsed: z.infer<typeof PullRequestCheckoutTargetSchema>,
|
||||
): PullRequestCheckoutTarget {
|
||||
|
||||
Reference in New Issue
Block a user