Consume precomputed diff ranges in analyze and avoid getting them from the API.

This commit is contained in:
Alex Eyers-Taylor
2025-10-08 17:57:03 +01:00
parent 9bb8375aed
commit 4c0acfe29e
4 changed files with 45 additions and 197 deletions
+19 -169
View File
@@ -90112,29 +90112,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",
@@ -91534,34 +91511,9 @@ var GitHubFeatureFlags = class {
};
// 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 === 1 /* 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 path9.join(getTemporaryDirectory(), "pr-diff-range.json");
}
function writeDiffRangesJsonFile(logger, ranges) {
const jsonContents = JSON.stringify(ranges, null, 2);
const jsonFilePath = getDiffRangesJsonFilePath();
fs8.writeFileSync(jsonFilePath, jsonContents);
logger.debug(
`Wrote pr-diff-range JSON file to ${jsonFilePath}:
${jsonContents}`
);
}
function readDiffRangesJsonFile(logger) {
const jsonFilePath = getDiffRangesJsonFilePath();
if (!fs8.existsSync(jsonFilePath)) {
@@ -91575,117 +91527,6 @@ ${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
}
);
logger.debug(
`Response from compareCommitsWithBasehead(${basehead}):
${JSON.stringify(response, null, 2)}`
);
return response.data.files;
} catch (error2) {
if (error2.status) {
logger.warning(`Error retrieving diff ${basehead}: ${error2.message}`);
logger.debug(
`Error running compareCommitsWithBasehead(${basehead}):
Request: ${JSON.stringify(error2.request, null, 2)}
Error Response: ${JSON.stringify(error2.response, null, 2)}`
);
return void 0;
} else {
throw error2;
}
}
}
function getDiffRanges(fileDiff, logger) {
const filename = path9.join(getRequiredInput("checkout_path"), fileDiff.filename).replaceAll(path9.sep, "/");
if (fileDiff.patch === void 0) {
if (fileDiff.changes === 0) {
return [];
}
return [
{
path: 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: 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/trap-caching.ts
var actionsCache2 = __toESM(require_cache3());
@@ -93747,14 +93588,29 @@ async function finalizeDatabaseCreation(codeql, config, threadsFlag, memoryFlag,
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 () => {
let diffRanges;
try {
diffRanges = readDiffRangesJsonFile(logger);
} catch (e) {
logger.debug(
`Failed to read precomputed diff ranges: ${getErrorMessage(e)}`
);
diffRanges = void 0;
}
if (diffRanges === void 0) {
logger.info(
"No precomputed diff ranges found; skipping diff-informed analysis stage."
);
return void 0;
}
const fileCount = new Set(diffRanges.filter((r) => r.path).map((r) => r.path)).size;
logger.info(
`Calculating diff ranges for ${branches.base}...${branches.head}`
`Using precomputed diff ranges (${diffRanges.length} ranges across ${fileCount} files).`
);
const diffRanges = await getPullRequestEditedDiffRanges(branches, logger);
const packDir = writeDiffRangeDataExtensionPack(logger, diffRanges);
if (packDir === void 0) {
logger.warning(
@@ -93817,7 +93673,6 @@ extensions:
`Wrote pr-diff-range extension pack to ${extensionFilePath}:
${extensionContents}`
);
writeDiffRangesJsonFile(logger, ranges);
return diffRangeDir;
}
var defaultSuites = /* @__PURE__ */ new Set([
@@ -96206,12 +96061,7 @@ async function run() {
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(
-6
View File
@@ -91255,16 +91255,10 @@ async function computeAndPersistDiffRangesEarly(codeql, features, logger) {
logger
);
if (!branches) {
logger.debug(
"Diff-informed analysis not enabled (feature flag or context); skipping diff range computation."
);
return;
}
const ranges = await getPullRequestEditedDiffRanges(branches, logger);
if (ranges === void 0) {
logger.info(
"Diff ranges unavailable (API limits, truncation, or error); will fall back to analyze-time computation."
);
return;
}
writeDiffRangesJsonFile(logger, ranges);
+2 -9
View File
@@ -30,7 +30,6 @@ import {
DependencyCacheUploadStatusReport,
uploadDependencyCaches,
} from "./dependency-caching";
import { getDiffInformedAnalysisBranches } from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment";
import { Feature, Features } from "./feature-flags";
import { KnownLanguage } from "./languages";
@@ -299,14 +298,8 @@ async function run() {
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);
+24 -13
View File
@@ -6,7 +6,7 @@ import * as io from "@actions/io";
import * as del from "del";
import * as yaml from "js-yaml";
import { getTemporaryDirectory, PullRequestBranches } from "./actions-util";
import { getTemporaryDirectory } from "./actions-util";
import * as analyses from "./analyses";
// (getApiClient import removed; no longer needed after diff refactor)
import { setupCppAutobuild } from "./autobuild";
@@ -14,11 +14,7 @@ import { type CodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { getJavaTempDependencyDir } from "./dependency-caching";
import { addDiagnostic, makeDiagnostic } from "./diagnostics";
import {
DiffThunkRange,
writeDiffRangesJsonFile,
getPullRequestEditedDiffRanges,
} from "./diff-informed-analysis-utils";
import { DiffThunkRange, readDiffRangesJsonFile } from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment";
import { FeatureEnablement, Feature } from "./feature-flags";
import { KnownLanguage, Language } from "./languages";
@@ -284,17 +280,35 @@ 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 () => {
// Only use precomputed diff ranges; never recompute here.
let diffRanges: DiffThunkRange[] | undefined;
try {
diffRanges = readDiffRangesJsonFile(logger);
} catch (e) {
logger.debug(
`Failed to read precomputed diff ranges: ${util.getErrorMessage(e)}`,
);
diffRanges = undefined;
}
if (diffRanges === undefined) {
logger.info(
"No precomputed diff ranges found; skipping diff-informed analysis stage.",
);
return undefined;
}
const fileCount = new Set(diffRanges.filter((r) => r.path).map((r) => r.path)).size;
logger.info(
`Calculating diff ranges for ${branches.base}...${branches.head}`,
`Using precomputed diff ranges (${diffRanges.length} ranges across ${fileCount} files).`,
);
const diffRanges = await getPullRequestEditedDiffRanges(branches, logger);
const packDir = writeDiffRangeDataExtensionPack(logger, diffRanges);
const packDir = writeDiffRangeDataExtensionPack(logger, diffRanges);
if (packDir === undefined) {
logger.warning(
"Cannot create diff range extension pack for diff-informed queries; " +
@@ -392,9 +406,6 @@ extensions:
`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;
}