Make jsonFilePath an explicit input to functions in diff-informed/index.ts

This commit is contained in:
Michael B. Gale
2026-11-03 11:01:09 +00:00
parent 3a4f195d36
commit 8c16574468
9 changed files with 81 additions and 49 deletions

29
lib/entry-points.js generated
View File

@@ -181851,7 +181851,7 @@ async function getDiffInformedAnalysisBranches(codeql, features, logger2) {
}
return branches;
}
async function prepareDiffInformedAnalysis(codeql, features, logger2) {
async function prepareDiffInformedAnalysis(logger2, features, codeql, jsonFilePath) {
let branches;
try {
branches = await getDiffInformedAnalysisBranches(codeql, features, logger2);
@@ -181865,7 +181865,7 @@ async function prepareDiffInformedAnalysis(codeql, features, logger2) {
return false;
}
try {
return await computeAndPersistDiffRanges(branches, logger2);
return await computeAndPersistDiffRanges(logger2, jsonFilePath, branches);
} catch (e) {
logger2.warning(
`Failed to compute diff-informed analysis ranges: ${getErrorMessage(e)}`
@@ -181873,17 +181873,15 @@ async function prepareDiffInformedAnalysis(codeql, features, logger2) {
return false;
}
}
function writeDiffRangesJsonFile(logger2, ranges) {
function writeDiffRangesJsonFile(logger2, jsonFilePath, ranges) {
const jsonContents = JSON.stringify(ranges, null, 2);
const jsonFilePath = getDiffRangesJsonFilePath();
fs8.writeFileSync(jsonFilePath, jsonContents);
logger2.debug(
`Wrote pr-diff-range JSON file to ${jsonFilePath}:
${jsonContents}`
);
}
function readDiffRangesJsonFile(logger2) {
const jsonFilePath = getDiffRangesJsonFilePath();
function readDiffRangesJsonFile(logger2, jsonFilePath) {
if (!fs8.existsSync(jsonFilePath)) {
logger2.debug(`Diff ranges JSON file does not exist at ${jsonFilePath}`);
return void 0;
@@ -181923,13 +181921,13 @@ async function getPullRequestEditedDiffRanges(branches, logger2) {
}
return results;
}
async function computeAndPersistDiffRanges(branches, logger2) {
async function computeAndPersistDiffRanges(logger2, jsonFilePath, branches) {
logger2.info("Computing PR diff ranges...");
const ranges = await getPullRequestEditedDiffRanges(branches, logger2);
if (ranges === void 0) {
return false;
}
writeDiffRangesJsonFile(logger2, ranges);
writeDiffRangesJsonFile(logger2, jsonFilePath, ranges);
const distinctFiles = new Set(ranges.map((r) => r.path)).size;
logger2.info(
`Persisted ${ranges.length} diff range(s) across ${distinctFiles} file(s).`
@@ -183066,9 +183064,10 @@ async function initConfig(actionState, inputs) {
);
}
const hasDiffRanges = await prepareDiffInformedAnalysis(
inputs.codeql,
logger2,
inputs.features,
logger2
inputs.codeql,
getDiffRangesJsonFilePath(actionState.env)
);
await applyIncrementalAnalysisSettings(
config,
@@ -186255,7 +186254,10 @@ async function setupDiffInformedQueryRun(logger2, checkoutPath) {
return await withGroupAsync(
"Generating diff range extension pack",
async () => {
const diffRanges = readDiffRangesJsonFile(logger2);
const diffRanges = readDiffRangesJsonFile(
logger2,
getDiffRangesJsonFilePath()
);
if (diffRanges === void 0) {
logger2.info(
"No precomputed diff ranges found; skipping diff-informed analysis stage."
@@ -188758,7 +188760,10 @@ function sanitize(str) {
return (str ?? "_").replace(/[^a-zA-Z0-9_]/g, "_").toLocaleUpperCase();
}
function filterAlertsByDiffRange(logger2, sarifLog) {
const diffRanges = readDiffRangesJsonFile(logger2);
const diffRanges = readDiffRangesJsonFile(
logger2,
getDiffRangesJsonFilePath()
);
if (!diffRanges?.length) {
return sarifLog;
}

2
package-lock.json generated
View File

@@ -10570,7 +10570,7 @@
"@octokit/core": "^7.0.8",
"@octokit/plugin-paginate-rest": ">=16.0.0",
"@octokit/plugin-rest-endpoint-methods": "^18.0.0",
"glob": "^13.0.6",
"glob": "^14.0.3",
"lite-matter": "^0.1.2",
"mdast-util-from-markdown": "^2.1.0",
"semver": "^7.8.5",

View File

@@ -6,7 +6,10 @@ import * as io from "@actions/io";
import * as yaml from "js-yaml";
import type { ActionState } from "./action-common";
import { getTemporaryDirectory } from "./actions-util";
import {
getDiffRangesJsonFilePath,
getTemporaryDirectory,
} from "./actions-util";
import * as analyses from "./analyses";
import { setupCppAutobuild } from "./autobuild";
import { type CodeQL } from "./codeql";
@@ -292,7 +295,10 @@ export async function setupDiffInformedQueryRun(
return await withGroupAsync(
"Generating diff range extension pack",
async () => {
const diffRanges = readDiffRangesJsonFile(logger);
const diffRanges = readDiffRangesJsonFile(
logger,
getDiffRangesJsonFilePath(),
);
if (diffRanges === undefined) {
logger.info(
"No precomputed diff ranges found; skipping diff-informed analysis stage.",

View File

@@ -15,6 +15,7 @@ import { createStubCodeQL } from "./codeql";
import { UserConfig } from "./config/db-config";
import * as file from "./config/file";
import * as configUtils from "./config-utils";
import { ActionsEnvVars } from "./environment";
import * as errorMessages from "./error-messages";
import { Feature } from "./feature-flags";
import { RepositoryProperties } from "./feature-flags/properties";
@@ -42,6 +43,7 @@ import {
SAMPLE_DOTCOM_API_DETAILS,
AssertableTarget,
RecordingLogger,
getTestEnv,
} from "./testing-utils";
import {
GitHubVariant,
@@ -151,9 +153,11 @@ function mockListLanguages(languages: string[]) {
test.serial("load empty config", async (t) => {
return await withTmpDir(async (tempDir) => {
const logger = new RecordingLogger();
const state = initAllState({ logger });
const languages = "javascript,python";
setupActionsVars(tempDir, tempDir);
setupActionsVars(tempDir, tempDir, {}, state.env);
const codeql = createStubCodeQL({
async resolveLanguages() {
@@ -166,7 +170,6 @@ test.serial("load empty config", async (t) => {
},
});
const state = initAllState({ logger });
const config = await configUtils.initConfig(
state,
createTestInitConfigInputs({
@@ -197,9 +200,10 @@ test.serial("load empty config", async (t) => {
test.serial("load code quality config", async (t) => {
return await withTmpDir(async (tempDir) => {
const logger = new RecordingLogger();
const state = initAllState({ logger });
const languages = "actions";
setupActionsVars(tempDir, tempDir);
setupActionsVars(tempDir, tempDir, {}, state.env);
const codeql = createStubCodeQL({
async resolveLanguages() {
@@ -211,7 +215,6 @@ test.serial("load code quality config", async (t) => {
},
});
const state = initAllState({ logger });
const config = await configUtils.initConfig(
state,
createTestInitConfigInputs({
@@ -253,9 +256,10 @@ test.serial(
async (t) => {
return await withTmpDir(async (tempDir) => {
const logger = new RecordingLogger();
const state = initAllState({ logger });
const languages = "javascript";
setupActionsVars(tempDir, tempDir);
setupActionsVars(tempDir, tempDir, {}, state.env);
const codeql = createStubCodeQL({
async resolveLanguages() {
@@ -291,7 +295,6 @@ test.serial(
repositoryProperties,
});
const state = initAllState({ logger });
await t.notThrowsAsync(async () => {
const config = await configUtils.initConfig(
state,
@@ -316,6 +319,8 @@ test.serial(
test.serial("loading a saved config produces the same config", async (t) => {
return await withTmpDir(async (tempDir) => {
const logger = new RecordingLogger();
const state = initAllState({ logger });
setupActionsVars(tempDir, tempDir, {}, state.env);
const codeql = createStubCodeQL({
async resolveLanguages() {
@@ -334,7 +339,6 @@ test.serial("loading a saved config produces the same config", async (t) => {
// Sanity check that getConfig returns undefined before we have called initConfig
t.deepEqual(await configUtils.getConfig(tempDir, logger), undefined);
const state = initAllState({ logger });
const config1 = await configUtils.initConfig(
state,
createTestInitConfigInputs({
@@ -364,6 +368,8 @@ test.serial("loading a saved config produces the same config", async (t) => {
test.serial("loading config with version mismatch throws", async (t) => {
return await withTmpDir(async (tempDir) => {
const logger = new RecordingLogger();
const state = initAllState({ logger });
setupActionsVars(tempDir, tempDir, {}, state.env);
const codeql = createStubCodeQL({
async resolveLanguages() {
@@ -387,7 +393,6 @@ test.serial("loading config with version mismatch throws", async (t) => {
.stub(actionsUtil, "getActionVersion")
.returns("does-not-exist");
const state = initAllState({ logger });
const config = await configUtils.initConfig(
state,
createTestInitConfigInputs({
@@ -497,7 +502,8 @@ const otherConfigFileContents = `
test.serial("load non-empty input", async (t) => {
return await withTmpDir(async (tempDir) => {
setupActionsVars(tempDir, tempDir);
const state = initAllState();
setupActionsVars(tempDir, tempDir, {}, state.env);
const codeql = createStubCodeQL({
async resolveLanguages() {
@@ -538,7 +544,6 @@ test.serial("load non-empty input", async (t) => {
const languagesInput = "javascript";
const configFilePath = createConfigFile(otherConfigFileContents, tempDir);
const state = initAllState();
const actualConfig = await configUtils.initConfig(
state,
createTestInitConfigInputs({
@@ -657,7 +662,9 @@ test.serial("API client used when reading remote config", async (t) => {
const configFile = "octo-org/codeql-config/config.yaml@main";
const languagesInput = "javascript";
const state = initAllState();
const state = initAllState({
env: getTestEnv({ [ActionsEnvVars.RUNNER_TEMP]: tempDir }),
});
await configUtils.initConfig(
state,
createTestInitConfigInputs({

View File

@@ -7,6 +7,7 @@ import * as yaml from "js-yaml";
import { ActionState } from "./action-common";
import {
getActionVersion,
getDiffRangesJsonFilePath,
getOptionalInput,
isAnalyzingPullRequest,
isDefaultSetup,
@@ -1288,9 +1289,10 @@ export async function initConfig(
}
const hasDiffRanges = await prepareDiffInformedAnalysis(
inputs.codeql,
inputs.features,
logger,
inputs.features,
inputs.codeql,
getDiffRangesJsonFilePath(actionState.env),
);
await applyIncrementalAnalysisSettings(

View File

@@ -4,6 +4,7 @@ import * as sinon from "sinon";
import * as actionsUtil from "../actions-util";
import type { PullRequestBranches } from "../actions-util";
import * as apiClient from "../api-client";
import { getEnv } from "../environment";
import { Feature, FeatureEnablement, initFeatures } from "../feature-flags";
import { parseRepositoryNwo } from "../repository";
import {
@@ -193,9 +194,10 @@ test.serial(
.resolves({ type: GitHubVariant.DOTCOM });
const result = await prepareDiffInformedAnalysis(
codeql,
features,
logger,
features,
codeql,
actionsUtil.getDiffRangesJsonFilePath(getEnv()),
);
t.false(result);
@@ -223,9 +225,10 @@ test.serial(
};
const result = await prepareDiffInformedAnalysis(
codeql,
features,
logger,
features,
codeql,
actionsUtil.getDiffRangesJsonFilePath(getEnv()),
);
t.false(result);
@@ -260,9 +263,10 @@ test.serial(
} as any);
const result = await prepareDiffInformedAnalysis(
codeql,
features,
logger,
features,
codeql,
actionsUtil.getDiffRangesJsonFilePath(getEnv()),
);
t.true(result);
@@ -297,9 +301,10 @@ test.serial(
} as any);
const result = await prepareDiffInformedAnalysis(
codeql,
features,
logger,
features,
codeql,
actionsUtil.getDiffRangesJsonFilePath(getEnv()),
);
t.false(result);

View File

@@ -67,9 +67,10 @@ export async function getDiffInformedAnalysisBranches(
* and are therefore available for use, `false` otherwise.
*/
export async function prepareDiffInformedAnalysis(
codeql: CodeQL,
features: FeatureEnablement,
logger: Logger,
features: FeatureEnablement,
codeql: CodeQL,
jsonFilePath: string,
): Promise<boolean> {
let branches: PullRequestBranches | undefined;
try {
@@ -88,7 +89,7 @@ export async function prepareDiffInformedAnalysis(
}
try {
return await computeAndPersistDiffRanges(branches, logger);
return await computeAndPersistDiffRanges(logger, jsonFilePath, branches);
} catch (e) {
logger.warning(
`Failed to compute diff-informed analysis ranges: ${getErrorMessage(e)}`,
@@ -108,18 +109,18 @@ export interface DiffThunkRange {
}
/**
* Writes an array of {@link DiffThunkRange} objects to the file given by
* {@link actionsUtil.getDiffRangesJsonFilePath}.
* Writes an array of {@link DiffThunkRange} objects to the file given by {@link jsonFilePath}.
*
* @param logger The logger to use.
* @param jsonFilePath The path of the file to write.
* @param ranges The changed line ranges to write.
*/
export function writeDiffRangesJsonFile(
logger: Logger,
jsonFilePath: string,
ranges: DiffThunkRange[],
): void {
const jsonContents = JSON.stringify(ranges, null, 2);
const jsonFilePath = actionsUtil.getDiffRangesJsonFilePath();
fs.writeFileSync(jsonFilePath, jsonContents);
logger.debug(
`Wrote pr-diff-range JSON file to ${jsonFilePath}:\n${jsonContents}`,
@@ -127,16 +128,15 @@ export function writeDiffRangesJsonFile(
}
/**
* Reads an array of {@link DiffThunkRange} objects from the file given by
* {@link actionsUtil.getDiffRangesJsonFilePath}.
* Reads an array of {@link DiffThunkRange} objects from the file given by {@link jsonFilePath}.
*
* @param logger The logger to use.
* @returns The array of {@link DiffThunkRange} objects or `undefined` if the file couldn't be read.
*/
export function readDiffRangesJsonFile(
logger: Logger,
jsonFilePath: string,
): DiffThunkRange[] | undefined {
const jsonFilePath = actionsUtil.getDiffRangesJsonFilePath();
if (!fs.existsSync(jsonFilePath)) {
logger.debug(`Diff ranges JSON file does not exist at ${jsonFilePath}`);
return undefined;
@@ -201,23 +201,25 @@ export async function getPullRequestEditedDiffRanges(
* diff from the GitHub API and writes it to the diff ranges JSON file so that
* CodeQL can use it for diff-informed analysis.
*
* @param logger The logger to use.
* @param jsonFilePath The path of the JSON file to write.
* @param branches The base and head branches of the pull request, as returned
* by `getDiffInformedAnalysisBranches`.
* @param logger The logger to use.
*
* @returns `true` if the diff ranges were successfully computed and persisted,
* otherwise `false`.
*/
export async function computeAndPersistDiffRanges(
branches: PullRequestBranches,
logger: Logger,
jsonFilePath: string,
branches: PullRequestBranches,
): Promise<boolean> {
logger.info("Computing PR diff ranges...");
const ranges = await getPullRequestEditedDiffRanges(branches, logger);
if (ranges === undefined) {
return false;
}
writeDiffRangesJsonFile(logger, ranges);
writeDiffRangesJsonFile(logger, jsonFilePath, ranges);
const distinctFiles = new Set(ranges.map((r) => r.path)).size;
logger.info(
`Persisted ${ranges.length} diff range(s) across ${distinctFiles} file(s).`,

View File

@@ -6,6 +6,7 @@ import { HTTPError } from "@actions/tool-cache";
import test from "ava";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as analyses from "./analyses";
import { AnalysisKind, CodeQuality, CodeScanning } from "./analyses";
import * as api from "./api-client";
@@ -1074,6 +1075,7 @@ function runFilterAlertsByDiffRange(
logger: Logger = new RecordingLogger(),
): Partial<sarif.Log> {
sinon.stub(diffUtils, "readDiffRangesJsonFile").returns(diffRanges);
sinon.stub(actionsUtil, "getDiffRangesJsonFilePath").returns("");
return uploadLib.filterAlertsByDiffRange(logger, input);
}

View File

@@ -1079,7 +1079,10 @@ export function filterAlertsByDiffRange(
logger: Logger,
sarifLog: Partial<sarif.Log>,
): Partial<sarif.Log> {
const diffRanges = readDiffRangesJsonFile(logger);
const diffRanges = readDiffRangesJsonFile(
logger,
actionsUtil.getDiffRangesJsonFilePath(),
);
if (!diffRanges?.length) {
return sarifLog;
}