From a19b9a98f87e8cd91736915711cac538bd0a5360 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Apr 2026 17:46:25 +0000 Subject: [PATCH 1/2] fix(web): handle separator chars in getCommit body parsing Co-authored-by: Michael Sukkarieh --- .../web/src/features/git/getCommitApi.test.ts | 97 +++++++++++++++++++ packages/web/src/features/git/getCommitApi.ts | 9 +- 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 packages/web/src/features/git/getCommitApi.test.ts diff --git a/packages/web/src/features/git/getCommitApi.test.ts b/packages/web/src/features/git/getCommitApi.test.ts new file mode 100644 index 000000000..74cd2e2f4 --- /dev/null +++ b/packages/web/src/features/git/getCommitApi.test.ts @@ -0,0 +1,97 @@ +import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest'; +import { getCommit } from './getCommitApi'; + +vi.mock('simple-git'); +vi.mock('@sourcebot/shared', () => ({ + getRepoPath: (repo: { id: number }) => ({ + path: `/mock/cache/dir/${repo.id}`, + }), + createLogger: () => ({ + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + }), +})); +vi.mock('@/middleware/sew', () => ({ + sew: async (fn: () => Promise | T): Promise => { + return await fn(); + }, +})); + +const mockFindFirst = vi.fn(); +vi.mock('@/middleware/withAuth', () => ({ + withOptionalAuth: async (fn: (args: { org: { id: number }; prisma: unknown }) => Promise): Promise => { + return await fn({ + org: { id: 1 }, + prisma: { + repo: { + findFirst: mockFindFirst, + }, + }, + }); + }, +})); +vi.mock('@/lib/serviceError', () => ({ + notFound: (message: string) => ({ + errorCode: 'NOT_FOUND', + message, + }), + invalidGitRef: (ref: string) => ({ + errorCode: 'INVALID_GIT_REF', + message: `Invalid git reference: "${ref}". Git refs cannot start with '-'.`, + }), + unexpectedError: (message: string) => ({ + errorCode: 'UNEXPECTED_ERROR', + message, + }), +})); + +import { simpleGit } from 'simple-git'; + +describe('getCommit', () => { + const mockRaw = vi.fn(); + const mockCwd = vi.fn(); + const mockSimpleGit = simpleGit as unknown as Mock; + + beforeEach(() => { + vi.clearAllMocks(); + mockFindFirst.mockResolvedValue({ id: 123, name: 'github.com/test/repo' }); + mockCwd.mockReturnValue({ + raw: mockRaw, + }); + mockSimpleGit.mockReturnValue({ + cwd: mockCwd, + }); + }); + + it('parses commit body correctly when it contains field separator', async () => { + const fieldSep = '\x1f'; + mockRaw.mockResolvedValue([ + 'abc123', + '2026-04-02T00:00:00Z', + 'subject line', + 'HEAD -> main', + `body before separator ${fieldSep} body after separator`, + 'Test User', + 'test@example.com', + 'p1 p2', + ].join(fieldSep)); + + const result = await getCommit({ + repo: 'github.com/test/repo', + ref: 'HEAD', + }); + + expect(result).toEqual({ + hash: 'abc123', + date: '2026-04-02T00:00:00Z', + message: 'subject line', + refs: 'HEAD -> main', + body: `body before separator ${fieldSep} body after separator`, + authorName: 'Test User', + authorEmail: 'test@example.com', + parents: ['p1', 'p2'], + }); + }); +}); diff --git a/packages/web/src/features/git/getCommitApi.ts b/packages/web/src/features/git/getCommitApi.ts index 5e4843629..a37539e09 100644 --- a/packages/web/src/features/git/getCommitApi.ts +++ b/packages/web/src/features/git/getCommitApi.ts @@ -63,7 +63,14 @@ export const getCommit = async ({ return unexpectedError(`Failed to parse commit data for revision "${ref}".`); } - const [hash, date, message, refs, body, authorName, authorEmail, parentStr] = fields; + const hash = fields[0]; + const date = fields[1]; + const message = fields[2]; + const refs = fields[3]; + const body = fields.slice(4, fields.length - 3).join(FIELD_SEP); + const authorName = fields[fields.length - 3]; + const authorEmail = fields[fields.length - 2]; + const parentStr = fields[fields.length - 1]; const parents = parentStr.trim() === '' ? [] : parentStr.trim().split(' '); return { From e13a1875bf87168bfb6254993e894124937decf7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 2 Apr 2026 17:48:03 +0000 Subject: [PATCH 2/2] chore: add changelog entry for commit parser fix Co-authored-by: Michael Sukkarieh --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 424544c3d..630f85132 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- Fixed `GET /api/commit` parsing for commit bodies containing control-character separators so author and parent fields are returned correctly. [#1081](https://github.com/sourcebot-dev/sourcebot/pull/1081) + ## [4.16.5] - 2026-04-02 ### Added