diff --git a/packages/source-control-bitbucket/src/server/BitbucketPullRequestProvider.test.ts b/packages/source-control-bitbucket/src/server/BitbucketPullRequestProvider.test.ts index b5470628be0aa9d7ca31bae2fd31e62cf98f0a96..ff5e47f6d1bd89093bd190b08bdd07f312dd5a2e 100644 --- a/packages/source-control-bitbucket/src/server/BitbucketPullRequestProvider.test.ts +++ b/packages/source-control-bitbucket/src/server/BitbucketPullRequestProvider.test.ts @@ -12,6 +12,59 @@ import { make, } from "./BitbucketPullRequestProvider.ts"; +it.effect.each([ + { + metadata: "source commit hash", + commit: { hash: "9f3a7c2d8b1e6f045a9c3d7e2b8f1a6c0d4e5f93" }, + headSha: "9f3a7c2d8b1e6f045a9c3d7e2b8f1a6c0d4e5f93", + }, + { metadata: "absent commit", commit: undefined, headSha: undefined }, + { metadata: "null commit", commit: null, headSha: undefined }, + { metadata: "absent hash", commit: {}, headSha: undefined }, + { metadata: "null hash", commit: { hash: null }, headSha: undefined }, +])("reads change request details with $metadata", ({ commit, headSha }) => + Effect.gen(function* () { + const pullRequest = Result.getOrThrow( + decodePullRequestJson( + JSON.stringify({ + id: 897, + title: "Add trustabl-pipe", + state: "OPEN", + source: { + branch: { name: "feat/page" }, + commit, + repository: { full_name: "fork/web" }, + }, + destination: { branch: { name: "master" }, commit: { hash: "base-commit" } }, + created_on: "2026-06-16T05:04:32.258456+00:00", + updated_on: "2026-06-16T05:04:33.750542+00:00", + links: { html: { href: "https://bitbucket.org/acme/web/pull-requests/897" } }, + }), + ), + ); + const provider = yield* make.pipe( + Effect.provide( + Layer.mock(BitbucketPullRequestApi.BitbucketPullRequestApi)({ + getPullRequest: () => Effect.succeed(pullRequest), + getDiffStat: () => Effect.succeed({ additions: 0, deletions: 0, changedFiles: 0 }), + getMergeability: () => Effect.succeed("unknown"), + listChecks: () => Effect.succeed([]), + getRepositoryPermission: () => Effect.succeed(true), + }), + ), + ); + const detail = yield* provider.getChangeRequest({ + cwd: "/repo", + repository: "acme/web", + host: "bitbucket.org", + number: 897, + }); + + expect(detail).toMatchObject({ number: 897, headBranch: "feat/page" }); + expect(detail.headSha).toBe(headSha); + }), +); + for (const operation of [ "getMergeability", "listChecks", diff --git a/packages/source-control-bitbucket/src/server/BitbucketPullRequestProvider.ts b/packages/source-control-bitbucket/src/server/BitbucketPullRequestProvider.ts index 3d26d5401602995b9e31c8a63fee3c501f3dfaa4..7f8794730be5aeb6f324ae9641247661d52bcd2e 100644 --- a/packages/source-control-bitbucket/src/server/BitbucketPullRequestProvider.ts +++ b/packages/source-control-bitbucket/src/server/BitbucketPullRequestProvider.ts @@ -98,6 +98,7 @@ function toChangeRequest(pullRequest: BitbucketPullRequest): ProviderChangeReque url: pullRequest.url, author: pullRequest.author, headBranch: pullRequest.headBranch, + ...(pullRequest.headSha ? { headSha: pullRequest.headSha } : {}), ...(pullRequest.headRepositoryNameWithOwner ? { headRepositoryNameWithOwner: pullRequest.headRepositoryNameWithOwner } : {}), diff --git a/packages/source-control-bitbucket/src/server/bitbucketPullRequestJson.test.ts b/packages/source-control-bitbucket/src/server/bitbucketPullRequestJson.test.ts index 356c5da96f333345ff4104a4217be496978f0357..00696287b7859d70e58fd79b17436b598ef54c40 100644 --- a/packages/source-control-bitbucket/src/server/bitbucketPullRequestJson.test.ts +++ b/packages/source-control-bitbucket/src/server/bitbucketPullRequestJson.test.ts @@ -24,8 +24,12 @@ function pullRequest(overrides: Record = {}): Record { url: "https://bitbucket.org/acme/web/pull-requests/897", author: { login: "bilal", name: "Bilal Hassan" }, headBranch: "feat/page", + headSha: "9f3a7c2d8b1e6f045a9c3d7e2b8f1a6c0d4e5f93", headRepositoryNameWithOwner: "fork/web", baseBranch: "master", state: "open", @@ -106,6 +111,27 @@ describe("decodePullRequestPageJson", () => { }); describe("decodePullRequestJson", () => { + it("preserves the source commit hash as the head SHA", () => { + const decoded = expectSuccess(decodePullRequestJson(JSON.stringify(pullRequest()))); + + expect(decoded).toMatchObject({ headSha: "9f3a7c2d8b1e6f045a9c3d7e2b8f1a6c0d4e5f93" }); + }); + + it.each([ + { metadata: "absent commit", commit: undefined }, + { metadata: "null commit", commit: null }, + { metadata: "absent hash", commit: {} }, + { metadata: "null hash", commit: { hash: null } }, + ])("reads detail and list payloads with $metadata", ({ commit }) => { + const raw = pullRequest({ source: { branch: { name: "feat/page" }, commit } }); + const detail = expectSuccess(decodePullRequestJson(JSON.stringify(raw))); + const list = expectSuccess(decodePullRequestPageJson(page([raw]))); + + expect(detail).toMatchObject({ number: 897, headBranch: "feat/page", headSha: null }); + expect(list.items).toHaveLength(1); + expect(list.items[0]).toMatchObject({ number: 897, headBranch: "feat/page", headSha: null }); + }); + it("reads reviewers as review requests", () => { const decoded = expectSuccess( decodePullRequestJson( diff --git a/packages/source-control-bitbucket/src/server/bitbucketPullRequestJson.ts b/packages/source-control-bitbucket/src/server/bitbucketPullRequestJson.ts index 8f0b520ca089b64d6851b8c1bd8e159964ac48b1..9337e888c840968df6b5a425cfe0cf17e8f42f9e 100644 --- a/packages/source-control-bitbucket/src/server/bitbucketPullRequestJson.ts +++ b/packages/source-control-bitbucket/src/server/bitbucketPullRequestJson.ts @@ -52,6 +52,9 @@ const RawUserSchema = Schema.Struct({ */ const RawBranchSchema = Schema.Struct({ branch: Schema.Struct({ name: TrimmedNonEmptyString }), + commit: Schema.optional( + Schema.NullOr(Schema.Struct({ hash: Schema.optional(Schema.NullOr(Schema.String)) })), + ), repository: Schema.optional(Schema.NullOr(Schema.Struct({ full_name: TrimmedNonEmptyString }))), }); @@ -178,6 +181,7 @@ export interface BitbucketPullRequest { readonly url: string; readonly author: PullRequestActor | null; readonly headBranch: string; + readonly headSha: string | null; readonly headRepositoryNameWithOwner: string | null; readonly baseBranch: string; readonly state: PullRequestState; @@ -293,6 +297,7 @@ function toPullRequest(raw: Schema.Schema.Type): Bi url: raw.links.html.href, author: toActor(raw.author), headBranch: raw.source.branch.name, + headSha: trimmed(raw.source.commit?.hash), headRepositoryNameWithOwner: raw.source.repository?.full_name ?? null, baseBranch: raw.destination.branch.name, state: toState(raw),