Extract language appropriately in analyze step when build mode specified

This commit is contained in:
Henry Mercer 2024-02-02 19:13:42 +00:00
parent db6e5fff06
commit 0fe34bd39a
23 changed files with 205 additions and 61 deletions

View file

@ -13,6 +13,7 @@ import {
getCodeQL,
} from "./codeql";
import * as configUtils from "./config-utils";
import { BuildMode } from "./config-utils";
import { addDiagnostic, makeDiagnostic } from "./diagnostics";
import { EnvVar } from "./environment";
import {
@ -23,6 +24,7 @@ import {
import { isScannedLanguage, Language } from "./languages";
import { Logger } from "./logging";
import { DatabaseCreationTimings, EventReport } from "./status-report";
import { ToolsFeature } from "./tools-features";
import { endTracingForCluster } from "./tracer-config";
import { validateSarifFileSchema } from "./upload-lib";
import * as util from "./util";
@ -166,29 +168,49 @@ async function setupPythonExtractor(
process.env["LGTM_PYTHON_SETUP_VERSION"] = output;
}
export async function createdDBForScannedLanguages(
export async function runExtraction(
codeql: CodeQL,
config: configUtils.Config,
logger: Logger,
features: FeatureEnablement,
) {
for (const language of config.languages) {
if (
isScannedLanguage(language) &&
!dbIsFinalized(config, language, logger)
) {
logger.startGroup(`Extracting ${language}`);
if (dbIsFinalized(config, language, logger)) {
logger.debug(
`Database for ${language} has already been finalized, skipping extraction.`,
);
continue;
}
if (shouldExtractLanguage(config, language)) {
logger.startGroup(`Extracting ${language}`);
if (language === Language.python) {
await setupPythonExtractor(logger, features, codeql);
}
await codeql.extractScannedLanguage(config, language);
if (
config.buildMode &&
(await codeql.supportsFeature(ToolsFeature.TraceCommandUseBuildMode))
) {
await codeql.extractUsingBuildMode(config, language);
} else {
await codeql.extractScannedLanguage(config, language);
}
logger.endGroup();
}
}
}
function shouldExtractLanguage(
config: configUtils.Config,
language: Language,
): boolean {
return (
config.buildMode === BuildMode.None ||
config.buildMode === BuildMode.Autobuild ||
(!config.buildMode && isScannedLanguage(language))
);
}
export function dbIsFinalized(
config: configUtils.Config,
language: Language,
@ -218,7 +240,7 @@ async function finalizeDatabaseCreation(
const codeql = await getCodeQL(config.codeQLCmd);
const extractionStart = performance.now();
await createdDBForScannedLanguages(codeql, config, logger, features);
await runExtraction(codeql, config, logger, features);
const extractionTime = performance.now() - extractionStart;
const trapImportStart = performance.now();

View file

@ -7,6 +7,7 @@ import {
} from "./actions-util";
import { getGitHubVersion } from "./api-client";
import { determineAutobuildLanguages, runAutobuild } from "./autobuild";
import { getCodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { Language } from "./languages";
import { Logger, getActionsLogger } from "./logging";
@ -87,7 +88,9 @@ async function run() {
);
}
languages = await determineAutobuildLanguages(config, logger);
const codeql = await getCodeQL(config.codeQLCmd);
languages = await determineAutobuildLanguages(codeql, config, logger);
if (languages !== undefined) {
const workingDirectory = getOptionalInput("working-directory");
if (workingDirectory) {

View file

@ -4,17 +4,29 @@ import { getTemporaryDirectory, getWorkflowEventName } from "./actions-util";
import { getGitHubVersion } from "./api-client";
import { CodeQL, getCodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { BuildMode } from "./config-utils";
import { EnvVar } from "./environment";
import { Feature, featureConfig, Features } from "./feature-flags";
import { isTracedLanguage, Language } from "./languages";
import { Logger } from "./logging";
import { parseRepositoryNwo } from "./repository";
import { ToolsFeature } from "./tools-features";
import { getRequiredEnvParam } from "./util";
export async function determineAutobuildLanguages(
codeql: CodeQL,
config: configUtils.Config,
logger: Logger,
): Promise<Language[] | undefined> {
if (
(config.buildMode === BuildMode.None &&
(await codeql.supportsFeature(ToolsFeature.TraceCommandUseBuildMode))) ||
config.buildMode === BuildMode.Manual
) {
logger.info(`Using ${config.buildMode} build mode, nothing to autobuild.`);
return undefined;
}
// Attempt to find a language to autobuild
// We want pick the dominant language in the repo from the ones we're able to build
// The languages are sorted in order specified by user or by lines of code if we got

View file

@ -114,6 +114,11 @@ export interface CodeQL {
* and running the language extractor.
*/
extractScannedLanguage(config: Config, language: Language): Promise<void>;
/**
* Extract code with 'codeql database trace-command --use-build-mode'. This can only be used when
* the database specifies a build mode. This requires the `traceCommandUseBuildMode` tool feature.
*/
extractUsingBuildMode(config: Config, language: Language): Promise<void>;
/**
* Finalize a database using 'codeql database finalize'.
*/
@ -474,6 +479,10 @@ export function setCodeQL(partialCodeql: Partial<CodeQL>): CodeQL {
partialCodeql,
"extractScannedLanguage",
),
extractUsingBuildMode: resolveFunction(
partialCodeql,
"extractUsingBuildMode",
),
finalizeDatabase: resolveFunction(partialCodeql, "finalizeDatabase"),
resolveLanguages: resolveFunction(partialCodeql, "resolveLanguages"),
betterResolveLanguages: resolveFunction(
@ -690,6 +699,16 @@ export async function getCodeQLForCmd(
util.getCodeQLDatabasePath(config, language),
]);
},
async extractUsingBuildMode(config: Config, language: Language) {
await runTool(cmd, [
"database",
"trace-command",
"--use-build-mode",
...(await getTrapCachingExtractorConfigArgsForLang(config, language)),
...getExtraOptionsFromEnv(["database", "trace-command"]),
util.getCodeQLDatabasePath(config, language),
]);
},
async finalizeDatabase(
databasePath: string,
threadsFlag: string,

View file

@ -14,6 +14,7 @@ import {
setCodeQL,
} from "./codeql";
import * as configUtils from "./config-utils";
import { BuildMode } from "./config-utils";
import { Language } from "./languages";
import { getRunnerLogger } from "./logging";
import { parseRepositoryNwo } from "./repository";
@ -323,7 +324,7 @@ test("load non-empty input", async (t) => {
// And the config we expect it to parse to
const expectedConfig: configUtils.Config = {
languages: [Language.javascript],
buildMode: "none",
buildMode: BuildMode.None,
originalUserInput: {
name: "my config",
"disable-default-queries": true,

View file

@ -72,6 +72,12 @@ interface IncludeQueryFilter {
include: Record<string, string[] | string>;
}
export enum BuildMode {
None = "none",
Autobuild = "autobuild",
Manual = "manual",
}
/**
* Format of the parsed config file.
*/
@ -83,7 +89,7 @@ export interface Config {
/**
* Build mode, if set. Currently only a single build mode is supported per job.
*/
buildMode: string | undefined;
buildMode: BuildMode | undefined;
/**
* A unaltered copy of the original user input.
* Mainly intended to be used for status reporting.
@ -466,7 +472,7 @@ export async function getDefaultConfig({
return {
languages,
buildMode: buildModeInput,
buildMode: validateBuildModeInput(buildModeInput),
originalUserInput: {},
tempDir,
codeQLCmd: codeql.getPath(),
@ -554,7 +560,7 @@ async function loadConfig({
return {
languages,
buildMode: buildModeInput,
buildMode: validateBuildModeInput(buildModeInput),
originalUserInput: parsedYAML,
tempDir,
codeQLCmd: codeql.getPath(),
@ -1056,3 +1062,20 @@ export async function wrapEnvironment(
}
}
}
function validateBuildModeInput(
buildModeInput: string | undefined,
): BuildMode | undefined {
if (buildModeInput === undefined) {
return undefined;
}
if (!Object.values(BuildMode).includes(buildModeInput as BuildMode)) {
throw new UserError(
`Invalid build mode: '${buildModeInput}'. Supported build modes are: ${Object.values(
BuildMode,
).join(", ")}.`,
);
}
return buildModeInput as BuildMode;
}

View file

@ -4,6 +4,7 @@ export enum ToolsFeature {
BuildModeOption = "buildModeOption",
IndirectTracingSupportsStaticBinaries = "indirectTracingSupportsStaticBinaries",
SetsCodeqlRunnerEnvVar = "setsCodeqlRunnerEnvVar",
TraceCommandUseBuildMode = "traceCommandUseBuildMode",
}
/**