From cfe940493d4e921d2ffcdb6d65bf96662bd5cc68 Mon Sep 17 00:00:00 2001 From: joeriddles Date: Wed, 15 Jul 2026 13:46:32 -0700 Subject: [PATCH] fix: support gh versions without headRepository.nameWithOwner Signed-off-by: joeriddles --- src/domain/model.ts | 2 +- src/services/code-host/GitHub.ts | 21 ++++++++++++++++++--- tests/stack.test.ts | 31 +++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/domain/model.ts b/src/domain/model.ts index 0b490a9..e7b30ca 100644 --- a/src/domain/model.ts +++ b/src/domain/model.ts @@ -257,7 +257,7 @@ export class CodeHostDecodeError extends Schema.TaggedErrorClass("PullView")({ title: Schema.String, body: Schema.String, headRefName: Schema.String, - headRepository: Schema.NullOr(Schema.Struct({ nameWithOwner: Schema.String })), + headRepository: Schema.NullOr( + Schema.Struct({ + name: Schema.optional(Schema.String), + nameWithOwner: Schema.optional(Schema.String), + }), + ), + headRepositoryOwner: Schema.optional(Schema.NullOr(Schema.Struct({ login: Schema.String }))), baseRefName: Schema.String, url: Schema.String, isDraft: Schema.Boolean, @@ -93,13 +99,22 @@ const listRef = (row: PullListData) => draft: row.draft, }); +const nameWithOwner = (row: PullView): string | null => { + const repo = row.headRepository; + if (!repo) return null; + if (repo.nameWithOwner) return repo.nameWithOwner.toLowerCase(); + const owner = row.headRepositoryOwner?.login; + if (owner && repo.name) return `${owner}/${repo.name}`.toLowerCase(); + return null; +}; + const meta = (row: PullView) => pullMeta({ number: row.number, title: row.title, body: row.body, head: row.headRefName, - headRepository: row.headRepository?.nameWithOwner.toLowerCase() ?? null, + headRepository: nameWithOwner(row), base: row.baseRefName, url: row.url, draft: row.isDraft, @@ -151,7 +166,7 @@ export const layer = Layer.effect( "view", `${pr}`, "--json", - "number,title,body,headRefName,headRepository,baseRefName,url,isDraft,labels", + "number,title,body,headRefName,headRepository,headRepositoryOwner,baseRefName,url,isDraft,labels", ]; return run(args).pipe( Effect.catchIf(missingPull, () => Effect.fail(new CodeHostChangeNotFoundError(pr))), diff --git a/tests/stack.test.ts b/tests/stack.test.ts index 2fc0e4f..3d55a19 100644 --- a/tests/stack.test.ts +++ b/tests/stack.test.ts @@ -1719,6 +1719,37 @@ describe("GitHub", () => { ); }); + it.effect("composes headRepository from headRepositoryOwner on older gh versions", () => { + const proc = Layer.succeed( + Proc.Service, + Proc.Service.of({ + exec: () => + Effect.succeed( + JSON.stringify({ + number: 42, + title: "old-gh", + body: "body", + headRefName: "feature/x", + headRepository: { id: "R_kg", name: "Project" }, + headRepositoryOwner: { id: "MDE", login: "Fork-Owner" }, + baseRefName: "main", + url: "u", + isDraft: false, + labels: [], + }), + ), + }), + ); + + return Effect.gen(function* () { + const github = yield* CodeHost.Service; + const meta = yield* github.change(42); + expect(meta.headRepository).toBe("fork-owner/project"); + }).pipe( + Effect.provide(CodeHostGitHub.layer.pipe(Layer.provideMerge(cfg), Layer.provideMerge(proc))), + ); + }); + it.effect("resolves a created GitHub fork PR by its returned URL", () => { const calls: Array> = []; const proc = Layer.succeed(