mirror of
https://github.com/github/codeql-action.git
synced 2026-04-29 02:18:47 +00:00
Merge pull request #3083 from github/henrymercer/resolve-languages-default-queries
Resolve supported languages using CodeQL CLI
This commit is contained in:
+13
-2
@@ -127,7 +127,9 @@ export interface CodeQL {
|
||||
/**
|
||||
* Run 'codeql resolve languages' with '--format=betterjson'.
|
||||
*/
|
||||
betterResolveLanguages(): Promise<BetterResolveLanguagesOutput>;
|
||||
betterResolveLanguages(options?: {
|
||||
filterToLanguagesWithQueries: boolean;
|
||||
}): Promise<BetterResolveLanguagesOutput>;
|
||||
/**
|
||||
* Run 'codeql resolve build-environment'
|
||||
*/
|
||||
@@ -736,13 +738,22 @@ export async function getCodeQLForCmd(
|
||||
);
|
||||
}
|
||||
},
|
||||
async betterResolveLanguages() {
|
||||
async betterResolveLanguages(
|
||||
{
|
||||
filterToLanguagesWithQueries,
|
||||
}: {
|
||||
filterToLanguagesWithQueries: boolean;
|
||||
} = { filterToLanguagesWithQueries: false },
|
||||
) {
|
||||
const codeqlArgs = [
|
||||
"resolve",
|
||||
"languages",
|
||||
"--format=betterjson",
|
||||
"--extractor-options-verbosity=4",
|
||||
"--extractor-include-aliases",
|
||||
...(filterToLanguagesWithQueries
|
||||
? ["--filter-to-languages-with-queries"]
|
||||
: []),
|
||||
...getExtraOptionsFromEnv(["resolve", "languages"]),
|
||||
];
|
||||
const output = await runCli(cmd, codeqlArgs);
|
||||
|
||||
+71
-50
@@ -1092,6 +1092,13 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
|
||||
expectedLanguages: ["javascript", "csharp", "cpp"],
|
||||
expectedApiCall: true,
|
||||
},
|
||||
{
|
||||
name: "unsupported languages from github api",
|
||||
languagesInput: "",
|
||||
languagesInRepository: ["html"],
|
||||
expectedApiCall: true,
|
||||
expectedError: configUtils.getNoLanguagesError(),
|
||||
},
|
||||
{
|
||||
name: "no languages",
|
||||
languagesInput: "",
|
||||
@@ -1121,57 +1128,71 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
|
||||
expectedLanguages: ["javascript"],
|
||||
},
|
||||
].forEach((args) => {
|
||||
test(`getLanguages: ${args.name}`, async (t) => {
|
||||
const mockRequest = mockLanguagesInRepo(args.languagesInRepository);
|
||||
const stubExtractorEntry = {
|
||||
extractor_root: "",
|
||||
};
|
||||
const codeQL = createStubCodeQL({
|
||||
betterResolveLanguages: () =>
|
||||
Promise.resolve({
|
||||
aliases: {
|
||||
"c#": KnownLanguage.csharp,
|
||||
c: KnownLanguage.cpp,
|
||||
kotlin: KnownLanguage.java,
|
||||
typescript: KnownLanguage.javascript,
|
||||
},
|
||||
extractors: {
|
||||
cpp: [stubExtractorEntry],
|
||||
csharp: [stubExtractorEntry],
|
||||
java: [stubExtractorEntry],
|
||||
javascript: [stubExtractorEntry],
|
||||
python: [stubExtractorEntry],
|
||||
},
|
||||
}),
|
||||
for (const resolveSupportedLanguagesUsingCli of [true, false]) {
|
||||
test(`getLanguages${resolveSupportedLanguagesUsingCli ? " (supported languages via CLI)" : ""}: ${args.name}`, async (t) => {
|
||||
const features = createFeatures(
|
||||
resolveSupportedLanguagesUsingCli
|
||||
? [Feature.ResolveSupportedLanguagesUsingCli]
|
||||
: [],
|
||||
);
|
||||
const mockRequest = mockLanguagesInRepo(args.languagesInRepository);
|
||||
const stubExtractorEntry = {
|
||||
extractor_root: "",
|
||||
};
|
||||
const codeQL = createStubCodeQL({
|
||||
betterResolveLanguages: (options) =>
|
||||
Promise.resolve({
|
||||
aliases: {
|
||||
"c#": KnownLanguage.csharp,
|
||||
c: KnownLanguage.cpp,
|
||||
kotlin: KnownLanguage.java,
|
||||
typescript: KnownLanguage.javascript,
|
||||
},
|
||||
extractors: {
|
||||
cpp: [stubExtractorEntry],
|
||||
csharp: [stubExtractorEntry],
|
||||
java: [stubExtractorEntry],
|
||||
javascript: [stubExtractorEntry],
|
||||
python: [stubExtractorEntry],
|
||||
...(options?.filterToLanguagesWithQueries
|
||||
? {}
|
||||
: {
|
||||
html: [stubExtractorEntry],
|
||||
}),
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (args.expectedLanguages) {
|
||||
// happy path
|
||||
const actualLanguages = await configUtils.getLanguages(
|
||||
codeQL,
|
||||
args.languagesInput,
|
||||
mockRepositoryNwo,
|
||||
".",
|
||||
features,
|
||||
mockLogger,
|
||||
);
|
||||
|
||||
t.deepEqual(actualLanguages.sort(), args.expectedLanguages.sort());
|
||||
} else {
|
||||
// there is an error
|
||||
await t.throwsAsync(
|
||||
async () =>
|
||||
await configUtils.getLanguages(
|
||||
codeQL,
|
||||
args.languagesInput,
|
||||
mockRepositoryNwo,
|
||||
".",
|
||||
features,
|
||||
mockLogger,
|
||||
),
|
||||
{ message: args.expectedError },
|
||||
);
|
||||
}
|
||||
t.deepEqual(mockRequest.called, args.expectedApiCall);
|
||||
});
|
||||
|
||||
if (args.expectedLanguages) {
|
||||
// happy path
|
||||
const actualLanguages = await configUtils.getLanguages(
|
||||
codeQL,
|
||||
args.languagesInput,
|
||||
mockRepositoryNwo,
|
||||
".",
|
||||
mockLogger,
|
||||
);
|
||||
|
||||
t.deepEqual(actualLanguages.sort(), args.expectedLanguages.sort());
|
||||
} else {
|
||||
// there is an error
|
||||
await t.throwsAsync(
|
||||
async () =>
|
||||
await configUtils.getLanguages(
|
||||
codeQL,
|
||||
args.languagesInput,
|
||||
mockRepositoryNwo,
|
||||
".",
|
||||
mockLogger,
|
||||
),
|
||||
{ message: args.expectedError },
|
||||
);
|
||||
}
|
||||
t.deepEqual(mockRequest.called, args.expectedApiCall);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
for (const { displayName, language, feature } of [
|
||||
|
||||
+24
-7
@@ -316,16 +316,31 @@ export function getUnknownLanguagesError(languages: string[]): string {
|
||||
|
||||
export async function getSupportedLanguageMap(
|
||||
codeql: CodeQL,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
): Promise<Record<string, string>> {
|
||||
const resolveResult = await codeql.betterResolveLanguages();
|
||||
const resolveSupportedLanguagesUsingCli = await features.getValue(
|
||||
Feature.ResolveSupportedLanguagesUsingCli,
|
||||
codeql,
|
||||
);
|
||||
const resolveResult = await codeql.betterResolveLanguages({
|
||||
filterToLanguagesWithQueries: resolveSupportedLanguagesUsingCli,
|
||||
});
|
||||
if (resolveSupportedLanguagesUsingCli) {
|
||||
logger.debug(
|
||||
`The CodeQL CLI supports the following languages: ${Object.keys(resolveResult.extractors).join(", ")}`,
|
||||
);
|
||||
}
|
||||
const supportedLanguages: Record<string, string> = {};
|
||||
// Populate canonical language names
|
||||
for (const extractor of Object.keys(resolveResult.extractors)) {
|
||||
// Require the language to be a known language.
|
||||
// This is a temporary workaround since we have extractors that are not
|
||||
// supported languages, such as `csv`, `html`, `properties`, `xml`, and
|
||||
// `yaml`. We should replace this with a more robust solution in the future.
|
||||
if (KnownLanguage[extractor] !== undefined) {
|
||||
// If the CLI supports resolving languages with default queries, use these
|
||||
// as the set of supported languages. Otherwise, require the language to be
|
||||
// a known language.
|
||||
if (
|
||||
resolveSupportedLanguagesUsingCli ||
|
||||
KnownLanguage[extractor] !== undefined
|
||||
) {
|
||||
supportedLanguages[extractor] = extractor;
|
||||
}
|
||||
}
|
||||
@@ -407,6 +422,7 @@ export async function getLanguages(
|
||||
languagesInput: string | undefined,
|
||||
repository: RepositoryNwo,
|
||||
sourceRoot: string,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
): Promise<Language[]> {
|
||||
// Obtain languages without filtering them.
|
||||
@@ -417,7 +433,7 @@ export async function getLanguages(
|
||||
logger,
|
||||
);
|
||||
|
||||
const languageMap = await getSupportedLanguageMap(codeql);
|
||||
const languageMap = await getSupportedLanguageMap(codeql, features, logger);
|
||||
const languagesSet = new Set<Language>();
|
||||
const unknownLanguages: string[] = [];
|
||||
|
||||
@@ -564,6 +580,7 @@ export async function initActionState(
|
||||
languagesInput,
|
||||
repository,
|
||||
sourceRoot,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ export enum Feature {
|
||||
OverlayAnalysisSwift = "overlay_analysis_swift",
|
||||
PythonDefaultIsToNotExtractStdlib = "python_default_is_to_not_extract_stdlib",
|
||||
QaTelemetryEnabled = "qa_telemetry_enabled",
|
||||
ResolveSupportedLanguagesUsingCli = "resolve_supported_languages_using_cli",
|
||||
}
|
||||
|
||||
export const featureConfig: Record<
|
||||
@@ -145,6 +146,12 @@ export const featureConfig: Record<
|
||||
legacyApi: true,
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.ResolveSupportedLanguagesUsingCli]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI",
|
||||
minimumVersion: undefined,
|
||||
toolsFeature: ToolsFeature.BuiltinExtractorsSpecifyDefaultQueries,
|
||||
},
|
||||
[Feature.OverlayAnalysis]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS",
|
||||
|
||||
@@ -4,11 +4,12 @@ import type { VersionInfo } from "./codeql";
|
||||
|
||||
export enum ToolsFeature {
|
||||
AnalysisSummaryV2IsDefault = "analysisSummaryV2Default",
|
||||
BuiltinExtractorsSpecifyDefaultQueries = "builtinExtractorsSpecifyDefaultQueries",
|
||||
DatabaseInterpretResultsSupportsSarifRunProperty = "databaseInterpretResultsSupportsSarifRunProperty",
|
||||
IndirectTracingSupportsStaticBinaries = "indirectTracingSupportsStaticBinaries",
|
||||
SarifMergeRunsFromEqualCategory = "sarifMergeRunsFromEqualCategory",
|
||||
ForceOverwrite = "forceOverwrite",
|
||||
IndirectTracingSupportsStaticBinaries = "indirectTracingSupportsStaticBinaries",
|
||||
PythonDefaultIsToNotExtractStdlib = "pythonDefaultIsToNotExtractStdlib",
|
||||
SarifMergeRunsFromEqualCategory = "sarifMergeRunsFromEqualCategory",
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user