Skip to content

Commit 0fb03bc

Browse files
authored
feat: Resolve issues and PRs by full GitHub URL (#2094)
1 parent 1663f08 commit 0fb03bc

4 files changed

Lines changed: 255 additions & 238 deletions

File tree

apps/code/src/main/services/folders/service.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from "node:fs";
22
import path from "node:path";
33
import { getRemoteUrl, isGitRepository } from "@posthog/git/queries";
44
import { InitRepositorySaga } from "@posthog/git/sagas/init";
5-
import { parseGitHubUrl } from "@posthog/git/utils";
5+
import { parseGithubUrl } from "@posthog/git/utils";
66
import { WorktreeManager } from "@posthog/git/worktree";
77
import type { IDialog } from "@posthog/platform/dialog";
88
import { normalizeRepoKey } from "@shared/utils/repo";
@@ -237,14 +237,15 @@ export class FoldersService {
237237
folderPath: string,
238238
overrideRemoteUrl: string | undefined,
239239
): Promise<string | null> {
240+
const slug = (url: string | null | undefined) => {
241+
const parsed = parseGithubUrl(url);
242+
return parsed ? `${parsed.owner}/${parsed.repo}` : null;
243+
};
240244
if (overrideRemoteUrl) {
241-
return (
242-
parseGitHubUrl(overrideRemoteUrl)?.path ??
243-
normalizeRepoKey(overrideRemoteUrl)
244-
);
245+
return slug(overrideRemoteUrl) ?? normalizeRepoKey(overrideRemoteUrl);
245246
}
246247
const localRemoteUrl = await getRemoteUrl(folderPath);
247-
return parseGitHubUrl(localRemoteUrl)?.path ?? null;
248+
return slug(localRemoteUrl);
248249
}
249250

250251
getRepositoryByRemoteUrl(

apps/code/src/main/services/git/service.ts

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { CommitSaga } from "@posthog/git/sagas/commit";
3535
import { DiscardFileChangesSaga } from "@posthog/git/sagas/discard";
3636
import { PullSaga } from "@posthog/git/sagas/pull";
3737
import { PushSaga } from "@posthog/git/sagas/push";
38-
import { parseGitHubUrl, parsePrUrl } from "@posthog/git/utils";
38+
import { parseGithubUrl } from "@posthog/git/utils";
3939
import { inject, injectable } from "inversify";
4040
import { MAIN_TOKENS } from "../../di/tokens";
4141
import { logger } from "../../utils/logger";
@@ -218,15 +218,15 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
218218
const remoteUrl = await getRemoteUrl(directoryPath);
219219
if (!remoteUrl) return null;
220220

221-
const repo = parseGitHubUrl(remoteUrl);
222-
if (!repo) return null;
221+
const parsed = parseGithubUrl(remoteUrl);
222+
if (!parsed) return null;
223223

224224
const branch = await getCurrentBranch(directoryPath);
225225
if (!branch) return null;
226226

227227
return {
228-
organization: repo.organization,
229-
repository: repo.repository,
228+
organization: parsed.owner,
229+
repository: parsed.repo,
230230
remote: remoteUrl,
231231
branch,
232232
};
@@ -426,20 +426,20 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
426426
const remoteUrl = await getRemoteUrl(directoryPath);
427427
if (!remoteUrl) return null;
428428

429-
const parsed = parseGitHubUrl(remoteUrl);
429+
const parsed = parseGithubUrl(remoteUrl);
430430
if (!parsed) return null;
431431

432432
const currentBranch = await getCurrentBranch(directoryPath);
433433
const defaultBranch = await getDefaultBranch(directoryPath);
434434

435435
let compareUrl: string | null = null;
436436
if (currentBranch && currentBranch !== defaultBranch) {
437-
compareUrl = `https://github.com/${parsed.path}/compare/${defaultBranch}...${currentBranch}?expand=1`;
437+
compareUrl = `https://github.com/${parsed.owner}/${parsed.repo}/compare/${defaultBranch}...${currentBranch}?expand=1`;
438438
}
439439

440440
return {
441-
organization: parsed.organization,
442-
repository: parsed.repository,
441+
organization: parsed.owner,
442+
repository: parsed.repo,
443443
currentBranch: currentBranch ?? null,
444444
defaultBranch,
445445
compareUrl,
@@ -819,7 +819,7 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
819819

820820
try {
821821
const remoteUrl = await getRemoteUrl(directoryPath);
822-
const isGitHubRepo = !!(remoteUrl && parseGitHubUrl(remoteUrl));
822+
const isGitHubRepo = !!(remoteUrl && parseGithubUrl(remoteUrl));
823823
const currentBranch = await getCurrentBranch(directoryPath);
824824
const defaultBranch = await getDefaultBranch(directoryPath).catch(
825825
() => null,
@@ -890,7 +890,7 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
890890
const remoteUrl = await getRemoteUrl(directoryPath);
891891
if (!remoteUrl) return null;
892892

893-
const parsed = parseGitHubUrl(remoteUrl);
893+
const parsed = parseGithubUrl(remoteUrl);
894894
if (!parsed) return null;
895895

896896
const result = await execGh([
@@ -905,7 +905,7 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
905905
"--limit",
906906
"1",
907907
"--repo",
908-
parsed.path,
908+
`${parsed.owner}/${parsed.repo}`,
909909
]);
910910

911911
if (result.exitCode !== 0) {
@@ -980,8 +980,8 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
980980
}
981981

982982
public async getPrChangedFiles(prUrl: string): Promise<ChangedFile[]> {
983-
const pr = parsePrUrl(prUrl);
984-
if (!pr) return [];
983+
const pr = parseGithubUrl(prUrl);
984+
if (pr?.kind !== "pr") return [];
985985

986986
const { owner, repo, number } = pr;
987987

@@ -1053,8 +1053,8 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
10531053
public async getPrDetailsByUrl(
10541054
prUrl: string,
10551055
): Promise<PrDetailsByUrlOutput | null> {
1056-
const pr = parsePrUrl(prUrl);
1057-
if (!pr) return null;
1056+
const pr = parseGithubUrl(prUrl);
1057+
if (pr?.kind !== "pr") return null;
10581058

10591059
try {
10601060
const result = await execGh([
@@ -1089,8 +1089,8 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
10891089
prUrl: string,
10901090
action: PrActionType,
10911091
): Promise<UpdatePrByUrlOutput> {
1092-
const pr = parsePrUrl(prUrl);
1093-
if (!pr) {
1092+
const pr = parseGithubUrl(prUrl);
1093+
if (pr?.kind !== "pr") {
10941094
return { success: false, message: "Invalid PR URL" };
10951095
}
10961096

@@ -1123,8 +1123,8 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
11231123
}
11241124

11251125
public async getPrReviewComments(prUrl: string): Promise<PrReviewComment[]> {
1126-
const pr = parsePrUrl(prUrl);
1127-
if (!pr) return [];
1126+
const pr = parseGithubUrl(prUrl);
1127+
if (pr?.kind !== "pr") return [];
11281128

11291129
const { owner, repo, number } = pr;
11301130

@@ -1155,8 +1155,8 @@ export class GitService extends TypedEventEmitter<GitServiceEvents> {
11551155
commentId: number,
11561156
body: string,
11571157
): Promise<ReplyToPrCommentOutput> {
1158-
const pr = parsePrUrl(prUrl);
1159-
if (!pr) {
1158+
const pr = parseGithubUrl(prUrl);
1159+
if (pr?.kind !== "pr") {
11601160
return { success: false, comment: null };
11611161
}
11621162

@@ -1544,6 +1544,17 @@ ${truncatedDiff || "(no diff available)"}${contextSection}`;
15441544
const repoInfo = await this.getGitRepoInfo(directoryPath);
15451545
if (!repoInfo) return [];
15461546

1547+
// Full GitHub URL: look up directly. May target a different repo than the local one.
1548+
const urlRef = parseGithubUrl(query);
1549+
if (urlRef && urlRef.kind !== "repo" && kinds.includes(urlRef.kind)) {
1550+
const repoSlug = `${urlRef.owner}/${urlRef.repo}`;
1551+
return this.fetchGhRefs(
1552+
[urlRef.kind, "view", String(urlRef.number), "--repo", repoSlug],
1553+
repoSlug,
1554+
urlRef.kind,
1555+
);
1556+
}
1557+
15471558
const repo = await this.resolveCanonicalRepo(
15481559
`${repoInfo.organization}/${repoInfo.repository}`,
15491560
);

0 commit comments

Comments
 (0)