-
Notifications
You must be signed in to change notification settings - Fork 264
Expand file tree
/
Copy pathgitlabMrParser.ts
More file actions
105 lines (90 loc) · 4.04 KB
/
gitlabMrParser.ts
File metadata and controls
105 lines (90 loc) · 4.04 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
104
105
import { sourcebot_pr_payload, sourcebot_file_diff, sourcebot_diff } from "@/features/agents/review-agent/types";
import { GitLabMergeRequestPayload } from "@/features/agents/review-agent/types";
import parse from "parse-diff";
import { Gitlab } from "@gitbeaker/rest";
import { createLogger } from "@sourcebot/shared";
const logger = createLogger('gitlab-mr-parser');
export const gitlabMrParser = async (
gitlabClient: InstanceType<typeof Gitlab>,
mrPayload: GitLabMergeRequestPayload,
hostDomain: string,
): Promise<sourcebot_pr_payload> => {
logger.debug("Executing gitlab_mr_parser");
const projectId = mrPayload.project.id;
const mrIid = mrPayload.object_attributes.iid;
// Fetch the full MR from the API to guarantee diff_refs and last_commit are present.
// The webhook payload omits or nulls diff_refs on some action types (e.g. "update").
let mr: Awaited<ReturnType<typeof gitlabClient.MergeRequests.show>>;
let fileDiffs: Awaited<ReturnType<typeof gitlabClient.MergeRequests.allDiffs>> = [];
try {
[mr, fileDiffs] = await Promise.all([
gitlabClient.MergeRequests.show(projectId, mrIid),
gitlabClient.MergeRequests.allDiffs(projectId, mrIid),
]);
} catch (error) {
logger.error("Error fetching MR data: ", error);
throw error;
}
const pathParts = mrPayload.project.path_with_namespace.split('/');
const namespace = pathParts.slice(0, -1).join('/');
const repoName = pathParts[pathParts.length - 1];
const sourcebotFileDiffs: (sourcebot_file_diff | null)[] = fileDiffs.map((fileDiff) => {
const fromPath = fileDiff.old_path as string;
const toPath = fileDiff.new_path as string;
if (!fromPath || !toPath) {
logger.debug(`Skipping file due to missing old_path (${fromPath}) or new_path (${toPath})`);
return null;
}
if (!fileDiff.diff) {
logger.debug(`Skipping file ${toPath} due to empty diff`);
return null;
}
// Construct a standard unified diff header so parse-diff can process it
const unifiedDiff = `--- a/${fromPath}\n+++ b/${toPath}\n${fileDiff.diff}`;
const parsed = parse(unifiedDiff);
if (parsed.length === 0) {
return null;
}
const parsedFile = parsed[0];
const diffs: sourcebot_diff[] = parsedFile.chunks.map((chunk) => {
let oldSnippet = `@@ -${chunk.oldStart},${chunk.oldLines} +${chunk.newStart},${chunk.newLines} @@\n`;
let newSnippet = `@@ -${chunk.oldStart},${chunk.oldLines} +${chunk.newStart},${chunk.newLines} @@\n`;
for (const change of chunk.changes) {
if (change.type === "normal") {
oldSnippet += change.ln1 + ":" + change.content + "\n";
newSnippet += change.ln2 + ":" + change.content + "\n";
} else if (change.type === "add") {
newSnippet += change.ln + ":" + change.content + "\n";
} else if (change.type === "del") {
oldSnippet += change.ln + ":" + change.content + "\n";
}
}
return {
oldSnippet,
newSnippet,
};
});
return {
from: fromPath,
to: toPath,
diffs,
};
});
const filteredSourcebotFileDiffs: sourcebot_file_diff[] = sourcebotFileDiffs.filter(
(file): file is sourcebot_file_diff => file !== null,
);
logger.debug(`Completed gitlab_mr_parser: ${filteredSourcebotFileDiffs.length} file(s) parsed`);
return {
title: mr.title,
description: mr.description ?? "",
hostDomain,
owner: namespace,
repo: repoName,
file_diffs: filteredSourcebotFileDiffs,
number: mrIid,
head_sha: mr.sha ?? mrPayload.object_attributes.last_commit.id,
diff_refs: mr.diff_refs != null
? mr.diff_refs as { base_sha: string; head_sha: string; start_sha: string }
: undefined,
};
};