Merge branch 'main' into henrymercer/bump-minimum-codeql-version

This commit is contained in:
Henry Mercer
2023-07-10 13:16:09 +01:00
234 changed files with 26797 additions and 28982 deletions

View File

@@ -5,6 +5,7 @@ import test from "ava";
import * as sinon from "sinon";
import * as actionsutil from "./actions-util";
import { EnvVar } from "./environment";
import { setupActionsVars, setupTests } from "./testing-utils";
import { initializeEnvironment, withTmpDir } from "./util";
@@ -210,7 +211,7 @@ test("computeAutomationID()", async (t) => {
test("initializeEnvironment", (t) => {
initializeEnvironment("1.2.3");
t.deepEqual(process.env.CODEQL_ACTION_VERSION, "1.2.3");
t.deepEqual(process.env[EnvVar.VERSION], "1.2.3");
});
test("isAnalyzingDefaultBranch()", async (t) => {
@@ -265,3 +266,53 @@ test("isAnalyzingDefaultBranch()", async (t) => {
getAdditionalInputStub.restore();
});
});
test("createStatusReportBase", async (t) => {
await withTmpDir(async (tmpDir: string) => {
setupActionsVars(tmpDir, tmpDir);
process.env["GITHUB_REF"] = "refs/heads/main";
process.env["GITHUB_SHA"] = "a".repeat(40);
process.env["GITHUB_RUN_ID"] = "100";
process.env["GITHUB_RUN_ATTEMPT"] = "2";
process.env["GITHUB_REPOSITORY"] = "octocat/HelloWorld";
process.env["CODEQL_ACTION_ANALYSIS_KEY"] = "analysis-key";
process.env["RUNNER_OS"] = "macOS";
const getRequiredInput = sinon.stub(actionsutil, "getRequiredInput");
getRequiredInput.withArgs("matrix").resolves("input/matrix");
const statusReport = await actionsutil.createStatusReportBase(
"init",
"failure",
new Date("May 19, 2023 05:19:00"),
"failure cause",
"exception stack trace"
);
t.assert(typeof statusReport.job_run_uuid === "string");
t.assert(statusReport.workflow_run_id === 100);
t.assert(statusReport.workflow_run_attempt === 2);
t.assert(
statusReport.workflow_name === (process.env["GITHUB_WORKFLOW"] || "")
);
t.assert(statusReport.job_name === (process.env["GITHUB_JOB"] || ""));
t.assert(statusReport.analysis_key === "analysis-key");
t.assert(statusReport.commit_oid === process.env["GITHUB_SHA"]);
t.assert(statusReport.ref === process.env["GITHUB_REF"]);
t.assert(statusReport.action_name === "init");
t.assert(statusReport.action_oid === "unknown");
t.assert(
statusReport.started_at === process.env[EnvVar.WORKFLOW_STARTED_AT]
);
t.assert(
statusReport.action_started_at ===
new Date("May 19, 2023 05:19:00").toISOString()
);
t.assert(statusReport.status === "failure");
t.assert(statusReport.cause === "failure cause");
t.assert(statusReport.exception === "exception stack trace");
t.assert(statusReport.runner_os === process.env["RUNNER_OS"]);
t.assert(typeof statusReport.action_version === "string");
});
});

View File

@@ -9,7 +9,7 @@ import { JSONSchemaForNPMPackageJsonFiles } from "@schemastore/package";
import * as api from "./api-client";
import { Config } from "./config-utils";
import * as sharedEnv from "./shared-environment";
import { EnvVar } from "./environment";
import {
doesDirectoryExist,
getCachedCodeQlVersion,
@@ -36,9 +36,9 @@ const pkg = require("../package.json") as JSONSchemaForNPMPackageJsonFiles;
*
* This allows us to get stronger type checking of required/optional inputs.
*/
export function getRequiredInput(name: string): string {
export const getRequiredInput = function (name: string): string {
return core.getInput(name, { required: true });
}
};
/**
* Wrapper around core.getInput that converts empty inputs to undefined.
@@ -306,7 +306,8 @@ type ActionName =
| "autobuild"
| "finish"
| "upload-sarif"
| "init-post";
| "init-post"
| "resolve-environment";
export type ActionStatus =
| "starting"
| "aborted"
@@ -314,7 +315,38 @@ export type ActionStatus =
| "failure"
| "user-error";
// Any status report may include an array of EventReports associated with it.
export interface EventReport {
/** An enumerable description of the event. */
event: string;
/** Time this event started. */
started_at: string;
/** Time this event ended. */
completed_at: string;
/** eg: `success`, `failure`, `timeout`, etc. */
exit_status?: string;
/** If the event is language-specific. */
language?: string;
/**
* A generic JSON blob of data related to this event.
* Use Object.assign() to append additional fields to the object.
*/
properties?: object;
}
export interface StatusReportBase {
/**
* UUID representing the job run that this status report belongs to. We
* generate our own UUID here because Actions currently does not expose a
* unique job run identifier. This UUID will allow us to more easily match
* reports from different steps in the same workflow job.
*
* If and when Actions does expose a unique job ID, we plan to populate a
* separate int field, `job_run_id`, with the Actions-generated identifier,
* as it will allow us to more easily join our telemetry data with Actions
* telemetry tables.
*/
job_run_uuid: string;
/** ID of the workflow run containing the action run. */
workflow_run_id: number;
/** Attempt number of the run containing the action run. */
@@ -411,34 +443,29 @@ export async function createStatusReportBase(
): Promise<StatusReportBase> {
const commitOid = getOptionalInput("sha") || process.env["GITHUB_SHA"] || "";
const ref = await getRef();
const jobRunUUID = process.env[EnvVar.JOB_RUN_UUID] || "";
const workflowRunID = getWorkflowRunID();
const workflowRunAttempt = getWorkflowRunAttempt();
const workflowName = process.env["GITHUB_WORKFLOW"] || "";
const jobName = process.env["GITHUB_JOB"] || "";
const analysis_key = await getAnalysisKey();
let workflowStartedAt = process.env[sharedEnv.CODEQL_WORKFLOW_STARTED_AT];
let workflowStartedAt = process.env[EnvVar.WORKFLOW_STARTED_AT];
if (workflowStartedAt === undefined) {
workflowStartedAt = actionStartedAt.toISOString();
core.exportVariable(
sharedEnv.CODEQL_WORKFLOW_STARTED_AT,
workflowStartedAt
);
core.exportVariable(EnvVar.WORKFLOW_STARTED_AT, workflowStartedAt);
}
const runnerOs = getRequiredEnvParam("RUNNER_OS");
const codeQlCliVersion = getCachedCodeQlVersion();
const actionRef = process.env["GITHUB_ACTION_REF"];
const testingEnvironment =
process.env[sharedEnv.CODEQL_ACTION_TESTING_ENVIRONMENT] || "";
const testingEnvironment = process.env[EnvVar.TESTING_ENVIRONMENT] || "";
// re-export the testing environment variable so that it is available to subsequent steps,
// even if it was only set for this step
if (testingEnvironment !== "") {
core.exportVariable(
sharedEnv.CODEQL_ACTION_TESTING_ENVIRONMENT,
testingEnvironment
);
core.exportVariable(EnvVar.TESTING_ENVIRONMENT, testingEnvironment);
}
const statusReport: StatusReportBase = {
job_run_uuid: jobRunUUID,
workflow_run_id: workflowRunID,
workflow_run_attempt: workflowRunAttempt,
workflow_name: workflowName,

View File

@@ -19,22 +19,19 @@ import { runAutobuild } from "./autobuild";
import { getCodeQL } from "./codeql";
import { Config, getConfig } from "./config-utils";
import { uploadDatabases } from "./database-upload";
import { EnvVar } from "./environment";
import { Features } from "./feature-flags";
import { Language } from "./languages";
import { getActionsLogger, Logger } from "./logging";
import { parseRepositoryNwo } from "./repository";
import {
CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY,
CODEQL_ACTION_DID_AUTOBUILD_GOLANG,
} from "./shared-environment";
import { getTotalCacheSize, uploadTrapCaches } from "./trap-caching";
import * as upload_lib from "./upload-lib";
import * as uploadLib from "./upload-lib";
import { UploadResult } from "./upload-lib";
import * as util from "./util";
import { checkForTimeout, wrapError } from "./util";
interface AnalysisStatusReport
extends upload_lib.UploadStatusReport,
extends uploadLib.UploadStatusReport,
QueriesStatusReport {}
interface FinishStatusReport
@@ -144,7 +141,7 @@ async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) {
if (!config.languages.includes(Language.go)) {
return;
}
if (process.env[CODEQL_ACTION_DID_AUTOBUILD_GOLANG] === "true") {
if (process.env[EnvVar.DID_AUTOBUILD_GOLANG] === "true") {
logger.debug("Won't run Go autobuild since it has already been run.");
return;
}
@@ -213,9 +210,6 @@ async function run() {
actionsUtil.getOptionalInput("threads") || process.env["CODEQL_THREADS"],
logger
);
const memory = util.getMemoryFlag(
actionsUtil.getOptionalInput("ram") || process.env["CODEQL_RAM"]
);
const repositoryNwo = parseRepositoryNwo(
util.getRequiredEnvParam("GITHUB_REPOSITORY")
@@ -230,6 +224,11 @@ async function run() {
logger
);
const memory = await util.getMemoryFlag(
actionsUtil.getOptionalInput("ram") || process.env["CODEQL_RAM"],
features
);
await runAutobuildIfLegacyGoWorkflow(config, logger);
dbCreationTimings = await runFinalize(
@@ -269,7 +268,7 @@ async function run() {
core.setOutput("db-locations", dbLocations);
const uploadInput = actionsUtil.getOptionalInput("upload");
if (runStats && actionsUtil.getUploadValue(uploadInput) === "always") {
uploadResult = await upload_lib.uploadFromActions(
uploadResult = await uploadLib.uploadFromActions(
outputDir,
actionsUtil.getRequiredInput("checkout_path"),
actionsUtil.getOptionalInput("category"),
@@ -296,7 +295,7 @@ async function run() {
uploadResult !== undefined &&
actionsUtil.getRequiredInput("wait-for-processing") === "true"
) {
await upload_lib.waitForProcessing(
await uploadLib.waitForProcessing(
parseRepositoryNwo(util.getRequiredEnvParam("GITHUB_REPOSITORY")),
uploadResult.sarifID,
getActionsLogger()
@@ -308,10 +307,7 @@ async function run() {
`expect-error input was set to true but no error was thrown.`
);
}
core.exportVariable(
CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY,
"true"
);
core.exportVariable(EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY, "true");
} catch (unwrappedError) {
const error = wrapError(unwrappedError);
if (

View File

@@ -18,13 +18,18 @@ import { Feature } from "./feature-flags";
import { Language } from "./languages";
import { getRunnerLogger } from "./logging";
import { setupTests, setupActionsVars, createFeatures } from "./testing-utils";
import * as uploadLib from "./upload-lib";
import * as util from "./util";
setupTests(test);
// Checks that the duration fields are populated for the correct language
// and correct case of builtin or custom. Also checks the correct search
// paths are set in the database analyze invocation.
/** Checks that the duration fields are populated for the correct language
* and correct case of builtin or custom. Also checks the correct search
* paths are set in the database analyze invocation.
*
* Mocks the QA telemetry feature flag and checks the appropriate status report
* fields.
*/
test("status report fields and search path setting", async (t) => {
let searchPathsUsed: Array<string | undefined> = [];
return await util.withTmpDir(async (tmpDir) => {
@@ -38,6 +43,8 @@ test("status report fields and search path setting", async (t) => {
[Language.java]: ["c/d@2.0.0"],
};
sinon.stub(uploadLib, "validateSarifFileSchema");
for (const language of Object.values(Language)) {
setCodeQL({
packDownload: async () => ({ packs: [] }),
@@ -135,12 +142,12 @@ test("status report fields and search path setting", async (t) => {
undefined,
config,
getRunnerLogger(true),
createFeatures([])
createFeatures([Feature.QaTelemetryEnabled])
);
const hasPacks = language in packs;
const statusReportKeys = Object.keys(builtinStatusReport).sort();
if (hasPacks) {
t.deepEqual(statusReportKeys.length, 3, statusReportKeys.toString());
t.deepEqual(statusReportKeys.length, 4, statusReportKeys.toString());
t.deepEqual(
statusReportKeys[0],
`analyze_builtin_queries_${language}_duration_ms`
@@ -149,8 +156,9 @@ test("status report fields and search path setting", async (t) => {
statusReportKeys[1],
`analyze_custom_queries_${language}_duration_ms`
);
t.deepEqual(statusReportKeys[2], "event_reports");
t.deepEqual(
statusReportKeys[2],
statusReportKeys[3],
`interpret_results_${language}_duration_ms`
);
} else {
@@ -158,11 +166,17 @@ test("status report fields and search path setting", async (t) => {
statusReportKeys[0],
`analyze_builtin_queries_${language}_duration_ms`
);
t.deepEqual(statusReportKeys[1], "event_reports");
t.deepEqual(
statusReportKeys[1],
statusReportKeys[2],
`interpret_results_${language}_duration_ms`
);
}
if (builtinStatusReport.event_reports) {
for (const eventReport of builtinStatusReport.event_reports) {
t.deepEqual(eventReport.event, "codeql database interpret-results");
}
}
config.queries[language] = {
builtin: [],
@@ -185,9 +199,9 @@ test("status report fields and search path setting", async (t) => {
undefined,
config,
getRunnerLogger(true),
createFeatures([])
createFeatures([Feature.QaTelemetryEnabled])
);
t.deepEqual(Object.keys(customStatusReport).length, 2);
t.deepEqual(Object.keys(customStatusReport).length, 3);
t.true(
`analyze_custom_queries_${language}_duration_ms` in customStatusReport
);
@@ -196,6 +210,12 @@ test("status report fields and search path setting", async (t) => {
: [undefined, "/1", "/2"];
t.deepEqual(searchPathsUsed, expectedSearchPathsUsed);
t.true(`interpret_results_${language}_duration_ms` in customStatusReport);
t.true("event_reports" in customStatusReport);
if (customStatusReport.event_reports) {
for (const eventReport of customStatusReport.event_reports) {
t.deepEqual(eventReport.event, "codeql database interpret-results");
}
}
}
verifyQuerySuites(tmpDir);

View File

@@ -6,7 +6,7 @@ import * as toolrunner from "@actions/exec/lib/toolrunner";
import del from "del";
import * as yaml from "js-yaml";
import { DatabaseCreationTimings } from "./actions-util";
import { DatabaseCreationTimings, EventReport } from "./actions-util";
import * as analysisPaths from "./analysis-paths";
import { CodeQL, getCodeQL } from "./codeql";
import * as configUtils from "./config-utils";
@@ -14,6 +14,7 @@ import { FeatureEnablement, Feature } from "./feature-flags";
import { isScannedLanguage, Language } from "./languages";
import { Logger } from "./logging";
import { endTracingForCluster } from "./tracer-config";
import { validateSarifFileSchema } from "./upload-lib";
import * as util from "./util";
export class CodeQLAnalysisError extends Error {
@@ -78,6 +79,8 @@ export interface QueriesStatusReport {
interpret_results_swift_duration_ms?: number;
/** Name of language that errored during analysis (or undefined if no language failed). */
analyze_failure_language?: string;
/** Reports on discrete events associated with this status report. */
event_reports?: EventReport[];
}
async function setupPythonExtractor(
@@ -92,7 +95,10 @@ async function setupPythonExtractor(
}
if (
await features.getValue(Feature.DisablePythonDependencyInstallation, codeql)
await features.getValue(
Feature.DisablePythonDependencyInstallationEnabled,
codeql
)
) {
logger.warning(
"We recommend that you remove the CODEQL_PYTHON environment variable from your workflow. This environment variable was originally used to specify a Python executable that included the dependencies of your Python code, however Python analysis no longer uses these dependencies." +
@@ -239,6 +245,9 @@ export async function runQueries(
const packsWithVersion = config.packs[language] || [];
try {
const sarifFile = path.join(sarifFolder, `${language}.sarif`);
let startTimeInterpretResults: Date;
let endTimeInterpretResults: Date;
if (await util.useCodeScanningConfigInCli(codeql, features)) {
// If we are using the code scanning config in the CLI,
// much of the work needed to generate the query suites
@@ -254,16 +263,17 @@ export async function runQueries(
new Date().getTime() - startTimeBuiltIn;
logger.startGroup(`Interpreting results for ${language}`);
const startTimeInterpretResults = new Date().getTime();
const sarifFile = path.join(sarifFolder, `${language}.sarif`);
startTimeInterpretResults = new Date();
const analysisSummary = await runInterpretResults(
language,
undefined,
sarifFile,
config.debugMode
);
endTimeInterpretResults = new Date();
statusReport[`interpret_results_${language}_duration_ms`] =
new Date().getTime() - startTimeInterpretResults;
endTimeInterpretResults.getTime() -
startTimeInterpretResults.getTime();
logger.endGroup();
logger.info(analysisSummary);
} else {
@@ -339,19 +349,38 @@ export async function runQueries(
}
logger.endGroup();
logger.startGroup(`Interpreting results for ${language}`);
const startTimeInterpretResults = new Date().getTime();
const sarifFile = path.join(sarifFolder, `${language}.sarif`);
startTimeInterpretResults = new Date();
const analysisSummary = await runInterpretResults(
language,
querySuitePaths,
sarifFile,
config.debugMode
);
endTimeInterpretResults = new Date();
statusReport[`interpret_results_${language}_duration_ms`] =
new Date().getTime() - startTimeInterpretResults;
endTimeInterpretResults.getTime() -
startTimeInterpretResults.getTime();
logger.endGroup();
logger.info(analysisSummary);
}
if (await features.getValue(Feature.QaTelemetryEnabled)) {
const perQueryAlertCounts = getPerQueryAlertCounts(sarifFile, logger);
const perQueryAlertCountEventReport: EventReport = {
event: "codeql database interpret-results",
started_at: startTimeInterpretResults.toISOString(),
completed_at: endTimeInterpretResults.toISOString(),
exit_status: "success",
language,
properties: perQueryAlertCounts,
};
if (statusReport["event_reports"] === undefined) {
statusReport["event_reports"] = [];
}
statusReport["event_reports"].push(perQueryAlertCountEventReport);
}
await runPrintLinesOfCode(language);
} catch (e) {
logger.info(String(e));
@@ -389,6 +418,34 @@ export async function runQueries(
);
}
/** Get an object with all queries and their counts parsed from a SARIF file path. */
function getPerQueryAlertCounts(
sarifPath: string,
log: Logger
): Record<string, number> {
validateSarifFileSchema(sarifPath, log);
const sarifObject = JSON.parse(
fs.readFileSync(sarifPath, "utf8")
) as util.SarifFile;
// We do not need to compute fingerprints because we are not sending data based off of locations.
// Generate the query: alert count object
const perQueryAlertCounts: Record<string, number> = {};
// All rules (queries), from all results, from all runs
for (const sarifRun of sarifObject.runs) {
if (sarifRun.results) {
for (const result of sarifRun.results) {
const query = result.rule?.id || result.ruleId;
if (query) {
perQueryAlertCounts[query] = (perQueryAlertCounts[query] || 0) + 1;
}
}
}
}
return perQueryAlertCounts;
}
async function runPrintLinesOfCode(language: Language): Promise<string> {
const databasePath = util.getCodeQLDatabasePath(config, language);
return await codeql.databasePrintBaseline(databasePath);

View File

@@ -12,9 +12,9 @@ import {
import { getGitHubVersion } from "./api-client";
import { determineAutobuildLanguages, runAutobuild } from "./autobuild";
import * as configUtils from "./config-utils";
import { EnvVar } from "./environment";
import { Language } from "./languages";
import { getActionsLogger } from "./logging";
import { CODEQL_ACTION_DID_AUTOBUILD_GOLANG } from "./shared-environment";
import {
checkGitHubVersionInRange,
initializeEnvironment,
@@ -89,7 +89,7 @@ async function run() {
currentLanguage = language;
await runAutobuild(language, config, logger);
if (language === Language.go) {
core.exportVariable(CODEQL_ACTION_DID_AUTOBUILD_GOLANG, "true");
core.exportVariable(EnvVar.DID_AUTOBUILD_GOLANG, "true");
}
}
}

View File

@@ -11,7 +11,6 @@ import nock from "nock";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as api from "./api-client";
import { GitHubApiDetails } from "./api-client";
import * as codeql from "./codeql";
import { AugmentationProperties, Config } from "./config-utils";
@@ -90,7 +89,7 @@ async function installIntoToolcache({
tmpDir,
util.GitHubVariant.GHES,
cliVersion !== undefined
? { cliVersion, tagName, variant: util.GitHubVariant.GHES }
? { cliVersion, tagName }
: SAMPLE_DEFAULT_CLI_VERSION,
getRunnerLogger(true),
false
@@ -246,21 +245,11 @@ for (const {
});
}
for (const { githubReleases, toolcacheVersion } of [
for (const toolcacheVersion of [
// Test that we use the tools from the toolcache when `SAMPLE_DEFAULT_CLI_VERSION` is requested
// and `SAMPLE_DEFAULT_CLI_VERSION-` is in the toolcache.
{
toolcacheVersion: SAMPLE_DEFAULT_CLI_VERSION.cliVersion,
},
{
githubReleases: {
"codeql-bundle-20230101": `cli-version-${SAMPLE_DEFAULT_CLI_VERSION.cliVersion}.txt`,
},
toolcacheVersion: "0.0.0-20230101",
},
{
toolcacheVersion: `${SAMPLE_DEFAULT_CLI_VERSION.cliVersion}-20230101`,
},
SAMPLE_DEFAULT_CLI_VERSION.cliVersion,
`${SAMPLE_DEFAULT_CLI_VERSION.cliVersion}-20230101`,
]) {
test(
`uses tools from toolcache when ${SAMPLE_DEFAULT_CLI_VERSION.cliVersion} is requested and ` +
@@ -275,26 +264,6 @@ for (const { githubReleases, toolcacheVersion } of [
.returns("path/to/cached/codeql");
sinon.stub(toolcache, "findAllVersions").returns([toolcacheVersion]);
if (githubReleases) {
sinon.stub(api, "getApiClient").value(() => ({
repos: {
listReleases: sinon.stub().resolves(undefined),
},
paginate: sinon.stub().resolves(
Object.entries(githubReleases).map(
([releaseTagName, cliVersionMarkerFile]) => ({
assets: [
{
name: cliVersionMarkerFile,
},
],
tag_name: releaseTagName,
})
)
),
}));
}
const result = await codeql.setupCodeQL(
undefined,
SAMPLE_DOTCOM_API_DETAILS,
@@ -331,7 +300,6 @@ for (const variant of [util.GitHubVariant.GHAE, util.GitHubVariant.GHES]) {
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
variant,
},
getRunnerLogger(true),
false
@@ -366,7 +334,6 @@ for (const variant of [util.GitHubVariant.GHAE, util.GitHubVariant.GHES]) {
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
variant,
},
getRunnerLogger(true),
false
@@ -474,7 +441,6 @@ for (const isBundleVersionInUrl of [true, false]) {
{
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
variant: util.GitHubVariant.GHAE,
},
getRunnerLogger(true),
false

View File

@@ -1,12 +1,14 @@
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 yaml from "js-yaml";
import { getOptionalInput } from "./actions-util";
import * as api from "./api-client";
import { Config, getGeneratedCodeScanningConfigPath } from "./config-utils";
import { EnvVar } from "./environment";
import { errorMatchers } from "./error-matcher";
import {
CodeQLDefaultVersionInfo,
@@ -118,6 +120,13 @@ export interface CodeQL {
queries: string[],
extraSearchPath: string | undefined
): Promise<ResolveQueriesOutput>;
/**
* Run 'codeql resolve build-environment'
*/
resolveBuildEnvironment(
workingDir: string | undefined,
language: Language
): Promise<ResolveBuildEnvironmentOutput>;
/**
* Run 'codeql pack download'.
@@ -193,8 +202,7 @@ export interface CodeQL {
diagnosticsExport(
sarifFile: string,
automationDetailsId: string | undefined,
config: Config,
features: FeatureEnablement
config: Config
): Promise<void>;
/** Get the location of an extractor for the specified language. */
resolveExtractor(language: Language): Promise<string>;
@@ -229,6 +237,14 @@ export interface ResolveQueriesOutput {
};
}
export interface ResolveBuildEnvironmentOutput {
configuration?: {
[language: string]: {
[key: string]: unknown;
};
};
}
export interface PackDownloadOutput {
packs: PackDownloadItem[];
}
@@ -256,6 +272,11 @@ let cachedCodeQL: CodeQL | undefined = undefined;
*/
const CODEQL_MINIMUM_VERSION = "2.9.4";
/**
* This version will shortly become the oldest version of CodeQL that the Action will run with.
*/
const CODEQL_NEXT_MINIMUM_VERSION = "2.9.4";
/**
* Versions of CodeQL that version-flag certain functionality in the Action.
* For convenience, please keep these in descending order. Once a version
@@ -273,15 +294,37 @@ const CODEQL_VERSION_FILE_BASELINE_INFORMATION = "2.11.3";
export const CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = "2.10.3";
/**
* Versions 2.11.1+ of the CodeQL Bundle include a `security-experimental` built-in query suite for each language.
* Versions 2.11.1+ of the CodeQL Bundle include a `security-experimental` built-in query suite for
* each language.
*/
export const CODEQL_VERSION_SECURITY_EXPERIMENTAL_SUITE = "2.12.1";
/**
* Versions 2.12.3+ of the CodeQL CLI support exporting configuration information from a code
* scanning config file to SARIF.
*/
export const CODEQL_VERSION_EXPORT_CODE_SCANNING_CONFIG = "2.12.3";
/**
* Versions 2.12.4+ of the CodeQL CLI support the `--qlconfig-file` flag in calls to `database init`.
*/
export const CODEQL_VERSION_INIT_WITH_QLCONFIG = "2.12.4";
/**
* Versions 2.13.4+ of the CodeQL CLI support the `resolve build-environment` command.
*/
export const CODEQL_VERSION_RESOLVE_ENVIRONMENT = "2.13.4";
/**
* Versions 2.13.4+ of the CodeQL CLI have an associated CodeQL Bundle release that is semantically versioned.
*/
export const CODEQL_VERSION_BUNDLE_SEMANTICALLY_VERSIONED = "2.13.4";
/**
* Versions 2.14.0+ of the CodeQL CLI support new analysis summaries.
*/
export const CODEQL_VERSION_NEW_ANALYSIS_SUMMARY = "2.14.0";
/**
* Set up CodeQL CLI access.
*
@@ -395,6 +438,10 @@ export function setCodeQL(partialCodeql: Partial<CodeQL>): CodeQL {
"betterResolveLanguages"
),
resolveQueries: resolveFunction(partialCodeql, "resolveQueries"),
resolveBuildEnvironment: resolveFunction(
partialCodeql,
"resolveBuildEnvironment"
),
packDownload: resolveFunction(partialCodeql, "packDownload"),
databaseCleanup: resolveFunction(partialCodeql, "databaseCleanup"),
databaseBundle: resolveFunction(partialCodeql, "databaseBundle"),
@@ -674,6 +721,29 @@ export async function getCodeQLForCmd(
throw new Error(`Unexpected output from codeql resolve queries: ${e}`);
}
},
async resolveBuildEnvironment(
workingDir: string | undefined,
language: Language
) {
const codeqlArgs = [
"resolve",
"build-environment",
`--language=${language}`,
...getExtraOptionsFromEnv(["resolve", "build-environment"]),
];
if (workingDir !== undefined) {
codeqlArgs.push("--working-dir", workingDir);
}
const output = await runTool(cmd, codeqlArgs);
try {
return JSON.parse(output);
} catch (e) {
throw new Error(
`Unexpected output from codeql resolve build-environment: ${e} in\n${output}`
);
}
},
async databaseRunQueries(
databasePath: string,
extraSearchPath: string | undefined,
@@ -737,7 +807,7 @@ export async function getCodeQLForCmd(
"--print-metrics-summary",
"--sarif-add-query-help",
"--sarif-group-rules-by-pack",
...(await getCodeScanningConfigExportArguments(config, this, features)),
...(await getCodeScanningConfigExportArguments(config, this)),
...getExtraOptionsFromEnv(["database", "interpret-results"]),
];
if (automationDetailsId !== undefined) {
@@ -756,6 +826,16 @@ export async function getCodeQLForCmd(
} else if (await util.codeQlVersionAbove(this, "2.12.4")) {
codeqlArgs.push("--no-sarif-include-diagnostics");
}
if (await features.getValue(Feature.NewAnalysisSummaryEnabled, codeql)) {
codeqlArgs.push("--new-analysis-summary");
} else if (
await util.codeQlVersionAbove(
codeql,
CODEQL_VERSION_NEW_ANALYSIS_SUMMARY
)
) {
codeqlArgs.push("--no-new-analysis-summary");
}
codeqlArgs.push(databasePath);
if (querySuitePaths) {
codeqlArgs.push(...querySuitePaths);
@@ -899,15 +979,14 @@ export async function getCodeQLForCmd(
async diagnosticsExport(
sarifFile: string,
automationDetailsId: string | undefined,
config: Config,
features: FeatureEnablement
config: Config
): Promise<void> {
const args = [
"diagnostics",
"export",
"--format=sarif-latest",
`--output=${sarifFile}`,
...(await getCodeScanningConfigExportArguments(config, this, features)),
...(await getCodeScanningConfigExportArguments(config, this)),
...getExtraOptionsFromEnv(["diagnostics", "export"]),
];
if (automationDetailsId !== undefined) {
@@ -958,6 +1037,24 @@ export async function getCodeQLForCmd(
throw new Error(
`Expected a CodeQL CLI with version at least ${CODEQL_MINIMUM_VERSION} but got version ${await codeql.getVersion()}`
);
} else if (
checkVersion &&
process.env[EnvVar.SUPPRESS_DEPRECATED_SOON_WARNING] !== "true" &&
!(await util.codeQlVersionAbove(codeql, CODEQL_NEXT_MINIMUM_VERSION))
) {
core.warning(
`CodeQL CLI version ${await codeql.getVersion()} was deprecated on 2023-06-20 alongside ` +
"GitHub Enterprise Server 3.5 and will not be supported by the next release of the " +
`CodeQL Action. Please update to CodeQL CLI version ${CODEQL_NEXT_MINIMUM_VERSION} or ` +
"later. For instance, if you have specified a custom version of the CLI using the " +
"'tools' input to the 'init' Action, you can remove this input to use the default " +
"version.\n\n" +
"Alternatively, if you want to continue using CodeQL CLI version " +
`${await codeql.getVersion()}, you can replace 'github/codeql-action/*@v2' by ` +
"'github/codeql-action/*@v2.20.4' in your code scanning workflow to ensure you continue " +
"using this version of the CodeQL Action."
);
core.exportVariable(EnvVar.SUPPRESS_DEPRECATED_SOON_WARNING, "true");
}
return codeql;
}
@@ -1160,13 +1257,15 @@ function cloneObject<T>(obj: T): T {
*/
async function getCodeScanningConfigExportArguments(
config: Config,
codeql: CodeQL,
features: FeatureEnablement
codeql: CodeQL
): Promise<string[]> {
const codeScanningConfigPath = getGeneratedCodeScanningConfigPath(config);
if (
fs.existsSync(codeScanningConfigPath) &&
(await features.getValue(Feature.ExportCodeScanningConfigEnabled, codeql))
(await util.codeQlVersionAbove(
codeql,
CODEQL_VERSION_EXPORT_CODE_SCANNING_CONFIG
))
) {
return ["--sarif-codescanning-config", codeScanningConfigPath];
}

View File

@@ -1,6 +1,6 @@
{
"bundleVersion": "codeql-bundle-20230524",
"cliVersion": "2.13.3",
"priorBundleVersion": "codeql-bundle-20230428",
"priorCliVersion": "2.13.1"
"bundleVersion": "codeql-bundle-v2.13.5",
"cliVersion": "2.13.5",
"priorBundleVersion": "codeql-bundle-v2.13.4",
"priorCliVersion": "2.13.4"
}

63
src/environment.ts Normal file
View File

@@ -0,0 +1,63 @@
export enum EnvVar {
/** Set to true when the `analyze` Action completes successfully. */
ANALYZE_DID_COMPLETE_SUCCESSFULLY = "CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY",
/** Set to "true" when the CodeQL Action has invoked the Go autobuilder. */
DID_AUTOBUILD_GOLANG = "CODEQL_ACTION_DID_AUTOBUILD_GOLANG",
/**
* Used to disable the SARIF post-processing in the Action that removes duplicate locations from
* notifications in the `run[].invocations[].toolExecutionNotifications` SARIF property.
*/
DISABLE_DUPLICATE_LOCATION_FIX = "CODEQL_ACTION_DISABLE_DUPLICATE_LOCATION_FIX",
/**
* If set to the "true" string, then the CodeQL Action is using its
* own deprecated and non-standard way of scanning for multiple
* languages.
*/
FEATURE_MULTI_LANGUAGE = "CODEQL_ACTION_FEATURE_MULTI_LANGUAGE",
/**
* If set to the "true" string, then the CodeQL Action is using its
* own sandwiched workflow mechanism.
*/
FEATURE_SANDWICH = "CODEQL_ACTION_FEATURE_SANDWICH",
/**
* If set to a truthy value, then the CodeQL Action might combine SARIF
* output from several `interpret-results` runs for the same language.
*/
FEATURE_SARIF_COMBINE = "CODEQL_ACTION_FEATURE_SARIF_COMBINE",
/**
* If set to the "true" string, then the CodeQL Action will upload SARIF,
* not the CLI.
*/
FEATURE_WILL_UPLOAD = "CODEQL_ACTION_FEATURE_WILL_UPLOAD",
/** UUID representing the current job run. */
JOB_RUN_UUID = "JOB_RUN_UUID",
ODASA_TRACER_CONFIGURATION = "ODASA_TRACER_CONFIGURATION",
/** Whether to suppress the warning if the current CLI will soon be unsupported. */
SUPPRESS_DEPRECATED_SOON_WARNING = "CODEQL_ACTION_SUPPRESS_DEPRECATED_SOON_WARNING",
/** Used to disable uploading SARIF results or status reports to the GitHub API */
TEST_MODE = "CODEQL_ACTION_TEST_MODE",
TESTING_ENVIRONMENT = "CODEQL_ACTION_TESTING_ENVIRONMENT",
/** Semver of the CodeQL Action as specified in `package.json`. */
VERSION = "CODEQL_ACTION_VERSION",
/**
* The time at which the first action (normally init) started executing.
* If a workflow invokes a different action without first invoking the init
* action (i.e. the upload action is being used by a third-party integrator)
* then this variable will be assigned the start time of the action invoked
* rather that the init action.
*/
WORKFLOW_STARTED_AT = "CODEQL_WORKFLOW_STARTED_AT",
}

View File

@@ -366,31 +366,48 @@ for (const variant of [GitHubVariant.GHAE, GitHubVariant.GHES]) {
t.deepEqual(defaultCliVersion, {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
variant,
});
});
});
}
test("selects CLI v2.12.1 on Dotcom when feature flags enable v2.12.0 and v2.12.1", async (t) => {
test("selects CLI v2.20.1 on Dotcom when feature flags enable v2.20.0 and v2.20.1", async (t) => {
await withTmpDir(async (tmpDir) => {
const features = setUpFeatureFlagTests(tmpDir);
const expectedFeatureEnablement = initializeFeatures(true);
expectedFeatureEnablement["default_codeql_version_2_12_0_enabled"] = true;
expectedFeatureEnablement["default_codeql_version_2_12_1_enabled"] = true;
expectedFeatureEnablement["default_codeql_version_2_12_2_enabled"] = false;
expectedFeatureEnablement["default_codeql_version_2_12_3_enabled"] = false;
expectedFeatureEnablement["default_codeql_version_2_12_4_enabled"] = false;
expectedFeatureEnablement["default_codeql_version_2_12_5_enabled"] = false;
expectedFeatureEnablement["default_codeql_version_2_20_0_enabled"] = true;
expectedFeatureEnablement["default_codeql_version_2_20_1_enabled"] = true;
expectedFeatureEnablement["default_codeql_version_2_20_2_enabled"] = false;
expectedFeatureEnablement["default_codeql_version_2_20_3_enabled"] = false;
expectedFeatureEnablement["default_codeql_version_2_20_4_enabled"] = false;
expectedFeatureEnablement["default_codeql_version_2_20_5_enabled"] = false;
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
const defaultCliVersion = await features.getDefaultCliVersion(
GitHubVariant.DOTCOM
);
t.deepEqual(defaultCliVersion, {
cliVersion: "2.12.1",
cliVersion: "2.20.1",
tagName: "codeql-bundle-v2.20.1",
toolsFeatureFlagsValid: true,
});
});
});
test("includes tag name when feature flags enable version greater than v2.13.4", async (t) => {
await withTmpDir(async (tmpDir) => {
const features = setUpFeatureFlagTests(tmpDir);
const expectedFeatureEnablement = initializeFeatures(true);
expectedFeatureEnablement["default_codeql_version_2_20_0_enabled"] = true;
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
const defaultCliVersion = await features.getDefaultCliVersion(
GitHubVariant.DOTCOM
);
t.deepEqual(defaultCliVersion, {
cliVersion: "2.20.0",
tagName: "codeql-bundle-v2.20.0",
toolsFeatureFlagsValid: true,
variant: GitHubVariant.DOTCOM,
});
});
});
@@ -406,8 +423,27 @@ test(`selects CLI from defaults.json on Dotcom when no default version feature f
);
t.deepEqual(defaultCliVersion, {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
toolsFeatureFlagsValid: false,
});
});
});
test(`selects CLI from defaults.json on Dotcom when default version feature flags are unsupported`, async (t) => {
await withTmpDir(async (tmpDir) => {
const features = setUpFeatureFlagTests(tmpDir);
const expectedFeatureEnablement = initializeFeatures(true);
// Doesn't have a semantically versioned bundle
expectedFeatureEnablement["default_codeql_version_2_13_3_enabled"] = true;
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
const defaultCliVersion = await features.getDefaultCliVersion(
GitHubVariant.DOTCOM
);
t.deepEqual(defaultCliVersion, {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
toolsFeatureFlagsValid: false,
variant: GitHubVariant.DOTCOM,
});
});
});
@@ -420,9 +456,9 @@ test("ignores invalid version numbers in default version feature flags", async (
getRecordingLogger(loggedMessages)
);
const expectedFeatureEnablement = initializeFeatures(true);
expectedFeatureEnablement["default_codeql_version_2_12_0_enabled"] = true;
expectedFeatureEnablement["default_codeql_version_2_12_1_enabled"] = true;
expectedFeatureEnablement["default_codeql_version_2_12_invalid_enabled"] =
expectedFeatureEnablement["default_codeql_version_2_20_0_enabled"] = true;
expectedFeatureEnablement["default_codeql_version_2_20_1_enabled"] = true;
expectedFeatureEnablement["default_codeql_version_2_20_invalid_enabled"] =
true;
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
@@ -430,9 +466,9 @@ test("ignores invalid version numbers in default version feature flags", async (
GitHubVariant.DOTCOM
);
t.deepEqual(defaultCliVersion, {
cliVersion: "2.12.1",
cliVersion: "2.20.1",
tagName: "codeql-bundle-v2.20.1",
toolsFeatureFlagsValid: true,
variant: GitHubVariant.DOTCOM,
});
t.assert(
@@ -440,7 +476,7 @@ test("ignores invalid version numbers in default version feature flags", async (
(v: LoggedMessage) =>
v.type === "warning" &&
v.message ===
"Ignoring feature flag default_codeql_version_2_12_invalid_enabled as it does not specify a valid CodeQL version."
"Ignoring feature flag default_codeql_version_2_20_invalid_enabled as it does not specify a valid CodeQL version."
) !== undefined
);
});

View File

@@ -4,7 +4,11 @@ import * as path from "path";
import * as semver from "semver";
import { getApiClient } from "./api-client";
import { CodeQL } from "./codeql";
import {
CODEQL_VERSION_BUNDLE_SEMANTICALLY_VERSIONED,
CODEQL_VERSION_NEW_ANALYSIS_SUMMARY,
CodeQL,
} from "./codeql";
import * as defaults from "./defaults.json";
import { Logger } from "./logging";
import { RepositoryNwo } from "./repository";
@@ -13,20 +17,11 @@ import * as util from "./util";
const DEFAULT_VERSION_FEATURE_FLAG_PREFIX = "default_codeql_version_";
const DEFAULT_VERSION_FEATURE_FLAG_SUFFIX = "_enabled";
export type CodeQLDefaultVersionInfo =
| {
cliVersion: string;
toolsFeatureFlagsValid?: boolean;
variant: util.GitHubVariant.DOTCOM;
}
| {
cliVersion: string;
tagName: string;
variant:
| util.GitHubVariant.GHAE
| util.GitHubVariant.GHES
| util.GitHubVariant.GHE_DOTCOM;
};
export interface CodeQLDefaultVersionInfo {
cliVersion: string;
tagName: string;
toolsFeatureFlagsValid?: boolean;
}
export interface FeatureEnablement {
/** Gets the default version of the CodeQL tools. */
@@ -39,11 +34,13 @@ export interface FeatureEnablement {
export enum Feature {
CliConfigFileEnabled = "cli_config_file_enabled",
DisableKotlinAnalysisEnabled = "disable_kotlin_analysis_enabled",
ExportCodeScanningConfigEnabled = "export_code_scanning_config_enabled",
DisablePythonDependencyInstallationEnabled = "disable_python_dependency_installation_enabled",
ExportDiagnosticsEnabled = "export_diagnostics_enabled",
MlPoweredQueriesEnabled = "ml_powered_queries_enabled",
NewAnalysisSummaryEnabled = "new_analysis_summary_enabled",
QaTelemetryEnabled = "qa_telemetry_enabled",
ScalingReservedRam = "scaling_reserved_ram",
UploadFailedSarifEnabled = "upload_failed_sarif_enabled",
DisablePythonDependencyInstallation = "disable_python_dependency_installation",
}
export const featureConfig: Record<
@@ -60,28 +57,37 @@ export const featureConfig: Record<
minimumVersion: "2.11.6",
defaultValue: true,
},
[Feature.ExportCodeScanningConfigEnabled]: {
envVar: "CODEQL_ACTION_EXPORT_CODE_SCANNING_CONFIG",
minimumVersion: "2.12.3",
defaultValue: true,
},
[Feature.ExportDiagnosticsEnabled]: {
envVar: "CODEQL_ACTION_EXPORT_DIAGNOSTICS",
minimumVersion: "2.12.4",
defaultValue: true,
},
[Feature.MlPoweredQueriesEnabled]: {
envVar: "CODEQL_ML_POWERED_QUERIES",
minimumVersion: undefined,
defaultValue: false,
},
[Feature.NewAnalysisSummaryEnabled]: {
envVar: "CODEQL_ACTION_NEW_ANALYSIS_SUMMARY",
minimumVersion: CODEQL_VERSION_NEW_ANALYSIS_SUMMARY,
defaultValue: false,
},
[Feature.QaTelemetryEnabled]: {
envVar: "CODEQL_ACTION_QA_TELEMETRY",
minimumVersion: undefined,
defaultValue: false,
},
[Feature.ScalingReservedRam]: {
envVar: "CODEQL_ACTION_SCALING_RESERVED_RAM",
minimumVersion: undefined,
defaultValue: false,
},
[Feature.UploadFailedSarifEnabled]: {
envVar: "CODEQL_ACTION_UPLOAD_FAILED_SARIF",
minimumVersion: "2.11.3",
defaultValue: true,
},
[Feature.DisablePythonDependencyInstallation]: {
[Feature.DisablePythonDependencyInstallationEnabled]: {
envVar: "CODEQL_ACTION_DISABLE_PYTHON_DEPENDENCY_INSTALLATION",
// Although the python extractor only started supporting not extracting installed
// dependencies in 2.13.1, the init-action can still benefit from not installing
@@ -251,33 +257,27 @@ class GitHubFeatureFlags {
variant: util.GitHubVariant
): Promise<CodeQLDefaultVersionInfo> {
if (variant === util.GitHubVariant.DOTCOM) {
const defaultDotComCliVersion = await this.getDefaultDotcomCliVersion();
return {
cliVersion: defaultDotComCliVersion.version,
toolsFeatureFlagsValid: this.hasAccessedRemoteFeatureFlags
? defaultDotComCliVersion.toolsFeatureFlagsValid
: undefined,
variant,
};
return await this.getDefaultDotcomCliVersion();
}
return {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
variant,
};
}
async getDefaultDotcomCliVersion(): Promise<{
version: string;
toolsFeatureFlagsValid: boolean | undefined;
}> {
async getDefaultDotcomCliVersion(): Promise<CodeQLDefaultVersionInfo> {
const response = await this.getAllFeatures();
const enabledFeatureFlagCliVersions = Object.entries(response)
.map(([f, isEnabled]) =>
isEnabled ? this.getCliVersionFromFeatureFlag(f) : undefined
)
.filter((f) => f !== undefined)
.filter(
(f) =>
f !== undefined &&
// Only consider versions that have semantically versioned bundles.
semver.gte(f, CODEQL_VERSION_BUNDLE_SEMANTICALLY_VERSIONED)
)
.map((f) => f as string);
if (enabledFeatureFlagCliVersions.length === 0) {
@@ -295,12 +295,14 @@ class GitHubFeatureFlags {
"Feature flags do not specify a default CLI version. Falling back to the CLI version " +
`shipped with the Action. This is ${defaults.cliVersion}.`
);
return {
version: defaults.cliVersion,
toolsFeatureFlagsValid: this.hasAccessedRemoteFeatureFlags
? false
: undefined,
const result: CodeQLDefaultVersionInfo = {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
};
if (this.hasAccessedRemoteFeatureFlags) {
result.toolsFeatureFlagsValid = false;
}
return result;
}
const maxCliVersion = enabledFeatureFlagCliVersions.reduce(
@@ -311,7 +313,11 @@ class GitHubFeatureFlags {
this.logger.debug(
`Derived default CLI version of ${maxCliVersion} from feature flags.`
);
return { version: maxCliVersion, toolsFeatureFlagsValid: true };
return {
cliVersion: maxCliVersion,
tagName: `codeql-bundle-v${maxCliVersion}`,
toolsFeatureFlagsValid: true,
};
}
async getValue(feature: Feature): Promise<boolean | undefined> {

View File

@@ -409,8 +409,7 @@ async function testFailedSarifUpload(
diagnosticsExportStub.calledOnceWith(
sinon.match.string,
category,
config,
sinon.match.any
config
),
`Actual args were: ${diagnosticsExportStub.args}`
);

View File

@@ -3,10 +3,10 @@ import * as core from "@actions/core";
import * as actionsUtil from "./actions-util";
import { getCodeQL } from "./codeql";
import { Config, getConfig } from "./config-utils";
import { EnvVar } from "./environment";
import { Feature, FeatureEnablement } from "./feature-flags";
import { Logger } from "./logging";
import { RepositoryNwo } from "./repository";
import { CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY } from "./shared-environment";
import * as uploadLib from "./upload-lib";
import {
getRequiredEnvParam,
@@ -80,7 +80,7 @@ async function maybeUploadFailedSarif(
databasePath === undefined ||
!(await features.getValue(Feature.ExportDiagnosticsEnabled, codeql))
) {
await codeql.diagnosticsExport(sarifFile, category, config, features);
await codeql.diagnosticsExport(sarifFile, category, config);
} else {
// We call 'database export-diagnostics' to find any per-database diagnostics.
await codeql.databaseExportDiagnostics(
@@ -114,7 +114,7 @@ export async function tryUploadSarifIfRunFailed(
features: FeatureEnablement,
logger: Logger
): Promise<UploadFailedSarifResult> {
if (process.env[CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY] !== "true") {
if (process.env[EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY] !== "true") {
try {
return await maybeUploadFailedSarif(
config,

View File

@@ -1,6 +1,7 @@
import * as path from "path";
import * as core from "@actions/core";
import { v4 as uuidV4 } from "uuid";
import {
createStatusReportBase,
@@ -15,6 +16,7 @@ import {
import { getGitHubVersion } from "./api-client";
import { CodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { EnvVar } from "./environment";
import { Feature, Features } from "./feature-flags";
import {
initCodeQL,
@@ -36,7 +38,6 @@ import {
getMlPoweredJsQueriesStatus,
getRequiredEnvParam,
getThreadsFlagValue,
GitHubVariant,
initializeEnvironment,
isHostedRunner,
wrapError,
@@ -212,6 +213,8 @@ async function run() {
logger
);
core.exportVariable(EnvVar.JOB_RUN_UUID, uuidV4());
try {
const workflowErrors = await validateWorkflow(logger);
@@ -231,9 +234,7 @@ async function run() {
const codeQLDefaultVersionInfo = await features.getDefaultCliVersion(
gitHubVersion.type
);
if (codeQLDefaultVersionInfo.variant === GitHubVariant.DOTCOM) {
toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid;
}
toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid;
const initCodeQLResult = await initCodeQL(
getOptionalInput("tools"),
apiDetails,
@@ -279,7 +280,7 @@ async function run() {
) {
if (
await features.getValue(
Feature.DisablePythonDependencyInstallation,
Feature.DisablePythonDependencyInstallationEnabled,
codeql
)
) {
@@ -328,7 +329,7 @@ async function run() {
core.exportVariable(
"CODEQL_RAM",
process.env["CODEQL_RAM"] ||
getMemoryFlagValue(getOptionalInput("ram")).toString()
(await getMemoryFlagValue(getOptionalInput("ram"), features)).toString()
);
core.exportVariable(
"CODEQL_THREADS",
@@ -343,7 +344,7 @@ async function run() {
// Disable Python dependency extraction if feature flag set
if (
await features.getValue(
Feature.DisablePythonDependencyInstallation,
Feature.DisablePythonDependencyInstallationEnabled,
codeql
)
) {

View File

@@ -0,0 +1,98 @@
import * as core from "@actions/core";
import {
createStatusReportBase,
getActionsStatus,
getOptionalInput,
getRequiredInput,
getTemporaryDirectory,
sendStatusReport,
} from "./actions-util";
import { getGitHubVersion } from "./api-client";
import { CommandInvocationError } from "./codeql";
import * as configUtils from "./config-utils";
import { Language, resolveAlias } from "./languages";
import { getActionsLogger } from "./logging";
import { runResolveBuildEnvironment } from "./resolve-environment";
import { checkForTimeout, checkGitHubVersionInRange, wrapError } from "./util";
const ACTION_NAME = "resolve-environment";
const ENVIRONMENT_OUTPUT_NAME = "environment";
async function run() {
const startedAt = new Date();
const logger = getActionsLogger();
const language: Language = resolveAlias(getRequiredInput("language"));
try {
if (
!(await sendStatusReport(
await createStatusReportBase(ACTION_NAME, "starting", startedAt)
))
) {
return;
}
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);
const config = await configUtils.getConfig(getTemporaryDirectory(), logger);
if (config === undefined) {
throw new Error(
"Config file could not be found at expected location. Has the 'init' action been called?"
);
}
const workingDirectory = getOptionalInput("working-directory");
const result = await runResolveBuildEnvironment(
config.codeQLCmd,
logger,
workingDirectory,
language
);
core.setOutput(ENVIRONMENT_OUTPUT_NAME, result);
} catch (unwrappedError) {
const error = wrapError(unwrappedError);
if (error instanceof CommandInvocationError) {
// If the CLI failed to run successfully for whatever reason,
// we just return an empty JSON object and proceed with the workflow.
core.setOutput(ENVIRONMENT_OUTPUT_NAME, {});
logger.warning(
`Failed to resolve a build environment suitable for automatically building your code. ${error.message}`
);
} else {
// For any other error types, something has more seriously gone wrong and we fail.
core.setFailed(
`Failed to resolve a build environment suitable for automatically building your code. ${error.message}`
);
await sendStatusReport(
await createStatusReportBase(
ACTION_NAME,
getActionsStatus(error),
startedAt,
error.message,
error.stack
)
);
}
return;
}
await sendStatusReport(
await createStatusReportBase(ACTION_NAME, "success", startedAt)
);
}
async function runWrapper() {
try {
await run();
} catch (error) {
core.setFailed(`${ACTION_NAME} action failed: ${wrapError(error).message}`);
}
await checkForTimeout();
}
void runWrapper();

View File

@@ -0,0 +1,36 @@
import { CODEQL_VERSION_RESOLVE_ENVIRONMENT, getCodeQL } from "./codeql";
import { Language } from "./languages";
import { Logger } from "./logging";
import * as util from "./util";
export async function runResolveBuildEnvironment(
cmd: string,
logger: Logger,
workingDir: string | undefined,
language: Language
) {
logger.startGroup(`Attempting to resolve build environment for ${language}`);
const codeql = await getCodeQL(cmd);
let result = {};
// If the CodeQL version in use does not support the `resolve build-environment`
// command, just return an empty configuration. Otherwise invoke the CLI.
if (
!(await util.codeQlVersionAbove(codeql, CODEQL_VERSION_RESOLVE_ENVIRONMENT))
) {
logger.warning(
"Unsupported CodeQL CLI version for `resolve build-environment` command, " +
"returning an empty configuration."
);
} else {
if (workingDir !== undefined) {
logger.info(`Using ${workingDir} as the working directory.`);
}
result = await codeql.resolveBuildEnvironment(workingDir, language);
}
logger.endGroup();
return result;
}

View File

@@ -4,7 +4,6 @@ import test from "ava";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as api from "./api-client";
import { getRunnerLogger } from "./logging";
import * as setupCodeql from "./setup-codeql";
import {
@@ -77,64 +76,6 @@ test("getCodeQLActionRepository", (t) => {
t.deepEqual(repoEnv, "xxx/yyy");
});
test("findCodeQLBundleTagDotcomOnly() matches GitHub Release with marker file", async (t) => {
// Look for GitHub Releases in github/codeql-action
sinon.stub(actionsUtil, "isRunningLocalAction").resolves(true);
sinon.stub(api, "getApiClient").value(() => ({
repos: {
listReleases: sinon.stub().resolves(undefined),
},
paginate: sinon.stub().resolves([
{
assets: [
{
name: "cli-version-2.12.0.txt",
},
],
tag_name: "codeql-bundle-20230106",
},
]),
}));
t.is(
await setupCodeql.findCodeQLBundleTagDotcomOnly(
"2.12.0",
getRunnerLogger(true)
),
"codeql-bundle-20230106"
);
});
test("findCodeQLBundleTagDotcomOnly() errors if no GitHub Release matches marker file", async (t) => {
// Look for GitHub Releases in github/codeql-action
sinon.stub(actionsUtil, "isRunningLocalAction").resolves(true);
sinon.stub(api, "getApiClient").value(() => ({
repos: {
listReleases: sinon.stub().resolves(undefined),
},
paginate: sinon.stub().resolves([
{
assets: [
{
name: "cli-version-2.12.0.txt",
},
],
tag_name: "codeql-bundle-20230106",
},
]),
}));
await t.throwsAsync(
async () =>
await setupCodeql.findCodeQLBundleTagDotcomOnly(
"2.12.1",
getRunnerLogger(true)
),
{
message:
"Failed to find a release of the CodeQL tools that contains CodeQL CLI 2.12.1.",
}
);
});
test("getCodeQLSource sets CLI version for a semver tagged bundle", async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);

View File

@@ -50,40 +50,6 @@ export function getCodeQLActionRepository(logger: Logger): string {
return util.getRequiredEnvParam("GITHUB_ACTION_REPOSITORY");
}
/**
* Gets the tag name and, if known, the CodeQL CLI version for each CodeQL bundle release.
*
* CodeQL bundles are currently tagged in the form `codeql-bundle-yyyymmdd`, so it is not possible
* to directly find the CodeQL bundle release for a particular CLI version or find the CodeQL CLI
* version for a particular CodeQL bundle.
*
* To get around this, we add a `cli-version-x.y.z.txt` asset to each bundle release that specifies
* the CLI version for that bundle release. We can then use the GitHub Releases for the CodeQL
* Action as a source of truth.
*
* In the medium term, we should migrate to a tagging scheme that allows us to directly find the
* CodeQL bundle release for a particular CLI version, for example `codeql-bundle-vx.y.z`.
*/
async function getCodeQLBundleReleasesDotcomOnly(
logger: Logger
): Promise<Array<{ cliVersion?: string; tagName: string }>> {
logger.debug(
`Fetching CodeQL CLI version and CodeQL bundle tag name information for releases of the CodeQL tools.`
);
const apiClient = api.getApiClient();
const codeQLActionRepository = getCodeQLActionRepository(logger);
const releases = await apiClient.paginate(apiClient.repos.listReleases, {
owner: codeQLActionRepository.split("/")[0],
repo: codeQLActionRepository.split("/")[1],
});
logger.debug(`Found ${releases.length} releases.`);
return releases.map((release) => ({
cliVersion: tryGetCodeQLCliVersionForRelease(release, logger),
tagName: release.tag_name,
}));
}
function tryGetCodeQLCliVersionForRelease(
release,
logger: Logger
@@ -106,26 +72,6 @@ function tryGetCodeQLCliVersionForRelease(
return cliVersionsFromMarkerFiles[0];
}
export async function findCodeQLBundleTagDotcomOnly(
cliVersion: string,
logger: Logger
): Promise<string> {
const filtered = (await getCodeQLBundleReleasesDotcomOnly(logger)).filter(
(release) => release.cliVersion === cliVersion
);
if (filtered.length === 0) {
throw new Error(
`Failed to find a release of the CodeQL tools that contains CodeQL CLI ${cliVersion}.`
);
} else if (filtered.length > 1) {
throw new Error(
`Found multiple releases of the CodeQL tools that contain CodeQL CLI ${cliVersion}. ` +
`Only one such release should exist.`
);
}
return filtered[0].tagName;
}
export async function tryFindCliVersionDotcomOnly(
tagName: string,
logger: Logger
@@ -411,7 +357,7 @@ export async function getCodeQLSource(
} else {
// Otherwise, use the default CLI version passed in.
cliVersion = defaultCliVersion.cliVersion;
tagName = defaultCliVersion["tagName"];
tagName = defaultCliVersion.tagName;
}
const bundleVersion =
@@ -430,7 +376,7 @@ export async function getCodeQLSource(
`URL: ${url ?? "unspecified"}.`
);
let codeqlFolder;
let codeqlFolder: string | undefined;
if (cliVersion) {
// If we find the specified CLI version, we always use that.
@@ -475,26 +421,18 @@ export async function getCodeQLSource(
}
// Fall back to matching `0.0.0-<bundleVersion>`.
if (!codeqlFolder && (cliVersion || tagName)) {
if (cliVersion || tagName) {
const fallbackVersion = await tryGetFallbackToolcacheVersion(
cliVersion,
tagName,
variant,
logger
);
if (fallbackVersion) {
codeqlFolder = toolcache.find("CodeQL", fallbackVersion);
} else {
logger.debug(
"Could not determine a fallback toolcache version number for CodeQL tools version " +
`${humanReadableVersion}.`
);
}
if (!codeqlFolder && tagName) {
const fallbackVersion = await tryGetFallbackToolcacheVersion(
cliVersion,
tagName,
logger
);
if (fallbackVersion) {
codeqlFolder = toolcache.find("CodeQL", fallbackVersion);
} else {
logger.debug(
"Both the CLI version and the bundle version are unknown, so we will not be able to find " +
"the requested version of the CodeQL tools in the toolcache."
"Could not determine a fallback toolcache version number for CodeQL tools version " +
`${humanReadableVersion}.`
);
}
}
@@ -535,16 +473,8 @@ export async function getCodeQLSource(
}
if (!url) {
if (!tagName && cliVersion && variant === util.GitHubVariant.DOTCOM) {
tagName = await findCodeQLBundleTagDotcomOnly(cliVersion, logger);
} else if (!tagName) {
throw new Error(
`Could not obtain the requested version (${humanReadableVersion}) of the CodeQL tools ` +
"since we could not compute the tag name."
);
}
url = await getCodeQLBundleDownloadURL(
tagName,
tagName!,
apiDetails,
variant,
logger
@@ -566,19 +496,9 @@ export async function getCodeQLSource(
*/
export async function tryGetFallbackToolcacheVersion(
cliVersion: string | undefined,
tagName: string | undefined,
variant: util.GitHubVariant,
tagName: string,
logger: Logger
): Promise<string | undefined> {
//
// If we are on Dotcom, we will make an HTTP request to the Releases API here
// to find the tag name for the requested version.
if (cliVersion && !tagName && variant === util.GitHubVariant.DOTCOM) {
tagName = await findCodeQLBundleTagDotcomOnly(cliVersion, logger);
}
if (!tagName) {
return undefined;
}
const bundleVersion = tryGetBundleVersionFromTagName(tagName, logger);
if (!bundleVersion) {
return undefined;

View File

@@ -1,73 +0,0 @@
/**
* Environment variables to be set by codeql-action and used by the
* CLI.
*/
export enum EnvVar {
/**
* Semver of the codeql-action as specified in package.json.
*/
VERSION = "CODEQL_ACTION_VERSION",
/**
* If set to a truthy value, then the codeql-action might combine SARIF
* output from several `interpret-results` runs for the same Language.
*/
FEATURE_SARIF_COMBINE = "CODEQL_ACTION_FEATURE_SARIF_COMBINE",
/**
* If set to the "true" string, then the codeql-action will upload SARIF,
* not the cli.
*/
FEATURE_WILL_UPLOAD = "CODEQL_ACTION_FEATURE_WILL_UPLOAD",
/**
* If set to the "true" string, then the codeql-action is using its
* own deprecated and non-standard way of scanning for multiple
* languages.
*/
FEATURE_MULTI_LANGUAGE = "CODEQL_ACTION_FEATURE_MULTI_LANGUAGE",
/**
* If set to the "true" string, then the codeql-action is using its
* own sandwiched workflow mechanism
*/
FEATURE_SANDWICH = "CODEQL_ACTION_FEATURE_SANDWICH",
}
/**
* Environment variable that is set to true when the CodeQL Action has invoked
* the Go autobuilder.
*/
export const CODEQL_ACTION_DID_AUTOBUILD_GOLANG =
"CODEQL_ACTION_DID_AUTOBUILD_GOLANG";
/**
* This environment variable is set to true when the `analyze` Action
* completes successfully.
*/
export const CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY =
"CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY";
export const CODEQL_ACTION_TESTING_ENVIRONMENT =
"CODEQL_ACTION_TESTING_ENVIRONMENT";
/** Used to disable uploading SARIF results or status reports to the GitHub API */
export const CODEQL_ACTION_TEST_MODE = "CODEQL_ACTION_TEST_MODE";
/**
* Used to disable the SARIF post-processing in the Action that removes duplicate locations from
* notifications in the `run[].invocations[].toolExecutionNotifications` SARIF property.
*/
export const CODEQL_ACTION_DISABLE_DUPLICATE_LOCATION_FIX =
"CODEQL_ACTION_DISABLE_DUPLICATE_LOCATION_FIX";
/**
* The time at which the first action (normally init) started executing.
* If a workflow invokes a different action without first invoking the init
* action (i.e. the upload action is being used by a third-party integrator)
* then this variable will be assigned the start time of the action invoked
* rather that the init action.
*/
export const CODEQL_WORKFLOW_STARTED_AT = "CODEQL_WORKFLOW_STARTED_AT";
export const ODASA_TRACER_CONFIGURATION = "ODASA_TRACER_CONFIGURATION";

View File

@@ -15,7 +15,7 @@ import {
FeatureEnablement,
} from "./feature-flags";
import { Logger } from "./logging";
import { GitHubVariant, HTTPError } from "./util";
import { HTTPError } from "./util";
export const SAMPLE_DOTCOM_API_DETAILS = {
auth: "token",
@@ -24,8 +24,8 @@ export const SAMPLE_DOTCOM_API_DETAILS = {
};
export const SAMPLE_DEFAULT_CLI_VERSION: CodeQLDefaultVersionInfo = {
cliVersion: "2.0.0",
variant: GitHubVariant.DOTCOM,
cliVersion: "2.20.0",
tagName: "codeql-bundle-v2.20.0",
};
type TestContext = {

View File

@@ -10,17 +10,17 @@ import * as jsonschema from "jsonschema";
import * as actionsUtil from "./actions-util";
import * as api from "./api-client";
import { EnvVar } from "./environment";
import * as fingerprints from "./fingerprints";
import { Logger } from "./logging";
import { parseRepositoryNwo, RepositoryNwo } from "./repository";
import { CODEQL_WORKFLOW_STARTED_AT } from "./shared-environment";
import * as util from "./util";
import { SarifFile, SarifResult, SarifRun, wrapError } from "./util";
import * as workflow from "./workflow";
// Takes a list of paths to sarif files and combines them together,
// returning the contents of the combined sarif file.
export function combineSarifFiles(sarifFiles: string[]): SarifFile {
function combineSarifFiles(sarifFiles: string[]): SarifFile {
const combinedSarif: SarifFile = {
version: null,
runs: [],
@@ -198,7 +198,7 @@ function getSarifFilePaths(sarifPath: string) {
}
// Counts the number of results in the given SARIF file
export function countResultsInSarif(sarif: string): number {
function countResultsInSarif(sarif: string): number {
let numResults = 0;
let parsedSarif;
try {
@@ -224,7 +224,7 @@ export function countResultsInSarif(sarif: string): number {
// Validates that the given file path refers to a valid SARIF file.
// Throws an error if the file is invalid.
export function validateSarifFileSchema(sarifFilePath: string, logger: Logger) {
const sarif = JSON.parse(fs.readFileSync(sarifFilePath, "utf8"));
const sarif = JSON.parse(fs.readFileSync(sarifFilePath, "utf8")) as SarifFile;
const schema = require("../src/sarif-schema-2.1.0.json") as jsonschema.Schema;
const result = new jsonschema.Validator().validate(sarif, schema);
@@ -287,7 +287,7 @@ export function buildPayload(
workflow_run_attempt: workflowRunAttempt,
checkout_uri: checkoutURI,
environment,
started_at: process.env[CODEQL_WORKFLOW_STARTED_AT],
started_at: process.env[EnvVar.WORKFLOW_STARTED_AT],
tool_names: toolNames,
base_ref: undefined as undefined | string,
base_sha: undefined as undefined | string,
@@ -506,18 +506,23 @@ function handleProcessingResultForUnsuccessfulExecution(
) {
logger.debug(
"Successfully uploaded a SARIF file for the unsuccessful execution. Received expected " +
'"unsuccessful execution" error, and no other errors.'
'"unsuccessful execution" processing error, and no other errors.'
);
} else if (status === "failed") {
logger.warning(
`Failed to upload a SARIF file for the unsuccessful execution. Code scanning status ` +
`information for the repository may be out of date as a result. Processing errors: ${response.data.errors}`
);
} else if (status === "complete") {
// There is a known transient issue with the code scanning API where it sometimes reports
// `complete` for an unsuccessful execution submission.
logger.debug(
"Uploaded a SARIF file for the unsuccessful execution, but did not receive the expected " +
'"unsuccessful execution" processing error. This is a known transient issue with the ' +
"code scanning API, and does not cause out of date code scanning status information."
);
} else {
const shortMessage =
"Failed to upload a SARIF file for the unsuccessful execution. Code scanning status " +
"information for the repository may be out of date as a result.";
const longMessage =
shortMessage + status === "failed"
? ` Processing errors: ${response.data.errors}`
: ' Encountered no processing errors, but expected to receive an "unsuccessful execution" error.';
logger.debug(longMessage);
throw new Error(shortMessage);
util.assertNever(status);
}
}

View File

@@ -8,8 +8,14 @@ import * as sinon from "sinon";
import * as api from "./api-client";
import { Config } from "./config-utils";
import { Feature } from "./feature-flags";
import { getRunnerLogger } from "./logging";
import { getRecordingLogger, LoggedMessage, setupTests } from "./testing-utils";
import {
createFeatures,
getRecordingLogger,
LoggedMessage,
setupTests,
} from "./testing-utils";
import * as util from "./util";
setupTests(test);
@@ -23,25 +29,37 @@ test("getToolNames", (t) => {
t.deepEqual(toolNames, ["CodeQL command-line toolchain", "ESLint"]);
});
test("getMemoryFlag() should return the correct --ram flag", (t) => {
const totalMem = Math.floor(os.totalmem() / (1024 * 1024));
const expectedThreshold = process.platform === "win32" ? 1536 : 1024;
test("getMemoryFlag() should return the correct --ram flag", async (t) => {
const totalMem = os.totalmem() / (1024 * 1024);
const fixedAmount = process.platform === "win32" ? 1536 : 1024;
const scaledAmount = 0.02 * totalMem;
const expectedMemoryValue = Math.floor(totalMem - fixedAmount);
const expectedMemoryValueWithScaling = Math.floor(
totalMem - fixedAmount - scaledAmount
);
const tests: Array<[string | undefined, string]> = [
[undefined, `--ram=${totalMem - expectedThreshold}`],
["", `--ram=${totalMem - expectedThreshold}`],
["512", "--ram=512"],
const tests: Array<[string | undefined, boolean, string]> = [
[undefined, false, `--ram=${expectedMemoryValue}`],
["", false, `--ram=${expectedMemoryValue}`],
["512", false, "--ram=512"],
[undefined, true, `--ram=${expectedMemoryValueWithScaling}`],
["", true, `--ram=${expectedMemoryValueWithScaling}`],
];
for (const [input, expectedFlag] of tests) {
const flag = util.getMemoryFlag(input);
for (const [input, withScaling, expectedFlag] of tests) {
const features = createFeatures(
withScaling ? [Feature.ScalingReservedRam] : []
);
const flag = await util.getMemoryFlag(input, features);
t.deepEqual(flag, expectedFlag);
}
});
test("getMemoryFlag() throws if the ram input is < 0 or NaN", (t) => {
test("getMemoryFlag() throws if the ram input is < 0 or NaN", async (t) => {
for (const input of ["-1", "hello!"]) {
t.throws(() => util.getMemoryFlag(input));
await t.throwsAsync(
async () => await util.getMemoryFlag(input, createFeatures([]))
);
}
});

View File

@@ -16,14 +16,10 @@ import {
parsePacksSpecification,
prettyPrintPack,
} from "./config-utils";
import { EnvVar } from "./environment";
import { Feature, FeatureEnablement } from "./feature-flags";
import { Language } from "./languages";
import { Logger } from "./logging";
import {
CODEQL_ACTION_DISABLE_DUPLICATE_LOCATION_FIX,
CODEQL_ACTION_TEST_MODE,
EnvVar,
} from "./shared-environment";
/**
* Specifies bundle versions that are known to be broken
@@ -72,6 +68,9 @@ export interface SarifInvocation {
export interface SarifResult {
ruleId?: string;
rule?: {
id?: string;
};
message?: {
text?: string;
};
@@ -158,9 +157,21 @@ export async function withTmpDir<T>(
* from committing too much of the available memory to CodeQL.
* @returns number
*/
function getSystemReservedMemoryMegaBytes(): number {
async function getSystemReservedMemoryMegaBytes(
totalMemoryMegaBytes: number,
features: FeatureEnablement
): Promise<number> {
// Windows needs more memory for OS processes.
return 1024 * (process.platform === "win32" ? 1.5 : 1);
const fixedAmount = 1024 * (process.platform === "win32" ? 1.5 : 1);
if (await features.getValue(Feature.ScalingReservedRam)) {
// Reserve an additional 2% of the total memory, since the amount used by
// the kernel for page tables scales with the size of physical memory.
const scaledAmount = 0.02 * totalMemoryMegaBytes;
return fixedAmount + scaledAmount;
} else {
return fixedAmount;
}
}
/**
@@ -170,7 +181,10 @@ function getSystemReservedMemoryMegaBytes(): number {
*
* @returns {number} the amount of RAM to use, in megabytes
*/
export function getMemoryFlagValue(userInput: string | undefined): number {
export async function getMemoryFlagValue(
userInput: string | undefined,
features: FeatureEnablement
): Promise<number> {
let memoryToUseMegaBytes: number;
if (userInput) {
memoryToUseMegaBytes = Number(userInput);
@@ -180,7 +194,10 @@ export function getMemoryFlagValue(userInput: string | undefined): number {
} else {
const totalMemoryBytes = os.totalmem();
const totalMemoryMegaBytes = totalMemoryBytes / (1024 * 1024);
const reservedMemoryMegaBytes = getSystemReservedMemoryMegaBytes();
const reservedMemoryMegaBytes = await getSystemReservedMemoryMegaBytes(
totalMemoryMegaBytes,
features
);
memoryToUseMegaBytes = totalMemoryMegaBytes - reservedMemoryMegaBytes;
}
return Math.floor(memoryToUseMegaBytes);
@@ -193,8 +210,12 @@ export function getMemoryFlagValue(userInput: string | undefined): number {
*
* @returns string
*/
export function getMemoryFlag(userInput: string | undefined): string {
return `--ram=${getMemoryFlagValue(userInput)}`;
export async function getMemoryFlag(
userInput: string | undefined,
features: FeatureEnablement
): Promise<string> {
const megabytes = await getMemoryFlagValue(userInput, features);
return `--ram=${megabytes}`;
}
/**
@@ -623,7 +644,7 @@ export function getMlPoweredJsQueriesStatus(config: Config): string {
* In test mode, we don't upload SARIF results or status reports to the GitHub API.
*/
export function isInTestMode(): boolean {
return process.env[CODEQL_ACTION_TEST_MODE] === "true";
return process.env[EnvVar.TEST_MODE] === "true";
}
/**
@@ -887,10 +908,10 @@ export function fixInvalidNotificationsInFile(
outputPath: string,
logger: Logger
): void {
if (process.env[CODEQL_ACTION_DISABLE_DUPLICATE_LOCATION_FIX] === "true") {
if (process.env[EnvVar.DISABLE_DUPLICATE_LOCATION_FIX] === "true") {
logger.info(
"SARIF notification object duplicate location fix disabled by the " +
`${CODEQL_ACTION_DISABLE_DUPLICATE_LOCATION_FIX} environment variable.`
`${EnvVar.DISABLE_DUPLICATE_LOCATION_FIX} environment variable.`
);
fs.renameSync(inputPath, outputPath);
} else {

View File

@@ -6,6 +6,7 @@ import * as core from "@actions/core";
import * as yaml from "js-yaml";
import * as api from "./api-client";
import { EnvVar } from "./environment";
import { Logger } from "./logging";
import { getRequiredEnvParam, isInTestMode } from "./util";
@@ -391,8 +392,7 @@ function getInputOrThrow(
function getAnalyzeActionName() {
if (
isInTestMode() ||
process.env["CODEQL_ACTION_TESTING_ENVIRONMENT"] ===
"codeql-action-pr-checks"
process.env[EnvVar.TESTING_ENVIRONMENT] === "codeql-action-pr-checks"
) {
return "./analyze";
} else {