Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 59 additions & 36 deletions lib/init-action.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 50 additions & 7 deletions src/config-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ import {
addNoLanguageDiagnostic,
makeTelemetryDiagnostic,
} from "./diagnostics";
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
import {
computeAndPersistDiffRanges,
getDiffInformedAnalysisBranches,
} from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment";
import * as errorMessages from "./error-messages";
import { Feature, FeatureEnablement } from "./feature-flags";
Expand All @@ -49,8 +52,12 @@ import {
isAnalyzingDefaultBranch,
} from "./git-utils";
import { KnownLanguage, Language } from "./languages";
import { Logger } from "./logging";
import { CODEQL_OVERLAY_MINIMUM_VERSION, OverlayDatabaseMode } from "./overlay";
import { Logger, withGroupAsync } from "./logging";
import {
CODEQL_OVERLAY_MINIMUM_VERSION,
OverlayDatabaseMode,
revertOverlayModeIfDiffInformedUnavailable,
} from "./overlay";
import {
addOverlayDisablementDiagnostics,
OverlayDisabledReason,
Expand Down Expand Up @@ -1229,19 +1236,55 @@ export async function initConfig(
);
}

if (
config.overlayDatabaseMode === OverlayDatabaseMode.Overlay ||
(await shouldPerformDiffInformedAnalysis(
let shouldRunDiffInformedAnalysis = false;
let diffInformedRangesPrepared = false;
try {
const diffInformedAnalysisBranches = await getDiffInformedAnalysisBranches(
inputs.codeql,
inputs.features,
logger,
))
);
shouldRunDiffInformedAnalysis = diffInformedAnalysisBranches !== undefined;

if (diffInformedAnalysisBranches) {
await withGroupAsync("Computing PR diff ranges", async () => {
try {
diffInformedRangesPrepared = await computeAndPersistDiffRanges(
diffInformedAnalysisBranches,
logger,
);
} catch (e) {
logger.warning(
`Failed to compute diff-informed analysis ranges: ${getErrorMessage(e)}`,
);
}
});
}
} catch (e) {
logger.warning(
`Failed to determine diff-informed analysis availability: ${getErrorMessage(e)}`,
);
// Treat errors conservatively so overlay falls back if diff-informed
// availability could not be checked.
shouldRunDiffInformedAnalysis = true;
}

if (
config.overlayDatabaseMode === OverlayDatabaseMode.Overlay ||
shouldRunDiffInformedAnalysis
) {
config.extraQueryExclusions.push({
exclude: { tags: "exclude-from-incremental" },
});
}

revertOverlayModeIfDiffInformedUnavailable(
config,
shouldRunDiffInformedAnalysis,
diffInformedRangesPrepared,
logger,
);

if (await isTrapCachingEnabled(features, config.overlayDatabaseMode)) {
const { trapCaches, trapCacheDownloadTime } = await downloadCacheWithTime(
inputs.codeql,
Expand Down
27 changes: 27 additions & 0 deletions src/diff-informed-analysis-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,33 @@ export async function getPullRequestEditedDiffRanges(
return results;
}

/**
* Compute and persist the diff ranges for a pull request. This fetches the
* 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 branches The base and head branches of the pull request, as returned
* by `getDiffInformedAnalysisBranches`.
* @param logger
* @returns `true` if the diff ranges were successfully computed and persisted,
* otherwise `false`.
*/
export async function computeAndPersistDiffRanges(
branches: PullRequestBranches,
logger: Logger,
): Promise<boolean> {
const ranges = await getPullRequestEditedDiffRanges(branches, logger);
if (ranges === undefined) {
return false;
}
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).`,
);
return true;
}

async function getFileDiffsWithBasehead(
branches: PullRequestBranches,
logger: Logger,
Expand Down
44 changes: 1 addition & 43 deletions src/init-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ 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 {
Expand All @@ -59,7 +54,7 @@ import {
runDatabaseInitCluster,
} from "./init";
import { JavaEnvVars, KnownLanguage } from "./languages";
import { getActionsLogger, Logger, withGroupAsync } from "./logging";
import { getActionsLogger, Logger } from "./logging";
import {
downloadOverlayBaseDatabaseFromCache,
OverlayBaseDatabaseDownloadStats,
Expand Down Expand Up @@ -427,7 +422,6 @@ 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);
Expand Down Expand Up @@ -818,42 +812,6 @@ 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> {
await withGroupAsync("Computing PR diff ranges", async () => {
try {
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,
Expand Down
52 changes: 52 additions & 0 deletions src/overlay/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
getCacheRestoreKeyPrefix,
getCacheSaveKey,
OverlayDatabaseMode,
revertOverlayModeIfDiffInformedUnavailable,
writeBaseDatabaseOidsFile,
writeOverlayChangesFile,
} from ".";
Expand Down Expand Up @@ -602,3 +603,54 @@ test.serial("overlay-base database cache keys remain stable", async (t) => {
`Expected save key "${saveKey}" to start with restore key prefix "${restoreKeyPrefix}"`,
);
});

test.serial(
"revertOverlayModeIfDiffInformedUnavailable: no-op when mode is not Overlay",
(t) => {
const config = createTestConfig({});
config.overlayDatabaseMode = OverlayDatabaseMode.None;
const logger = getRunnerLogger(true);

revertOverlayModeIfDiffInformedUnavailable(config, true, false, logger);

t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
},
);

test.serial(
"revertOverlayModeIfDiffInformedUnavailable: no-op when diff-informed analysis is available",
(t) => {
const config = createTestConfig({});
config.overlayDatabaseMode = OverlayDatabaseMode.Overlay;
const logger = getRunnerLogger(true);

revertOverlayModeIfDiffInformedUnavailable(config, true, true, logger);

t.is(config.overlayDatabaseMode, OverlayDatabaseMode.Overlay);
},
);

test.serial(
"revertOverlayModeIfDiffInformedUnavailable: reverts to None when diff-informed analysis is unavailable",
(t) => {
const config = createTestConfig({});
config.overlayDatabaseMode =
OverlayDatabaseMode.Overlay as OverlayDatabaseMode;
const logger = getRunnerLogger(true);

revertOverlayModeIfDiffInformedUnavailable(config, true, false, logger);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
},
);

test.serial(
"revertOverlayModeIfDiffInformedUnavailable: no-op when diff-informed analysis is disabled",
(t) => {
const config = createTestConfig({});
config.overlayDatabaseMode = OverlayDatabaseMode.Overlay;
const logger = getRunnerLogger(true);

revertOverlayModeIfDiffInformedUnavailable(config, false, false, logger);
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.Overlay);
},
);
Loading
Loading