Merge branch 'main' into henrymercer/use-codeql-2.11.6
This commit is contained in:
commit
f629dada4c
42 changed files with 302 additions and 2464 deletions
|
|
@ -295,7 +295,12 @@ function getRefFromEnv(): string {
|
|||
return refEnv;
|
||||
}
|
||||
|
||||
type ActionName = "init" | "autobuild" | "finish" | "upload-sarif";
|
||||
type ActionName =
|
||||
| "init"
|
||||
| "autobuild"
|
||||
| "finish"
|
||||
| "upload-sarif"
|
||||
| "init-post";
|
||||
type ActionStatus =
|
||||
| "starting"
|
||||
| "aborted"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import * as toolrunner from "@actions/exec/lib/toolrunner";
|
|||
import * as toolcache from "@actions/tool-cache";
|
||||
import { default as deepEqual } from "fast-deep-equal";
|
||||
import * as yaml from "js-yaml";
|
||||
import { default as queryString } from "query-string";
|
||||
import * as semver from "semver";
|
||||
import { v4 as uuidV4 } from "uuid";
|
||||
|
||||
|
|
@ -489,7 +488,7 @@ export async function setupCodeQL(
|
|||
}
|
||||
|
||||
const parsedCodeQLURL = new URL(codeqlURL);
|
||||
const parsedQueryString = queryString.parse(parsedCodeQLURL.search);
|
||||
const searchParams = new URLSearchParams(parsedCodeQLURL.search);
|
||||
const headers: OutgoingHttpHeaders = {
|
||||
accept: "application/octet-stream",
|
||||
};
|
||||
|
|
@ -499,7 +498,7 @@ export async function setupCodeQL(
|
|||
// We also don't want to send an authorization header if there's already a token provided in the URL.
|
||||
if (
|
||||
codeqlURL.startsWith(`${apiDetails.url}/`) &&
|
||||
parsedQueryString["token"] === undefined
|
||||
!searchParams.has("token")
|
||||
) {
|
||||
logger.debug("Downloading CodeQL bundle with token.");
|
||||
headers.authorization = `token ${apiDetails.auth}`;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { parseRepositoryNwo } from "./repository";
|
|||
import {
|
||||
createFeatures,
|
||||
getRecordingLogger,
|
||||
LoggedMessage,
|
||||
setupTests,
|
||||
} from "./testing-utils";
|
||||
import * as uploadLib from "./upload-lib";
|
||||
|
|
@ -111,6 +112,40 @@ test("uploads failed SARIF run for typical workflow", async (t) => {
|
|||
await testFailedSarifUpload(t, actionsWorkflow, { category: "my-category" });
|
||||
});
|
||||
|
||||
test("doesn't upload failed SARIF for workflow with upload: false", 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",
|
||||
upload: false,
|
||||
},
|
||||
},
|
||||
]);
|
||||
await testFailedSarifUpload(t, actionsWorkflow, {
|
||||
expectedLogs: [
|
||||
{
|
||||
message:
|
||||
"Won't upload a failed SARIF file since SARIF upload is disabled.",
|
||||
type: "debug",
|
||||
},
|
||||
],
|
||||
expectUpload: false,
|
||||
});
|
||||
});
|
||||
|
||||
test("uploading failed SARIF run fails when workflow does not reference github/codeql-action", async (t) => {
|
||||
const actionsWorkflow = createTestWorkflow([
|
||||
{
|
||||
|
|
@ -149,7 +184,15 @@ function createTestWorkflow(
|
|||
async function testFailedSarifUpload(
|
||||
t: ExecutionContext<unknown>,
|
||||
actionsWorkflow: workflow.Workflow,
|
||||
{ category }: { category?: string } = {}
|
||||
{
|
||||
category,
|
||||
expectedLogs = [],
|
||||
expectUpload = true,
|
||||
}: {
|
||||
category?: string;
|
||||
expectedLogs?: LoggedMessage[];
|
||||
expectUpload?: boolean;
|
||||
} = {}
|
||||
): Promise<void> {
|
||||
const config = {
|
||||
codeQLCmd: "codeql",
|
||||
|
|
@ -180,23 +223,29 @@ async function testFailedSarifUpload(
|
|||
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,
|
||||
})
|
||||
);
|
||||
t.deepEqual(messages, expectedLogs);
|
||||
if (expectUpload) {
|
||||
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,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
t.true(diagnosticsExportStub.notCalled);
|
||||
t.true(uploadFromActions.notCalled);
|
||||
t.true(waitForProcessing.notCalled);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,17 +16,37 @@ import {
|
|||
getWorkflow,
|
||||
} from "./workflow";
|
||||
|
||||
export interface UploadFailedSarifResult extends uploadLib.UploadStatusReport {
|
||||
/** If there was an error while uploading a failed run, this is its message. */
|
||||
upload_failed_run_error?: string;
|
||||
/** If there was an error while uploading a failed run, this is its stack trace. */
|
||||
upload_failed_run_stack_trace?: string;
|
||||
/** Reason why we did not upload a SARIF payload with `executionSuccessful: false`. */
|
||||
upload_failed_run_skipped_because?: string;
|
||||
}
|
||||
|
||||
function createFailedUploadFailedSarifResult(
|
||||
error: unknown
|
||||
): UploadFailedSarifResult {
|
||||
return {
|
||||
upload_failed_run_error:
|
||||
error instanceof Error ? error.message : String(error),
|
||||
upload_failed_run_stack_trace:
|
||||
error instanceof Error ? error.stack : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export async function uploadFailedSarif(
|
||||
config: Config,
|
||||
repositoryNwo: RepositoryNwo,
|
||||
featureEnablement: FeatureEnablement,
|
||||
logger: Logger
|
||||
) {
|
||||
): Promise<UploadFailedSarifResult> {
|
||||
if (!config.codeQLCmd) {
|
||||
logger.warning(
|
||||
"CodeQL command not found. Unable to upload failed SARIF file."
|
||||
);
|
||||
return;
|
||||
return { upload_failed_run_skipped_because: "CodeQL command not found" };
|
||||
}
|
||||
const codeql = await getCodeQL(config.codeQLCmd);
|
||||
if (
|
||||
|
|
@ -36,7 +56,7 @@ export async function uploadFailedSarif(
|
|||
))
|
||||
) {
|
||||
logger.debug("Uploading failed SARIF is disabled.");
|
||||
return;
|
||||
return { upload_failed_run_skipped_because: "Feature disabled" };
|
||||
}
|
||||
const workflow = await getWorkflow();
|
||||
const jobName = getRequiredEnvParam("GITHUB_JOB");
|
||||
|
|
@ -48,7 +68,7 @@ export async function uploadFailedSarif(
|
|||
logger.debug(
|
||||
"Won't upload a failed SARIF file since SARIF upload is disabled."
|
||||
);
|
||||
return;
|
||||
return { upload_failed_run_skipped_because: "SARIF upload is disabled" };
|
||||
}
|
||||
const category = getCategoryInputOrThrow(workflow, jobName, matrix);
|
||||
const checkoutPath = getCheckoutPathInputOrThrow(workflow, jobName, matrix);
|
||||
|
|
@ -69,6 +89,48 @@ export async function uploadFailedSarif(
|
|||
logger,
|
||||
{ isUnsuccessfulExecution: true }
|
||||
);
|
||||
return uploadResult?.statusReport ?? {};
|
||||
}
|
||||
|
||||
export async function uploadSarifIfRunFailed(
|
||||
config: Config,
|
||||
repositoryNwo: RepositoryNwo,
|
||||
featureEnablement: FeatureEnablement,
|
||||
logger: Logger
|
||||
): Promise<UploadFailedSarifResult> {
|
||||
// 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 {
|
||||
return 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}`
|
||||
);
|
||||
return createFailedUploadFailedSarifResult(e);
|
||||
}
|
||||
} else if (expectFailedSarifUpload) {
|
||||
throw new Error(
|
||||
"Expected to upload a SARIF file for the failed run, but didn't."
|
||||
);
|
||||
} else {
|
||||
return {
|
||||
upload_failed_run_skipped_because: "SARIF file already uploaded",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function run(
|
||||
|
|
@ -87,29 +149,12 @@ export async function run(
|
|||
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."
|
||||
);
|
||||
}
|
||||
const uploadFailedSarifResult = await uploadSarifIfRunFailed(
|
||||
config,
|
||||
repositoryNwo,
|
||||
featureEnablement,
|
||||
logger
|
||||
);
|
||||
|
||||
// Upload appropriate Actions artifacts for debugging
|
||||
if (config.debugMode) {
|
||||
|
|
@ -121,4 +166,6 @@ export async function run(
|
|||
|
||||
await printDebugLogs(config);
|
||||
}
|
||||
|
||||
return uploadFailedSarifResult;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,14 @@
|
|||
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import {
|
||||
createStatusReportBase,
|
||||
getActionsStatus,
|
||||
getTemporaryDirectory,
|
||||
printDebugLogs,
|
||||
sendStatusReport,
|
||||
StatusReportBase,
|
||||
} from "./actions-util";
|
||||
import { getGitHubVersion } from "./api-client";
|
||||
import * as debugArtifacts from "./debug-artifacts";
|
||||
import { Features } from "./feature-flags";
|
||||
|
|
@ -15,7 +22,15 @@ import { getActionsLogger } from "./logging";
|
|||
import { parseRepositoryNwo } from "./repository";
|
||||
import { checkGitHubVersionInRange, getRequiredEnvParam } from "./util";
|
||||
|
||||
interface InitPostStatusReport
|
||||
extends StatusReportBase,
|
||||
initActionPostHelper.UploadFailedSarifResult {}
|
||||
|
||||
async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
let uploadFailedSarifResult:
|
||||
| initActionPostHelper.UploadFailedSarifResult
|
||||
| undefined;
|
||||
try {
|
||||
const logger = getActionsLogger();
|
||||
const gitHubVersion = await getGitHubVersion();
|
||||
|
|
@ -27,22 +42,43 @@ async function runWrapper() {
|
|||
const features = new Features(
|
||||
gitHubVersion,
|
||||
repositoryNwo,
|
||||
actionsUtil.getTemporaryDirectory(),
|
||||
getTemporaryDirectory(),
|
||||
logger
|
||||
);
|
||||
|
||||
await initActionPostHelper.run(
|
||||
uploadFailedSarifResult = await initActionPostHelper.run(
|
||||
debugArtifacts.uploadDatabaseBundleDebugArtifact,
|
||||
debugArtifacts.uploadLogsDebugArtifact,
|
||||
actionsUtil.printDebugLogs,
|
||||
printDebugLogs,
|
||||
repositoryNwo,
|
||||
features,
|
||||
logger
|
||||
);
|
||||
} catch (error) {
|
||||
core.setFailed(`init post-action step failed: ${error}`);
|
||||
console.log(error);
|
||||
} catch (e) {
|
||||
core.setFailed(e instanceof Error ? e.message : String(e));
|
||||
|
||||
console.log(e);
|
||||
await sendStatusReport(
|
||||
await createStatusReportBase(
|
||||
"init-post",
|
||||
getActionsStatus(e),
|
||||
startedAt,
|
||||
String(e),
|
||||
e instanceof Error ? e.stack : undefined
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
"init-post",
|
||||
"success",
|
||||
startedAt
|
||||
);
|
||||
const statusReport: InitPostStatusReport = {
|
||||
...statusReportBase,
|
||||
...uploadFailedSarifResult,
|
||||
};
|
||||
await sendStatusReport(statusReport);
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export interface WorkflowJobStep {
|
|||
name?: string;
|
||||
run?: any;
|
||||
uses?: string;
|
||||
with?: { [key: string]: string };
|
||||
with?: { [key: string]: boolean | number | string };
|
||||
}
|
||||
|
||||
interface WorkflowJob {
|
||||
|
|
@ -350,12 +350,12 @@ function getInputOrThrow(
|
|||
);
|
||||
}
|
||||
|
||||
let input = stepsCallingAction[0].with?.[inputName];
|
||||
let input = stepsCallingAction[0].with?.[inputName]?.toString();
|
||||
|
||||
if (input !== undefined && matrixVars !== undefined) {
|
||||
// Make a basic attempt to substitute matrix variables
|
||||
// First normalize by removing whitespace
|
||||
// Normalize by removing whitespace
|
||||
input = input.replace(/\${{\s+/, "${{").replace(/\s+}}/, "}}");
|
||||
// Make a basic attempt to substitute matrix variables
|
||||
for (const [key, value] of Object.entries(matrixVars)) {
|
||||
input = input.replace(`\${{matrix.${key}}}`, value);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue