mirror of
https://github.com/github/codeql-action.git
synced 2026-10-29 17:17:15 +00:00
Merge pull request #2967 from github/cklin/overlay-feature-flags
Overlay: additional feature flags
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
||||
} from "./actions-util";
|
||||
import * as api from "./api-client";
|
||||
import { CliError, wrapCliConfigurationError } from "./cli-errors";
|
||||
import { type Config } from "./config-utils";
|
||||
import { generateCodeScanningConfig, type Config } from "./config-utils";
|
||||
import { DocUrl } from "./doc-url";
|
||||
import { EnvVar } from "./environment";
|
||||
import {
|
||||
@@ -35,7 +35,7 @@ import { ToolsDownloadStatusReport } from "./tools-download";
|
||||
import { ToolsFeature, isSupportedToolsFeature } from "./tools-features";
|
||||
import { shouldEnableIndirectTracing } from "./tracer-config";
|
||||
import * as util from "./util";
|
||||
import { BuildMode, cloneObject, getErrorMessage } from "./util";
|
||||
import { BuildMode, getErrorMessage } from "./util";
|
||||
|
||||
type Options = Array<string | number | boolean>;
|
||||
|
||||
@@ -574,7 +574,7 @@ export async function getCodeQLForCmd(
|
||||
extraArgs.push(`--trace-process-name=${processName}`);
|
||||
}
|
||||
|
||||
const codeScanningConfigFile = await generateCodeScanningConfig(
|
||||
const codeScanningConfigFile = await writeCodeScanningConfigFile(
|
||||
config,
|
||||
logger,
|
||||
);
|
||||
@@ -1217,66 +1217,15 @@ async function runCli(
|
||||
* @param config The configuration to use.
|
||||
* @returns the path to the generated user configuration file.
|
||||
*/
|
||||
async function generateCodeScanningConfig(
|
||||
async function writeCodeScanningConfigFile(
|
||||
config: Config,
|
||||
logger: Logger,
|
||||
): Promise<string> {
|
||||
const codeScanningConfigFile = getGeneratedCodeScanningConfigPath(config);
|
||||
|
||||
// make a copy so we can modify it
|
||||
const augmentedConfig = cloneObject(config.originalUserInput);
|
||||
|
||||
// Inject the queries from the input
|
||||
if (config.augmentationProperties.queriesInput) {
|
||||
if (config.augmentationProperties.queriesInputCombines) {
|
||||
augmentedConfig.queries = (augmentedConfig.queries || []).concat(
|
||||
config.augmentationProperties.queriesInput,
|
||||
);
|
||||
} else {
|
||||
augmentedConfig.queries = config.augmentationProperties.queriesInput;
|
||||
}
|
||||
}
|
||||
if (augmentedConfig.queries?.length === 0) {
|
||||
delete augmentedConfig.queries;
|
||||
}
|
||||
|
||||
// Inject the packs from the input
|
||||
if (config.augmentationProperties.packsInput) {
|
||||
if (config.augmentationProperties.packsInputCombines) {
|
||||
// At this point, we already know that this is a single-language analysis
|
||||
if (Array.isArray(augmentedConfig.packs)) {
|
||||
augmentedConfig.packs = (augmentedConfig.packs || []).concat(
|
||||
config.augmentationProperties.packsInput,
|
||||
);
|
||||
} else if (!augmentedConfig.packs) {
|
||||
augmentedConfig.packs = config.augmentationProperties.packsInput;
|
||||
} else {
|
||||
// At this point, we know there is only one language.
|
||||
// If there were more than one language, an error would already have been thrown.
|
||||
const language = Object.keys(augmentedConfig.packs)[0];
|
||||
augmentedConfig.packs[language] = augmentedConfig.packs[
|
||||
language
|
||||
].concat(config.augmentationProperties.packsInput);
|
||||
}
|
||||
} else {
|
||||
augmentedConfig.packs = config.augmentationProperties.packsInput;
|
||||
}
|
||||
}
|
||||
if (Array.isArray(augmentedConfig.packs) && !augmentedConfig.packs.length) {
|
||||
delete augmentedConfig.packs;
|
||||
}
|
||||
|
||||
augmentedConfig["query-filters"] = [
|
||||
// Ordering matters. If the first filter is an inclusion, it implicitly
|
||||
// excludes all queries that are not included. If it is an exclusion,
|
||||
// it implicitly includes all queries that are not excluded. So user
|
||||
// filters (if any) should always be first to preserve intent.
|
||||
...(augmentedConfig["query-filters"] || []),
|
||||
...(config.augmentationProperties.extraQueryExclusions || []),
|
||||
];
|
||||
if (augmentedConfig["query-filters"]?.length === 0) {
|
||||
delete augmentedConfig["query-filters"];
|
||||
}
|
||||
const augmentedConfig = generateCodeScanningConfig(
|
||||
config.originalUserInput,
|
||||
config.augmentationProperties,
|
||||
);
|
||||
|
||||
logger.info(
|
||||
`Writing augmented user configuration file to ${codeScanningConfigFile}`,
|
||||
|
||||
@@ -818,16 +818,10 @@ const calculateAugmentationMacro = test.macro({
|
||||
) => {
|
||||
const actualAugmentationProperties =
|
||||
await configUtils.calculateAugmentation(
|
||||
getCachedCodeQL(),
|
||||
{ owner: "github", repo: "repo" },
|
||||
createFeatures([]),
|
||||
rawPacksInput,
|
||||
rawQueriesInput,
|
||||
rawQualityQueriesInput,
|
||||
languages,
|
||||
"", // sourceRoot
|
||||
undefined, // buildMode
|
||||
mockLogger,
|
||||
);
|
||||
t.deepEqual(actualAugmentationProperties, expectedAugmentationProperties);
|
||||
},
|
||||
@@ -950,16 +944,10 @@ const calculateAugmentationErrorMacro = test.macro({
|
||||
await t.throwsAsync(
|
||||
() =>
|
||||
configUtils.calculateAugmentation(
|
||||
getCachedCodeQL(),
|
||||
{ owner: "github", repo: "repo" },
|
||||
createFeatures([]),
|
||||
rawPacksInput,
|
||||
rawQueriesInput,
|
||||
rawQualityQueriesInput,
|
||||
languages,
|
||||
"", // sourceRoot
|
||||
undefined, // buildMode
|
||||
mockLogger,
|
||||
),
|
||||
{ message: expectedError },
|
||||
);
|
||||
@@ -1206,7 +1194,7 @@ for (const { displayName, language, feature } of [
|
||||
|
||||
interface OverlayDatabaseModeTestSetup {
|
||||
overlayDatabaseEnvVar: string | undefined;
|
||||
isFeatureEnabled: boolean;
|
||||
features: Feature[];
|
||||
isPullRequest: boolean;
|
||||
isDefaultBranch: boolean;
|
||||
repositoryOwner: string;
|
||||
@@ -1214,11 +1202,12 @@ interface OverlayDatabaseModeTestSetup {
|
||||
languages: Language[];
|
||||
codeqlVersion: string;
|
||||
gitRoot: string | undefined;
|
||||
codeScanningConfig: configUtils.UserConfig;
|
||||
}
|
||||
|
||||
const defaultOverlayDatabaseModeTestSetup: OverlayDatabaseModeTestSetup = {
|
||||
overlayDatabaseEnvVar: undefined,
|
||||
isFeatureEnabled: false,
|
||||
features: [],
|
||||
isPullRequest: false,
|
||||
isDefaultBranch: false,
|
||||
repositoryOwner: "github",
|
||||
@@ -1226,6 +1215,7 @@ const defaultOverlayDatabaseModeTestSetup: OverlayDatabaseModeTestSetup = {
|
||||
languages: [Language.javascript],
|
||||
codeqlVersion: "2.21.0",
|
||||
gitRoot: "/some/git/root",
|
||||
codeScanningConfig: {},
|
||||
};
|
||||
|
||||
const getOverlayDatabaseModeMacro = test.macro({
|
||||
@@ -1259,9 +1249,7 @@ const getOverlayDatabaseModeMacro = test.macro({
|
||||
}
|
||||
|
||||
// Mock feature flags
|
||||
const features = createFeatures(
|
||||
setup.isFeatureEnabled ? [Feature.OverlayAnalysis] : [],
|
||||
);
|
||||
const features = createFeatures(setup.features);
|
||||
|
||||
// Mock isAnalyzingPullRequest function
|
||||
sinon
|
||||
@@ -1294,6 +1282,7 @@ const getOverlayDatabaseModeMacro = test.macro({
|
||||
setup.languages,
|
||||
tempDir, // sourceRoot
|
||||
setup.buildMode,
|
||||
setup.codeScanningConfig,
|
||||
logger,
|
||||
);
|
||||
|
||||
@@ -1359,7 +1348,8 @@ test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"Ignore feature flag when analyzing non-default branch",
|
||||
{
|
||||
isFeatureEnabled: true,
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
@@ -1371,7 +1361,8 @@ test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"Overlay-base database on default branch when feature enabled",
|
||||
{
|
||||
isFeatureEnabled: true,
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
|
||||
isDefaultBranch: true,
|
||||
},
|
||||
{
|
||||
@@ -1382,8 +1373,152 @@ test(
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay-base database on default branch when feature disabled",
|
||||
"Overlay-base database on default branch when feature enabled with custom analysis",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
|
||||
codeScanningConfig: {
|
||||
packs: ["some-custom-pack@1.0.0"],
|
||||
} as configUtils.UserConfig,
|
||||
isDefaultBranch: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.OverlayBase,
|
||||
useOverlayDatabaseCaching: true,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"Overlay-base database on default branch when code-scanning feature enabled",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [
|
||||
Feature.OverlayAnalysis,
|
||||
Feature.OverlayAnalysisCodeScanningJavascript,
|
||||
],
|
||||
isDefaultBranch: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.OverlayBase,
|
||||
useOverlayDatabaseCaching: true,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay-base database on default branch when code-scanning feature enabled with disable-default-queries",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [
|
||||
Feature.OverlayAnalysis,
|
||||
Feature.OverlayAnalysisCodeScanningJavascript,
|
||||
],
|
||||
codeScanningConfig: {
|
||||
"disable-default-queries": true,
|
||||
} as configUtils.UserConfig,
|
||||
isDefaultBranch: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay-base database on default branch when code-scanning feature enabled with packs",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [
|
||||
Feature.OverlayAnalysis,
|
||||
Feature.OverlayAnalysisCodeScanningJavascript,
|
||||
],
|
||||
codeScanningConfig: {
|
||||
packs: ["some-custom-pack@1.0.0"],
|
||||
} as configUtils.UserConfig,
|
||||
isDefaultBranch: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay-base database on default branch when code-scanning feature enabled with queries",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [
|
||||
Feature.OverlayAnalysis,
|
||||
Feature.OverlayAnalysisCodeScanningJavascript,
|
||||
],
|
||||
codeScanningConfig: {
|
||||
queries: [{ uses: "some-query.ql" }],
|
||||
} as configUtils.UserConfig,
|
||||
isDefaultBranch: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay-base database on default branch when code-scanning feature enabled with query-filters",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [
|
||||
Feature.OverlayAnalysis,
|
||||
Feature.OverlayAnalysisCodeScanningJavascript,
|
||||
],
|
||||
codeScanningConfig: {
|
||||
"query-filters": [{ include: { "security-severity": "high" } }],
|
||||
} as configUtils.UserConfig,
|
||||
isDefaultBranch: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay-base database on default branch when only language-specific feature enabled",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysisJavascript],
|
||||
isDefaultBranch: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay-base database on default branch when only code-scanning feature enabled",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysisCodeScanningJavascript],
|
||||
isDefaultBranch: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay-base database on default branch when language-specific feature disabled",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysis],
|
||||
isDefaultBranch: true,
|
||||
},
|
||||
{
|
||||
@@ -1396,7 +1531,8 @@ test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"Overlay analysis on PR when feature enabled",
|
||||
{
|
||||
isFeatureEnabled: true,
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
|
||||
isPullRequest: true,
|
||||
},
|
||||
{
|
||||
@@ -1407,8 +1543,152 @@ test(
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay analysis on PR when feature disabled",
|
||||
"Overlay analysis on PR when feature enabled with custom analysis",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
|
||||
codeScanningConfig: {
|
||||
packs: ["some-custom-pack@1.0.0"],
|
||||
} as configUtils.UserConfig,
|
||||
isPullRequest: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.Overlay,
|
||||
useOverlayDatabaseCaching: true,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"Overlay analysis on PR when code-scanning feature enabled",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [
|
||||
Feature.OverlayAnalysis,
|
||||
Feature.OverlayAnalysisCodeScanningJavascript,
|
||||
],
|
||||
isPullRequest: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.Overlay,
|
||||
useOverlayDatabaseCaching: true,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay analysis on PR when code-scanning feature enabled with disable-default-queries",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [
|
||||
Feature.OverlayAnalysis,
|
||||
Feature.OverlayAnalysisCodeScanningJavascript,
|
||||
],
|
||||
codeScanningConfig: {
|
||||
"disable-default-queries": true,
|
||||
} as configUtils.UserConfig,
|
||||
isPullRequest: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay analysis on PR when code-scanning feature enabled with packs",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [
|
||||
Feature.OverlayAnalysis,
|
||||
Feature.OverlayAnalysisCodeScanningJavascript,
|
||||
],
|
||||
codeScanningConfig: {
|
||||
packs: ["some-custom-pack@1.0.0"],
|
||||
} as configUtils.UserConfig,
|
||||
isPullRequest: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay analysis on PR when code-scanning feature enabled with queries",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [
|
||||
Feature.OverlayAnalysis,
|
||||
Feature.OverlayAnalysisCodeScanningJavascript,
|
||||
],
|
||||
codeScanningConfig: {
|
||||
queries: [{ uses: "some-query.ql" }],
|
||||
} as configUtils.UserConfig,
|
||||
isPullRequest: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay analysis on PR when code-scanning feature enabled with query-filters",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [
|
||||
Feature.OverlayAnalysis,
|
||||
Feature.OverlayAnalysisCodeScanningJavascript,
|
||||
],
|
||||
codeScanningConfig: {
|
||||
"query-filters": [{ include: { "security-severity": "high" } }],
|
||||
} as configUtils.UserConfig,
|
||||
isPullRequest: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay analysis on PR when only language-specific feature enabled",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysisJavascript],
|
||||
isPullRequest: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay analysis on PR when only code-scanning feature enabled",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysisCodeScanningJavascript],
|
||||
isPullRequest: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay analysis on PR when language-specific feature disabled",
|
||||
{
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysis],
|
||||
isPullRequest: true,
|
||||
},
|
||||
{
|
||||
@@ -1447,7 +1727,8 @@ test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"Overlay PR analysis by feature flag for dsp-testing",
|
||||
{
|
||||
isFeatureEnabled: true,
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
|
||||
isPullRequest: true,
|
||||
repositoryOwner: "dsp-testing",
|
||||
},
|
||||
@@ -1461,7 +1742,8 @@ test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
"No overlay PR analysis by feature flag for other-org",
|
||||
{
|
||||
isFeatureEnabled: true,
|
||||
languages: [Language.javascript],
|
||||
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
|
||||
isPullRequest: true,
|
||||
repositoryOwner: "other-org",
|
||||
},
|
||||
@@ -1524,3 +1806,20 @@ test(
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
|
||||
// Exercise language-specific overlay analysis features code paths
|
||||
for (const language in Language) {
|
||||
test(
|
||||
getOverlayDatabaseModeMacro,
|
||||
`Check default overlay analysis feature for ${language}`,
|
||||
{
|
||||
languages: [language as Language],
|
||||
features: [Feature.OverlayAnalysis],
|
||||
isPullRequest: true,
|
||||
},
|
||||
{
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as semver from "semver";
|
||||
import { isAnalyzingPullRequest } from "./actions-util";
|
||||
import * as api from "./api-client";
|
||||
import { CachingKind, getCachingKind } from "./caching-utils";
|
||||
import { CodeQL } from "./codeql";
|
||||
import { type CodeQL } from "./codeql";
|
||||
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { getGitRoot, isAnalyzingDefaultBranch } from "./git-utils";
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
ConfigurationError,
|
||||
BuildMode,
|
||||
codeQlVersionAtLeast,
|
||||
cloneObject,
|
||||
} from "./util";
|
||||
|
||||
// Property names from the user-supplied config file.
|
||||
@@ -195,7 +196,7 @@ export interface AugmentationProperties {
|
||||
/**
|
||||
* Extra query exclusions to append to the config.
|
||||
*/
|
||||
extraQueryExclusions?: ExcludeQueryFilter[];
|
||||
extraQueryExclusions: ExcludeQueryFilter[];
|
||||
|
||||
/**
|
||||
* The overlay database mode to use.
|
||||
@@ -459,17 +460,8 @@ export interface InitConfigInputs {
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
type GetDefaultConfigInputs = Omit<
|
||||
InitConfigInputs,
|
||||
"configFile" | "configInput"
|
||||
>;
|
||||
|
||||
type LoadConfigInputs = Omit<InitConfigInputs, "configInput"> & {
|
||||
configFile: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the default config for when the user has not supplied one.
|
||||
* Get the default config, populated without user configuration file.
|
||||
*/
|
||||
export async function getDefaultConfig({
|
||||
languagesInput,
|
||||
@@ -486,11 +478,10 @@ export async function getDefaultConfig({
|
||||
repository,
|
||||
tempDir,
|
||||
codeql,
|
||||
sourceRoot,
|
||||
githubVersion,
|
||||
features,
|
||||
logger,
|
||||
}: GetDefaultConfigInputs): Promise<Config> {
|
||||
}: InitConfigInputs): Promise<Config> {
|
||||
const languages = await getLanguages(
|
||||
codeql,
|
||||
languagesInput,
|
||||
@@ -506,16 +497,10 @@ export async function getDefaultConfig({
|
||||
);
|
||||
|
||||
const augmentationProperties = await calculateAugmentation(
|
||||
codeql,
|
||||
repository,
|
||||
features,
|
||||
packsInput,
|
||||
queriesInput,
|
||||
qualityQueriesInput,
|
||||
languages,
|
||||
sourceRoot,
|
||||
buildMode,
|
||||
logger,
|
||||
);
|
||||
|
||||
const { trapCaches, trapCacheDownloadTime } = await downloadCacheWithTime(
|
||||
@@ -562,34 +547,12 @@ async function downloadCacheWithTime(
|
||||
return { trapCaches, trapCacheDownloadTime };
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the config from the given file.
|
||||
*/
|
||||
async function loadConfig({
|
||||
languagesInput,
|
||||
queriesInput,
|
||||
qualityQueriesInput,
|
||||
packsInput,
|
||||
buildModeInput,
|
||||
configFile,
|
||||
dbLocation,
|
||||
trapCachingEnabled,
|
||||
dependencyCachingEnabled,
|
||||
debugMode,
|
||||
debugArtifactName,
|
||||
debugDatabaseName,
|
||||
repository,
|
||||
tempDir,
|
||||
codeql,
|
||||
workspacePath,
|
||||
sourceRoot,
|
||||
githubVersion,
|
||||
apiDetails,
|
||||
features,
|
||||
logger,
|
||||
}: LoadConfigInputs): Promise<Config> {
|
||||
let parsedYAML: UserConfig;
|
||||
|
||||
async function loadUserConfig(
|
||||
configFile: string,
|
||||
workspacePath: string,
|
||||
apiDetails: api.GitHubApiCombinedDetails,
|
||||
tempDir: string,
|
||||
): Promise<UserConfig> {
|
||||
if (isLocal(configFile)) {
|
||||
if (configFile !== userConfigFromActionPath(tempDir)) {
|
||||
// If the config file is not generated by the Action, it should be relative to the workspace.
|
||||
@@ -601,61 +564,10 @@ async function loadConfig({
|
||||
);
|
||||
}
|
||||
}
|
||||
parsedYAML = getLocalConfig(configFile);
|
||||
return getLocalConfig(configFile);
|
||||
} else {
|
||||
parsedYAML = await getRemoteConfig(configFile, apiDetails);
|
||||
return await getRemoteConfig(configFile, apiDetails);
|
||||
}
|
||||
|
||||
const languages = await getLanguages(
|
||||
codeql,
|
||||
languagesInput,
|
||||
repository,
|
||||
logger,
|
||||
);
|
||||
|
||||
const buildMode = await parseBuildModeInput(
|
||||
buildModeInput,
|
||||
languages,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
const augmentationProperties = await calculateAugmentation(
|
||||
codeql,
|
||||
repository,
|
||||
features,
|
||||
packsInput,
|
||||
queriesInput,
|
||||
qualityQueriesInput,
|
||||
languages,
|
||||
sourceRoot,
|
||||
buildMode,
|
||||
logger,
|
||||
);
|
||||
|
||||
const { trapCaches, trapCacheDownloadTime } = await downloadCacheWithTime(
|
||||
trapCachingEnabled,
|
||||
codeql,
|
||||
languages,
|
||||
logger,
|
||||
);
|
||||
|
||||
return {
|
||||
languages,
|
||||
buildMode,
|
||||
originalUserInput: parsedYAML,
|
||||
tempDir,
|
||||
codeQLCmd: codeql.getPath(),
|
||||
gitHubVersion: githubVersion,
|
||||
dbLocation: dbLocationOrDefault(dbLocation, tempDir),
|
||||
debugMode,
|
||||
debugArtifactName,
|
||||
debugDatabaseName,
|
||||
augmentationProperties,
|
||||
trapCaches,
|
||||
trapCacheDownloadTime,
|
||||
dependencyCachingEnabled: getCachingKind(dependencyCachingEnabled),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -665,17 +577,11 @@ async function loadConfig({
|
||||
* and the CLI does not know about these inputs so we need to inject them into
|
||||
* the config file sent to the CLI.
|
||||
*
|
||||
* @param codeql The CodeQL object.
|
||||
* @param repository The repository to analyze.
|
||||
* @param features The feature enablement object.
|
||||
* @param rawPacksInput The packs input from the action configuration.
|
||||
* @param rawQueriesInput The queries input from the action configuration.
|
||||
* @param languages The languages that the config file is for. If the packs input
|
||||
* is non-empty, then there must be exactly one language. Otherwise, an
|
||||
* error is thrown.
|
||||
* @param sourceRoot The source root of the repository.
|
||||
* @param buildMode The build mode to use.
|
||||
* @param logger The logger to use for logging.
|
||||
*
|
||||
* @returns The properties that need to be augmented in the config file.
|
||||
*
|
||||
@@ -684,16 +590,10 @@ async function loadConfig({
|
||||
*/
|
||||
// exported for testing.
|
||||
export async function calculateAugmentation(
|
||||
codeql: CodeQL,
|
||||
repository: RepositoryNwo,
|
||||
features: FeatureEnablement,
|
||||
rawPacksInput: string | undefined,
|
||||
rawQueriesInput: string | undefined,
|
||||
rawQualityQueriesInput: string | undefined,
|
||||
languages: Language[],
|
||||
sourceRoot: string,
|
||||
buildMode: BuildMode | undefined,
|
||||
logger: Logger,
|
||||
): Promise<AugmentationProperties> {
|
||||
const packsInputCombines = shouldCombine(rawPacksInput);
|
||||
const packsInput = parsePacksFromInput(
|
||||
@@ -706,42 +606,21 @@ export async function calculateAugmentation(
|
||||
rawQueriesInput,
|
||||
queriesInputCombines,
|
||||
);
|
||||
const { overlayDatabaseMode, useOverlayDatabaseCaching } =
|
||||
await getOverlayDatabaseMode(
|
||||
codeql,
|
||||
repository,
|
||||
features,
|
||||
languages,
|
||||
sourceRoot,
|
||||
buildMode,
|
||||
logger,
|
||||
);
|
||||
logger.info(
|
||||
`Using overlay database mode: ${overlayDatabaseMode} ` +
|
||||
`${useOverlayDatabaseCaching ? "with" : "without"} caching.`,
|
||||
);
|
||||
|
||||
const qualityQueriesInput = parseQueriesFromInput(
|
||||
rawQualityQueriesInput,
|
||||
false,
|
||||
);
|
||||
|
||||
const extraQueryExclusions: ExcludeQueryFilter[] = [];
|
||||
if (await shouldPerformDiffInformedAnalysis(codeql, features, logger)) {
|
||||
extraQueryExclusions.push({
|
||||
exclude: { tags: "exclude-from-incremental" },
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
packsInputCombines,
|
||||
packsInput: packsInput?.[languages[0]],
|
||||
queriesInput,
|
||||
queriesInputCombines,
|
||||
qualityQueriesInput,
|
||||
extraQueryExclusions,
|
||||
overlayDatabaseMode,
|
||||
useOverlayDatabaseCaching,
|
||||
extraQueryExclusions: [],
|
||||
overlayDatabaseMode: OverlayDatabaseMode.None,
|
||||
useOverlayDatabaseCaching: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -768,6 +647,78 @@ function parseQueriesFromInput(
|
||||
return trimmedInput.split(",").map((query) => ({ uses: query.trim() }));
|
||||
}
|
||||
|
||||
const OVERLAY_ANALYSIS_FEATURES: Record<Language, Feature> = {
|
||||
actions: Feature.OverlayAnalysisActions,
|
||||
cpp: Feature.OverlayAnalysisCpp,
|
||||
csharp: Feature.OverlayAnalysisCsharp,
|
||||
go: Feature.OverlayAnalysisGo,
|
||||
java: Feature.OverlayAnalysisJava,
|
||||
javascript: Feature.OverlayAnalysisJavascript,
|
||||
python: Feature.OverlayAnalysisPython,
|
||||
ruby: Feature.OverlayAnalysisRuby,
|
||||
rust: Feature.OverlayAnalysisRust,
|
||||
swift: Feature.OverlayAnalysisSwift,
|
||||
};
|
||||
|
||||
const OVERLAY_ANALYSIS_CODE_SCANNING_FEATURES: Record<Language, Feature> = {
|
||||
actions: Feature.OverlayAnalysisCodeScanningActions,
|
||||
cpp: Feature.OverlayAnalysisCodeScanningCpp,
|
||||
csharp: Feature.OverlayAnalysisCodeScanningCsharp,
|
||||
go: Feature.OverlayAnalysisCodeScanningGo,
|
||||
java: Feature.OverlayAnalysisCodeScanningJava,
|
||||
javascript: Feature.OverlayAnalysisCodeScanningJavascript,
|
||||
python: Feature.OverlayAnalysisCodeScanningPython,
|
||||
ruby: Feature.OverlayAnalysisCodeScanningRuby,
|
||||
rust: Feature.OverlayAnalysisCodeScanningRust,
|
||||
swift: Feature.OverlayAnalysisCodeScanningSwift,
|
||||
};
|
||||
|
||||
async function isOverlayAnalysisFeatureEnabled(
|
||||
repository: RepositoryNwo,
|
||||
features: FeatureEnablement,
|
||||
codeql: CodeQL,
|
||||
languages: Language[],
|
||||
codeScanningConfig: UserConfig,
|
||||
): Promise<boolean> {
|
||||
// TODO: Remove the repository owner check once support for overlay analysis
|
||||
// stabilizes, and no more backward-incompatible changes are expected.
|
||||
if (!["github", "dsp-testing"].includes(repository.owner)) {
|
||||
return false;
|
||||
}
|
||||
if (!(await features.getValue(Feature.OverlayAnalysis, codeql))) {
|
||||
return false;
|
||||
}
|
||||
let enableForCodeScanningOnly = false;
|
||||
for (const language of languages) {
|
||||
const feature = OVERLAY_ANALYSIS_FEATURES[language];
|
||||
if (feature && (await features.getValue(feature, codeql))) {
|
||||
continue;
|
||||
}
|
||||
const codeScanningFeature =
|
||||
OVERLAY_ANALYSIS_CODE_SCANNING_FEATURES[language];
|
||||
if (
|
||||
codeScanningFeature &&
|
||||
(await features.getValue(codeScanningFeature, codeql))
|
||||
) {
|
||||
enableForCodeScanningOnly = true;
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (enableForCodeScanningOnly) {
|
||||
// A code-scanning configuration runs only the (default) code-scanning suite
|
||||
// if the default queries are not disabled, and no packs, queries, or
|
||||
// query-filters are specified.
|
||||
return (
|
||||
codeScanningConfig["disable-default-queries"] !== true &&
|
||||
codeScanningConfig.packs === undefined &&
|
||||
codeScanningConfig.queries === undefined &&
|
||||
codeScanningConfig["query-filters"] === undefined
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate and validate the overlay database mode and caching to use.
|
||||
*
|
||||
@@ -796,6 +747,7 @@ export async function getOverlayDatabaseMode(
|
||||
languages: Language[],
|
||||
sourceRoot: string,
|
||||
buildMode: BuildMode | undefined,
|
||||
codeScanningConfig: UserConfig,
|
||||
logger: Logger,
|
||||
): Promise<{
|
||||
overlayDatabaseMode: OverlayDatabaseMode;
|
||||
@@ -818,10 +770,13 @@ export async function getOverlayDatabaseMode(
|
||||
"from the CODEQL_OVERLAY_DATABASE_MODE environment variable.",
|
||||
);
|
||||
} else if (
|
||||
// TODO: Remove the repository owner check once support for overlay analysis
|
||||
// stabilizes, and no more backward-incompatible changes are expected.
|
||||
["github", "dsp-testing"].includes(repository.owner) &&
|
||||
(await features.getValue(Feature.OverlayAnalysis, codeql))
|
||||
await isOverlayAnalysisFeatureEnabled(
|
||||
repository,
|
||||
features,
|
||||
codeql,
|
||||
languages,
|
||||
codeScanningConfig,
|
||||
)
|
||||
) {
|
||||
if (isAnalyzingPullRequest()) {
|
||||
overlayDatabaseMode = OverlayDatabaseMode.Overlay;
|
||||
@@ -1053,8 +1008,6 @@ function userConfigFromActionPath(tempDir: string): string {
|
||||
* a default config. The parsed config is then stored to a known location.
|
||||
*/
|
||||
export async function initConfig(inputs: InitConfigInputs): Promise<Config> {
|
||||
let config: Config;
|
||||
|
||||
const { logger, tempDir } = inputs;
|
||||
|
||||
// if configInput is set, it takes precedence over configFile
|
||||
@@ -1069,13 +1022,56 @@ export async function initConfig(inputs: InitConfigInputs): Promise<Config> {
|
||||
logger.debug(`Using config from action input: ${inputs.configFile}`);
|
||||
}
|
||||
|
||||
// If no config file was provided create an empty one
|
||||
let userConfig: UserConfig = {};
|
||||
if (!inputs.configFile) {
|
||||
logger.debug("No configuration file was provided");
|
||||
config = await getDefaultConfig(inputs);
|
||||
} else {
|
||||
// Convince the type checker that inputs.configFile is defined.
|
||||
config = await loadConfig({ ...inputs, configFile: inputs.configFile });
|
||||
logger.debug(`Using configuration file: ${inputs.configFile}`);
|
||||
userConfig = await loadUserConfig(
|
||||
inputs.configFile,
|
||||
inputs.workspacePath,
|
||||
inputs.apiDetails,
|
||||
tempDir,
|
||||
);
|
||||
}
|
||||
|
||||
const config = await getDefaultConfig(inputs);
|
||||
const augmentationProperties = config.augmentationProperties;
|
||||
config.originalUserInput = userConfig;
|
||||
|
||||
// The choice of overlay database mode depends on the selection of languages
|
||||
// and queries, which in turn depends on the user config and the augmentation
|
||||
// properties. So we need to calculate the overlay database mode after the
|
||||
// rest of the config has been populated.
|
||||
const { overlayDatabaseMode, useOverlayDatabaseCaching } =
|
||||
await getOverlayDatabaseMode(
|
||||
inputs.codeql,
|
||||
inputs.repository,
|
||||
inputs.features,
|
||||
config.languages,
|
||||
inputs.sourceRoot,
|
||||
config.buildMode,
|
||||
generateCodeScanningConfig(userConfig, augmentationProperties),
|
||||
logger,
|
||||
);
|
||||
logger.info(
|
||||
`Using overlay database mode: ${overlayDatabaseMode} ` +
|
||||
`${useOverlayDatabaseCaching ? "with" : "without"} caching.`,
|
||||
);
|
||||
augmentationProperties.overlayDatabaseMode = overlayDatabaseMode;
|
||||
augmentationProperties.useOverlayDatabaseCaching = useOverlayDatabaseCaching;
|
||||
|
||||
if (
|
||||
overlayDatabaseMode === OverlayDatabaseMode.Overlay ||
|
||||
(await shouldPerformDiffInformedAnalysis(
|
||||
inputs.codeql,
|
||||
inputs.features,
|
||||
logger,
|
||||
))
|
||||
) {
|
||||
augmentationProperties.extraQueryExclusions.push({
|
||||
exclude: { tags: "exclude-from-incremental" },
|
||||
});
|
||||
}
|
||||
|
||||
// Save the config so we can easily access it again in the future
|
||||
@@ -1355,3 +1351,64 @@ export async function parseBuildModeInput(
|
||||
}
|
||||
return input as BuildMode;
|
||||
}
|
||||
|
||||
export function generateCodeScanningConfig(
|
||||
originalUserInput: UserConfig,
|
||||
augmentationProperties: AugmentationProperties,
|
||||
): UserConfig {
|
||||
// make a copy so we can modify it
|
||||
const augmentedConfig = cloneObject(originalUserInput);
|
||||
|
||||
// Inject the queries from the input
|
||||
if (augmentationProperties.queriesInput) {
|
||||
if (augmentationProperties.queriesInputCombines) {
|
||||
augmentedConfig.queries = (augmentedConfig.queries || []).concat(
|
||||
augmentationProperties.queriesInput,
|
||||
);
|
||||
} else {
|
||||
augmentedConfig.queries = augmentationProperties.queriesInput;
|
||||
}
|
||||
}
|
||||
if (augmentedConfig.queries?.length === 0) {
|
||||
delete augmentedConfig.queries;
|
||||
}
|
||||
|
||||
// Inject the packs from the input
|
||||
if (augmentationProperties.packsInput) {
|
||||
if (augmentationProperties.packsInputCombines) {
|
||||
// At this point, we already know that this is a single-language analysis
|
||||
if (Array.isArray(augmentedConfig.packs)) {
|
||||
augmentedConfig.packs = (augmentedConfig.packs || []).concat(
|
||||
augmentationProperties.packsInput,
|
||||
);
|
||||
} else if (!augmentedConfig.packs) {
|
||||
augmentedConfig.packs = augmentationProperties.packsInput;
|
||||
} else {
|
||||
// At this point, we know there is only one language.
|
||||
// If there were more than one language, an error would already have been thrown.
|
||||
const language = Object.keys(augmentedConfig.packs)[0];
|
||||
augmentedConfig.packs[language] = augmentedConfig.packs[
|
||||
language
|
||||
].concat(augmentationProperties.packsInput);
|
||||
}
|
||||
} else {
|
||||
augmentedConfig.packs = augmentationProperties.packsInput;
|
||||
}
|
||||
}
|
||||
if (Array.isArray(augmentedConfig.packs) && !augmentedConfig.packs.length) {
|
||||
delete augmentedConfig.packs;
|
||||
}
|
||||
|
||||
augmentedConfig["query-filters"] = [
|
||||
// Ordering matters. If the first filter is an inclusion, it implicitly
|
||||
// excludes all queries that are not included. If it is an exclusion,
|
||||
// it implicitly includes all queries that are not excluded. So user
|
||||
// filters (if any) should always be first to preserve intent.
|
||||
...(augmentedConfig["query-filters"] || []),
|
||||
...augmentationProperties.extraQueryExclusions,
|
||||
];
|
||||
if (augmentedConfig["query-filters"]?.length === 0) {
|
||||
delete augmentedConfig["query-filters"];
|
||||
}
|
||||
return augmentedConfig;
|
||||
}
|
||||
|
||||
@@ -15,11 +15,13 @@ import { getRunnerLogger } from "./logging";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import {
|
||||
getRecordingLogger,
|
||||
initializeFeatures,
|
||||
LoggedMessage,
|
||||
mockCodeQLVersion,
|
||||
mockFeatureFlagApiEndpoint,
|
||||
setupActionsVars,
|
||||
setupTests,
|
||||
stubFeatureFlagApiEndpoint,
|
||||
} from "./testing-utils";
|
||||
import { ToolsFeature } from "./tools-features";
|
||||
import * as util from "./util";
|
||||
@@ -131,6 +133,29 @@ test("Features use default value if they're not returned in API response", async
|
||||
});
|
||||
});
|
||||
|
||||
test("Include no more than 25 features in each API request", async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const features = setUpFeatureFlagTests(tmpDir);
|
||||
|
||||
stubFeatureFlagApiEndpoint((request) => {
|
||||
const requestedFeatures = (request.features as string).split(",");
|
||||
return {
|
||||
status: requestedFeatures.length <= 25 ? 200 : 400,
|
||||
messageIfError: "Can request a maximum of 25 features.",
|
||||
data: {},
|
||||
};
|
||||
});
|
||||
|
||||
// We only need to call getValue once, and it does not matter which feature
|
||||
// we ask for. Under the hood, the features library will request all features
|
||||
// from the API.
|
||||
const feature = Object.values(Feature)[0];
|
||||
await t.notThrowsAsync(async () =>
|
||||
features.getValue(feature, includeCodeQlIfRequired(feature)),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("Feature flags exception is propagated if the API request errors", async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const features = setUpFeatureFlagTests(tmpDir);
|
||||
@@ -552,13 +577,6 @@ function assertAllFeaturesUndefinedInApi(
|
||||
}
|
||||
}
|
||||
|
||||
export function initializeFeatures(initialValue: boolean) {
|
||||
return Object.keys(featureConfig).reduce((features, key) => {
|
||||
features[key] = initialValue;
|
||||
return features;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function setUpFeatureFlagTests(
|
||||
tmpDir: string,
|
||||
logger = getRunnerLogger(true),
|
||||
|
||||
@@ -54,6 +54,26 @@ export enum Feature {
|
||||
ExportDiagnosticsEnabled = "export_diagnostics_enabled",
|
||||
ExtractToToolcache = "extract_to_toolcache",
|
||||
OverlayAnalysis = "overlay_analysis",
|
||||
OverlayAnalysisActions = "overlay_analysis_actions",
|
||||
OverlayAnalysisCodeScanningActions = "overlay_analysis_code_scanning_actions",
|
||||
OverlayAnalysisCodeScanningCpp = "overlay_analysis_code_scanning_cpp",
|
||||
OverlayAnalysisCodeScanningCsharp = "overlay_analysis_code_scanning_csharp",
|
||||
OverlayAnalysisCodeScanningGo = "overlay_analysis_code_scanning_go",
|
||||
OverlayAnalysisCodeScanningJava = "overlay_analysis_code_scanning_java",
|
||||
OverlayAnalysisCodeScanningJavascript = "overlay_analysis_code_scanning_javascript",
|
||||
OverlayAnalysisCodeScanningPython = "overlay_analysis_code_scanning_python",
|
||||
OverlayAnalysisCodeScanningRuby = "overlay_analysis_code_scanning_ruby",
|
||||
OverlayAnalysisCodeScanningRust = "overlay_analysis_code_scanning_rust",
|
||||
OverlayAnalysisCodeScanningSwift = "overlay_analysis_code_scanning_swift",
|
||||
OverlayAnalysisCpp = "overlay_analysis_cpp",
|
||||
OverlayAnalysisCsharp = "overlay_analysis_csharp",
|
||||
OverlayAnalysisGo = "overlay_analysis_go",
|
||||
OverlayAnalysisJava = "overlay_analysis_java",
|
||||
OverlayAnalysisJavascript = "overlay_analysis_javascript",
|
||||
OverlayAnalysisPython = "overlay_analysis_python",
|
||||
OverlayAnalysisRuby = "overlay_analysis_ruby",
|
||||
OverlayAnalysisRust = "overlay_analysis_rust",
|
||||
OverlayAnalysisSwift = "overlay_analysis_swift",
|
||||
PythonDefaultIsToNotExtractStdlib = "python_default_is_to_not_extract_stdlib",
|
||||
QaTelemetryEnabled = "qa_telemetry_enabled",
|
||||
RustAnalysis = "rust_analysis",
|
||||
@@ -155,6 +175,106 @@ export const featureConfig: Record<
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS",
|
||||
minimumVersion: CODEQL_OVERLAY_MINIMUM_VERSION,
|
||||
},
|
||||
[Feature.OverlayAnalysisActions]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_ACTIONS",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisCodeScanningActions]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_ACTIONS",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisCodeScanningCpp]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_CPP",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisCodeScanningCsharp]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_CSHARP",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisCodeScanningGo]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_GO",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisCodeScanningJava]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_JAVA",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisCodeScanningJavascript]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_JAVASCRIPT",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisCodeScanningPython]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_PYTHON",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisCodeScanningRuby]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_RUBY",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisCodeScanningRust]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_RUST",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisCodeScanningSwift]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CODE_SCANNING_SWIFT",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisCpp]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CPP",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisCsharp]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_CSHARP",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisGo]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_GO",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisJava]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_JAVA",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisJavascript]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_JAVASCRIPT",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisPython]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_PYTHON",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisRuby]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_RUBY",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisRust]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_RUST",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.OverlayAnalysisSwift]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_SWIFT",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.PythonDefaultIsToNotExtractStdlib]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_DISABLE_PYTHON_STANDARD_LIBRARY_EXTRACTION",
|
||||
@@ -502,18 +622,29 @@ class GitHubFeatureFlags {
|
||||
try {
|
||||
const featuresToRequest = Object.entries(featureConfig)
|
||||
.filter(([, config]) => !config.legacyApi)
|
||||
.map(([f]) => f)
|
||||
.join(",");
|
||||
.map(([f]) => f);
|
||||
|
||||
const FEATURES_PER_REQUEST = 25;
|
||||
const featureChunks: string[][] = [];
|
||||
while (featuresToRequest.length > 0) {
|
||||
featureChunks.push(featuresToRequest.splice(0, FEATURES_PER_REQUEST));
|
||||
}
|
||||
|
||||
let remoteFlags: GitHubFeatureFlagsApiResponse = {};
|
||||
|
||||
for (const chunk of featureChunks) {
|
||||
const response = await getApiClient().request(
|
||||
"GET /repos/:owner/:repo/code-scanning/codeql-action/features",
|
||||
{
|
||||
owner: this.repositoryNwo.owner,
|
||||
repo: this.repositoryNwo.repo,
|
||||
features: chunk.join(","),
|
||||
},
|
||||
);
|
||||
const chunkFlags = response.data as GitHubFeatureFlagsApiResponse;
|
||||
remoteFlags = { ...remoteFlags, ...chunkFlags };
|
||||
}
|
||||
|
||||
const response = await getApiClient().request(
|
||||
"GET /repos/:owner/:repo/code-scanning/codeql-action/features",
|
||||
{
|
||||
owner: this.repositoryNwo.owner,
|
||||
repo: this.repositoryNwo.repo,
|
||||
features: featuresToRequest,
|
||||
},
|
||||
);
|
||||
const remoteFlags = response.data as GitHubFeatureFlagsApiResponse;
|
||||
this.logger.debug(
|
||||
"Loaded the following default values for the feature flags from the Code Scanning API:",
|
||||
);
|
||||
|
||||
@@ -5,7 +5,6 @@ import * as sinon from "sinon";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { initializeFeatures } from "./feature-flags.test";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import * as setupCodeql from "./setup-codeql";
|
||||
import {
|
||||
@@ -14,6 +13,7 @@ import {
|
||||
SAMPLE_DEFAULT_CLI_VERSION,
|
||||
SAMPLE_DOTCOM_API_DETAILS,
|
||||
getRecordingLogger,
|
||||
initializeFeatures,
|
||||
mockBundleDownloadApi,
|
||||
setupActionsVars,
|
||||
setupTests,
|
||||
|
||||
@@ -14,6 +14,7 @@ import * as defaults from "./defaults.json";
|
||||
import {
|
||||
CodeQLDefaultVersionInfo,
|
||||
Feature,
|
||||
featureConfig,
|
||||
FeatureEnablement,
|
||||
} from "./feature-flags";
|
||||
import { Logger } from "./logging";
|
||||
@@ -179,6 +180,21 @@ export function getRecordingLogger(messages: LoggedMessage[]): Logger {
|
||||
export function mockFeatureFlagApiEndpoint(
|
||||
responseStatusCode: number,
|
||||
response: { [flagName: string]: boolean },
|
||||
) {
|
||||
stubFeatureFlagApiEndpoint(() => ({
|
||||
status: responseStatusCode,
|
||||
messageIfError: "some error message",
|
||||
data: response,
|
||||
}));
|
||||
}
|
||||
|
||||
/** Stub the HTTP request to the feature flags enablement API endpoint. */
|
||||
export function stubFeatureFlagApiEndpoint(
|
||||
responseFunction: (params: any) => {
|
||||
status: number;
|
||||
messageIfError?: string;
|
||||
data: { [flagName: string]: boolean };
|
||||
},
|
||||
) {
|
||||
// Passing an auth token is required, so we just use a dummy value
|
||||
const client = github.getOctokit("123");
|
||||
@@ -188,16 +204,23 @@ export function mockFeatureFlagApiEndpoint(
|
||||
const optInSpy = requestSpy.withArgs(
|
||||
"GET /repos/:owner/:repo/code-scanning/codeql-action/features",
|
||||
);
|
||||
if (responseStatusCode < 300) {
|
||||
optInSpy.resolves({
|
||||
status: responseStatusCode,
|
||||
data: response,
|
||||
headers: {},
|
||||
url: "GET /repos/:owner/:repo/code-scanning/codeql-action/features",
|
||||
});
|
||||
} else {
|
||||
optInSpy.throws(new HTTPError("some error message", responseStatusCode));
|
||||
}
|
||||
|
||||
optInSpy.callsFake((_route, params) => {
|
||||
const response = responseFunction(params);
|
||||
if (response.status < 300) {
|
||||
return Promise.resolve({
|
||||
status: response.status,
|
||||
data: response.data,
|
||||
headers: {},
|
||||
url: "GET /repos/:owner/:repo/code-scanning/codeql-action/features",
|
||||
});
|
||||
} else {
|
||||
throw new HTTPError(
|
||||
response.messageIfError || "default stub error message",
|
||||
response.status,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
sinon.stub(apiClient, "getApiClient").value(() => client);
|
||||
}
|
||||
@@ -263,6 +286,13 @@ export function createFeatures(enabledFeatures: Feature[]): FeatureEnablement {
|
||||
};
|
||||
}
|
||||
|
||||
export function initializeFeatures(initialValue: boolean) {
|
||||
return Object.keys(featureConfig).reduce((features, key) => {
|
||||
features[key] = initialValue;
|
||||
return features;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mocks the API for downloading the bundle tagged `tagName`.
|
||||
*
|
||||
@@ -335,6 +365,7 @@ export function createTestConfig(overrides: Partial<Config>): Config {
|
||||
augmentationProperties: {
|
||||
packsInputCombines: false,
|
||||
queriesInputCombines: false,
|
||||
extraQueryExclusions: [],
|
||||
},
|
||||
trapCaches: {},
|
||||
trapCacheDownloadTime: 0,
|
||||
|
||||
@@ -5,8 +5,8 @@ import * as actionsCache from "@actions/cache";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as apiClient from "./api-client";
|
||||
import { CodeQL } from "./codeql";
|
||||
import type { Config } from "./config-utils";
|
||||
import { type CodeQL } from "./codeql";
|
||||
import { type Config } from "./config-utils";
|
||||
import { DocUrl } from "./doc-url";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import * as gitUtils from "./git-utils";
|
||||
|
||||
Reference in New Issue
Block a user