feat: always include files from diff in overlay changed files

This commit is contained in:
Sam Robson
2026-03-06 16:03:16 +00:00
parent 3d564d9359
commit 521c3536d3
15 changed files with 1486 additions and 640 deletions

View File

@@ -100540,8 +100540,8 @@ var require_follow_redirects = __commonJS({
}
return parsed;
}
function resolveUrl(relative, base) {
return useNativeURL ? new URL2(relative, base) : parseUrl2(url.resolve(base, relative));
function resolveUrl(relative2, base) {
return useNativeURL ? new URL2(relative2, base) : parseUrl2(url.resolve(base, relative2));
}
function validateUrl(input) {
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -104424,8 +104424,8 @@ var require_readdir_glob = __commonJS({
useStat = true;
}
const filename = dir + "/" + name;
const relative = filename.slice(1);
const absolute = path7 + "/" + relative;
const relative2 = filename.slice(1);
const absolute = path7 + "/" + relative2;
let stats = null;
if (useStat || followSymlinks) {
stats = await stat(absolute, followSymlinks);
@@ -104437,12 +104437,12 @@ var require_readdir_glob = __commonJS({
stats = { isDirectory: () => false };
}
if (stats.isDirectory()) {
if (!shouldSkip(relative)) {
yield { relative, absolute, stats };
if (!shouldSkip(relative2)) {
yield { relative: relative2, absolute, stats };
yield* exploreWalkAsync(filename, path7, followSymlinks, useStat, shouldSkip, false);
}
} else {
yield { relative, absolute, stats };
yield { relative: relative2, absolute, stats };
}
}
}
@@ -104512,11 +104512,11 @@ var require_readdir_glob = __commonJS({
}
setTimeout(() => this._next(), 0);
}
_shouldSkipDirectory(relative) {
return this.skipMatchers.some((m) => m.match(relative));
_shouldSkipDirectory(relative2) {
return this.skipMatchers.some((m) => m.match(relative2));
}
_fileMatches(relative, isDirectory) {
const file = relative + (isDirectory ? "/" : "");
_fileMatches(relative2, isDirectory) {
const file = relative2 + (isDirectory ? "/" : "");
return (this.matchers.length === 0 || this.matchers.some((m) => m.match(file))) && !this.ignoreMatchers.some((m) => m.match(file)) && (!this.options.nodir || !isDirectory);
}
_next() {
@@ -104525,16 +104525,16 @@ var require_readdir_glob = __commonJS({
if (!obj.done) {
const isDirectory = obj.value.stats.isDirectory();
if (this._fileMatches(obj.value.relative, isDirectory)) {
let relative = obj.value.relative;
let relative2 = obj.value.relative;
let absolute = obj.value.absolute;
if (this.options.mark && isDirectory) {
relative += "/";
relative2 += "/";
absolute += "/";
}
if (this.options.stat) {
this.emit("match", { relative, absolute, stat: obj.value.stats });
this.emit("match", { relative: relative2, absolute, stat: obj.value.stats });
} else {
this.emit("match", { relative, absolute });
this.emit("match", { relative: relative2, absolute });
}
}
this._next(this.iterator);
@@ -110019,8 +110019,8 @@ var require_primordials = __commonJS({
ArrayPrototypeIndexOf(self2, el) {
return self2.indexOf(el);
},
ArrayPrototypeJoin(self2, sep3) {
return self2.join(sep3);
ArrayPrototypeJoin(self2, sep4) {
return self2.join(sep4);
},
ArrayPrototypeMap(self2, fn) {
return self2.map(fn);
@@ -122273,7 +122273,7 @@ var require_commonjs23 = __commonJS({
*
* @internal
*/
constructor(cwd = process.cwd(), pathImpl, sep3, { nocase, childrenCacheSize = 16 * 1024, fs: fs8 = defaultFS } = {}) {
constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs8 = defaultFS } = {}) {
this.#fs = fsFromOption(fs8);
if (cwd instanceof URL || cwd.startsWith("file://")) {
cwd = (0, node_url_1.fileURLToPath)(cwd);
@@ -122284,7 +122284,7 @@ var require_commonjs23 = __commonJS({
this.#resolveCache = new ResolveCache();
this.#resolvePosixCache = new ResolveCache();
this.#children = new ChildrenCache(childrenCacheSize);
const split = cwdPath.substring(this.rootPath.length).split(sep3);
const split = cwdPath.substring(this.rootPath.length).split(sep4);
if (split.length === 1 && !split[0]) {
split.pop();
}
@@ -123127,10 +123127,10 @@ var require_ignore = __commonJS({
ignored(p) {
const fullpath = p.fullpath();
const fullpaths = `${fullpath}/`;
const relative = p.relative() || ".";
const relatives = `${relative}/`;
const relative2 = p.relative() || ".";
const relatives = `${relative2}/`;
for (const m of this.relative) {
if (m.match(relative) || m.match(relatives))
if (m.match(relative2) || m.match(relatives))
return true;
}
for (const m of this.absolute) {
@@ -123141,9 +123141,9 @@ var require_ignore = __commonJS({
}
childrenIgnored(p) {
const fullpath = p.fullpath() + "/";
const relative = (p.relative() || ".") + "/";
const relative2 = (p.relative() || ".") + "/";
for (const m of this.relativeChildren) {
if (m.match(relative))
if (m.match(relative2))
return true;
}
for (const m of this.absoluteChildren) {
@@ -162155,6 +162155,18 @@ var decodeGitFilePath = function(filePath) {
}
return filePath;
};
var getGitRoot = async function(sourceRoot) {
try {
const stdout = await runGitCommand(
sourceRoot,
["rev-parse", "--show-toplevel"],
`Cannot find Git repository root from the source root ${sourceRoot}.`
);
return stdout.trim();
} catch {
return void 0;
}
};
var getFileOidsUnderPath = async function(basePath) {
const stdout = await runGitCommand(
basePath,
@@ -162277,10 +162289,12 @@ async function readBaseDatabaseOidsFile(config, logger) {
async function writeOverlayChangesFile(config, sourceRoot, logger) {
const baseFileOids = await readBaseDatabaseOidsFile(config, logger);
const overlayFileOids = await getFileOidsUnderPath(sourceRoot);
const changedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
const oidChangedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
logger.info(
`Found ${changedFiles.length} changed file(s) under ${sourceRoot}.`
`Found ${oidChangedFiles.length} changed file(s) under ${sourceRoot} from OID comparison.`
);
const diffRangeFiles = await getDiffRangeFilePaths(sourceRoot, logger);
const changedFiles = [.../* @__PURE__ */ new Set([...oidChangedFiles, ...diffRangeFiles])];
const changedFilesJson = JSON.stringify({ changes: changedFiles });
const overlayChangesFile = path2.join(
getTemporaryDirectory(),
@@ -162306,6 +162320,48 @@ function computeChangedFiles(baseFileOids, overlayFileOids) {
}
return changes;
}
async function getDiffRangeFilePaths(sourceRoot, logger) {
const jsonFilePath = path2.join(getTemporaryDirectory(), "pr-diff-range.json");
if (!fs2.existsSync(jsonFilePath)) {
return [];
}
let diffRanges;
try {
diffRanges = JSON.parse(fs2.readFileSync(jsonFilePath, "utf8"));
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`
);
return [];
}
logger.debug(
`Read ${diffRanges.length} diff range(s) from ${jsonFilePath} for overlay changes.`
);
const repoRelativePaths = [...new Set(diffRanges.map((r) => r.path))];
const repoRoot = await getGitRoot(sourceRoot);
if (repoRoot === void 0) {
logger.warning(
"Cannot determine git root; returning diff range paths as-is."
);
return repoRelativePaths;
}
const sourceRootRelPrefix = path2.relative(repoRoot, sourceRoot).replaceAll(path2.sep, "/");
if (sourceRootRelPrefix === "") {
return repoRelativePaths;
}
const prefixWithSlash = `${sourceRootRelPrefix}/`;
const result = [];
for (const p of repoRelativePaths) {
if (p.startsWith(prefixWithSlash)) {
result.push(p.slice(prefixWithSlash.length));
} else {
logger.debug(
`Skipping diff range path "${p}" (not under source root "${sourceRootRelPrefix}").`
);
}
}
return result;
}
// src/tools-features.ts
var semver4 = __toESM(require_semver2());

246
lib/analyze-action.js generated
View File

@@ -100540,8 +100540,8 @@ var require_follow_redirects = __commonJS({
}
return parsed;
}
function resolveUrl(relative2, base) {
return useNativeURL ? new URL2(relative2, base) : parseUrl2(url2.resolve(base, relative2));
function resolveUrl(relative3, base) {
return useNativeURL ? new URL2(relative3, base) : parseUrl2(url2.resolve(base, relative3));
}
function validateUrl(input) {
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -106859,29 +106859,6 @@ var persistInputs = function() {
);
core4.saveState(persistedInputsKey, JSON.stringify(inputEnvironmentVariables));
};
function getPullRequestBranches() {
const pullRequest = github.context.payload.pull_request;
if (pullRequest) {
return {
base: pullRequest.base.ref,
// We use the head label instead of the head ref here, because the head
// ref lacks owner information and by itself does not uniquely identify
// the head branch (which may be in a forked repository).
head: pullRequest.head.label
};
}
const codeScanningRef = process.env.CODE_SCANNING_REF;
const codeScanningBaseBranch = process.env.CODE_SCANNING_BASE_BRANCH;
if (codeScanningRef && codeScanningBaseBranch) {
return {
base: codeScanningBaseBranch,
// PR analysis under Default Setup analyzes the PR head commit instead of
// the merge commit, so we can use the provided ref directly.
head: codeScanningRef
};
}
return void 0;
}
var qualityCategoryMapping = {
"c#": "csharp",
cpp: "c-cpp",
@@ -107775,6 +107752,18 @@ var decodeGitFilePath = function(filePath) {
}
return filePath;
};
var getGitRoot = async function(sourceRoot) {
try {
const stdout = await runGitCommand(
sourceRoot,
["rev-parse", "--show-toplevel"],
`Cannot find Git repository root from the source root ${sourceRoot}.`
);
return stdout.trim();
} catch {
return void 0;
}
};
var getFileOidsUnderPath = async function(basePath) {
const stdout = await runGitCommand(
basePath,
@@ -107897,10 +107886,12 @@ async function readBaseDatabaseOidsFile(config, logger) {
async function writeOverlayChangesFile(config, sourceRoot, logger) {
const baseFileOids = await readBaseDatabaseOidsFile(config, logger);
const overlayFileOids = await getFileOidsUnderPath(sourceRoot);
const changedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
const oidChangedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
logger.info(
`Found ${changedFiles.length} changed file(s) under ${sourceRoot}.`
`Found ${oidChangedFiles.length} changed file(s) under ${sourceRoot} from OID comparison.`
);
const diffRangeFiles = await getDiffRangeFilePaths(sourceRoot, logger);
const changedFiles = [.../* @__PURE__ */ new Set([...oidChangedFiles, ...diffRangeFiles])];
const changedFilesJson = JSON.stringify({ changes: changedFiles });
const overlayChangesFile = path4.join(
getTemporaryDirectory(),
@@ -107926,6 +107917,48 @@ function computeChangedFiles(baseFileOids, overlayFileOids) {
}
return changes;
}
async function getDiffRangeFilePaths(sourceRoot, logger) {
const jsonFilePath = path4.join(getTemporaryDirectory(), "pr-diff-range.json");
if (!fs3.existsSync(jsonFilePath)) {
return [];
}
let diffRanges;
try {
diffRanges = JSON.parse(fs3.readFileSync(jsonFilePath, "utf8"));
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`
);
return [];
}
logger.debug(
`Read ${diffRanges.length} diff range(s) from ${jsonFilePath} for overlay changes.`
);
const repoRelativePaths = [...new Set(diffRanges.map((r) => r.path))];
const repoRoot = await getGitRoot(sourceRoot);
if (repoRoot === void 0) {
logger.warning(
"Cannot determine git root; returning diff range paths as-is."
);
return repoRelativePaths;
}
const sourceRootRelPrefix = path4.relative(repoRoot, sourceRoot).replaceAll(path4.sep, "/");
if (sourceRootRelPrefix === "") {
return repoRelativePaths;
}
const prefixWithSlash = `${sourceRootRelPrefix}/`;
const result = [];
for (const p of repoRelativePaths) {
if (p.startsWith(prefixWithSlash)) {
result.push(p.slice(prefixWithSlash.length));
} else {
logger.debug(
`Skipping diff range path "${p}" (not under source root "${sourceRootRelPrefix}").`
);
}
}
return result;
}
var CACHE_VERSION = 1;
var CACHE_PREFIX = "codeql-overlay-base-database";
var MAX_CACHE_OPERATION_MS = 6e5;
@@ -108611,34 +108644,9 @@ function initFeatures(gitHubVersion, repositoryNwo, tempDir, logger) {
}
// src/diff-informed-analysis-utils.ts
async function getDiffInformedAnalysisBranches(codeql, features, logger) {
if (!await features.getValue("diff_informed_queries" /* DiffInformedQueries */, codeql)) {
return void 0;
}
const gitHubVersion = await getGitHubVersion();
if (gitHubVersion.type === "GitHub Enterprise Server" /* GHES */ && satisfiesGHESVersion(gitHubVersion.version, "<3.19", true)) {
return void 0;
}
const branches = getPullRequestBranches();
if (!branches) {
logger.info(
"Not performing diff-informed analysis because we are not analyzing a pull request."
);
}
return branches;
}
function getDiffRangesJsonFilePath() {
return path6.join(getTemporaryDirectory(), "pr-diff-range.json");
}
function writeDiffRangesJsonFile(logger, ranges) {
const jsonContents = JSON.stringify(ranges, null, 2);
const jsonFilePath = getDiffRangesJsonFilePath();
fs5.writeFileSync(jsonFilePath, jsonContents);
logger.debug(
`Wrote pr-diff-range JSON file to ${jsonFilePath}:
${jsonContents}`
);
}
function readDiffRangesJsonFile(logger) {
const jsonFilePath = getDiffRangesJsonFilePath();
if (!fs5.existsSync(jsonFilePath)) {
@@ -108650,118 +108658,15 @@ function readDiffRangesJsonFile(logger) {
`Read pr-diff-range JSON file from ${jsonFilePath}:
${jsonContents}`
);
return JSON.parse(jsonContents);
}
async function getPullRequestEditedDiffRanges(branches, logger) {
const fileDiffs = await getFileDiffsWithBasehead(branches, logger);
if (fileDiffs === void 0) {
return void 0;
}
if (fileDiffs.length >= 300) {
logger.warning(
`Cannot retrieve the full diff because there are too many (${fileDiffs.length}) changed files in the pull request.`
);
return void 0;
}
const results = [];
for (const filediff of fileDiffs) {
const diffRanges = getDiffRanges(filediff, logger);
if (diffRanges === void 0) {
return void 0;
}
results.push(...diffRanges);
}
return results;
}
async function getFileDiffsWithBasehead(branches, logger) {
const repositoryNwo = getRepositoryNwoFromEnv(
"CODE_SCANNING_REPOSITORY",
"GITHUB_REPOSITORY"
);
const basehead = `${branches.base}...${branches.head}`;
try {
const response = await getApiClient().rest.repos.compareCommitsWithBasehead(
{
owner: repositoryNwo.owner,
repo: repositoryNwo.repo,
basehead,
per_page: 1
}
return JSON.parse(jsonContents);
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`
);
logger.debug(
`Response from compareCommitsWithBasehead(${basehead}):
${JSON.stringify(response, null, 2)}`
);
return response.data.files;
} catch (error3) {
if (error3.status) {
logger.warning(`Error retrieving diff ${basehead}: ${error3.message}`);
logger.debug(
`Error running compareCommitsWithBasehead(${basehead}):
Request: ${JSON.stringify(error3.request, null, 2)}
Error Response: ${JSON.stringify(error3.response, null, 2)}`
);
return void 0;
} else {
throw error3;
}
return void 0;
}
}
function getDiffRanges(fileDiff, logger) {
if (fileDiff.patch === void 0) {
if (fileDiff.changes === 0) {
return [];
}
return [
{
path: fileDiff.filename,
startLine: 0,
endLine: 0
}
];
}
let currentLine = 0;
let additionRangeStartLine = void 0;
const diffRanges = [];
const diffLines = fileDiff.patch.split("\n");
diffLines.push(" ");
for (const diffLine of diffLines) {
if (diffLine.startsWith("-")) {
continue;
}
if (diffLine.startsWith("+")) {
if (additionRangeStartLine === void 0) {
additionRangeStartLine = currentLine;
}
currentLine++;
continue;
}
if (additionRangeStartLine !== void 0) {
diffRanges.push({
path: fileDiff.filename,
startLine: additionRangeStartLine,
endLine: currentLine - 1
});
additionRangeStartLine = void 0;
}
if (diffLine.startsWith("@@ ")) {
const match = diffLine.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
if (match === null) {
logger.warning(
`Cannot parse diff hunk header for ${fileDiff.filename}: ${diffLine}`
);
return void 0;
}
currentLine = parseInt(match[1], 10);
continue;
}
if (diffLine.startsWith(" ")) {
currentLine++;
continue;
}
}
return diffRanges;
}
// src/overlay/status.ts
var actionsCache2 = __toESM(require_cache5());
@@ -110915,14 +110820,17 @@ async function finalizeDatabaseCreation(codeql, features, config, threadsFlag, m
trap_import_duration_ms: Math.round(trapImportTime)
};
}
async function setupDiffInformedQueryRun(branches, logger) {
async function setupDiffInformedQueryRun(logger) {
return await withGroupAsync(
"Generating diff range extension pack",
async () => {
logger.info(
`Calculating diff ranges for ${branches.base}...${branches.head}`
);
const diffRanges = await getPullRequestEditedDiffRanges(branches, logger);
const diffRanges = readDiffRangesJsonFile(logger);
if (diffRanges === void 0) {
logger.info(
"No precomputed diff ranges found; skipping diff-informed analysis stage."
);
return void 0;
}
const checkoutPath = getRequiredInput("checkout_path");
const packDir = writeDiffRangeDataExtensionPack(
logger,
@@ -110992,7 +110900,6 @@ dataExtensions:
`Wrote pr-diff-range extension pack to ${extensionFilePath}:
${extensionContents}`
);
writeDiffRangesJsonFile(logger, ranges);
return diffRangeDir;
}
var defaultSuites = /* @__PURE__ */ new Set([
@@ -113544,12 +113451,7 @@ async function run(startedAt2) {
getOptionalInput("ram") || process.env["CODEQL_RAM"],
logger
);
const branches = await getDiffInformedAnalysisBranches(
codeql,
features,
logger
);
const diffRangePackDir = branches ? await setupDiffInformedQueryRun(branches, logger) : void 0;
const diffRangePackDir = await setupDiffInformedQueryRun(logger);
await warnIfGoInstalledAfterInit(config, logger);
await runAutobuildIfLegacyGoWorkflow(config, logger);
dbCreationTimings = await runFinalize(

View File

@@ -100540,8 +100540,8 @@ var require_follow_redirects = __commonJS({
}
return parsed;
}
function resolveUrl(relative, base) {
return useNativeURL ? new URL2(relative, base) : parseUrl2(url.resolve(base, relative));
function resolveUrl(relative2, base) {
return useNativeURL ? new URL2(relative2, base) : parseUrl2(url.resolve(base, relative2));
}
function validateUrl(input) {
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -104209,6 +104209,18 @@ var decodeGitFilePath = function(filePath) {
}
return filePath;
};
var getGitRoot = async function(sourceRoot) {
try {
const stdout = await runGitCommand(
sourceRoot,
["rev-parse", "--show-toplevel"],
`Cannot find Git repository root from the source root ${sourceRoot}.`
);
return stdout.trim();
} catch {
return void 0;
}
};
var getFileOidsUnderPath = async function(basePath) {
const stdout = await runGitCommand(
basePath,
@@ -104331,10 +104343,12 @@ async function readBaseDatabaseOidsFile(config, logger) {
async function writeOverlayChangesFile(config, sourceRoot, logger) {
const baseFileOids = await readBaseDatabaseOidsFile(config, logger);
const overlayFileOids = await getFileOidsUnderPath(sourceRoot);
const changedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
const oidChangedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
logger.info(
`Found ${changedFiles.length} changed file(s) under ${sourceRoot}.`
`Found ${oidChangedFiles.length} changed file(s) under ${sourceRoot} from OID comparison.`
);
const diffRangeFiles = await getDiffRangeFilePaths(sourceRoot, logger);
const changedFiles = [.../* @__PURE__ */ new Set([...oidChangedFiles, ...diffRangeFiles])];
const changedFilesJson = JSON.stringify({ changes: changedFiles });
const overlayChangesFile = path2.join(
getTemporaryDirectory(),
@@ -104360,6 +104374,48 @@ function computeChangedFiles(baseFileOids, overlayFileOids) {
}
return changes;
}
async function getDiffRangeFilePaths(sourceRoot, logger) {
const jsonFilePath = path2.join(getTemporaryDirectory(), "pr-diff-range.json");
if (!fs2.existsSync(jsonFilePath)) {
return [];
}
let diffRanges;
try {
diffRanges = JSON.parse(fs2.readFileSync(jsonFilePath, "utf8"));
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`
);
return [];
}
logger.debug(
`Read ${diffRanges.length} diff range(s) from ${jsonFilePath} for overlay changes.`
);
const repoRelativePaths = [...new Set(diffRanges.map((r) => r.path))];
const repoRoot = await getGitRoot(sourceRoot);
if (repoRoot === void 0) {
logger.warning(
"Cannot determine git root; returning diff range paths as-is."
);
return repoRelativePaths;
}
const sourceRootRelPrefix = path2.relative(repoRoot, sourceRoot).replaceAll(path2.sep, "/");
if (sourceRootRelPrefix === "") {
return repoRelativePaths;
}
const prefixWithSlash = `${sourceRootRelPrefix}/`;
const result = [];
for (const p of repoRelativePaths) {
if (p.startsWith(prefixWithSlash)) {
result.push(p.slice(prefixWithSlash.length));
} else {
logger.debug(
`Skipping diff range path "${p}" (not under source root "${sourceRootRelPrefix}").`
);
}
}
return result;
}
// src/tools-features.ts
var semver4 = __toESM(require_semver2());

117
lib/init-action-post.js generated
View File

@@ -100540,8 +100540,8 @@ var require_follow_redirects = __commonJS({
}
return parsed;
}
function resolveUrl(relative2, base) {
return useNativeURL ? new URL2(relative2, base) : parseUrl2(url2.resolve(base, relative2));
function resolveUrl(relative3, base) {
return useNativeURL ? new URL2(relative3, base) : parseUrl2(url2.resolve(base, relative3));
}
function validateUrl(input) {
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -104424,8 +104424,8 @@ var require_readdir_glob = __commonJS({
useStat = true;
}
const filename = dir + "/" + name;
const relative2 = filename.slice(1);
const absolute = path19 + "/" + relative2;
const relative3 = filename.slice(1);
const absolute = path19 + "/" + relative3;
let stats = null;
if (useStat || followSymlinks) {
stats = await stat(absolute, followSymlinks);
@@ -104437,12 +104437,12 @@ var require_readdir_glob = __commonJS({
stats = { isDirectory: () => false };
}
if (stats.isDirectory()) {
if (!shouldSkip(relative2)) {
yield { relative: relative2, absolute, stats };
if (!shouldSkip(relative3)) {
yield { relative: relative3, absolute, stats };
yield* exploreWalkAsync(filename, path19, followSymlinks, useStat, shouldSkip, false);
}
} else {
yield { relative: relative2, absolute, stats };
yield { relative: relative3, absolute, stats };
}
}
}
@@ -104512,11 +104512,11 @@ var require_readdir_glob = __commonJS({
}
setTimeout(() => this._next(), 0);
}
_shouldSkipDirectory(relative2) {
return this.skipMatchers.some((m) => m.match(relative2));
_shouldSkipDirectory(relative3) {
return this.skipMatchers.some((m) => m.match(relative3));
}
_fileMatches(relative2, isDirectory) {
const file = relative2 + (isDirectory ? "/" : "");
_fileMatches(relative3, isDirectory) {
const file = relative3 + (isDirectory ? "/" : "");
return (this.matchers.length === 0 || this.matchers.some((m) => m.match(file))) && !this.ignoreMatchers.some((m) => m.match(file)) && (!this.options.nodir || !isDirectory);
}
_next() {
@@ -104525,16 +104525,16 @@ var require_readdir_glob = __commonJS({
if (!obj.done) {
const isDirectory = obj.value.stats.isDirectory();
if (this._fileMatches(obj.value.relative, isDirectory)) {
let relative2 = obj.value.relative;
let relative3 = obj.value.relative;
let absolute = obj.value.absolute;
if (this.options.mark && isDirectory) {
relative2 += "/";
relative3 += "/";
absolute += "/";
}
if (this.options.stat) {
this.emit("match", { relative: relative2, absolute, stat: obj.value.stats });
this.emit("match", { relative: relative3, absolute, stat: obj.value.stats });
} else {
this.emit("match", { relative: relative2, absolute });
this.emit("match", { relative: relative3, absolute });
}
}
this._next(this.iterator);
@@ -110019,8 +110019,8 @@ var require_primordials = __commonJS({
ArrayPrototypeIndexOf(self2, el) {
return self2.indexOf(el);
},
ArrayPrototypeJoin(self2, sep4) {
return self2.join(sep4);
ArrayPrototypeJoin(self2, sep5) {
return self2.join(sep5);
},
ArrayPrototypeMap(self2, fn) {
return self2.map(fn);
@@ -122273,7 +122273,7 @@ var require_commonjs23 = __commonJS({
*
* @internal
*/
constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs20 = defaultFS } = {}) {
constructor(cwd = process.cwd(), pathImpl, sep5, { nocase, childrenCacheSize = 16 * 1024, fs: fs20 = defaultFS } = {}) {
this.#fs = fsFromOption(fs20);
if (cwd instanceof URL || cwd.startsWith("file://")) {
cwd = (0, node_url_1.fileURLToPath)(cwd);
@@ -122284,7 +122284,7 @@ var require_commonjs23 = __commonJS({
this.#resolveCache = new ResolveCache();
this.#resolvePosixCache = new ResolveCache();
this.#children = new ChildrenCache(childrenCacheSize);
const split = cwdPath.substring(this.rootPath.length).split(sep4);
const split = cwdPath.substring(this.rootPath.length).split(sep5);
if (split.length === 1 && !split[0]) {
split.pop();
}
@@ -123127,10 +123127,10 @@ var require_ignore = __commonJS({
ignored(p) {
const fullpath = p.fullpath();
const fullpaths = `${fullpath}/`;
const relative2 = p.relative() || ".";
const relatives = `${relative2}/`;
const relative3 = p.relative() || ".";
const relatives = `${relative3}/`;
for (const m of this.relative) {
if (m.match(relative2) || m.match(relatives))
if (m.match(relative3) || m.match(relatives))
return true;
}
for (const m of this.absolute) {
@@ -123141,9 +123141,9 @@ var require_ignore = __commonJS({
}
childrenIgnored(p) {
const fullpath = p.fullpath() + "/";
const relative2 = (p.relative() || ".") + "/";
const relative3 = (p.relative() || ".") + "/";
for (const m of this.relativeChildren) {
if (m.match(relative2))
if (m.match(relative3))
return true;
}
for (const m of this.absoluteChildren) {
@@ -165669,6 +165669,18 @@ var decodeGitFilePath = function(filePath) {
}
return filePath;
};
var getGitRoot = async function(sourceRoot) {
try {
const stdout = await runGitCommand(
sourceRoot,
["rev-parse", "--show-toplevel"],
`Cannot find Git repository root from the source root ${sourceRoot}.`
);
return stdout.trim();
} catch {
return void 0;
}
};
var getFileOidsUnderPath = async function(basePath) {
const stdout = await runGitCommand(
basePath,
@@ -165791,10 +165803,12 @@ async function readBaseDatabaseOidsFile(config, logger) {
async function writeOverlayChangesFile(config, sourceRoot, logger) {
const baseFileOids = await readBaseDatabaseOidsFile(config, logger);
const overlayFileOids = await getFileOidsUnderPath(sourceRoot);
const changedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
const oidChangedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
logger.info(
`Found ${changedFiles.length} changed file(s) under ${sourceRoot}.`
`Found ${oidChangedFiles.length} changed file(s) under ${sourceRoot} from OID comparison.`
);
const diffRangeFiles = await getDiffRangeFilePaths(sourceRoot, logger);
const changedFiles = [.../* @__PURE__ */ new Set([...oidChangedFiles, ...diffRangeFiles])];
const changedFilesJson = JSON.stringify({ changes: changedFiles });
const overlayChangesFile = path4.join(
getTemporaryDirectory(),
@@ -165820,6 +165834,48 @@ function computeChangedFiles(baseFileOids, overlayFileOids) {
}
return changes;
}
async function getDiffRangeFilePaths(sourceRoot, logger) {
const jsonFilePath = path4.join(getTemporaryDirectory(), "pr-diff-range.json");
if (!fs3.existsSync(jsonFilePath)) {
return [];
}
let diffRanges;
try {
diffRanges = JSON.parse(fs3.readFileSync(jsonFilePath, "utf8"));
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`
);
return [];
}
logger.debug(
`Read ${diffRanges.length} diff range(s) from ${jsonFilePath} for overlay changes.`
);
const repoRelativePaths = [...new Set(diffRanges.map((r) => r.path))];
const repoRoot = await getGitRoot(sourceRoot);
if (repoRoot === void 0) {
logger.warning(
"Cannot determine git root; returning diff range paths as-is."
);
return repoRelativePaths;
}
const sourceRootRelPrefix = path4.relative(repoRoot, sourceRoot).replaceAll(path4.sep, "/");
if (sourceRootRelPrefix === "") {
return repoRelativePaths;
}
const prefixWithSlash = `${sourceRootRelPrefix}/`;
const result = [];
for (const p of repoRelativePaths) {
if (p.startsWith(prefixWithSlash)) {
result.push(p.slice(prefixWithSlash.length));
} else {
logger.debug(
`Skipping diff range path "${p}" (not under source root "${sourceRootRelPrefix}").`
);
}
}
return result;
}
// src/tools-features.ts
var semver4 = __toESM(require_semver2());
@@ -166388,7 +166444,14 @@ function readDiffRangesJsonFile(logger) {
`Read pr-diff-range JSON file from ${jsonFilePath}:
${jsonContents}`
);
return JSON.parse(jsonContents);
try {
return JSON.parse(jsonContents);
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`
);
return void 0;
}
}
// src/overlay/status.ts

912
lib/init-action.js generated

File diff suppressed because it is too large Load Diff

View File

@@ -100540,8 +100540,8 @@ var require_follow_redirects = __commonJS({
}
return parsed;
}
function resolveUrl(relative, base) {
return useNativeURL ? new URL2(relative, base) : parseUrl2(url.resolve(base, relative));
function resolveUrl(relative2, base) {
return useNativeURL ? new URL2(relative2, base) : parseUrl2(url.resolve(base, relative2));
}
function validateUrl(input) {
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -104202,6 +104202,18 @@ var decodeGitFilePath = function(filePath) {
}
return filePath;
};
var getGitRoot = async function(sourceRoot) {
try {
const stdout = await runGitCommand(
sourceRoot,
["rev-parse", "--show-toplevel"],
`Cannot find Git repository root from the source root ${sourceRoot}.`
);
return stdout.trim();
} catch {
return void 0;
}
};
var getFileOidsUnderPath = async function(basePath) {
const stdout = await runGitCommand(
basePath,
@@ -104324,10 +104336,12 @@ async function readBaseDatabaseOidsFile(config, logger) {
async function writeOverlayChangesFile(config, sourceRoot, logger) {
const baseFileOids = await readBaseDatabaseOidsFile(config, logger);
const overlayFileOids = await getFileOidsUnderPath(sourceRoot);
const changedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
const oidChangedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
logger.info(
`Found ${changedFiles.length} changed file(s) under ${sourceRoot}.`
`Found ${oidChangedFiles.length} changed file(s) under ${sourceRoot} from OID comparison.`
);
const diffRangeFiles = await getDiffRangeFilePaths(sourceRoot, logger);
const changedFiles = [.../* @__PURE__ */ new Set([...oidChangedFiles, ...diffRangeFiles])];
const changedFilesJson = JSON.stringify({ changes: changedFiles });
const overlayChangesFile = path2.join(
getTemporaryDirectory(),
@@ -104353,6 +104367,48 @@ function computeChangedFiles(baseFileOids, overlayFileOids) {
}
return changes;
}
async function getDiffRangeFilePaths(sourceRoot, logger) {
const jsonFilePath = path2.join(getTemporaryDirectory(), "pr-diff-range.json");
if (!fs2.existsSync(jsonFilePath)) {
return [];
}
let diffRanges;
try {
diffRanges = JSON.parse(fs2.readFileSync(jsonFilePath, "utf8"));
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`
);
return [];
}
logger.debug(
`Read ${diffRanges.length} diff range(s) from ${jsonFilePath} for overlay changes.`
);
const repoRelativePaths = [...new Set(diffRanges.map((r) => r.path))];
const repoRoot = await getGitRoot(sourceRoot);
if (repoRoot === void 0) {
logger.warning(
"Cannot determine git root; returning diff range paths as-is."
);
return repoRelativePaths;
}
const sourceRootRelPrefix = path2.relative(repoRoot, sourceRoot).replaceAll(path2.sep, "/");
if (sourceRootRelPrefix === "") {
return repoRelativePaths;
}
const prefixWithSlash = `${sourceRootRelPrefix}/`;
const result = [];
for (const p of repoRelativePaths) {
if (p.startsWith(prefixWithSlash)) {
result.push(p.slice(prefixWithSlash.length));
} else {
logger.debug(
`Skipping diff range path "${p}" (not under source root "${sourceRootRelPrefix}").`
);
}
}
return result;
}
// src/tools-features.ts
var semver4 = __toESM(require_semver2());

View File

@@ -100540,8 +100540,8 @@ var require_follow_redirects = __commonJS({
}
return parsed;
}
function resolveUrl(relative2, base) {
return useNativeURL ? new URL2(relative2, base) : parseUrl2(url.resolve(base, relative2));
function resolveUrl(relative3, base) {
return useNativeURL ? new URL2(relative3, base) : parseUrl2(url.resolve(base, relative3));
}
function validateUrl(input) {
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -104069,6 +104069,18 @@ var decodeGitFilePath = function(filePath) {
}
return filePath;
};
var getGitRoot = async function(sourceRoot) {
try {
const stdout = await runGitCommand(
sourceRoot,
["rev-parse", "--show-toplevel"],
`Cannot find Git repository root from the source root ${sourceRoot}.`
);
return stdout.trim();
} catch {
return void 0;
}
};
var getFileOidsUnderPath = async function(basePath) {
const stdout = await runGitCommand(
basePath,
@@ -104216,10 +104228,12 @@ async function readBaseDatabaseOidsFile(config, logger) {
async function writeOverlayChangesFile(config, sourceRoot, logger) {
const baseFileOids = await readBaseDatabaseOidsFile(config, logger);
const overlayFileOids = await getFileOidsUnderPath(sourceRoot);
const changedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
const oidChangedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
logger.info(
`Found ${changedFiles.length} changed file(s) under ${sourceRoot}.`
`Found ${oidChangedFiles.length} changed file(s) under ${sourceRoot} from OID comparison.`
);
const diffRangeFiles = await getDiffRangeFilePaths(sourceRoot, logger);
const changedFiles = [.../* @__PURE__ */ new Set([...oidChangedFiles, ...diffRangeFiles])];
const changedFilesJson = JSON.stringify({ changes: changedFiles });
const overlayChangesFile = path3.join(
getTemporaryDirectory(),
@@ -104245,6 +104259,48 @@ function computeChangedFiles(baseFileOids, overlayFileOids) {
}
return changes;
}
async function getDiffRangeFilePaths(sourceRoot, logger) {
const jsonFilePath = path3.join(getTemporaryDirectory(), "pr-diff-range.json");
if (!fs3.existsSync(jsonFilePath)) {
return [];
}
let diffRanges;
try {
diffRanges = JSON.parse(fs3.readFileSync(jsonFilePath, "utf8"));
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`
);
return [];
}
logger.debug(
`Read ${diffRanges.length} diff range(s) from ${jsonFilePath} for overlay changes.`
);
const repoRelativePaths = [...new Set(diffRanges.map((r) => r.path))];
const repoRoot = await getGitRoot(sourceRoot);
if (repoRoot === void 0) {
logger.warning(
"Cannot determine git root; returning diff range paths as-is."
);
return repoRelativePaths;
}
const sourceRootRelPrefix = path3.relative(repoRoot, sourceRoot).replaceAll(path3.sep, "/");
if (sourceRootRelPrefix === "") {
return repoRelativePaths;
}
const prefixWithSlash = `${sourceRootRelPrefix}/`;
const result = [];
for (const p of repoRelativePaths) {
if (p.startsWith(prefixWithSlash)) {
result.push(p.slice(prefixWithSlash.length));
} else {
logger.debug(
`Skipping diff range path "${p}" (not under source root "${sourceRootRelPrefix}").`
);
}
}
return result;
}
// src/tools-features.ts
var semver3 = __toESM(require_semver2());

73
lib/upload-lib.js generated
View File

@@ -100540,8 +100540,8 @@ var require_follow_redirects = __commonJS({
}
return parsed;
}
function resolveUrl(relative2, base) {
return useNativeURL ? new URL2(relative2, base) : parseUrl2(url2.resolve(base, relative2));
function resolveUrl(relative3, base) {
return useNativeURL ? new URL2(relative3, base) : parseUrl2(url2.resolve(base, relative3));
}
function validateUrl(input) {
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -107360,6 +107360,18 @@ var decodeGitFilePath = function(filePath) {
}
return filePath;
};
var getGitRoot = async function(sourceRoot) {
try {
const stdout = await runGitCommand(
sourceRoot,
["rev-parse", "--show-toplevel"],
`Cannot find Git repository root from the source root ${sourceRoot}.`
);
return stdout.trim();
} catch {
return void 0;
}
};
var getFileOidsUnderPath = async function(basePath) {
const stdout = await runGitCommand(
basePath,
@@ -107482,10 +107494,12 @@ async function readBaseDatabaseOidsFile(config, logger) {
async function writeOverlayChangesFile(config, sourceRoot, logger) {
const baseFileOids = await readBaseDatabaseOidsFile(config, logger);
const overlayFileOids = await getFileOidsUnderPath(sourceRoot);
const changedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
const oidChangedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
logger.info(
`Found ${changedFiles.length} changed file(s) under ${sourceRoot}.`
`Found ${oidChangedFiles.length} changed file(s) under ${sourceRoot} from OID comparison.`
);
const diffRangeFiles = await getDiffRangeFilePaths(sourceRoot, logger);
const changedFiles = [.../* @__PURE__ */ new Set([...oidChangedFiles, ...diffRangeFiles])];
const changedFilesJson = JSON.stringify({ changes: changedFiles });
const overlayChangesFile = path4.join(
getTemporaryDirectory(),
@@ -107511,6 +107525,48 @@ function computeChangedFiles(baseFileOids, overlayFileOids) {
}
return changes;
}
async function getDiffRangeFilePaths(sourceRoot, logger) {
const jsonFilePath = path4.join(getTemporaryDirectory(), "pr-diff-range.json");
if (!fs3.existsSync(jsonFilePath)) {
return [];
}
let diffRanges;
try {
diffRanges = JSON.parse(fs3.readFileSync(jsonFilePath, "utf8"));
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`
);
return [];
}
logger.debug(
`Read ${diffRanges.length} diff range(s) from ${jsonFilePath} for overlay changes.`
);
const repoRelativePaths = [...new Set(diffRanges.map((r) => r.path))];
const repoRoot = await getGitRoot(sourceRoot);
if (repoRoot === void 0) {
logger.warning(
"Cannot determine git root; returning diff range paths as-is."
);
return repoRelativePaths;
}
const sourceRootRelPrefix = path4.relative(repoRoot, sourceRoot).replaceAll(path4.sep, "/");
if (sourceRootRelPrefix === "") {
return repoRelativePaths;
}
const prefixWithSlash = `${sourceRootRelPrefix}/`;
const result = [];
for (const p of repoRelativePaths) {
if (p.startsWith(prefixWithSlash)) {
result.push(p.slice(prefixWithSlash.length));
} else {
logger.debug(
`Skipping diff range path "${p}" (not under source root "${sourceRootRelPrefix}").`
);
}
}
return result;
}
// src/tools-features.ts
var semver4 = __toESM(require_semver2());
@@ -107750,7 +107806,14 @@ function readDiffRangesJsonFile(logger) {
`Read pr-diff-range JSON file from ${jsonFilePath}:
${jsonContents}`
);
return JSON.parse(jsonContents);
try {
return JSON.parse(jsonContents);
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`
);
return void 0;
}
}
// src/overlay/status.ts

View File

@@ -100540,8 +100540,8 @@ var require_follow_redirects = __commonJS({
}
return parsed;
}
function resolveUrl(relative2, base) {
return useNativeURL ? new URL2(relative2, base) : parseUrl2(url2.resolve(base, relative2));
function resolveUrl(relative3, base) {
return useNativeURL ? new URL2(relative3, base) : parseUrl2(url2.resolve(base, relative3));
}
function validateUrl(input) {
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -107044,6 +107044,18 @@ var decodeGitFilePath = function(filePath) {
}
return filePath;
};
var getGitRoot = async function(sourceRoot) {
try {
const stdout = await runGitCommand(
sourceRoot,
["rev-parse", "--show-toplevel"],
`Cannot find Git repository root from the source root ${sourceRoot}.`
);
return stdout.trim();
} catch {
return void 0;
}
};
var getFileOidsUnderPath = async function(basePath) {
const stdout = await runGitCommand(
basePath,
@@ -107191,10 +107203,12 @@ async function readBaseDatabaseOidsFile(config, logger) {
async function writeOverlayChangesFile(config, sourceRoot, logger) {
const baseFileOids = await readBaseDatabaseOidsFile(config, logger);
const overlayFileOids = await getFileOidsUnderPath(sourceRoot);
const changedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
const oidChangedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
logger.info(
`Found ${changedFiles.length} changed file(s) under ${sourceRoot}.`
`Found ${oidChangedFiles.length} changed file(s) under ${sourceRoot} from OID comparison.`
);
const diffRangeFiles = await getDiffRangeFilePaths(sourceRoot, logger);
const changedFiles = [.../* @__PURE__ */ new Set([...oidChangedFiles, ...diffRangeFiles])];
const changedFilesJson = JSON.stringify({ changes: changedFiles });
const overlayChangesFile = path3.join(
getTemporaryDirectory(),
@@ -107220,6 +107234,48 @@ function computeChangedFiles(baseFileOids, overlayFileOids) {
}
return changes;
}
async function getDiffRangeFilePaths(sourceRoot, logger) {
const jsonFilePath = path3.join(getTemporaryDirectory(), "pr-diff-range.json");
if (!fs3.existsSync(jsonFilePath)) {
return [];
}
let diffRanges;
try {
diffRanges = JSON.parse(fs3.readFileSync(jsonFilePath, "utf8"));
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`
);
return [];
}
logger.debug(
`Read ${diffRanges.length} diff range(s) from ${jsonFilePath} for overlay changes.`
);
const repoRelativePaths = [...new Set(diffRanges.map((r) => r.path))];
const repoRoot = await getGitRoot(sourceRoot);
if (repoRoot === void 0) {
logger.warning(
"Cannot determine git root; returning diff range paths as-is."
);
return repoRelativePaths;
}
const sourceRootRelPrefix = path3.relative(repoRoot, sourceRoot).replaceAll(path3.sep, "/");
if (sourceRootRelPrefix === "") {
return repoRelativePaths;
}
const prefixWithSlash = `${sourceRootRelPrefix}/`;
const result = [];
for (const p of repoRelativePaths) {
if (p.startsWith(prefixWithSlash)) {
result.push(p.slice(prefixWithSlash.length));
} else {
logger.debug(
`Skipping diff range path "${p}" (not under source root "${sourceRootRelPrefix}").`
);
}
}
return result;
}
// src/tools-features.ts
var semver3 = __toESM(require_semver2());
@@ -107948,7 +108004,14 @@ function readDiffRangesJsonFile(logger) {
`Read pr-diff-range JSON file from ${jsonFilePath}:
${jsonContents}`
);
return JSON.parse(jsonContents);
try {
return JSON.parse(jsonContents);
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`
);
return void 0;
}
}
// src/overlay/status.ts

View File

@@ -28,7 +28,6 @@ import {
DependencyCacheUploadStatusReport,
uploadDependencyCaches,
} from "./dependency-caching";
import { getDiffInformedAnalysisBranches } from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment";
import { initFeatures } from "./feature-flags";
import { KnownLanguage } from "./languages";
@@ -305,14 +304,8 @@ async function run(startedAt: Date) {
logger,
);
const branches = await getDiffInformedAnalysisBranches(
codeql,
features,
logger,
);
const diffRangePackDir = branches
? await setupDiffInformedQueryRun(branches, logger)
: undefined;
// Setup diff informed analysis if needed (based on whether init created the file)
const diffRangePackDir = await setupDiffInformedQueryRun(logger);
await warnIfGoInstalledAfterInit(config, logger);
await runAutobuildIfLegacyGoWorkflow(config, logger);

View File

@@ -5,11 +5,7 @@ import { performance } from "perf_hooks";
import * as io from "@actions/io";
import * as yaml from "js-yaml";
import {
getTemporaryDirectory,
getRequiredInput,
PullRequestBranches,
} from "./actions-util";
import { getTemporaryDirectory, getRequiredInput } from "./actions-util";
import * as analyses from "./analyses";
import { setupCppAutobuild } from "./autobuild";
import { type CodeQL } from "./codeql";
@@ -21,8 +17,7 @@ import {
import { addDiagnostic, makeDiagnostic } from "./diagnostics";
import {
DiffThunkRange,
writeDiffRangesJsonFile,
getPullRequestEditedDiffRanges,
readDiffRangesJsonFile,
} from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment";
import { FeatureEnablement, Feature } from "./feature-flags";
@@ -237,16 +232,19 @@ async function finalizeDatabaseCreation(
* the diff range information, or `undefined` if the feature is disabled.
*/
export async function setupDiffInformedQueryRun(
branches: PullRequestBranches,
logger: Logger,
): Promise<string | undefined> {
return await withGroupAsync(
"Generating diff range extension pack",
async () => {
logger.info(
`Calculating diff ranges for ${branches.base}...${branches.head}`,
);
const diffRanges = await getPullRequestEditedDiffRanges(branches, logger);
const diffRanges = readDiffRangesJsonFile(logger);
if (diffRanges === undefined) {
logger.info(
"No precomputed diff ranges found; skipping diff-informed analysis stage.",
);
return undefined;
}
const checkoutPath = getRequiredInput("checkout_path");
const packDir = writeDiffRangeDataExtensionPack(
logger,
@@ -368,10 +366,6 @@ dataExtensions:
`Wrote pr-diff-range extension pack to ${extensionFilePath}:\n${extensionContents}`,
);
// Write the diff ranges to a JSON file, for action-side alert filtering by the
// upload-lib module.
writeDiffRangesJsonFile(logger, ranges);
return diffRangeDir;
}

View File

@@ -105,7 +105,14 @@ export function readDiffRangesJsonFile(
logger.debug(
`Read pr-diff-range JSON file from ${jsonFilePath}:\n${jsonContents}`,
);
return JSON.parse(jsonContents) as DiffThunkRange[];
try {
return JSON.parse(jsonContents) as DiffThunkRange[];
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`,
);
return undefined;
}
}
/**

View File

@@ -37,6 +37,11 @@ import {
makeDiagnostic,
makeTelemetryDiagnostic,
} from "./diagnostics";
import {
getDiffInformedAnalysisBranches,
getPullRequestEditedDiffRanges,
writeDiffRangesJsonFile,
} from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment";
import { Feature, FeatureEnablement, initFeatures } from "./feature-flags";
import {
@@ -54,7 +59,7 @@ import {
runDatabaseInitCluster,
} from "./init";
import { JavaEnvVars, KnownLanguage } from "./languages";
import { getActionsLogger, Logger } from "./logging";
import { getActionsLogger, Logger, withGroupAsync } from "./logging";
import {
downloadOverlayBaseDatabaseFromCache,
OverlayBaseDatabaseDownloadStats,
@@ -413,6 +418,7 @@ async function run(startedAt: Date) {
}
await checkInstallPython311(config.languages, codeql);
await computeAndPersistDiffRanges(codeql, features, logger);
} catch (unwrappedError) {
const error = wrapError(unwrappedError);
core.setFailed(error.message);
@@ -833,6 +839,42 @@ async function loadRepositoryProperties(
}
}
/**
* Compute and persist diff ranges when diff-informed analysis is enabled
* (feature flag + PR context). This writes the standard pr-diff-range.json
* file for later reuse in the analyze step. Failures are logged but non-fatal.
*/
async function computeAndPersistDiffRanges(
codeql: CodeQL,
features: FeatureEnablement,
logger: Logger,
): Promise<void> {
try {
await withGroupAsync("Compute PR diff ranges", async () => {
const branches = await getDiffInformedAnalysisBranches(
codeql,
features,
logger,
);
if (!branches) {
return;
}
const ranges = await getPullRequestEditedDiffRanges(branches, logger);
if (ranges === undefined) {
return;
}
writeDiffRangesJsonFile(logger, ranges);
const distinctFiles = new Set(ranges.map((r) => r.path)).size;
logger.info(
`Persisted ${ranges.length} diff range(s) across ${distinctFiles} file(s).`,
);
});
} catch (e) {
logger.warning(
`Failed to compute and persist PR diff ranges: ${getErrorMessage(e)}`,
);
}
}
async function recordZstdAvailability(
config: configUtils.Config,
zstdAvailability: ZstdAvailability,

View File

@@ -34,12 +34,14 @@ test.serial(
"writeOverlayChangesFile generates correct changes file",
async (t) => {
await withTmpDir(async (tmpDir) => {
const dbLocation = path.join(tmpDir, "db");
await fs.promises.mkdir(dbLocation, { recursive: true });
const sourceRoot = path.join(tmpDir, "src");
await fs.promises.mkdir(sourceRoot, { recursive: true });
const tempDir = path.join(tmpDir, "temp");
await fs.promises.mkdir(tempDir, { recursive: true });
const [dbLocation, sourceRoot, tempDir] = ["db", "src", "temp"].map((d) =>
path.join(tmpDir, d),
);
await Promise.all(
[dbLocation, sourceRoot, tempDir].map((d) =>
fs.promises.mkdir(d, { recursive: true }),
),
);
const logger = getRunnerLogger(true);
const config = createTestConfig({ dbLocation });
@@ -73,6 +75,9 @@ test.serial(
const getTempDirStub = sinon
.stub(actionsUtil, "getTemporaryDirectory")
.returns(tempDir);
const getGitRootStub = sinon
.stub(gitUtils, "getGitRoot")
.resolves(sourceRoot);
const changesFilePath = await writeOverlayChangesFile(
config,
sourceRoot,
@@ -80,6 +85,7 @@ test.serial(
);
getFileOidsStubForOverlay.restore();
getTempDirStub.restore();
getGitRootStub.restore();
const fileContent = await fs.promises.readFile(changesFilePath, "utf-8");
const parsedContent = JSON.parse(fileContent) as { changes: string[] };
@@ -93,6 +99,232 @@ test.serial(
},
);
test.serial(
"writeOverlayChangesFile merges additional diff files into overlay changes",
async (t) => {
await withTmpDir(async (tmpDir) => {
const [dbLocation, sourceRoot, tempDir] = ["db", "src", "temp"].map((d) =>
path.join(tmpDir, d),
);
await Promise.all(
[dbLocation, sourceRoot, tempDir].map((d) =>
fs.promises.mkdir(d, { recursive: true }),
),
);
const logger = getRunnerLogger(true);
const config = createTestConfig({ dbLocation });
// Mock the getFileOidsUnderPath function to return base OIDs
// "reverted.js" has the same OID in both base and current, simulating
// a revert PR where the file content matches the overlay-base
const baseOids = {
"unchanged.js": "aaa111",
"modified.js": "bbb222",
"reverted.js": "eee555",
};
const getFileOidsStubForBase = sinon
.stub(gitUtils, "getFileOidsUnderPath")
.resolves(baseOids);
// Write the base database OIDs file
await writeBaseDatabaseOidsFile(config, sourceRoot);
getFileOidsStubForBase.restore();
// Mock the getFileOidsUnderPath function to return overlay OIDs
// "reverted.js" has the same OID as the base -- OID comparison alone
// would NOT include it, only additionalChangedFiles causes it to appear
const currentOids = {
"unchanged.js": "aaa111",
"modified.js": "ddd444", // Changed OID
"reverted.js": "eee555", // Same OID as base -- not detected by OID comparison
};
const getFileOidsStubForOverlay = sinon
.stub(gitUtils, "getFileOidsUnderPath")
.resolves(currentOids);
const getTempDirStub = sinon
.stub(actionsUtil, "getTemporaryDirectory")
.returns(tempDir);
const getGitRootStub = sinon
.stub(gitUtils, "getGitRoot")
.resolves(sourceRoot);
// Write a pr-diff-range.json file with diff ranges including
// "reverted.js" (unchanged OIDs) and "modified.js" (already in OID changes)
await fs.promises.writeFile(
path.join(tempDir, "pr-diff-range.json"),
JSON.stringify([
{ path: "reverted.js", startLine: 1, endLine: 10 },
{ path: "modified.js", startLine: 1, endLine: 5 },
{ path: "diff-only.js", startLine: 1, endLine: 3 },
]),
);
const changesFilePath = await writeOverlayChangesFile(
config,
sourceRoot,
logger,
);
getFileOidsStubForOverlay.restore();
getTempDirStub.restore();
getGitRootStub.restore();
const fileContent = await fs.promises.readFile(changesFilePath, "utf-8");
const parsedContent = JSON.parse(fileContent) as { changes: string[] };
t.deepEqual(
parsedContent.changes.sort(),
["diff-only.js", "modified.js", "reverted.js"],
"Should include OID-changed files, diff-only files, and deduplicate overlapping files",
);
});
},
);
test.serial(
"writeOverlayChangesFile works without additional diff files",
async (t) => {
await withTmpDir(async (tmpDir) => {
const [dbLocation, sourceRoot, tempDir] = ["db", "src", "temp"].map((d) =>
path.join(tmpDir, d),
);
await Promise.all(
[dbLocation, sourceRoot, tempDir].map((d) =>
fs.promises.mkdir(d, { recursive: true }),
),
);
const logger = getRunnerLogger(true);
const config = createTestConfig({ dbLocation });
// Mock the getFileOidsUnderPath function to return base OIDs
const baseOids = {
"unchanged.js": "aaa111",
"modified.js": "bbb222",
};
const getFileOidsStubForBase = sinon
.stub(gitUtils, "getFileOidsUnderPath")
.resolves(baseOids);
await writeBaseDatabaseOidsFile(config, sourceRoot);
getFileOidsStubForBase.restore();
const currentOids = {
"unchanged.js": "aaa111",
"modified.js": "ddd444",
};
const getFileOidsStubForOverlay = sinon
.stub(gitUtils, "getFileOidsUnderPath")
.resolves(currentOids);
const getTempDirStub = sinon
.stub(actionsUtil, "getTemporaryDirectory")
.returns(tempDir);
const getGitRootStub = sinon
.stub(gitUtils, "getGitRoot")
.resolves(sourceRoot);
// No pr-diff-range.json file exists - should work the same as before
const changesFilePath = await writeOverlayChangesFile(
config,
sourceRoot,
logger,
);
getFileOidsStubForOverlay.restore();
getTempDirStub.restore();
getGitRootStub.restore();
const fileContent = await fs.promises.readFile(changesFilePath, "utf-8");
const parsedContent = JSON.parse(fileContent) as { changes: string[] };
t.deepEqual(
parsedContent.changes.sort(),
["modified.js"],
"Should only include OID-changed files when no additional files provided",
);
});
},
);
test.serial(
"writeOverlayChangesFile converts diff range paths to sourceRoot-relative when sourceRoot is a subdirectory",
async (t) => {
await withTmpDir(async (tmpDir) => {
// Simulate: repo root = tmpDir, sourceRoot = tmpDir/src
const repoRoot = tmpDir;
const sourceRoot = path.join(tmpDir, "src");
const [dbLocation, tempDir] = ["db", "temp"].map((d) =>
path.join(tmpDir, d),
);
await Promise.all(
[dbLocation, sourceRoot, tempDir].map((d) =>
fs.promises.mkdir(d, { recursive: true }),
),
);
const logger = getRunnerLogger(true);
const config = createTestConfig({ dbLocation });
// Base OIDs (sourceRoot-relative paths)
const baseOids = {
"app.js": "aaa111",
"lib/util.js": "bbb222",
};
const getFileOidsStubForBase = sinon
.stub(gitUtils, "getFileOidsUnderPath")
.resolves(baseOids);
await writeBaseDatabaseOidsFile(config, sourceRoot);
getFileOidsStubForBase.restore();
// Current OIDs — same as base (no OID changes)
const currentOids = {
"app.js": "aaa111",
"lib/util.js": "bbb222",
};
const getFileOidsStubForOverlay = sinon
.stub(gitUtils, "getFileOidsUnderPath")
.resolves(currentOids);
const getTempDirStub = sinon
.stub(actionsUtil, "getTemporaryDirectory")
.returns(tempDir);
// getGitRoot returns the repo root (parent of sourceRoot)
const getGitRootStub = sinon
.stub(gitUtils, "getGitRoot")
.resolves(repoRoot);
// Diff ranges use repo-root-relative paths (as returned by the GitHub compare API)
await fs.promises.writeFile(
path.join(tempDir, "pr-diff-range.json"),
JSON.stringify([
{ path: "src/app.js", startLine: 1, endLine: 10 },
{ path: "src/lib/util.js", startLine: 5, endLine: 8 },
{ path: "other/outside.js", startLine: 1, endLine: 3 }, // not under sourceRoot
]),
);
const changesFilePath = await writeOverlayChangesFile(
config,
sourceRoot,
logger,
);
getFileOidsStubForOverlay.restore();
getTempDirStub.restore();
getGitRootStub.restore();
const fileContent = await fs.promises.readFile(changesFilePath, "utf-8");
const parsedContent = JSON.parse(fileContent) as { changes: string[] };
t.deepEqual(
parsedContent.changes.sort(),
["app.js", "lib/util.js"],
"Should convert repo-root-relative paths to sourceRoot-relative and filter out files outside sourceRoot",
);
});
},
);
interface DownloadOverlayBaseDatabaseTestCase {
overlayDatabaseMode: OverlayDatabaseMode;
useOverlayDatabaseCaching: boolean;

View File

@@ -13,7 +13,7 @@ import { getAutomationID } from "../api-client";
import { createCacheKeyHash } from "../caching-utils";
import { type CodeQL } from "../codeql";
import { type Config } from "../config-utils";
import { getCommitOid, getFileOidsUnderPath } from "../git-utils";
import { getCommitOid, getFileOidsUnderPath, getGitRoot } from "../git-utils";
import { Logger, withGroupAsync } from "../logging";
import {
CleanupLevel,
@@ -130,11 +130,17 @@ export async function writeOverlayChangesFile(
): Promise<string> {
const baseFileOids = await readBaseDatabaseOidsFile(config, logger);
const overlayFileOids = await getFileOidsUnderPath(sourceRoot);
const changedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
const oidChangedFiles = computeChangedFiles(baseFileOids, overlayFileOids);
logger.info(
`Found ${changedFiles.length} changed file(s) under ${sourceRoot}.`,
`Found ${oidChangedFiles.length} changed file(s) under ${sourceRoot} from OID comparison.`,
);
// Merge in any file paths from precomputed PR diff ranges to ensure the
// overlay always includes all files from the PR diff, even in edge cases
// like revert PRs where OID comparison shows no change.
const diffRangeFiles = await getDiffRangeFilePaths(sourceRoot, logger);
const changedFiles = [...new Set([...oidChangedFiles, ...diffRangeFiles])];
const changedFilesJson = JSON.stringify({ changes: changedFiles });
const overlayChangesFile = path.join(
getTemporaryDirectory(),
@@ -165,6 +171,65 @@ function computeChangedFiles(
return changes;
}
async function getDiffRangeFilePaths(
sourceRoot: string,
logger: Logger,
): Promise<string[]> {
const jsonFilePath = path.join(getTemporaryDirectory(), "pr-diff-range.json");
if (!fs.existsSync(jsonFilePath)) {
return [];
}
let diffRanges: Array<{ path: string }>;
try {
diffRanges = JSON.parse(fs.readFileSync(jsonFilePath, "utf8")) as Array<{
path: string;
}>;
} catch (e) {
logger.warning(
`Failed to parse diff ranges JSON file at ${jsonFilePath}: ${e}`,
);
return [];
}
logger.debug(
`Read ${diffRanges.length} diff range(s) from ${jsonFilePath} for overlay changes.`,
);
const repoRelativePaths = [...new Set(diffRanges.map((r) => r.path))];
// Diff-range paths are relative to the repo root (from the GitHub compare
// API), but overlay changed files must be relative to sourceRoot (to match
// getFileOidsUnderPath output). Convert and filter accordingly.
const repoRoot = await getGitRoot(sourceRoot);
if (repoRoot === undefined) {
logger.warning(
"Cannot determine git root; returning diff range paths as-is.",
);
return repoRelativePaths;
}
// e.g. if repoRoot=/workspace and sourceRoot=/workspace/src, prefix="src"
const sourceRootRelPrefix = path
.relative(repoRoot, sourceRoot)
.replaceAll(path.sep, "/");
// If sourceRoot IS the repo root, prefix is "" and all paths pass through.
if (sourceRootRelPrefix === "") {
return repoRelativePaths;
}
const prefixWithSlash = `${sourceRootRelPrefix}/`;
const result: string[] = [];
for (const p of repoRelativePaths) {
if (p.startsWith(prefixWithSlash)) {
result.push(p.slice(prefixWithSlash.length));
} else {
logger.debug(
`Skipping diff range path "${p}" (not under source root "${sourceRootRelPrefix}").`,
);
}
}
return result;
}
// Constants for database caching
const CACHE_VERSION = 1;
const CACHE_PREFIX = "codeql-overlay-base-database";