refactor: address review feedback on overlay fallback

This commit is contained in:
Sam Robson
2026-04-23 11:57:41 +01:00
parent 5d1c58464f
commit faca00d3ae
5 changed files with 250 additions and 127 deletions
+24 -27
View File
@@ -106371,35 +106371,32 @@ async function getDiffInformedAnalysisBranches(codeql, features, logger) {
return branches;
}
async function prepareDiffInformedAnalysis(codeql, features, logger) {
let branches;
try {
const branches = await getDiffInformedAnalysisBranches(
codeql,
features,
logger
);
if (!branches) {
return { shouldRun: false, isAvailable: false };
}
const isAvailable = await withGroupAsync(
"Computing PR diff ranges",
async () => {
try {
return await computeAndPersistDiffRanges(branches, logger);
} catch (e) {
logger.warning(
`Failed to compute diff-informed analysis ranges: ${getErrorMessage(e)}`
);
return false;
}
}
);
return { shouldRun: true, isAvailable };
branches = await getDiffInformedAnalysisBranches(codeql, features, logger);
} catch (e) {
logger.warning(
`Failed to determine diff-informed analysis availability: ${getErrorMessage(e)}`
`Failed to determine branch information for diff-informed analysis: ${getErrorMessage(e)}`
);
return { shouldRun: true, isAvailable: false };
return { shouldRun: false, hasDiffRanges: false };
}
if (!branches) {
return { shouldRun: false, hasDiffRanges: false };
}
const hasDiffRanges = await withGroupAsync(
"Computing PR diff ranges",
async () => {
try {
return await computeAndPersistDiffRanges(branches, logger);
} catch (e) {
logger.warning(
`Failed to compute diff-informed analysis ranges: ${getErrorMessage(e)}`
);
return false;
}
}
);
return { shouldRun: true, hasDiffRanges };
}
function writeDiffRangesJsonFile(logger, ranges) {
const jsonContents = JSON.stringify(ranges, null, 2);
@@ -107287,13 +107284,13 @@ function hasQueryCustomisation(userConfig) {
return isDefined2(userConfig["disable-default-queries"]) || isDefined2(userConfig.queries) || isDefined2(userConfig["query-filters"]);
}
function applyIncrementalAnalysisSettings(config, diffInformedAnalysis, logger) {
if (config.overlayDatabaseMode === "overlay" /* Overlay */ && diffInformedAnalysis.shouldRun && !diffInformedAnalysis.isAvailable) {
logger.warning(
if (config.overlayDatabaseMode === "overlay" /* Overlay */ && diffInformedAnalysis.shouldRun && !diffInformedAnalysis.hasDiffRanges) {
logger.info(
`Diff-informed analysis is not available for this pull request. Reverting overlay database mode to ${"none" /* None */}.`
);
config.overlayDatabaseMode = "none" /* None */;
}
if (config.overlayDatabaseMode === "overlay" /* Overlay */ || diffInformedAnalysis.isAvailable) {
if (config.overlayDatabaseMode === "overlay" /* Overlay */ || diffInformedAnalysis.hasDiffRanges) {
config.extraQueryExclusions.push({
exclude: { tags: "exclude-from-incremental" }
});
+53 -65
View File
@@ -2201,79 +2201,67 @@ test.serial(
},
);
test.serial(
"applyIncrementalAnalysisSettings: no-op when mode is not Overlay and diff-informed is unavailable",
(t) => {
const config = createTestConfig({});
config.overlayDatabaseMode = OverlayDatabaseMode.None;
const logger = getRunnerLogger(true);
test("applyIncrementalAnalysisSettings: no-op when mode is not Overlay and diff-informed is unavailable", (t) => {
const config = createTestConfig({});
config.overlayDatabaseMode = OverlayDatabaseMode.None;
const logger = getRunnerLogger(true);
configUtils.applyIncrementalAnalysisSettings(
config,
{ shouldRun: false, isAvailable: false },
logger,
);
configUtils.applyIncrementalAnalysisSettings(
config,
{ shouldRun: false, hasDiffRanges: false },
logger,
);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
t.deepEqual(config.extraQueryExclusions, []);
},
);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
t.deepEqual(config.extraQueryExclusions, []);
});
test.serial(
"applyIncrementalAnalysisSettings: keeps overlay mode and adds exclusions when diff-informed analysis is disabled",
(t) => {
const config = createTestConfig({});
config.overlayDatabaseMode = OverlayDatabaseMode.Overlay;
const logger = getRunnerLogger(true);
test("applyIncrementalAnalysisSettings: keeps overlay mode and adds exclusions when diff-informed analysis shouldn't run", (t) => {
const config = createTestConfig({});
config.overlayDatabaseMode = OverlayDatabaseMode.Overlay;
const logger = getRunnerLogger(true);
configUtils.applyIncrementalAnalysisSettings(
config,
{ shouldRun: false, isAvailable: false },
logger,
);
configUtils.applyIncrementalAnalysisSettings(
config,
{ shouldRun: false, hasDiffRanges: false },
logger,
);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.Overlay);
t.deepEqual(config.extraQueryExclusions, [
{ exclude: { tags: "exclude-from-incremental" } },
]);
},
);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.Overlay);
t.deepEqual(config.extraQueryExclusions, [
{ exclude: { tags: "exclude-from-incremental" } },
]);
});
test.serial(
"applyIncrementalAnalysisSettings: reverts to None without exclusions when diff-informed analysis is unavailable",
(t) => {
const config = createTestConfig({});
config.overlayDatabaseMode =
OverlayDatabaseMode.Overlay as OverlayDatabaseMode;
const logger = getRunnerLogger(true);
test("applyIncrementalAnalysisSettings: disables overlay analysis when diff-informed analysis is unavailable", (t) => {
const config = createTestConfig({
overlayDatabaseMode: OverlayDatabaseMode.Overlay,
});
const logger = getRunnerLogger(true);
configUtils.applyIncrementalAnalysisSettings(
config,
{ shouldRun: true, isAvailable: false },
logger,
);
configUtils.applyIncrementalAnalysisSettings(
config,
{ shouldRun: true, hasDiffRanges: false },
logger,
);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
t.deepEqual(config.extraQueryExclusions, []);
},
);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
t.deepEqual(config.extraQueryExclusions, []);
});
test.serial(
"applyIncrementalAnalysisSettings: adds exclusions for diff-informed-only runs",
(t) => {
const config = createTestConfig({});
config.overlayDatabaseMode = OverlayDatabaseMode.None;
const logger = getRunnerLogger(true);
test("applyIncrementalAnalysisSettings: adds exclusions for diff-informed-only runs", (t) => {
const config = createTestConfig({});
config.overlayDatabaseMode = OverlayDatabaseMode.None;
const logger = getRunnerLogger(true);
configUtils.applyIncrementalAnalysisSettings(
config,
{ shouldRun: true, isAvailable: true },
logger,
);
configUtils.applyIncrementalAnalysisSettings(
config,
{ shouldRun: true, hasDiffRanges: true },
logger,
);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
t.deepEqual(config.extraQueryExclusions, [
{ exclude: { tags: "exclude-from-incremental" } },
]);
},
);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
t.deepEqual(config.extraQueryExclusions, [
{ exclude: { tags: "exclude-from-incremental" } },
]);
});
+11 -5
View File
@@ -1085,8 +1085,14 @@ function hasQueryCustomisation(userConfig: UserConfig): boolean {
* If overlay mode was selected for a PR but diff-informed analysis should have
* run and could not be prepared, fall back to a full non-overlay analysis.
* Query exclusions for incremental-only queries are applied only when the final
* configuration still uses overlay analysis or diff-informed analysis is
* actually available.
* configuration still uses overlay analysis or the diff ranges are available.
*
* Note that `overlayDatabaseMode === Overlay` does not imply
* `diffInformedAnalysis.shouldRun`. Overlay mode is selected based on language
* and feature-flag state and can apply outside of pull-request contexts (e.g.
* on branch pushes that build up the overlay cache), whereas diff-informed
* analysis only runs for pull requests where we can compute a diff. Each
* combination is therefore handled explicitly.
*/
export function applyIncrementalAnalysisSettings(
config: Config,
@@ -1096,9 +1102,9 @@ export function applyIncrementalAnalysisSettings(
if (
config.overlayDatabaseMode === OverlayDatabaseMode.Overlay &&
diffInformedAnalysis.shouldRun &&
!diffInformedAnalysis.isAvailable
!diffInformedAnalysis.hasDiffRanges
) {
logger.warning(
logger.info(
"Diff-informed analysis is not available for this pull request. " +
`Reverting overlay database mode to ${OverlayDatabaseMode.None}.`,
);
@@ -1107,7 +1113,7 @@ export function applyIncrementalAnalysisSettings(
if (
config.overlayDatabaseMode === OverlayDatabaseMode.Overlay ||
diffInformedAnalysis.isAvailable
diffInformedAnalysis.hasDiffRanges
) {
config.extraQueryExclusions.push({
exclude: { tags: "exclude-from-incremental" },
+132 -1
View File
@@ -6,13 +6,15 @@ import type { PullRequestBranches } from "./actions-util";
import * as apiClient from "./api-client";
import {
getDiffInformedAnalysisBranches,
prepareDiffInformedAnalysis,
exportedForTesting,
} from "./diff-informed-analysis-utils";
import { Feature, initFeatures } from "./feature-flags";
import { Feature, FeatureEnablement, initFeatures } from "./feature-flags";
import { getRunnerLogger } from "./logging";
import { parseRepositoryNwo } from "./repository";
import {
setupTests,
createFeatures,
mockCodeQLVersion,
mockFeatureFlagApiEndpoint,
setupActionsVars,
@@ -187,6 +189,135 @@ test.serial(
false,
);
test.serial(
"prepareDiffInformedAnalysis: returns shouldRun=false when not a pull request",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const logger = getRunnerLogger(true);
const codeql = mockCodeQLVersion("2.21.0");
const features = createFeatures([Feature.DiffInformedQueries]);
sinon.stub(actionsUtil, "getPullRequestBranches").returns(undefined);
sinon
.stub(apiClient, "getGitHubVersion")
.resolves({ type: GitHubVariant.DOTCOM });
const result = await prepareDiffInformedAnalysis(
codeql,
features,
logger,
);
t.deepEqual(result, { shouldRun: false, hasDiffRanges: false });
});
},
);
test.serial(
"prepareDiffInformedAnalysis: returns shouldRun=false when applicability check throws",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const logger = getRunnerLogger(true);
const codeql = mockCodeQLVersion("2.21.0");
// A features implementation whose getValue rejects, simulating an
// unexpected failure when determining whether diff-informed analysis
// should run.
const features: FeatureEnablement = {
getDefaultCliVersion: async () => {
throw new Error("not implemented");
},
getValue: async () => {
throw new Error("feature flag lookup failed");
},
};
const result = await prepareDiffInformedAnalysis(
codeql,
features,
logger,
);
t.deepEqual(result, { shouldRun: false, hasDiffRanges: false });
});
},
);
test.serial(
"prepareDiffInformedAnalysis: returns hasDiffRanges=true when the diff is fetched successfully",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const logger = getRunnerLogger(true);
const codeql = mockCodeQLVersion("2.21.0");
const features = createFeatures([Feature.DiffInformedQueries]);
sinon
.stub(actionsUtil, "getPullRequestBranches")
.returns({ base: "main", head: "feature" });
sinon
.stub(apiClient, "getGitHubVersion")
.resolves({ type: GitHubVariant.DOTCOM });
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
sinon.stub(apiClient, "getApiClient").returns({
rest: {
repos: {
compareCommitsWithBasehead: sinon
.stub()
.resolves({ data: { files: [] } }),
},
},
} as any);
const result = await prepareDiffInformedAnalysis(
codeql,
features,
logger,
);
t.deepEqual(result, { shouldRun: true, hasDiffRanges: true });
});
},
);
test.serial(
"prepareDiffInformedAnalysis: returns hasDiffRanges=false when the diff API call fails",
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const logger = getRunnerLogger(true);
const codeql = mockCodeQLVersion("2.21.0");
const features = createFeatures([Feature.DiffInformedQueries]);
sinon
.stub(actionsUtil, "getPullRequestBranches")
.returns({ base: "main", head: "feature" });
sinon
.stub(apiClient, "getGitHubVersion")
.resolves({ type: GitHubVariant.DOTCOM });
const notFoundError: any = new Error("Not Found");
notFoundError.status = 404;
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
sinon.stub(apiClient, "getApiClient").returns({
rest: {
repos: {
compareCommitsWithBasehead: sinon.stub().rejects(notFoundError),
},
},
} as any);
const result = await prepareDiffInformedAnalysis(
codeql,
features,
logger,
);
t.deepEqual(result, { shouldRun: true, hasDiffRanges: false });
});
},
);
function runGetDiffRanges(changes: number, patch: string[] | undefined): any {
return exportedForTesting.getDiffRanges(
{
+30 -29
View File
@@ -61,53 +61,54 @@ export interface DiffInformedAnalysisPreparation {
*/
shouldRun: boolean;
/**
* Whether the diff ranges were successfully prepared and can be used.
* Whether the diff ranges were successfully computed and persisted, and are
* therefore available for use.
*/
isAvailable: boolean;
hasDiffRanges: boolean;
}
/**
* Prepares the diff ranges needed for diff-informed analysis for the current
* run.
*
* @returns Whether diff-informed analysis applies to this run, and whether it
* was successfully prepared for use.
* @returns Whether diff-informed analysis applies to this run, and whether the
* diff ranges were successfully computed and persisted.
*/
export async function prepareDiffInformedAnalysis(
codeql: CodeQL,
features: FeatureEnablement,
logger: Logger,
): Promise<DiffInformedAnalysisPreparation> {
let branches: PullRequestBranches | undefined;
try {
const branches = await getDiffInformedAnalysisBranches(
codeql,
features,
logger,
);
if (!branches) {
return { shouldRun: false, isAvailable: false };
}
const isAvailable = await withGroupAsync(
"Computing PR diff ranges",
async () => {
try {
return await computeAndPersistDiffRanges(branches, logger);
} catch (e) {
logger.warning(
`Failed to compute diff-informed analysis ranges: ${getErrorMessage(e)}`,
);
return false;
}
},
);
return { shouldRun: true, isAvailable };
branches = await getDiffInformedAnalysisBranches(codeql, features, logger);
} catch (e) {
// If we cannot determine whether diff-informed analysis applies (for
// example, because a feature-flag lookup failed), treat it as not
// applicable rather than triggering the overlay fallback.
logger.warning(
`Failed to determine diff-informed analysis availability: ${getErrorMessage(e)}`,
`Failed to determine branch information for diff-informed analysis: ${getErrorMessage(e)}`,
);
return { shouldRun: true, isAvailable: false };
return { shouldRun: false, hasDiffRanges: false };
}
if (!branches) {
return { shouldRun: false, hasDiffRanges: false };
}
const hasDiffRanges = await withGroupAsync(
"Computing PR diff ranges",
async () => {
try {
return await computeAndPersistDiffRanges(branches, logger);
} catch (e) {
logger.warning(
`Failed to compute diff-informed analysis ranges: ${getErrorMessage(e)}`,
);
return false;
}
},
);
return { shouldRun: true, hasDiffRanges };
}
export interface DiffThunkRange {