-
-
Notifications
You must be signed in to change notification settings - Fork 290
Expand file tree
/
Copy pathget-diff.js
More file actions
102 lines (88 loc) · 3.35 KB
/
get-diff.js
File metadata and controls
102 lines (88 loc) · 3.35 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
// returns the unified diff string and list of changed files for the current GitHub event (PR or push)
const { execSync } = require("child_process");
const https = require("https");
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
const GITHUB_REPOSITORY = process.env.GITHUB_REPOSITORY; // "owner/repo"
const PR_NUMBER = process.env.PR_NUMBER;
const GITHUB_EVENT_NAME = process.env.GITHUB_EVENT_NAME;
const GITHUB_SHA = process.env.GITHUB_SHA;
// fetch the pr diff from the GitHub API, using the "diff" media type to get a unified diff string, returns raw unified diff string
function fetchPRDiff() {
return new Promise((resolve, reject) => {
const [owner, repo] = GITHUB_REPOSITORY.split("/");
const options = {
hostname: "api.github.com",
path: `/repos/${owner}/${repo}/pulls/${PR_NUMBER}`,
headers: {
Authorization: `Bearer ${GITHUB_TOKEN}`,
Accept: "application/vnd.github.v3.diff",
"User-Agent": "keploy-design-review-agent",
},
};
https
.get(options, (res) => {
let data = "";
res.on("data", (chunk) => (data += chunk));
res.on("end", () => {
if (res.statusCode < 200 || res.statusCode >= 300) {
reject(
new Error(
`GitHub API returned ${res.statusCode} fetching PR diff. ` +
`Check GITHUB_TOKEN permissions and that PR_NUMBER=${PR_NUMBER} is valid. ` +
`Response: ${data.trim().slice(0, 300)}`
)
);
return;
}
resolve(data);
});
})
.on("error", reject);
});
}
// get diff for a push event using git. Compares HEAD to its parent (HEAD~1).
function getCommitDiff() {
try {
const diff = execSync("git diff HEAD~1 HEAD", {
encoding: "utf8",
maxBuffer: 10 * 1024 * 1024, // 10MB
});
return diff;
} catch {
// first commit edge case, diff against empty tree
const diff = execSync(
"git diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD",
{ encoding: "utf8", maxBuffer: 10 * 1024 * 1024 }
);
return diff;
}
}
// for manual workflow_dispatch: diff the last commit. this is a best practice to get some diff for manual runs, but may not be perfect depending on the repo state.
function getManualDiff() {
return getCommitDiff();
}
// main export function that returns the diff and list of changed files based on the GitHub event type (pull_request or push)
// diff : raw unified diff string
// changedFiles: array of file paths that changed (extracted from diff headers)
async function getDiff() {
let diff = "";
if (
GITHUB_EVENT_NAME === "pull_request" ||
GITHUB_EVENT_NAME === "pull_request_target"
) {
// Both events carry PR_NUMBER and use the GitHub API diff endpoint.
// pull_request_target is used so secrets are available on fork PRs
// while still reviewing the actual PR changes via the API.
diff = await fetchPRDiff();
} else if (GITHUB_EVENT_NAME === "push") {
diff = getCommitDiff();
} else {
// workflow_dispatch or any other trigger
diff = getManualDiff();
}
// Extract unique file names from diff headers: "diff --git a/foo b/foo"
const fileMatches = [...diff.matchAll(/^diff --git a\/(.+) b\/.+$/gm)];
const changedFiles = [...new Set(fileMatches.map((m) => m[1]))];
return { diff, changedFiles };
}
module.exports = { getDiff };