mirror of
https://github.com/github/codeql-action.git
synced 2026-04-30 19:00:21 +00:00
refactor: address review feedback on overlay fallback
This commit is contained in:
Generated
+24
-27
@@ -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
@@ -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
@@ -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" },
|
||||
|
||||
@@ -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(
|
||||
{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user