Skip to content

Commit 66c4273

Browse files
fix: fixes a bug where Layne scans unrelated files
1 parent a11a412 commit 66c4273

File tree

3 files changed

+28
-3
lines changed

3 files changed

+28
-3
lines changed

.changeset/lucky-maps-find.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"layne": patch
3+
---
4+
5+
Fixes a bug in which Layne ends up scanning files that are unrelated to the PR

src/github.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,19 @@ export async function skipCheckRun({ installationId, owner, repo, headSha, summa
112112
});
113113
}
114114

115+
/**
116+
* Returns the merge base SHA between a base and head commit.
117+
* This matches GitHub's PR diff (three-dot / merge-base diff), ensuring Layne
118+
* only scans files the PR itself changed rather than files that differ because
119+
* the base branch advanced after the PR was opened.
120+
*/
121+
export async function getMergeBaseSha({ installationId, owner, repo, base, head }) {
122+
debug('github', `resolving merge base for ${owner}/${repo}: ${base}...${head}`);
123+
const octokit = await getInstallationOctokit(installationId);
124+
const { data } = await octokit.repos.compareCommits({ owner, repo, base, head });
125+
return data.merge_base_commit.sha;
126+
}
127+
115128
/**
116129
* Returns the first open pull request associated with a commit SHA,
117130
* or null if none is found. Used as a fallback when the PR metadata

src/worker.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'url';
44
import { Worker } from 'bullmq';
55
import { redis, scanQueue } from './queue.js';
66
import { getInstallationToken } from './auth.js';
7-
import { startCheckRun, completeCheckRun, ensureLabelsExist, setLabels } from './github.js';
7+
import { startCheckRun, completeCheckRun, ensureLabelsExist, setLabels, getMergeBaseSha } from './github.js';
88
import { createWorkspace, setupRepo, getChangedFiles, checkoutFiles, cleanupWorkspace } from './fetcher.js';
99
import { dispatch } from './dispatcher.js';
1010
import { buildAnnotations } from './reporter.js';
@@ -144,10 +144,17 @@ async function runScan(job) {
144144
const token = await getInstallationToken(installationId);
145145
debug('worker', 'installation token acquired');
146146

147+
// Resolve the merge base so the diff matches GitHub's PR view (three-dot diff).
148+
// pull_request.base.sha is the tip of the base branch, which may have advanced
149+
// since the PR was opened — diffing directly against it would include files
150+
// changed in the base branch that the PR never touched.
151+
const mergeBaseSha = await getMergeBaseSha({ installationId, owner, repo, base: baseSha, head: headSha });
152+
debug('worker', `merge base resolved: ${mergeBaseSha}`);
153+
147154
workspacePath = await createWorkspace(job.id);
148155

149-
await setupRepo({ token, cloneUrl, headSha, baseSha, workspacePath });
150-
const rawChanged = await getChangedFiles({ workspacePath, baseSha, headSha });
156+
await setupRepo({ token, cloneUrl, headSha, baseSha: mergeBaseSha, workspacePath });
157+
const rawChanged = await getChangedFiles({ workspacePath, baseSha: mergeBaseSha, headSha });
151158
const changedFiles = await checkoutFiles({ workspacePath, headSha, files: rawChanged });
152159
debug('worker', `dispatching ${changedFiles.length} file(s) to scanners`);
153160

0 commit comments

Comments
 (0)