-
Notifications
You must be signed in to change notification settings - Fork 261
Expand file tree
/
Copy pathgetCommitApi.ts
More file actions
103 lines (89 loc) · 3.21 KB
/
getCommitApi.ts
File metadata and controls
103 lines (89 loc) · 3.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import { sew } from "@/middleware/sew";
import { invalidGitRef, notFound, ServiceError, unexpectedError } from '@/lib/serviceError';
import { withOptionalAuth } from '@/middleware/withAuth';
import { getRepoPath } from '@sourcebot/shared';
import { z } from 'zod';
import { simpleGit } from 'simple-git';
import { commitDetailSchema } from './schemas';
import { isGitRefValid } from './utils';
export type CommitDetail = z.infer<typeof commitDetailSchema>;
type GetCommitRequest = {
repo: string;
ref: string;
}
// Field separator that won't appear in commit data
const FIELD_SEP = '\x1f';
const FORMAT = [
'%H', // hash
'%aI', // author date ISO 8601
'%s', // subject
'%D', // refs
'%b', // body
'%aN', // author name
'%aE', // author email
'%P', // parent hashes (space-separated)
].join(FIELD_SEP);
export const getCommit = async ({
repo: repoName,
ref,
}: GetCommitRequest): Promise<CommitDetail | ServiceError> => sew(() =>
withOptionalAuth(async ({ org, prisma }) => {
const repo = await prisma.repo.findFirst({
where: {
name: repoName,
orgId: org.id,
},
});
if (!repo) {
return notFound(`Repository "${repoName}" not found.`);
}
if (!isGitRefValid(ref)) {
return invalidGitRef(ref);
}
const { path: repoPath } = getRepoPath(repo);
const git = simpleGit().cwd(repoPath);
try {
const output = (await git.raw([
'log',
'-1',
`--format=${FORMAT}`,
ref,
])).trim();
const fields = output.split(FIELD_SEP);
if (fields.length < 8) {
return unexpectedError(`Failed to parse commit data for revision "${ref}".`);
}
const [hash, date, message, refs, body, authorName, authorEmail, parentStr] = fields;
const parents = parentStr.trim() === '' ? [] : parentStr.trim().split(' ');
return {
hash,
date,
message,
refs,
body,
authorName,
authorEmail,
parents,
};
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : String(error);
if (errorMessage.includes('not a git repository')) {
return unexpectedError(
`Invalid git repository at ${repoPath}. ` +
`The directory exists but is not a valid git repository.`
);
}
if (errorMessage.includes('unknown revision') || errorMessage.includes('bad object')) {
return notFound(`Revision "${ref}" not found in repository "${repoName}".`);
}
if (error instanceof Error) {
throw new Error(
`Failed to get commit in repository ${repoName}: ${error.message}`
);
} else {
throw new Error(
`Failed to get commit in repository ${repoName}: ${errorMessage}`
);
}
}
}));