From c8200a6747122ced1aa25d82ada957d1a581f73e Mon Sep 17 00:00:00 2001 From: Gabriel Brown Date: Sat, 11 Jul 2026 13:39:08 -0400 Subject: [PATCH] feat(sync): add resolveBranchHead to fetch branch tip SHA --- packages/backend/convex/githubClient.ts | 10 ++++++++ .../backend/tests/unit/github-head.test.ts | 24 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 packages/backend/tests/unit/github-head.test.ts diff --git a/packages/backend/convex/githubClient.ts b/packages/backend/convex/githubClient.ts index cea1e0a..4fd06c8 100644 --- a/packages/backend/convex/githubClient.ts +++ b/packages/backend/convex/githubClient.ts @@ -88,6 +88,16 @@ export const getRepository = async ( return result.data; }; +export const resolveBranchHead = async ( + octokit: Octokit, + owner: string, + repo: string, + branch: string, +): Promise<{ sha: string }> => { + const result = await octokit.rest.repos.getBranch({ owner, repo, branch }); + return { sha: result.data.commit.sha }; +}; + const toMillis = (value?: string | null) => value ? new Date(value).getTime() : undefined; diff --git a/packages/backend/tests/unit/github-head.test.ts b/packages/backend/tests/unit/github-head.test.ts new file mode 100644 index 0000000..9e6fd60 --- /dev/null +++ b/packages/backend/tests/unit/github-head.test.ts @@ -0,0 +1,24 @@ +import type { Octokit } from '@octokit/rest'; +import { describe, expect, it, vi } from 'vitest'; + +import { resolveBranchHead } from '../../convex/githubClient'; + +describe('resolveBranchHead', () => { + it('returns the branch tip sha from the branch ref', async () => { + const getBranch = vi.fn(async () => ({ + data: { commit: { sha: 'deadbeef' } }, + })); + const octokit = { + rest: { repos: { getBranch } }, + } as unknown as Octokit; + + const result = await resolveBranchHead(octokit, 'o', 'r', 'main'); + + expect(result.sha).toBe('deadbeef'); + expect(getBranch).toHaveBeenCalledWith({ + owner: 'o', + repo: 'r', + branch: 'main', + }); + }); +});