Merge pull request #3562 from github/henrymercer/skip-file-coverage-rollout

Prepare for rolling out skipping computing file coverage information on PRs
This commit is contained in:
Henry Mercer
2026-03-11 11:33:21 +00:00
committed by GitHub
20 changed files with 2153 additions and 1756 deletions

View File

@@ -4,6 +4,12 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
## [UNRELEASED]
- Upcoming change: Starting April 2026, the CodeQL Action will skip collecting file coverage information on pull requests to improve analysis performance. File coverage information will still be computed on non-PR analyses. Pull request analyses will log a warning about this upcoming change. [#3562](https://github.com/github/codeql-action/pull/3562)
To opt out of this change:
- **Repositories owned by an organization:** Create a custom repository property with the name `github-codeql-file-coverage-on-prs` and the type "True/false", then set this property to `true` in the repository's settings. For more information, see [Managing custom properties for repositories in your organization](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization). Alternatively, if you are using an advanced setup workflow, you can set the `CODEQL_ACTION_FILE_COVERAGE_ON_PRS` environment variable to `true` in your workflow.
- **User-owned repositories using default setup:** Switch to an advanced setup workflow and set the `CODEQL_ACTION_FILE_COVERAGE_ON_PRS` environment variable to `true` in your workflow.
- **User-owned repositories using advanced setup:** Set the `CODEQL_ACTION_FILE_COVERAGE_ON_PRS` environment variable to `true` in your workflow.
- Fixed [a bug](https://github.com/github/codeql-action/issues/3555) which caused the CodeQL Action to fail loading repository properties if a "Multi select" repository property was configured for the repository. [#3557](https://github.com/github/codeql-action/pull/3557)
- The CodeQL Action now loads [custom repository properties](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization) on GitHub Enterprise Server, enabling the customization of features such as `github-codeql-disable-overlay` that was previously only available on GitHub.com. [#3559](https://github.com/github/codeql-action/pull/3559)
- Fixed the retry mechanism for database uploads. Previously this would fail with the error "Response body object should not be disturbed or locked". [#3564](https://github.com/github/codeql-action/pull/3564)

View File

@@ -161731,6 +161731,7 @@ var semver2 = __toESM(require_semver2());
var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => {
RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay";
RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries";
RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs";
return RepositoryPropertyName2;
})(RepositoryPropertyName || {});
var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set(
@@ -162188,11 +162189,8 @@ var featureConfig = {
["skip_file_coverage_on_prs" /* SkipFileCoverageOnPrs */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_SKIP_FILE_COVERAGE_ON_PRS",
// For testing, this is not behind a CLI version check yet. However
// before rolling this out externally, we should set a minimum version here
// since current versions of the CodeQL CLI will log if baseline information
// cannot be found when interpreting results.
minimumVersion: void 0
minimumVersion: void 0,
toolsFeature: "suppressesMissingFileBaselineWarning" /* SuppressesMissingFileBaselineWarning */
},
["start_proxy_remove_unused_registries" /* StartProxyRemoveUnusedRegistries */]: {
defaultValue: false,

488
lib/analyze-action.js generated

File diff suppressed because it is too large Load Diff

View File

@@ -103784,6 +103784,7 @@ var semver2 = __toESM(require_semver2());
var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => {
RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay";
RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries";
RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs";
return RepositoryPropertyName2;
})(RepositoryPropertyName || {});
var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set(
@@ -104237,11 +104238,8 @@ var featureConfig = {
["skip_file_coverage_on_prs" /* SkipFileCoverageOnPrs */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_SKIP_FILE_COVERAGE_ON_PRS",
// For testing, this is not behind a CLI version check yet. However
// before rolling this out externally, we should set a minimum version here
// since current versions of the CodeQL CLI will log if baseline information
// cannot be found when interpreting results.
minimumVersion: void 0
minimumVersion: void 0,
toolsFeature: "suppressesMissingFileBaselineWarning" /* SuppressesMissingFileBaselineWarning */
},
["start_proxy_remove_unused_registries" /* StartProxyRemoveUnusedRegistries */]: {
defaultValue: false,

828
lib/init-action-post.js generated

File diff suppressed because it is too large Load Diff

657
lib/init-action.js generated

File diff suppressed because it is too large Load Diff

View File

@@ -103783,6 +103783,7 @@ var semver2 = __toESM(require_semver2());
var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => {
RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay";
RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries";
RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs";
return RepositoryPropertyName2;
})(RepositoryPropertyName || {});
var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set(
@@ -104228,11 +104229,8 @@ var featureConfig = {
["skip_file_coverage_on_prs" /* SkipFileCoverageOnPrs */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_SKIP_FILE_COVERAGE_ON_PRS",
// For testing, this is not behind a CLI version check yet. However
// before rolling this out externally, we should set a minimum version here
// since current versions of the CodeQL CLI will log if baseline information
// cannot be found when interpreting results.
minimumVersion: void 0
minimumVersion: void 0,
toolsFeature: "suppressesMissingFileBaselineWarning" /* SuppressesMissingFileBaselineWarning */
},
["start_proxy_remove_unused_registries" /* StartProxyRemoveUnusedRegistries */]: {
defaultValue: false,

File diff suppressed because it is too large Load Diff

View File

@@ -161366,6 +161366,7 @@ var semver2 = __toESM(require_semver2());
var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => {
RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay";
RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries";
RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs";
return RepositoryPropertyName2;
})(RepositoryPropertyName || {});
var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set(
@@ -161594,11 +161595,8 @@ var featureConfig = {
["skip_file_coverage_on_prs" /* SkipFileCoverageOnPrs */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_SKIP_FILE_COVERAGE_ON_PRS",
// For testing, this is not behind a CLI version check yet. However
// before rolling this out externally, we should set a minimum version here
// since current versions of the CodeQL CLI will log if baseline information
// cannot be found when interpreting results.
minimumVersion: void 0
minimumVersion: void 0,
toolsFeature: "suppressesMissingFileBaselineWarning" /* SuppressesMissingFileBaselineWarning */
},
["start_proxy_remove_unused_registries" /* StartProxyRemoveUnusedRegistries */]: {
defaultValue: false,

View File

@@ -120917,11 +120917,8 @@ var featureConfig = {
["skip_file_coverage_on_prs" /* SkipFileCoverageOnPrs */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_SKIP_FILE_COVERAGE_ON_PRS",
// For testing, this is not behind a CLI version check yet. However
// before rolling this out externally, we should set a minimum version here
// since current versions of the CodeQL CLI will log if baseline information
// cannot be found when interpreting results.
minimumVersion: void 0
minimumVersion: void 0,
toolsFeature: "suppressesMissingFileBaselineWarning" /* SuppressesMissingFileBaselineWarning */
},
["start_proxy_remove_unused_registries" /* StartProxyRemoveUnusedRegistries */]: {
defaultValue: false,
@@ -121353,6 +121350,7 @@ var semver5 = __toESM(require_semver2());
var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => {
RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay";
RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries";
RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs";
return RepositoryPropertyName2;
})(RepositoryPropertyName || {});
var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set(

468
lib/upload-lib.js generated

File diff suppressed because it is too large Load Diff

View File

@@ -161516,6 +161516,7 @@ var semver2 = __toESM(require_semver2());
var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => {
RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay";
RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries";
RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs";
return RepositoryPropertyName2;
})(RepositoryPropertyName || {});
var KNOWN_REPOSITORY_PROPERTY_NAMES = new Set(
@@ -161756,11 +161757,8 @@ var featureConfig = {
["skip_file_coverage_on_prs" /* SkipFileCoverageOnPrs */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_SKIP_FILE_COVERAGE_ON_PRS",
// For testing, this is not behind a CLI version check yet. However
// before rolling this out externally, we should set a minimum version here
// since current versions of the CodeQL CLI will log if baseline information
// cannot be found when interpreting results.
minimumVersion: void 0
minimumVersion: void 0,
toolsFeature: "suppressesMissingFileBaselineWarning" /* SuppressesMissingFileBaselineWarning */
},
["start_proxy_remove_unused_registries" /* StartProxyRemoveUnusedRegistries */]: {
defaultValue: false,

File diff suppressed because it is too large Load Diff

View File

@@ -47,6 +47,15 @@ export enum EnvVar {
/** Whether the init action has been run. */
INIT_ACTION_HAS_RUN = "CODEQL_ACTION_INIT_HAS_RUN",
/** Whether the deprecation warning for file coverage on PRs has been logged. */
DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION = "CODEQL_ACTION_DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION",
/**
* Set to `true` to opt out of the upcoming change that skips file coverage
* information on pull requests.
*/
FILE_COVERAGE_ON_PRS = "CODEQL_ACTION_FILE_COVERAGE_ON_PRS",
/** Whether the error for a deprecated version of the CodeQL Action was logged. */
LOG_VERSION_DEPRECATION = "CODEQL_ACTION_DID_LOG_VERSION_DEPRECATION",

View File

@@ -292,11 +292,8 @@ export const featureConfig = {
[Feature.SkipFileCoverageOnPrs]: {
defaultValue: false,
envVar: "CODEQL_ACTION_SKIP_FILE_COVERAGE_ON_PRS",
// For testing, this is not behind a CLI version check yet. However
// before rolling this out externally, we should set a minimum version here
// since current versions of the CodeQL CLI will log if baseline information
// cannot be found when interpreting results.
minimumVersion: undefined,
toolsFeature: ToolsFeature.SuppressesMissingFileBaselineWarning,
},
[Feature.StartProxyRemoveUnusedRegistries]: {
defaultValue: false,

View File

@@ -8,12 +8,14 @@ import { RepositoryNwo } from "../repository";
export enum RepositoryPropertyName {
DISABLE_OVERLAY = "github-codeql-disable-overlay",
EXTRA_QUERIES = "github-codeql-extra-queries",
FILE_COVERAGE_ON_PRS = "github-codeql-file-coverage-on-prs",
}
/** Parsed types of the known repository properties. */
export type AllRepositoryProperties = {
[RepositoryPropertyName.DISABLE_OVERLAY]: boolean;
[RepositoryPropertyName.EXTRA_QUERIES]: string;
[RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: boolean;
};
/** Parsed repository properties. */
@@ -23,6 +25,7 @@ export type RepositoryProperties = Partial<AllRepositoryProperties>;
export type RepositoryPropertyApiType = {
[RepositoryPropertyName.DISABLE_OVERLAY]: string;
[RepositoryPropertyName.EXTRA_QUERIES]: string;
[RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: string;
};
/** The type of functions which take the `value` from the API and try to convert it to the type we want. */
@@ -69,6 +72,7 @@ const repositoryPropertyParsers: {
} = {
[RepositoryPropertyName.DISABLE_OVERLAY]: booleanProperty,
[RepositoryPropertyName.EXTRA_QUERIES]: stringProperty,
[RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: booleanProperty,
};
/**

View File

@@ -48,6 +48,7 @@ import {
checkPacksForOverlayCompatibility,
cleanupDatabaseClusterDirectory,
getFileCoverageInformationEnabled,
logFileCoverageOnPrsDeprecationWarning,
initCodeQL,
initConfig,
runDatabaseInitCluster,
@@ -343,6 +344,14 @@ async function run(startedAt: Date) {
analysisKinds = await getAnalysisKinds(logger);
const debugMode = getOptionalInput("debug") === "true" || core.isDebug();
const repositoryProperties = repositoryPropertiesResult.orElse({});
const fileCoverageResult = await getFileCoverageInformationEnabled(
debugMode,
codeql,
features,
repositoryProperties,
);
config = await initConfig(features, {
analysisKinds,
languagesInput: getOptionalInput("languages"),
@@ -372,12 +381,8 @@ async function run(startedAt: Date) {
githubVersion: gitHubVersion,
apiDetails,
features,
repositoryProperties: repositoryPropertiesResult.orElse({}),
enableFileCoverageInformation: await getFileCoverageInformationEnabled(
debugMode,
repositoryNwo,
features,
),
repositoryProperties,
enableFileCoverageInformation: fileCoverageResult.enabled,
logger,
});
@@ -394,6 +399,21 @@ async function run(startedAt: Date) {
);
}
if (fileCoverageResult.enabledByRepositoryProperty) {
addNoLanguageDiagnostic(
config,
makeTelemetryDiagnostic(
"codeql-action/file-coverage-on-prs-enabled-by-repository-property",
"File coverage on PRs enabled by repository property",
{},
),
);
}
if (fileCoverageResult.showDeprecationWarning) {
logFileCoverageOnPrsDeprecationWarning(logger);
}
await checkInstallPython311(config.languages, codeql);
} catch (unwrappedError) {
const error = wrapError(unwrappedError);

View File

@@ -1,6 +1,8 @@
import * as fs from "fs";
import path from "path";
import * as core from "@actions/core";
import * as github from "@actions/github";
import test, { ExecutionContext } from "ava";
import * as sinon from "sinon";
@@ -11,9 +13,9 @@ import {
checkPacksForOverlayCompatibility,
cleanupDatabaseClusterDirectory,
getFileCoverageInformationEnabled,
logFileCoverageOnPrsDeprecationWarning,
} from "./init";
import { KnownLanguage } from "./languages";
import { parseRepositoryNwo } from "./repository";
import {
createFeatures,
LoggedMessage,
@@ -453,13 +455,15 @@ test(
);
test("file coverage information enabled when debugMode is true", async (t) => {
t.true(
await getFileCoverageInformationEnabled(
true, // debugMode
parseRepositoryNwo("github/codeql-action"),
createFeatures([Feature.SkipFileCoverageOnPrs]),
),
const result = await getFileCoverageInformationEnabled(
true, // debugMode
createStubCodeQL({}),
createFeatures([Feature.SkipFileCoverageOnPrs]),
{},
);
t.true(result.enabled);
t.false(result.enabledByRepositoryProperty);
t.false(result.showDeprecationWarning);
});
test.serial(
@@ -467,43 +471,69 @@ test.serial(
async (t) => {
sinon.stub(actionsUtil, "isAnalyzingPullRequest").returns(false);
t.true(
await getFileCoverageInformationEnabled(
false, // debugMode
parseRepositoryNwo("github/codeql-action"),
createFeatures([Feature.SkipFileCoverageOnPrs]),
),
const result = await getFileCoverageInformationEnabled(
false, // debugMode
createStubCodeQL({}),
createFeatures([Feature.SkipFileCoverageOnPrs]),
{},
);
t.true(result.enabled);
t.false(result.enabledByRepositoryProperty);
t.false(result.showDeprecationWarning);
},
);
test.serial(
"file coverage information enabled when owner is not 'github'",
"file coverage information enabled when feature flag is not enabled, with deprecation warning",
async (t) => {
sinon.stub(actionsUtil, "isAnalyzingPullRequest").returns(true);
t.true(
await getFileCoverageInformationEnabled(
false, // debugMode
parseRepositoryNwo("other-org/some-repo"),
createFeatures([Feature.SkipFileCoverageOnPrs]),
),
const result = await getFileCoverageInformationEnabled(
false, // debugMode
createStubCodeQL({}),
createFeatures([]),
{},
);
t.true(result.enabled);
t.false(result.enabledByRepositoryProperty);
t.true(result.showDeprecationWarning);
},
);
test.serial(
"file coverage information enabled when feature flag is not enabled",
"file coverage information enabled when repository property is set",
async (t) => {
sinon.stub(actionsUtil, "isAnalyzingPullRequest").returns(true);
t.true(
await getFileCoverageInformationEnabled(
false, // debugMode
parseRepositoryNwo("github/codeql-action"),
createFeatures([]),
),
const result = await getFileCoverageInformationEnabled(
false, // debugMode
createStubCodeQL({}),
createFeatures([Feature.SkipFileCoverageOnPrs]),
{
"github-codeql-file-coverage-on-prs": true,
},
);
t.true(result.enabled);
t.true(result.enabledByRepositoryProperty);
t.false(result.showDeprecationWarning);
},
);
test.serial(
"file coverage information enabled when env var opt-out is set",
async (t) => {
sinon.stub(actionsUtil, "isAnalyzingPullRequest").returns(true);
process.env["CODEQL_ACTION_FILE_COVERAGE_ON_PRS"] = "true";
const result = await getFileCoverageInformationEnabled(
false, // debugMode
createStubCodeQL({}),
createFeatures([Feature.SkipFileCoverageOnPrs]),
{},
);
t.true(result.enabled);
t.false(result.enabledByRepositoryProperty);
t.false(result.showDeprecationWarning);
},
);
@@ -512,12 +542,174 @@ test.serial(
async (t) => {
sinon.stub(actionsUtil, "isAnalyzingPullRequest").returns(true);
t.false(
await getFileCoverageInformationEnabled(
false, // debugMode
parseRepositoryNwo("github/codeql-action"),
createFeatures([Feature.SkipFileCoverageOnPrs]),
),
const result = await getFileCoverageInformationEnabled(
false, // debugMode
createStubCodeQL({}),
createFeatures([Feature.SkipFileCoverageOnPrs]),
{},
);
t.false(result.enabled);
t.false(result.enabledByRepositoryProperty);
t.false(result.showDeprecationWarning);
},
);
test.serial(
"file coverage deprecation warning for org-owned repo with default setup recommends repo property",
(t) => {
const exportVariableStub = sinon.stub(core, "exportVariable");
sinon.stub(actionsUtil, "isDefaultSetup").returns(true);
github.context.payload = {
repository: {
name: "test-repo",
owner: { login: "test-org", type: "Organization" },
},
};
const messages: LoggedMessage[] = [];
logFileCoverageOnPrsDeprecationWarning(getRecordingLogger(messages));
t.is(messages.length, 1);
t.is(messages[0].type, "warning");
t.is(
messages[0].message,
"Starting April 2026, the CodeQL Action will skip computing file coverage information on pull requests " +
"to improve analysis performance. File coverage information will still be computed on non-PR analyses.\n\n" +
"To opt out of this change, create a custom repository property " +
'with the name `github-codeql-file-coverage-on-prs` and the type "True/false", then set this property to ' +
"`true` in the repository's settings.",
);
t.true(exportVariableStub.calledOnce);
},
);
test.serial(
"file coverage deprecation warning for org-owned repo with advanced setup recommends env var and repo property",
(t) => {
const exportVariableStub = sinon.stub(core, "exportVariable");
sinon.stub(actionsUtil, "isDefaultSetup").returns(false);
github.context.payload = {
repository: {
name: "test-repo",
owner: { login: "test-org", type: "Organization" },
},
};
const messages: LoggedMessage[] = [];
logFileCoverageOnPrsDeprecationWarning(getRecordingLogger(messages));
t.is(messages.length, 1);
t.is(messages[0].type, "warning");
t.is(
messages[0].message,
"Starting April 2026, the CodeQL Action will skip computing file coverage information on pull requests " +
"to improve analysis performance. File coverage information will still be computed on non-PR analyses.\n\n" +
"To opt out of this change, set the `CODEQL_ACTION_FILE_COVERAGE_ON_PRS` environment variable to `true`. " +
"Alternatively, create a custom repository property " +
'with the name `github-codeql-file-coverage-on-prs` and the type "True/false", then set this property to ' +
"`true` in the repository's settings.",
);
t.true(exportVariableStub.calledOnce);
},
);
test.serial(
"file coverage deprecation warning for user-owned repo with default setup recommends advanced setup",
(t) => {
const exportVariableStub = sinon.stub(core, "exportVariable");
sinon.stub(actionsUtil, "isDefaultSetup").returns(true);
github.context.payload = {
repository: {
name: "test-repo",
owner: { login: "test-user", type: "User" },
},
};
const messages: LoggedMessage[] = [];
logFileCoverageOnPrsDeprecationWarning(getRecordingLogger(messages));
t.is(messages.length, 1);
t.is(messages[0].type, "warning");
t.is(
messages[0].message,
"Starting April 2026, the CodeQL Action will skip computing file coverage information on pull requests " +
"to improve analysis performance. File coverage information will still be computed on non-PR analyses.\n\n" +
"To opt out of this change, switch to an advanced setup workflow and " +
"set the `CODEQL_ACTION_FILE_COVERAGE_ON_PRS` environment variable to `true`.",
);
t.true(exportVariableStub.calledOnce);
},
);
test.serial(
"file coverage deprecation warning for user-owned repo with advanced setup recommends env var",
(t) => {
const exportVariableStub = sinon.stub(core, "exportVariable");
sinon.stub(actionsUtil, "isDefaultSetup").returns(false);
github.context.payload = {
repository: {
name: "test-repo",
owner: { login: "test-user", type: "User" },
},
};
const messages: LoggedMessage[] = [];
logFileCoverageOnPrsDeprecationWarning(getRecordingLogger(messages));
t.is(messages.length, 1);
t.is(messages[0].type, "warning");
t.is(
messages[0].message,
"Starting April 2026, the CodeQL Action will skip computing file coverage information on pull requests " +
"to improve analysis performance. File coverage information will still be computed on non-PR analyses.\n\n" +
"To opt out of this change, set the `CODEQL_ACTION_FILE_COVERAGE_ON_PRS` environment variable to `true`.",
);
t.true(exportVariableStub.calledOnce);
},
);
test.serial(
"file coverage deprecation warning for unknown owner type with default setup recommends advanced setup",
(t) => {
const exportVariableStub = sinon.stub(core, "exportVariable");
sinon.stub(actionsUtil, "isDefaultSetup").returns(true);
github.context.payload = { repository: undefined };
const messages: LoggedMessage[] = [];
logFileCoverageOnPrsDeprecationWarning(getRecordingLogger(messages));
t.is(messages.length, 1);
t.is(messages[0].type, "warning");
t.is(
messages[0].message,
"Starting April 2026, the CodeQL Action will skip computing file coverage information on pull requests " +
"to improve analysis performance. File coverage information will still be computed on non-PR analyses.\n\n" +
"To opt out of this change, switch to an advanced setup workflow and " +
"set the `CODEQL_ACTION_FILE_COVERAGE_ON_PRS` environment variable to `true`.",
);
t.true(exportVariableStub.calledOnce);
},
);
test.serial(
"file coverage deprecation warning for unknown owner type with advanced setup recommends env var",
(t) => {
const exportVariableStub = sinon.stub(core, "exportVariable");
sinon.stub(actionsUtil, "isDefaultSetup").returns(false);
github.context.payload = { repository: undefined };
const messages: LoggedMessage[] = [];
logFileCoverageOnPrsDeprecationWarning(getRecordingLogger(messages));
t.is(messages.length, 1);
t.is(messages[0].type, "warning");
t.is(
messages[0].message,
"Starting April 2026, the CodeQL Action will skip computing file coverage information on pull requests " +
"to improve analysis performance. File coverage information will still be computed on non-PR analyses.\n\n" +
"To opt out of this change, set the `CODEQL_ACTION_FILE_COVERAGE_ON_PRS` environment variable to `true`.",
);
t.true(exportVariableStub.calledOnce);
},
);
test.serial(
"logFileCoverageOnPrsDeprecationWarning does not log if already logged",
(t) => {
process.env["CODEQL_ACTION_DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION"] =
"true";
const exportVariableStub = sinon.stub(core, "exportVariable");
const messages: LoggedMessage[] = [];
logFileCoverageOnPrsDeprecationWarning(getRecordingLogger(messages));
t.is(messages.length, 0);
t.true(exportVariableStub.notCalled);
},
);

View File

@@ -1,26 +1,33 @@
import * as fs from "fs";
import * as path from "path";
import * as core from "@actions/core";
import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as github from "@actions/github";
import * as io from "@actions/io";
import * as yaml from "js-yaml";
import {
getOptionalInput,
isAnalyzingPullRequest,
isDefaultSetup,
isSelfHostedRunner,
} from "./actions-util";
import { GitHubApiDetails } from "./api-client";
import { CodeQL, setupCodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { EnvVar } from "./environment";
import {
CodeQLDefaultVersionInfo,
Feature,
FeatureEnablement,
} from "./feature-flags";
import {
RepositoryProperties,
RepositoryPropertyName,
} from "./feature-flags/properties";
import { KnownLanguage, Language } from "./languages";
import { Logger, withGroupAsync } from "./logging";
import { RepositoryNwo } from "./repository";
import { ToolsSource } from "./setup-codeql";
import { ZstdAvailability } from "./tar";
import { ToolsDownloadStatusReport } from "./tools-download";
@@ -300,18 +307,112 @@ export function cleanupDatabaseClusterDirectory(
export async function getFileCoverageInformationEnabled(
debugMode: boolean,
repositoryNwo: RepositoryNwo,
codeql: CodeQL,
features: FeatureEnablement,
): Promise<boolean> {
return (
// Always enable file coverage information in debug mode
debugMode ||
// We're most interested in speeding up PRs, and we want to keep
// submitting file coverage information for the default branch since
// it is used to populate the status page.
!isAnalyzingPullRequest() ||
// For now, restrict this feature to the GitHub org
repositoryNwo.owner !== "github" ||
!(await features.getValue(Feature.SkipFileCoverageOnPrs))
);
repositoryProperties: RepositoryProperties,
): Promise<{
enabled: boolean;
enabledByRepositoryProperty: boolean;
showDeprecationWarning: boolean;
}> {
// Always enable file coverage information in debug mode
if (debugMode) {
return {
enabled: true,
enabledByRepositoryProperty: false,
showDeprecationWarning: false,
};
}
// We're most interested in speeding up PRs, and we want to keep
// submitting file coverage information for the default branch since
// it is used to populate the status page.
if (!isAnalyzingPullRequest()) {
return {
enabled: true,
enabledByRepositoryProperty: false,
showDeprecationWarning: false,
};
}
// If the user has explicitly opted out via an environment variable, don't
// show the deprecation warning.
if (
(process.env[EnvVar.FILE_COVERAGE_ON_PRS] || "").toLocaleLowerCase() ===
"true"
) {
return {
enabled: true,
enabledByRepositoryProperty: false,
showDeprecationWarning: false,
};
}
// Allow repositories to opt in to file coverage information on PRs
// using a repository property. In this case, don't show the deprecation
// warning since the repository has explicitly opted in.
if (
repositoryProperties[RepositoryPropertyName.FILE_COVERAGE_ON_PRS] === true
) {
return {
enabled: true,
enabledByRepositoryProperty: true,
showDeprecationWarning: false,
};
}
// If the feature is disabled, then maintain the previous behavior of
// unconditionally computing file coverage information, but warn that
// file coverage on PRs will be disabled in a future release.
if (!(await features.getValue(Feature.SkipFileCoverageOnPrs, codeql))) {
return {
enabled: true,
enabledByRepositoryProperty: false,
showDeprecationWarning: true,
};
}
// Otherwise, disable file coverage information on PRs to speed up analysis.
return {
enabled: false,
enabledByRepositoryProperty: false,
showDeprecationWarning: false,
};
}
/**
* Log a warning about the deprecation of file coverage information on PRs, including how to opt
* back in via an environment variable or repository property.
*/
export function logFileCoverageOnPrsDeprecationWarning(logger: Logger): void {
if (process.env[EnvVar.DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION]) {
return;
}
const repositoryOwnerType: string | undefined =
github.context.payload.repository?.owner.type;
let message =
"Starting April 2026, the CodeQL Action will skip computing file coverage information on pull requests " +
"to improve analysis performance. File coverage information will still be computed on non-PR analyses.";
const envVarOptOut =
"set the `CODEQL_ACTION_FILE_COVERAGE_ON_PRS` environment variable to `true`.";
const repoPropertyOptOut =
"create a custom repository property with the name " +
'`github-codeql-file-coverage-on-prs` and the type "True/false", then set this property to ' +
"`true` in the repository's settings.";
if (repositoryOwnerType === "Organization") {
// Org-owned repo: can use the repository property
if (isDefaultSetup()) {
message += `\n\nTo opt out of this change, ${repoPropertyOptOut}`;
} else {
message += `\n\nTo opt out of this change, ${envVarOptOut} Alternatively, ${repoPropertyOptOut}`;
}
} else if (isDefaultSetup()) {
// User-owned repo on default setup: no repo property available and
// no way to set env vars, so need to switch to advanced setup.
message += `\n\nTo opt out of this change, switch to an advanced setup workflow and ${envVarOptOut}`;
} else {
// User-owned repo on advanced setup: can set the env var
message += `\n\nTo opt out of this change, ${envVarOptOut}`;
}
logger.warning(message);
core.exportVariable(EnvVar.DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION, "true");
}

View File

@@ -10,6 +10,7 @@ export enum ToolsFeature {
ForceOverwrite = "forceOverwrite",
IndirectTracingSupportsStaticBinaries = "indirectTracingSupportsStaticBinaries",
PythonDefaultIsToNotExtractStdlib = "pythonDefaultIsToNotExtractStdlib",
SuppressesMissingFileBaselineWarning = "suppressesMissingFileBaselineWarning",
}
/**