Merge branch 'main' into cklin/codeql-cli-2.11.5
This commit is contained in:
commit
c51babb6c6
65 changed files with 1105 additions and 335 deletions
|
|
@ -17,6 +17,7 @@ import {
|
|||
GITHUB_DOTCOM_URL,
|
||||
isHTTPError,
|
||||
isInTestMode,
|
||||
parseMatrixInput,
|
||||
UserError,
|
||||
} from "./util";
|
||||
import { getWorkflowPath } from "./workflow";
|
||||
|
|
@ -192,10 +193,10 @@ export function computeAutomationID(
|
|||
): string {
|
||||
let automationID = `${analysis_key}/`;
|
||||
|
||||
// the id has to be deterministic so we sort the fields
|
||||
if (environment !== undefined && environment !== "null") {
|
||||
const environmentObject = JSON.parse(environment);
|
||||
for (const entry of Object.entries(environmentObject).sort()) {
|
||||
const matrix = parseMatrixInput(environment);
|
||||
if (matrix !== undefined) {
|
||||
// the id has to be deterministic so we sort the fields
|
||||
for (const entry of Object.entries(matrix).sort()) {
|
||||
if (typeof entry[1] === "string") {
|
||||
automationID += `${entry[0]}:${entry[1]}/`;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { Features } from "./feature-flags";
|
|||
import { Language } from "./languages";
|
||||
import { getActionsLogger, Logger } from "./logging";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import { CODEQL_ACTION_ANALYZE_DID_UPLOAD_SARIF } from "./shared-environment";
|
||||
import { getTotalCacheSize, uploadTrapCaches } from "./trap-caching";
|
||||
import * as upload_lib from "./upload-lib";
|
||||
import { UploadResult } from "./upload-lib";
|
||||
|
|
@ -271,8 +272,14 @@ async function run() {
|
|||
core.setOutput("db-locations", dbLocations);
|
||||
|
||||
if (runStats && actionsUtil.getRequiredInput("upload") === "true") {
|
||||
uploadResult = await upload_lib.uploadFromActions(outputDir, logger);
|
||||
uploadResult = await upload_lib.uploadFromActions(
|
||||
outputDir,
|
||||
actionsUtil.getRequiredInput("checkout_path"),
|
||||
actionsUtil.getOptionalInput("category"),
|
||||
logger
|
||||
);
|
||||
core.setOutput("sarif-id", uploadResult.sarifID);
|
||||
core.exportVariable(CODEQL_ACTION_ANALYZE_DID_UPLOAD_SARIF, "true");
|
||||
} else {
|
||||
logger.info("Not uploading results");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -355,8 +355,7 @@ export async function runQueries(
|
|||
addSnippetsFlag,
|
||||
threadsFlag,
|
||||
enableDebugLogging ? "-vv" : "-v",
|
||||
automationDetailsId,
|
||||
featureEnablement
|
||||
automationDetailsId
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -445,16 +445,7 @@ test("databaseInterpretResults() does not set --sarif-add-query-help for 2.7.0",
|
|||
sinon.stub(codeqlObject, "getVersion").resolves("2.7.0");
|
||||
// safeWhich throws because of the test CodeQL object.
|
||||
sinon.stub(safeWhich, "safeWhich").resolves("");
|
||||
await codeqlObject.databaseInterpretResults(
|
||||
"",
|
||||
[],
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"-v",
|
||||
"",
|
||||
createFeatures([])
|
||||
);
|
||||
await codeqlObject.databaseInterpretResults("", [], "", "", "", "-v", "");
|
||||
t.false(
|
||||
runnerConstructorStub.firstCall.args[1].includes("--sarif-add-query-help"),
|
||||
"--sarif-add-query-help should be absent, but it is present"
|
||||
|
|
@ -467,16 +458,7 @@ test("databaseInterpretResults() sets --sarif-add-query-help for 2.7.1", async (
|
|||
sinon.stub(codeqlObject, "getVersion").resolves("2.7.1");
|
||||
// safeWhich throws because of the test CodeQL object.
|
||||
sinon.stub(safeWhich, "safeWhich").resolves("");
|
||||
await codeqlObject.databaseInterpretResults(
|
||||
"",
|
||||
[],
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"-v",
|
||||
"",
|
||||
createFeatures([])
|
||||
);
|
||||
await codeqlObject.databaseInterpretResults("", [], "", "", "", "-v", "");
|
||||
t.true(
|
||||
runnerConstructorStub.firstCall.args[1].includes("--sarif-add-query-help"),
|
||||
"--sarif-add-query-help should be present, but it is absent"
|
||||
|
|
@ -865,25 +847,13 @@ test("does not use injected config", async (t: ExecutionContext<unknown>) => {
|
|||
}
|
||||
});
|
||||
|
||||
test("databaseInterpretResults() sets --sarif-add-baseline-file-info when feature enabled", async (t) => {
|
||||
test("databaseInterpretResults() sets --sarif-add-baseline-file-info for 2.11.3", async (t) => {
|
||||
const runnerConstructorStub = stubToolRunnerConstructor();
|
||||
const codeqlObject = await codeql.getCodeQLForTesting();
|
||||
// We need to set a CodeQL version such that running `databaseInterpretResults` does not crash.
|
||||
// The version of CodeQL is checked separately to determine feature enablement, and does not
|
||||
// otherwise impact this test, so set it to 0.0.0.
|
||||
sinon.stub(codeqlObject, "getVersion").resolves("0.0.0");
|
||||
sinon.stub(codeqlObject, "getVersion").resolves("2.11.3");
|
||||
// safeWhich throws because of the test CodeQL object.
|
||||
sinon.stub(safeWhich, "safeWhich").resolves("");
|
||||
await codeqlObject.databaseInterpretResults(
|
||||
"",
|
||||
[],
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"-v",
|
||||
"",
|
||||
createFeatures([Feature.FileBaselineInformationEnabled])
|
||||
);
|
||||
await codeqlObject.databaseInterpretResults("", [], "", "", "", "-v", "");
|
||||
t.true(
|
||||
runnerConstructorStub.firstCall.args[1].includes(
|
||||
"--sarif-add-baseline-file-info"
|
||||
|
|
@ -892,25 +862,13 @@ test("databaseInterpretResults() sets --sarif-add-baseline-file-info when featur
|
|||
);
|
||||
});
|
||||
|
||||
test("databaseInterpretResults() does not set --sarif-add-baseline-file-info if feature disabled", async (t) => {
|
||||
test("databaseInterpretResults() does not set --sarif-add-baseline-file-info for 2.11.2", async (t) => {
|
||||
const runnerConstructorStub = stubToolRunnerConstructor();
|
||||
const codeqlObject = await codeql.getCodeQLForTesting();
|
||||
// We need to set a CodeQL version such that running `databaseInterpretResults` does not crash.
|
||||
// The version of CodeQL is checked upstream to determine feature enablement, so it does not
|
||||
// affect this test.
|
||||
sinon.stub(codeqlObject, "getVersion").resolves("0.0.0");
|
||||
sinon.stub(codeqlObject, "getVersion").resolves("2.11.2");
|
||||
// safeWhich throws because of the test CodeQL object.
|
||||
sinon.stub(safeWhich, "safeWhich").resolves("");
|
||||
await codeqlObject.databaseInterpretResults(
|
||||
"",
|
||||
[],
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"-v",
|
||||
"",
|
||||
createFeatures([])
|
||||
);
|
||||
await codeqlObject.databaseInterpretResults("", [], "", "", "", "-v", "");
|
||||
t.false(
|
||||
runnerConstructorStub.firstCall.args[1].includes(
|
||||
"--sarif-add-baseline-file-info"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import * as api from "./api-client";
|
|||
import { Config } from "./config-utils";
|
||||
import * as defaults from "./defaults.json"; // Referenced from codeql-action-sync-tool!
|
||||
import { errorMatchers } from "./error-matcher";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { FeatureEnablement } from "./feature-flags";
|
||||
import { isTracedLanguage, Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { toolrunnerErrorCatcher } from "./toolrunner-error-catcher";
|
||||
|
|
@ -172,13 +172,19 @@ export interface CodeQL {
|
|||
addSnippetsFlag: string,
|
||||
threadsFlag: string,
|
||||
verbosityFlag: string | undefined,
|
||||
automationDetailsId: string | undefined,
|
||||
featureEnablement: FeatureEnablement
|
||||
automationDetailsId: string | undefined
|
||||
): Promise<string>;
|
||||
/**
|
||||
* Run 'codeql database print-baseline'.
|
||||
*/
|
||||
databasePrintBaseline(databasePath: string): Promise<string>;
|
||||
/**
|
||||
* Run 'codeql diagnostics export'.
|
||||
*/
|
||||
diagnosticsExport(
|
||||
sarifFile: string,
|
||||
automationDetailsId: string | undefined
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ResolveLanguagesOutput {
|
||||
|
|
@ -250,6 +256,7 @@ const CODEQL_VERSION_LUA_TRACER_CONFIG = "2.10.0";
|
|||
export const CODEQL_VERSION_CONFIG_FILES = "2.10.1";
|
||||
const CODEQL_VERSION_LUA_TRACING_GO_WINDOWS_FIXED = "2.10.4";
|
||||
export const CODEQL_VERSION_GHES_PACK_DOWNLOAD = "2.10.4";
|
||||
const CODEQL_VERSION_FILE_BASELINE_INFORMATION = "2.11.3";
|
||||
|
||||
/**
|
||||
* This variable controls using the new style of tracing from the CodeQL
|
||||
|
|
@ -634,6 +641,7 @@ export function setCodeQL(partialCodeql: Partial<CodeQL>): CodeQL {
|
|||
partialCodeql,
|
||||
"databasePrintBaseline"
|
||||
),
|
||||
diagnosticsExport: resolveFunction(partialCodeql, "diagnosticsExport"),
|
||||
};
|
||||
return cachedCodeQL;
|
||||
}
|
||||
|
|
@ -675,7 +683,7 @@ async function getCodeQLForCmd(
|
|||
cmd: string,
|
||||
checkVersion: boolean
|
||||
): Promise<CodeQL> {
|
||||
const codeql = {
|
||||
const codeql: CodeQL = {
|
||||
getPath() {
|
||||
return cmd;
|
||||
},
|
||||
|
|
@ -1025,8 +1033,7 @@ async function getCodeQLForCmd(
|
|||
addSnippetsFlag: string,
|
||||
threadsFlag: string,
|
||||
verbosityFlag: string,
|
||||
automationDetailsId: string | undefined,
|
||||
featureEnablement: FeatureEnablement
|
||||
automationDetailsId: string | undefined
|
||||
): Promise<string> {
|
||||
const codeqlArgs = [
|
||||
"database",
|
||||
|
|
@ -1047,9 +1054,9 @@ async function getCodeQLForCmd(
|
|||
codeqlArgs.push("--sarif-category", automationDetailsId);
|
||||
}
|
||||
if (
|
||||
await featureEnablement.getValue(
|
||||
Feature.FileBaselineInformationEnabled,
|
||||
this
|
||||
await util.codeQlVersionAbove(
|
||||
this,
|
||||
CODEQL_VERSION_FILE_BASELINE_INFORMATION
|
||||
)
|
||||
) {
|
||||
codeqlArgs.push("--sarif-add-baseline-file-info");
|
||||
|
|
@ -1156,6 +1163,22 @@ async function getCodeQLForCmd(
|
|||
];
|
||||
await new toolrunner.ToolRunner(cmd, args).exec();
|
||||
},
|
||||
async diagnosticsExport(
|
||||
sarifFile: string,
|
||||
automationDetailsId: string | undefined
|
||||
): Promise<void> {
|
||||
const args = [
|
||||
"diagnostics",
|
||||
"export",
|
||||
"--format=sarif-latest",
|
||||
`--output=${sarifFile}`,
|
||||
...getExtraOptionsFromEnv(["diagnostics", "export"]),
|
||||
];
|
||||
if (automationDetailsId !== undefined) {
|
||||
args.push("--sarif-category", automationDetailsId);
|
||||
}
|
||||
await new toolrunner.ToolRunner(cmd, args).exec();
|
||||
},
|
||||
};
|
||||
// To ensure that status reports include the CodeQL CLI version wherever
|
||||
// possible, we want to call getVersion(), which populates the version value
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ export enum Feature {
|
|||
BypassToolcacheKotlinSwiftEnabled = "bypass_toolcache_kotlin_swift_enabled",
|
||||
CliConfigFileEnabled = "cli_config_file_enabled",
|
||||
DisableKotlinAnalysisEnabled = "disable_kotlin_analysis_enabled",
|
||||
FileBaselineInformationEnabled = "file_baseline_information_enabled",
|
||||
MlPoweredQueriesEnabled = "ml_powered_queries_enabled",
|
||||
TrapCachingEnabled = "trap_caching_enabled",
|
||||
UploadFailedSarifEnabled = "upload_failed_sarif_enabled",
|
||||
}
|
||||
|
||||
export const featureConfig: Record<
|
||||
|
|
@ -45,10 +45,6 @@ export const featureConfig: Record<
|
|||
envVar: "CODEQL_PASS_CONFIG_TO_CLI",
|
||||
minimumVersion: "2.11.1",
|
||||
},
|
||||
[Feature.FileBaselineInformationEnabled]: {
|
||||
envVar: "CODEQL_FILE_BASELINE_INFORMATION",
|
||||
minimumVersion: "2.11.3",
|
||||
},
|
||||
[Feature.MlPoweredQueriesEnabled]: {
|
||||
envVar: "CODEQL_ML_POWERED_QUERIES",
|
||||
minimumVersion: "2.7.5",
|
||||
|
|
@ -57,6 +53,10 @@ export const featureConfig: Record<
|
|||
envVar: "CODEQL_TRAP_CACHING",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.UploadFailedSarifEnabled]: {
|
||||
envVar: "CODEQL_ACTION_UPLOAD_FAILED_SARIF",
|
||||
minimumVersion: "2.11.3",
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,15 +1,27 @@
|
|||
import test from "ava";
|
||||
import test, { ExecutionContext } from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as codeql from "./codeql";
|
||||
import * as configUtils from "./config-utils";
|
||||
import { Feature } from "./feature-flags";
|
||||
import * as initActionPostHelper from "./init-action-post-helper";
|
||||
import { setupTests } from "./testing-utils";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import {
|
||||
createFeatures,
|
||||
getRecordingLogger,
|
||||
setupTests,
|
||||
} from "./testing-utils";
|
||||
import * as uploadLib from "./upload-lib";
|
||||
import * as util from "./util";
|
||||
import * as workflow from "./workflow";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test("post: init action with debug mode off", async (t) => {
|
||||
return await util.withTmpDir(async (tmpDir) => {
|
||||
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
|
||||
process.env["RUNNER_TEMP"] = tmpDir;
|
||||
|
||||
const gitHubVersion: util.GitHubVersion = {
|
||||
|
|
@ -29,7 +41,10 @@ test("post: init action with debug mode off", async (t) => {
|
|||
await initActionPostHelper.run(
|
||||
uploadDatabaseBundleSpy,
|
||||
uploadLogsSpy,
|
||||
printDebugLogsSpy
|
||||
printDebugLogsSpy,
|
||||
parseRepositoryNwo("github/codeql-action"),
|
||||
createFeatures([]),
|
||||
getRunnerLogger(true)
|
||||
);
|
||||
|
||||
t.assert(uploadDatabaseBundleSpy.notCalled);
|
||||
|
|
@ -40,6 +55,7 @@ test("post: init action with debug mode off", async (t) => {
|
|||
|
||||
test("post: init action with debug mode on", async (t) => {
|
||||
return await util.withTmpDir(async (tmpDir) => {
|
||||
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
|
||||
process.env["RUNNER_TEMP"] = tmpDir;
|
||||
|
||||
const gitHubVersion: util.GitHubVersion = {
|
||||
|
|
@ -59,7 +75,10 @@ test("post: init action with debug mode on", async (t) => {
|
|||
await initActionPostHelper.run(
|
||||
uploadDatabaseBundleSpy,
|
||||
uploadLogsSpy,
|
||||
printDebugLogsSpy
|
||||
printDebugLogsSpy,
|
||||
parseRepositoryNwo("github/codeql-action"),
|
||||
createFeatures([]),
|
||||
getRunnerLogger(true)
|
||||
);
|
||||
|
||||
t.assert(uploadDatabaseBundleSpy.called);
|
||||
|
|
@ -67,3 +86,117 @@ test("post: init action with debug mode on", async (t) => {
|
|||
t.assert(printDebugLogsSpy.called);
|
||||
});
|
||||
});
|
||||
|
||||
test("uploads failed SARIF run for typical workflow", async (t) => {
|
||||
const actionsWorkflow = createTestWorkflow([
|
||||
{
|
||||
name: "Checkout repository",
|
||||
uses: "actions/checkout@v3",
|
||||
},
|
||||
{
|
||||
name: "Initialize CodeQL",
|
||||
uses: "github/codeql-action/init@v2",
|
||||
with: {
|
||||
languages: "javascript",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Perform CodeQL Analysis",
|
||||
uses: "github/codeql-action/analyze@v2",
|
||||
with: {
|
||||
category: "my-category",
|
||||
},
|
||||
},
|
||||
]);
|
||||
await testFailedSarifUpload(t, actionsWorkflow, { category: "my-category" });
|
||||
});
|
||||
|
||||
test("uploading failed SARIF run fails when workflow does not reference github/codeql-action", async (t) => {
|
||||
const actionsWorkflow = createTestWorkflow([
|
||||
{
|
||||
name: "Checkout repository",
|
||||
uses: "actions/checkout@v3",
|
||||
},
|
||||
]);
|
||||
await t.throwsAsync(
|
||||
async () => await testFailedSarifUpload(t, actionsWorkflow)
|
||||
);
|
||||
});
|
||||
|
||||
function createTestWorkflow(
|
||||
steps: workflow.WorkflowJobStep[]
|
||||
): workflow.Workflow {
|
||||
return {
|
||||
name: "CodeQL",
|
||||
on: {
|
||||
push: {
|
||||
branches: ["main"],
|
||||
},
|
||||
pull_request: {
|
||||
branches: ["main"],
|
||||
},
|
||||
},
|
||||
jobs: {
|
||||
analyze: {
|
||||
name: "CodeQL Analysis",
|
||||
"runs-on": "ubuntu-latest",
|
||||
steps,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function testFailedSarifUpload(
|
||||
t: ExecutionContext<unknown>,
|
||||
actionsWorkflow: workflow.Workflow,
|
||||
{ category }: { category?: string } = {}
|
||||
): Promise<void> {
|
||||
const config = {
|
||||
codeQLCmd: "codeql",
|
||||
debugMode: true,
|
||||
languages: [],
|
||||
packs: [],
|
||||
} as unknown as configUtils.Config;
|
||||
const messages = [];
|
||||
process.env["GITHUB_JOB"] = "analyze";
|
||||
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
|
||||
process.env["GITHUB_WORKSPACE"] =
|
||||
"/home/runner/work/codeql-action/codeql-action";
|
||||
sinon.stub(actionsUtil, "getRequiredInput").withArgs("matrix").returns("{}");
|
||||
|
||||
const codeqlObject = await codeql.getCodeQLForTesting();
|
||||
sinon.stub(codeql, "getCodeQL").resolves(codeqlObject);
|
||||
const diagnosticsExportStub = sinon.stub(codeqlObject, "diagnosticsExport");
|
||||
|
||||
sinon.stub(workflow, "getWorkflow").resolves(actionsWorkflow);
|
||||
|
||||
const uploadFromActions = sinon.stub(uploadLib, "uploadFromActions");
|
||||
uploadFromActions.resolves({ sarifID: "42" } as uploadLib.UploadResult);
|
||||
const waitForProcessing = sinon.stub(uploadLib, "waitForProcessing");
|
||||
|
||||
await initActionPostHelper.uploadFailedSarif(
|
||||
config,
|
||||
parseRepositoryNwo("github/codeql-action"),
|
||||
createFeatures([Feature.UploadFailedSarifEnabled]),
|
||||
getRecordingLogger(messages)
|
||||
);
|
||||
t.deepEqual(messages, []);
|
||||
t.true(
|
||||
diagnosticsExportStub.calledOnceWith(sinon.match.string, category),
|
||||
`Actual args were: ${diagnosticsExportStub.args}`
|
||||
);
|
||||
t.true(
|
||||
uploadFromActions.calledOnceWith(
|
||||
sinon.match.string,
|
||||
sinon.match.string,
|
||||
category,
|
||||
sinon.match.any
|
||||
),
|
||||
`Actual args were: ${uploadFromActions.args}`
|
||||
);
|
||||
t.true(
|
||||
waitForProcessing.calledOnceWith(sinon.match.any, "42", sinon.match.any, {
|
||||
isUnsuccessfulExecution: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,118 @@
|
|||
import * as core from "@actions/core";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { getConfig } from "./config-utils";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { getCodeQL } from "./codeql";
|
||||
import { Config, getConfig } from "./config-utils";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { Logger } from "./logging";
|
||||
import { RepositoryNwo } from "./repository";
|
||||
import { CODEQL_ACTION_ANALYZE_DID_UPLOAD_SARIF } from "./shared-environment";
|
||||
import * as uploadLib from "./upload-lib";
|
||||
import { getRequiredEnvParam, isInTestMode, parseMatrixInput } from "./util";
|
||||
import {
|
||||
getCategoryInputOrThrow,
|
||||
getCheckoutPathInputOrThrow,
|
||||
getUploadInputOrThrow,
|
||||
getWorkflow,
|
||||
} from "./workflow";
|
||||
|
||||
export async function uploadFailedSarif(
|
||||
config: Config,
|
||||
repositoryNwo: RepositoryNwo,
|
||||
featureEnablement: FeatureEnablement,
|
||||
logger: Logger
|
||||
) {
|
||||
if (!config.codeQLCmd) {
|
||||
logger.warning(
|
||||
"CodeQL command not found. Unable to upload failed SARIF file."
|
||||
);
|
||||
return;
|
||||
}
|
||||
const codeql = await getCodeQL(config.codeQLCmd);
|
||||
if (
|
||||
!(await featureEnablement.getValue(
|
||||
Feature.UploadFailedSarifEnabled,
|
||||
codeql
|
||||
))
|
||||
) {
|
||||
logger.debug("Uploading failed SARIF is disabled.");
|
||||
return;
|
||||
}
|
||||
const workflow = await getWorkflow();
|
||||
const jobName = getRequiredEnvParam("GITHUB_JOB");
|
||||
const matrix = parseMatrixInput(actionsUtil.getRequiredInput("matrix"));
|
||||
if (
|
||||
getUploadInputOrThrow(workflow, jobName, matrix) !== "true" ||
|
||||
isInTestMode()
|
||||
) {
|
||||
logger.debug(
|
||||
"Won't upload a failed SARIF file since SARIF upload is disabled."
|
||||
);
|
||||
return;
|
||||
}
|
||||
const category = getCategoryInputOrThrow(workflow, jobName, matrix);
|
||||
const checkoutPath = getCheckoutPathInputOrThrow(workflow, jobName, matrix);
|
||||
|
||||
const sarifFile = "../codeql-failed-run.sarif";
|
||||
await codeql.diagnosticsExport(sarifFile, category);
|
||||
|
||||
core.info(`Uploading failed SARIF file ${sarifFile}`);
|
||||
const uploadResult = await uploadLib.uploadFromActions(
|
||||
sarifFile,
|
||||
checkoutPath,
|
||||
category,
|
||||
logger
|
||||
);
|
||||
await uploadLib.waitForProcessing(
|
||||
repositoryNwo,
|
||||
uploadResult.sarifID,
|
||||
logger,
|
||||
{ isUnsuccessfulExecution: true }
|
||||
);
|
||||
}
|
||||
|
||||
export async function run(
|
||||
uploadDatabaseBundleDebugArtifact: Function,
|
||||
uploadLogsDebugArtifact: Function,
|
||||
printDebugLogs: Function
|
||||
printDebugLogs: Function,
|
||||
repositoryNwo: RepositoryNwo,
|
||||
featureEnablement: FeatureEnablement,
|
||||
logger: Logger
|
||||
) {
|
||||
const logger = getActionsLogger();
|
||||
|
||||
const config = await getConfig(actionsUtil.getTemporaryDirectory(), logger);
|
||||
if (config === undefined) {
|
||||
logger.warning(
|
||||
"Debugging artifacts are unavailable since the 'init' Action failed before it could produce any."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Environment variable used to integration test uploading a SARIF file for failed runs
|
||||
const expectFailedSarifUpload =
|
||||
process.env["CODEQL_ACTION_EXPECT_UPLOAD_FAILED_SARIF"] === "true";
|
||||
|
||||
if (process.env[CODEQL_ACTION_ANALYZE_DID_UPLOAD_SARIF] !== "true") {
|
||||
try {
|
||||
await uploadFailedSarif(config, repositoryNwo, featureEnablement, logger);
|
||||
} catch (e) {
|
||||
if (expectFailedSarifUpload) {
|
||||
throw new Error(
|
||||
"Expected to upload a SARIF file for the failed run, but encountered " +
|
||||
`the following error: ${e}`
|
||||
);
|
||||
}
|
||||
logger.info(
|
||||
`Failed to upload a SARIF file for the failed run. Error: ${e}`
|
||||
);
|
||||
}
|
||||
} else if (expectFailedSarifUpload) {
|
||||
throw new Error(
|
||||
"Expected to upload a SARIF file for the failed run, but didn't."
|
||||
);
|
||||
}
|
||||
|
||||
// Upload appropriate Actions artifacts for debugging
|
||||
if (config?.debugMode) {
|
||||
if (config.debugMode) {
|
||||
core.info(
|
||||
"Debug mode is on. Uploading available database bundles and logs as Actions debugging artifacts..."
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,15 +7,37 @@
|
|||
import * as core from "@actions/core";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { getGitHubVersion } from "./api-client";
|
||||
import * as debugArtifacts from "./debug-artifacts";
|
||||
import { Features } from "./feature-flags";
|
||||
import * as initActionPostHelper from "./init-action-post-helper";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import { checkGitHubVersionInRange, getRequiredEnvParam } from "./util";
|
||||
|
||||
async function runWrapper() {
|
||||
try {
|
||||
const logger = getActionsLogger();
|
||||
const gitHubVersion = await getGitHubVersion();
|
||||
checkGitHubVersionInRange(gitHubVersion, logger);
|
||||
|
||||
const repositoryNwo = parseRepositoryNwo(
|
||||
getRequiredEnvParam("GITHUB_REPOSITORY")
|
||||
);
|
||||
const features = new Features(
|
||||
gitHubVersion,
|
||||
repositoryNwo,
|
||||
actionsUtil.getTemporaryDirectory(),
|
||||
logger
|
||||
);
|
||||
|
||||
await initActionPostHelper.run(
|
||||
debugArtifacts.uploadDatabaseBundleDebugArtifact,
|
||||
debugArtifacts.uploadLogsDebugArtifact,
|
||||
actionsUtil.printDebugLogs
|
||||
actionsUtil.printDebugLogs,
|
||||
repositoryNwo,
|
||||
features,
|
||||
logger
|
||||
);
|
||||
} catch (error) {
|
||||
core.setFailed(`init post-action step failed: ${error}`);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,24 @@
|
|||
export const ODASA_TRACER_CONFIGURATION = "ODASA_TRACER_CONFIGURATION";
|
||||
// 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";
|
||||
/**
|
||||
* This environment variable is set to true when the `analyze` Action
|
||||
* successfully uploads a SARIF file. It does NOT indicate whether the
|
||||
* SARIF file was processed successfully.
|
||||
*/
|
||||
export const CODEQL_ACTION_ANALYZE_DID_UPLOAD_SARIF =
|
||||
"CODEQL_ACTION_ANALYZE_DID_UPLOAD_SARIF";
|
||||
|
||||
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";
|
||||
|
||||
/**
|
||||
* 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";
|
||||
|
|
|
|||
|
|
@ -158,23 +158,22 @@ export function findSarifFilesInDir(sarifPath: string): string[] {
|
|||
|
||||
// Uploads a single sarif file or a directory of sarif files
|
||||
// depending on what the path happens to refer to.
|
||||
// Returns true iff the upload occurred and succeeded
|
||||
export async function uploadFromActions(
|
||||
sarifPath: string,
|
||||
checkoutPath: string,
|
||||
category: string | undefined,
|
||||
logger: Logger
|
||||
): Promise<UploadResult> {
|
||||
return await uploadFiles(
|
||||
getSarifFilePaths(sarifPath),
|
||||
parseRepositoryNwo(util.getRequiredEnvParam("GITHUB_REPOSITORY")),
|
||||
await actionsUtil.getCommitOid(
|
||||
actionsUtil.getRequiredInput("checkout_path")
|
||||
),
|
||||
await actionsUtil.getCommitOid(checkoutPath),
|
||||
await actionsUtil.getRef(),
|
||||
await actionsUtil.getAnalysisKey(),
|
||||
actionsUtil.getOptionalInput("category"),
|
||||
category,
|
||||
util.getRequiredEnvParam("GITHUB_WORKFLOW"),
|
||||
workflow.getWorkflowRunID(),
|
||||
actionsUtil.getRequiredInput("checkout_path"),
|
||||
checkoutPath,
|
||||
actionsUtil.getRequiredInput("matrix"),
|
||||
logger
|
||||
);
|
||||
|
|
@ -386,60 +385,119 @@ async function uploadFiles(
|
|||
const STATUS_CHECK_FREQUENCY_MILLISECONDS = 5 * 1000;
|
||||
const STATUS_CHECK_TIMEOUT_MILLISECONDS = 2 * 60 * 1000;
|
||||
|
||||
// Waits until either the analysis is successfully processed, a processing error is reported, or STATUS_CHECK_TIMEOUT_MILLISECONDS elapses.
|
||||
type ProcessingStatus = "pending" | "complete" | "failed";
|
||||
|
||||
/**
|
||||
* Waits until either the analysis is successfully processed, a processing error
|
||||
* is reported, or `STATUS_CHECK_TIMEOUT_MILLISECONDS` elapses.
|
||||
*
|
||||
* If `isUnsuccessfulExecution` is passed, will throw an error if the analysis
|
||||
* processing does not produce a single error mentioning the unsuccessful
|
||||
* execution.
|
||||
*/
|
||||
export async function waitForProcessing(
|
||||
repositoryNwo: RepositoryNwo,
|
||||
sarifID: string,
|
||||
logger: Logger
|
||||
logger: Logger,
|
||||
options: { isUnsuccessfulExecution: boolean } = {
|
||||
isUnsuccessfulExecution: false,
|
||||
}
|
||||
): Promise<void> {
|
||||
logger.startGroup("Waiting for processing to finish");
|
||||
const client = api.getApiClient();
|
||||
try {
|
||||
const client = api.getApiClient();
|
||||
|
||||
const statusCheckingStarted = Date.now();
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
if (
|
||||
Date.now() >
|
||||
statusCheckingStarted + STATUS_CHECK_TIMEOUT_MILLISECONDS
|
||||
) {
|
||||
// If the analysis hasn't finished processing in the allotted time, we continue anyway rather than failing.
|
||||
// It's possible the analysis will eventually finish processing, but it's not worth spending more Actions time waiting.
|
||||
logger.warning(
|
||||
"Timed out waiting for analysis to finish processing. Continuing."
|
||||
);
|
||||
break;
|
||||
}
|
||||
let response: OctokitResponse<any> | undefined = undefined;
|
||||
try {
|
||||
response = await client.request(
|
||||
"GET /repos/:owner/:repo/code-scanning/sarifs/:sarif_id",
|
||||
{
|
||||
owner: repositoryNwo.owner,
|
||||
repo: repositoryNwo.repo,
|
||||
sarif_id: sarifID,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
`An error occurred checking the status of the delivery. ${e} It should still be processed in the background, but errors that occur during processing may not be reported.`
|
||||
);
|
||||
break;
|
||||
}
|
||||
const status = response.data.processing_status;
|
||||
logger.info(`Analysis upload status is ${status}.`);
|
||||
if (status === "complete") {
|
||||
break;
|
||||
} else if (status === "pending") {
|
||||
logger.debug("Analysis processing is still pending...");
|
||||
} else if (status === "failed") {
|
||||
throw new Error(
|
||||
`Code Scanning could not process the submitted SARIF file:\n${response.data.errors}`
|
||||
);
|
||||
}
|
||||
const statusCheckingStarted = Date.now();
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
if (
|
||||
Date.now() >
|
||||
statusCheckingStarted + STATUS_CHECK_TIMEOUT_MILLISECONDS
|
||||
) {
|
||||
// If the analysis hasn't finished processing in the allotted time, we continue anyway rather than failing.
|
||||
// It's possible the analysis will eventually finish processing, but it's not worth spending more Actions time waiting.
|
||||
logger.warning(
|
||||
"Timed out waiting for analysis to finish processing. Continuing."
|
||||
);
|
||||
break;
|
||||
}
|
||||
let response: OctokitResponse<any> | undefined = undefined;
|
||||
try {
|
||||
response = await client.request(
|
||||
"GET /repos/:owner/:repo/code-scanning/sarifs/:sarif_id",
|
||||
{
|
||||
owner: repositoryNwo.owner,
|
||||
repo: repositoryNwo.repo,
|
||||
sarif_id: sarifID,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
`An error occurred checking the status of the delivery. ${e} It should still be processed in the background, but errors that occur during processing may not be reported.`
|
||||
);
|
||||
break;
|
||||
}
|
||||
const status = response.data.processing_status as ProcessingStatus;
|
||||
logger.info(`Analysis upload status is ${status}.`);
|
||||
|
||||
await util.delay(STATUS_CHECK_FREQUENCY_MILLISECONDS);
|
||||
if (status === "pending") {
|
||||
logger.debug("Analysis processing is still pending...");
|
||||
} else if (options.isUnsuccessfulExecution) {
|
||||
// We expect a specific processing error for unsuccessful executions, so
|
||||
// handle these separately.
|
||||
handleProcessingResultForUnsuccessfulExecution(
|
||||
response,
|
||||
status,
|
||||
logger
|
||||
);
|
||||
break;
|
||||
} else if (status === "complete") {
|
||||
break;
|
||||
} else if (status === "failed") {
|
||||
throw new Error(
|
||||
`Code Scanning could not process the submitted SARIF file:\n${response.data.errors}`
|
||||
);
|
||||
} else {
|
||||
util.assertNever(status);
|
||||
}
|
||||
|
||||
await util.delay(STATUS_CHECK_FREQUENCY_MILLISECONDS);
|
||||
}
|
||||
} finally {
|
||||
logger.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the processing result for an unsuccessful execution. Throws if the
|
||||
* result is not a failure with a single "unsuccessful execution" error.
|
||||
*/
|
||||
function handleProcessingResultForUnsuccessfulExecution(
|
||||
response: OctokitResponse<any, number>,
|
||||
status: Exclude<ProcessingStatus, "pending">,
|
||||
logger: Logger
|
||||
): void {
|
||||
if (
|
||||
status === "failed" &&
|
||||
Array.isArray(response.data.errors) &&
|
||||
response.data.errors.length === 1 &&
|
||||
response.data.errors[0].toString().startsWith("unsuccessful execution")
|
||||
) {
|
||||
logger.debug(
|
||||
"Successfully uploaded a SARIF file for the unsuccessful execution. Received expected " +
|
||||
'"unsuccessful execution" error, and no other errors.'
|
||||
);
|
||||
} 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);
|
||||
}
|
||||
logger.endGroup();
|
||||
}
|
||||
|
||||
export function validateUniqueCategory(sarif: SarifFile): void {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ async function run() {
|
|||
try {
|
||||
const uploadResult = await upload_lib.uploadFromActions(
|
||||
actionsUtil.getRequiredInput("sarif_file"),
|
||||
actionsUtil.getRequiredInput("checkout_path"),
|
||||
actionsUtil.getOptionalInput("category"),
|
||||
getActionsLogger()
|
||||
);
|
||||
core.setOutput("sarif-id", uploadResult.sarifID);
|
||||
|
|
|
|||
|
|
@ -892,3 +892,12 @@ export async function shouldBypassToolcache(
|
|||
}
|
||||
return bypass;
|
||||
}
|
||||
|
||||
export function parseMatrixInput(
|
||||
matrixInput: string | undefined
|
||||
): { [key: string]: string } | undefined {
|
||||
if (matrixInput === undefined || matrixInput === "null") {
|
||||
return undefined;
|
||||
}
|
||||
return JSON.parse(matrixInput);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -525,6 +525,7 @@ test("getWorkflowErrors() should not report an error if PRs are totally unconfig
|
|||
});
|
||||
|
||||
test("getCategoryInputOrThrow returns category for simple workflow with category", (t) => {
|
||||
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
|
||||
t.is(
|
||||
getCategoryInputOrThrow(
|
||||
yaml.load(`
|
||||
|
|
@ -546,6 +547,7 @@ test("getCategoryInputOrThrow returns category for simple workflow with category
|
|||
});
|
||||
|
||||
test("getCategoryInputOrThrow returns undefined for simple workflow without category", (t) => {
|
||||
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
|
||||
t.is(
|
||||
getCategoryInputOrThrow(
|
||||
yaml.load(`
|
||||
|
|
@ -565,6 +567,7 @@ test("getCategoryInputOrThrow returns undefined for simple workflow without cate
|
|||
});
|
||||
|
||||
test("getCategoryInputOrThrow returns category for workflow with multiple jobs", (t) => {
|
||||
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
|
||||
t.is(
|
||||
getCategoryInputOrThrow(
|
||||
yaml.load(`
|
||||
|
|
@ -596,6 +599,7 @@ test("getCategoryInputOrThrow returns category for workflow with multiple jobs",
|
|||
});
|
||||
|
||||
test("getCategoryInputOrThrow finds category for workflow with language matrix", (t) => {
|
||||
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
|
||||
t.is(
|
||||
getCategoryInputOrThrow(
|
||||
yaml.load(`
|
||||
|
|
@ -622,6 +626,7 @@ test("getCategoryInputOrThrow finds category for workflow with language matrix",
|
|||
});
|
||||
|
||||
test("getCategoryInputOrThrow throws error for workflow with dynamic category", (t) => {
|
||||
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
|
||||
t.throws(
|
||||
() =>
|
||||
getCategoryInputOrThrow(
|
||||
|
|
@ -646,7 +651,8 @@ test("getCategoryInputOrThrow throws error for workflow with dynamic category",
|
|||
);
|
||||
});
|
||||
|
||||
test("getCategoryInputOrThrow throws error for workflow with multiple categories", (t) => {
|
||||
test("getCategoryInputOrThrow throws error for workflow with multiple calls to analyze", (t) => {
|
||||
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
|
||||
t.throws(
|
||||
() =>
|
||||
getCategoryInputOrThrow(
|
||||
|
|
@ -669,8 +675,8 @@ test("getCategoryInputOrThrow throws error for workflow with multiple categories
|
|||
),
|
||||
{
|
||||
message:
|
||||
"Could not get category input to github/codeql-action/analyze since there were multiple steps " +
|
||||
"calling github/codeql-action/analyze with different values for category.",
|
||||
"Could not get category input to github/codeql-action/analyze since the analysis job " +
|
||||
"calls github/codeql-action/analyze multiple times.",
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
|||
127
src/workflow.ts
127
src/workflow.ts
|
|
@ -7,13 +7,16 @@ import * as yaml from "js-yaml";
|
|||
import * as api from "./api-client";
|
||||
import { getRequiredEnvParam } from "./util";
|
||||
|
||||
interface WorkflowJobStep {
|
||||
run: any;
|
||||
export interface WorkflowJobStep {
|
||||
name?: string;
|
||||
run?: any;
|
||||
uses?: string;
|
||||
with?: { [key: string]: string };
|
||||
}
|
||||
|
||||
interface WorkflowJob {
|
||||
name?: string;
|
||||
"runs-on"?: string;
|
||||
steps?: WorkflowJobStep[];
|
||||
}
|
||||
|
||||
|
|
@ -33,6 +36,7 @@ interface WorkflowTriggers {
|
|||
}
|
||||
|
||||
export interface Workflow {
|
||||
name?: string;
|
||||
jobs?: { [key: string]: WorkflowJob };
|
||||
on?: string | string[] | WorkflowTriggers;
|
||||
}
|
||||
|
|
@ -321,42 +325,42 @@ function getInputOrThrow(
|
|||
jobName: string,
|
||||
actionName: string,
|
||||
inputName: string,
|
||||
matrixVars: { [key: string]: string }
|
||||
matrixVars: { [key: string]: string } | undefined
|
||||
) {
|
||||
const preamble = `Could not get ${inputName} input to ${actionName} since`;
|
||||
if (!workflow.jobs) {
|
||||
throw new Error(
|
||||
`Could not get ${inputName} input to ${actionName} since the workflow has no jobs.`
|
||||
);
|
||||
throw new Error(`${preamble} the workflow has no jobs.`);
|
||||
}
|
||||
if (!workflow.jobs[jobName]) {
|
||||
throw new Error(`${preamble} the workflow has no job named ${jobName}.`);
|
||||
}
|
||||
|
||||
const stepsCallingAction = getStepsCallingAction(
|
||||
workflow.jobs[jobName],
|
||||
actionName
|
||||
);
|
||||
|
||||
if (stepsCallingAction.length === 0) {
|
||||
throw new Error(
|
||||
`Could not get ${inputName} input to ${actionName} since the workflow has no job named ${jobName}.`
|
||||
`${preamble} the ${jobName} job does not call ${actionName}.`
|
||||
);
|
||||
} else if (stepsCallingAction.length > 1) {
|
||||
throw new Error(
|
||||
`${preamble} the ${jobName} job calls ${actionName} multiple times.`
|
||||
);
|
||||
}
|
||||
|
||||
const inputs = getStepsCallingAction(workflow.jobs[jobName], actionName)
|
||||
.map((step) => step.with?.[inputName])
|
||||
.filter((input) => input !== undefined)
|
||||
.map((input) => input!);
|
||||
let input = stepsCallingAction[0].with?.[inputName];
|
||||
|
||||
if (inputs.length === 0) {
|
||||
return undefined;
|
||||
if (input !== undefined && matrixVars !== undefined) {
|
||||
// Make a basic attempt to substitute matrix variables
|
||||
// First normalize by removing whitespace
|
||||
input = input.replace(/\${{\s+/, "${{").replace(/\s+}}/, "}}");
|
||||
for (const [key, value] of Object.entries(matrixVars)) {
|
||||
input = input.replace(`\${{matrix.${key}}}`, value);
|
||||
}
|
||||
}
|
||||
if (!inputs.every((input) => input === inputs[0])) {
|
||||
throw new Error(
|
||||
`Could not get ${inputName} input to ${actionName} since there were multiple steps calling ` +
|
||||
`${actionName} with different values for ${inputName}.`
|
||||
);
|
||||
}
|
||||
|
||||
// Make a basic attempt to substitute matrix variables
|
||||
// First normalize by removing whitespace
|
||||
let input = inputs[0].replace(/\${{\s+/, "${{").replace(/\s+}}/, "}}");
|
||||
for (const [key, value] of Object.entries(matrixVars)) {
|
||||
input = input.replace(`\${{matrix.${key}}}`, value);
|
||||
}
|
||||
|
||||
if (input.includes("${{")) {
|
||||
if (input !== undefined && input.includes("${{")) {
|
||||
throw new Error(
|
||||
`Could not get ${inputName} input to ${actionName} since it contained an unrecognized dynamic value.`
|
||||
);
|
||||
|
|
@ -364,6 +368,19 @@ function getInputOrThrow(
|
|||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the expected name of the analyze Action.
|
||||
*
|
||||
* This allows us to test workflow parsing functionality as a CodeQL Action PR check.
|
||||
*/
|
||||
function getAnalyzeActionName() {
|
||||
if (getRequiredEnvParam("GITHUB_REPOSITORY") === "github/codeql-action") {
|
||||
return "./analyze";
|
||||
} else {
|
||||
return "github/codeql-action/analyze";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a best effort attempt to retrieve the category input for the particular job,
|
||||
* given a set of matrix variables.
|
||||
|
|
@ -376,13 +393,63 @@ function getInputOrThrow(
|
|||
export function getCategoryInputOrThrow(
|
||||
workflow: Workflow,
|
||||
jobName: string,
|
||||
matrixVars: { [key: string]: string }
|
||||
matrixVars: { [key: string]: string } | undefined
|
||||
): string | undefined {
|
||||
return getInputOrThrow(
|
||||
workflow,
|
||||
jobName,
|
||||
"github/codeql-action/analyze",
|
||||
getAnalyzeActionName(),
|
||||
"category",
|
||||
matrixVars
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a best effort attempt to retrieve the upload input for the particular job,
|
||||
* given a set of matrix variables.
|
||||
*
|
||||
* Typically you'll want to wrap this function in a try/catch block and handle the error.
|
||||
*
|
||||
* @returns the upload input
|
||||
* @throws an error if the upload input could not be determined
|
||||
*/
|
||||
export function getUploadInputOrThrow(
|
||||
workflow: Workflow,
|
||||
jobName: string,
|
||||
matrixVars: { [key: string]: string } | undefined
|
||||
): string {
|
||||
return (
|
||||
getInputOrThrow(
|
||||
workflow,
|
||||
jobName,
|
||||
getAnalyzeActionName(),
|
||||
"upload",
|
||||
matrixVars
|
||||
) || "true" // if unspecified, upload defaults to true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a best effort attempt to retrieve the checkout_path input for the
|
||||
* particular job, given a set of matrix variables.
|
||||
*
|
||||
* Typically you'll want to wrap this function in a try/catch block and handle the error.
|
||||
*
|
||||
* @returns the checkout_path input
|
||||
* @throws an error if the checkout_path input could not be determined
|
||||
*/
|
||||
export function getCheckoutPathInputOrThrow(
|
||||
workflow: Workflow,
|
||||
jobName: string,
|
||||
matrixVars: { [key: string]: string } | undefined
|
||||
): string {
|
||||
return (
|
||||
getInputOrThrow(
|
||||
workflow,
|
||||
jobName,
|
||||
getAnalyzeActionName(),
|
||||
"checkout_path",
|
||||
matrixVars
|
||||
) || getRequiredEnvParam("GITHUB_WORKSPACE") // if unspecified, checkout_path defaults to ${{ github.workspace }}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue