Resolve dependency cycles between actions-util and workflow

This commit is contained in:
Henry Mercer
2023-07-19 17:21:33 +01:00
parent 0bc4788cf7
commit 2637069a45
39 changed files with 532 additions and 527 deletions

View File

@@ -4,7 +4,8 @@ import * as path from "path";
import test from "ava";
import * as sinon from "sinon";
import * as actionsutil from "./actions-util";
import * as actionsUtil from "./actions-util";
import { computeAutomationID, createStatusReportBase } from "./api-client";
import { EnvVar } from "./environment";
import { setupActionsVars, setupTests } from "./testing-utils";
import { initializeEnvironment, withTmpDir } from "./util";
@@ -13,7 +14,7 @@ setupTests(test);
test("getRef() throws on the empty string", async (t) => {
process.env["GITHUB_REF"] = "";
await t.throwsAsync(actionsutil.getRef);
await t.throwsAsync(actionsUtil.getRef);
});
test("getRef() returns merge PR ref if GITHUB_SHA still checked out", async (t) => {
@@ -24,10 +25,10 @@ test("getRef() returns merge PR ref if GITHUB_SHA still checked out", async (t)
process.env["GITHUB_REF"] = expectedRef;
process.env["GITHUB_SHA"] = currentSha;
const callback = sinon.stub(actionsutil, "getCommitOid");
const callback = sinon.stub(actionsUtil, "getCommitOid");
callback.withArgs("HEAD").resolves(currentSha);
const actualRef = await actionsutil.getRef();
const actualRef = await actionsUtil.getRef();
t.deepEqual(actualRef, expectedRef);
callback.restore();
});
@@ -41,11 +42,11 @@ test("getRef() returns merge PR ref if GITHUB_REF still checked out but sha has
process.env["GITHUB_SHA"] = "b".repeat(40);
const sha = "a".repeat(40);
const callback = sinon.stub(actionsutil, "getCommitOid");
const callback = sinon.stub(actionsUtil, "getCommitOid");
callback.withArgs("refs/remotes/pull/1/merge").resolves(sha);
callback.withArgs("HEAD").resolves(sha);
const actualRef = await actionsutil.getRef();
const actualRef = await actionsUtil.getRef();
t.deepEqual(actualRef, expectedRef);
callback.restore();
});
@@ -57,11 +58,11 @@ test("getRef() returns head PR ref if GITHUB_REF no longer checked out", async (
process.env["GITHUB_REF"] = "refs/pull/1/merge";
process.env["GITHUB_SHA"] = "a".repeat(40);
const callback = sinon.stub(actionsutil, "getCommitOid");
const callback = sinon.stub(actionsUtil, "getCommitOid");
callback.withArgs(tmpDir, "refs/pull/1/merge").resolves("a".repeat(40));
callback.withArgs(tmpDir, "HEAD").resolves("b".repeat(40));
const actualRef = await actionsutil.getRef();
const actualRef = await actionsUtil.getRef();
t.deepEqual(actualRef, "refs/pull/1/head");
callback.restore();
});
@@ -70,7 +71,7 @@ test("getRef() returns head PR ref if GITHUB_REF no longer checked out", async (
test("getRef() returns ref provided as an input and ignores current HEAD", async (t) => {
await withTmpDir(async (tmpDir: string) => {
setupActionsVars(tmpDir, tmpDir);
const getAdditionalInputStub = sinon.stub(actionsutil, "getOptionalInput");
const getAdditionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
getAdditionalInputStub.withArgs("ref").resolves("refs/pull/2/merge");
getAdditionalInputStub.withArgs("sha").resolves("b".repeat(40));
@@ -78,11 +79,11 @@ test("getRef() returns ref provided as an input and ignores current HEAD", async
process.env["GITHUB_REF"] = "refs/pull/1/merge";
process.env["GITHUB_SHA"] = "a".repeat(40);
const callback = sinon.stub(actionsutil, "getCommitOid");
const callback = sinon.stub(actionsUtil, "getCommitOid");
callback.withArgs("refs/pull/1/merge").resolves("b".repeat(40));
callback.withArgs("HEAD").resolves("b".repeat(40));
const actualRef = await actionsutil.getRef();
const actualRef = await actionsUtil.getRef();
t.deepEqual(actualRef, "refs/pull/2/merge");
callback.restore();
getAdditionalInputStub.restore();
@@ -98,7 +99,7 @@ test("getRef() returns CODE_SCANNING_REF as a fallback for GITHUB_REF", async (t
process.env["GITHUB_REF"] = "";
process.env["GITHUB_SHA"] = currentSha;
const actualRef = await actionsutil.getRef();
const actualRef = await actionsUtil.getRef();
t.deepEqual(actualRef, expectedRef);
});
});
@@ -112,7 +113,7 @@ test("getRef() returns GITHUB_REF over CODE_SCANNING_REF if both are provided",
process.env["GITHUB_REF"] = expectedRef;
process.env["GITHUB_SHA"] = currentSha;
const actualRef = await actionsutil.getRef();
const actualRef = await actionsUtil.getRef();
t.deepEqual(actualRef, expectedRef);
});
});
@@ -120,12 +121,12 @@ test("getRef() returns GITHUB_REF over CODE_SCANNING_REF if both are provided",
test("getRef() throws an error if only `ref` is provided as an input", async (t) => {
await withTmpDir(async (tmpDir: string) => {
setupActionsVars(tmpDir, tmpDir);
const getAdditionalInputStub = sinon.stub(actionsutil, "getOptionalInput");
const getAdditionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
getAdditionalInputStub.withArgs("ref").resolves("refs/pull/1/merge");
await t.throwsAsync(
async () => {
await actionsutil.getRef();
await actionsUtil.getRef();
},
{
instanceOf: Error,
@@ -141,12 +142,12 @@ test("getRef() throws an error if only `sha` is provided as an input", async (t)
await withTmpDir(async (tmpDir: string) => {
setupActionsVars(tmpDir, tmpDir);
process.env["GITHUB_WORKSPACE"] = "/tmp";
const getAdditionalInputStub = sinon.stub(actionsutil, "getOptionalInput");
const getAdditionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
getAdditionalInputStub.withArgs("sha").resolves("a".repeat(40));
await t.throwsAsync(
async () => {
await actionsutil.getRef();
await actionsUtil.getRef();
},
{
instanceOf: Error,
@@ -159,7 +160,7 @@ test("getRef() throws an error if only `sha` is provided as an input", async (t)
});
test("computeAutomationID()", async (t) => {
let actualAutomationID = actionsutil.computeAutomationID(
let actualAutomationID = computeAutomationID(
".github/workflows/codeql-analysis.yml:analyze",
'{"language": "javascript", "os": "linux"}'
);
@@ -169,7 +170,7 @@ test("computeAutomationID()", async (t) => {
);
// check the environment sorting
actualAutomationID = actionsutil.computeAutomationID(
actualAutomationID = computeAutomationID(
".github/workflows/codeql-analysis.yml:analyze",
'{"os": "linux", "language": "javascript"}'
);
@@ -179,7 +180,7 @@ test("computeAutomationID()", async (t) => {
);
// check that an empty environment produces the right results
actualAutomationID = actionsutil.computeAutomationID(
actualAutomationID = computeAutomationID(
".github/workflows/codeql-analysis.yml:analyze",
"{}"
);
@@ -189,7 +190,7 @@ test("computeAutomationID()", async (t) => {
);
// check non string environment values
actualAutomationID = actionsutil.computeAutomationID(
actualAutomationID = computeAutomationID(
".github/workflows/codeql-analysis.yml:analyze",
'{"number": 1, "object": {"language": "javascript"}}'
);
@@ -199,7 +200,7 @@ test("computeAutomationID()", async (t) => {
);
// check undefined environment
actualAutomationID = actionsutil.computeAutomationID(
actualAutomationID = computeAutomationID(
".github/workflows/codeql-analysis.yml:analyze",
undefined
);
@@ -217,7 +218,7 @@ test("initializeEnvironment", (t) => {
test("isAnalyzingDefaultBranch()", async (t) => {
process.env["GITHUB_EVENT_NAME"] = "push";
process.env["CODE_SCANNING_IS_ANALYZING_DEFAULT_BRANCH"] = "true";
t.deepEqual(await actionsutil.isAnalyzingDefaultBranch(), true);
t.deepEqual(await actionsUtil.isAnalyzingDefaultBranch(), true);
process.env["CODE_SCANNING_IS_ANALYZING_DEFAULT_BRANCH"] = "false";
await withTmpDir(async (tmpDir) => {
@@ -235,13 +236,13 @@ test("isAnalyzingDefaultBranch()", async (t) => {
process.env["GITHUB_REF"] = "main";
process.env["GITHUB_SHA"] = "1234";
t.deepEqual(await actionsutil.isAnalyzingDefaultBranch(), true);
t.deepEqual(await actionsUtil.isAnalyzingDefaultBranch(), true);
process.env["GITHUB_REF"] = "refs/heads/main";
t.deepEqual(await actionsutil.isAnalyzingDefaultBranch(), true);
t.deepEqual(await actionsUtil.isAnalyzingDefaultBranch(), true);
process.env["GITHUB_REF"] = "feature";
t.deepEqual(await actionsutil.isAnalyzingDefaultBranch(), false);
t.deepEqual(await actionsUtil.isAnalyzingDefaultBranch(), false);
fs.writeFileSync(
envFile,
@@ -251,9 +252,9 @@ test("isAnalyzingDefaultBranch()", async (t) => {
);
process.env["GITHUB_EVENT_NAME"] = "schedule";
process.env["GITHUB_REF"] = "refs/heads/main";
t.deepEqual(await actionsutil.isAnalyzingDefaultBranch(), true);
t.deepEqual(await actionsUtil.isAnalyzingDefaultBranch(), true);
const getAdditionalInputStub = sinon.stub(actionsutil, "getOptionalInput");
const getAdditionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
getAdditionalInputStub
.withArgs("ref")
.resolves("refs/heads/something-else");
@@ -262,7 +263,7 @@ test("isAnalyzingDefaultBranch()", async (t) => {
.resolves("0000000000000000000000000000000000000000");
process.env["GITHUB_EVENT_NAME"] = "schedule";
process.env["GITHUB_REF"] = "refs/heads/main";
t.deepEqual(await actionsutil.isAnalyzingDefaultBranch(), false);
t.deepEqual(await actionsUtil.isAnalyzingDefaultBranch(), false);
getAdditionalInputStub.restore();
});
});
@@ -279,10 +280,10 @@ test("createStatusReportBase", async (t) => {
process.env["CODEQL_ACTION_ANALYSIS_KEY"] = "analysis-key";
process.env["RUNNER_OS"] = "macOS";
const getRequiredInput = sinon.stub(actionsutil, "getRequiredInput");
const getRequiredInput = sinon.stub(actionsUtil, "getRequiredInput");
getRequiredInput.withArgs("matrix").resolves("input/matrix");
const statusReport = await actionsutil.createStatusReportBase(
const statusReport = await createStatusReportBase(
"init",
"failure",
new Date("May 19, 2023 05:19:00"),

View File

@@ -1,5 +1,4 @@
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import * as core from "@actions/core";
@@ -8,20 +7,12 @@ import * as safeWhich from "@chrisgavin/safe-which";
import { JSONSchemaForNPMPackageJsonFiles } from "@schemastore/package";
import type { Config } from "./config-utils";
import { EnvVar } from "./environment";
import {
doesDirectoryExist,
getCachedCodeQlVersion,
getCodeQLDatabasePath,
getRequiredEnvParam,
parseMatrixInput,
UserError,
} from "./util";
import {
getWorkflowRunID,
getWorkflowRunAttempt,
getWorkflowRelativePath,
} from "./workflow";
// eslint-disable-next-line import/no-commonjs
const pkg = require("../package.json") as JSONSchemaForNPMPackageJsonFiles;
@@ -159,59 +150,6 @@ export const determineMergeBaseCommitOid = async function (): Promise<
}
};
/**
* Get the analysis key parameter for the current job.
*
* This will combine the workflow path and current job name.
* Computing this the first time requires making requests to
* the GitHub API, but after that the result will be cached.
*/
export async function getAnalysisKey(): Promise<string> {
const analysisKeyEnvVar = "CODEQL_ACTION_ANALYSIS_KEY";
let analysisKey = process.env[analysisKeyEnvVar];
if (analysisKey !== undefined) {
return analysisKey;
}
const workflowPath = await getWorkflowRelativePath();
const jobName = getRequiredEnvParam("GITHUB_JOB");
analysisKey = `${workflowPath}:${jobName}`;
core.exportVariable(analysisKeyEnvVar, analysisKey);
return analysisKey;
}
export async function getAutomationID(): Promise<string> {
const analysis_key = await getAnalysisKey();
const environment = getRequiredInput("matrix");
return computeAutomationID(analysis_key, environment);
}
export function computeAutomationID(
analysis_key: string,
environment: string | undefined
): string {
let automationID = `${analysis_key}/`;
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 {
// In code scanning we just handle the string values,
// the rest get converted to the empty string
automationID += `${entry[0]}:/`;
}
}
}
return automationID;
}
/**
* Get the ref currently being analyzed.
*/
@@ -297,7 +235,7 @@ function getRefFromEnv(): string {
return refEnv;
}
type ActionName =
export type ActionName =
| "init"
| "autobuild"
| "finish"
@@ -421,99 +359,6 @@ export function getActionVersion(): string {
return pkg.version!;
}
/**
* Compose a StatusReport.
*
* @param actionName The name of the action, e.g. 'init', 'finish', 'upload-sarif'
* @param status The status. Must be 'success', 'failure', or 'starting'
* @param startedAt The time this action started executing.
* @param cause Cause of failure (only supply if status is 'failure')
* @param exception Exception (only supply if status is 'failure')
*/
export async function createStatusReportBase(
actionName: ActionName,
status: ActionStatus,
actionStartedAt: Date,
cause?: string,
exception?: string
): 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[EnvVar.WORKFLOW_STARTED_AT];
if (workflowStartedAt === undefined) {
workflowStartedAt = actionStartedAt.toISOString();
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[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(EnvVar.TESTING_ENVIRONMENT, testingEnvironment);
}
const statusReport: StatusReportBase = {
job_run_uuid: jobRunUUID,
workflow_run_id: workflowRunID,
workflow_run_attempt: workflowRunAttempt,
workflow_name: workflowName,
job_name: jobName,
analysis_key,
commit_oid: commitOid,
ref,
action_name: actionName,
action_ref: actionRef,
action_oid: "unknown", // TODO decide if it's possible to fill this in
started_at: workflowStartedAt,
action_started_at: actionStartedAt.toISOString(),
status,
testing_environment: testingEnvironment,
runner_os: runnerOs,
action_version: getActionVersion(),
};
// Add optional parameters
if (cause) {
statusReport.cause = cause;
}
if (exception) {
statusReport.exception = exception;
}
if (
status === "success" ||
status === "failure" ||
status === "aborted" ||
status === "user-error"
) {
statusReport.completed_at = new Date().toISOString();
}
const matrix = getRequiredInput("matrix");
if (matrix) {
statusReport.matrix_vars = matrix;
}
if ("RUNNER_ARCH" in process.env) {
// RUNNER_ARCH is available only in GHES 3.4 and later
// Values other than X86, X64, ARM, or ARM64 are discarded server side
statusReport.runner_arch = process.env["RUNNER_ARCH"];
}
if (runnerOs === "Windows" || runnerOs === "macOS") {
statusReport.runner_os_release = os.release();
}
if (codeQlCliVersion !== undefined) {
statusReport.codeql_version = codeQlCliVersion;
}
return statusReport;
}
/**
* Returns the name of the event that triggered this workflow.
*
@@ -642,3 +487,41 @@ export function getUploadValue(input: string | undefined): UploadKind {
return "always";
}
}
/**
* Get the workflow run ID.
*/
export function getWorkflowRunID(): number {
const workflowRunIdString = getRequiredEnvParam("GITHUB_RUN_ID");
const workflowRunID = parseInt(workflowRunIdString, 10);
if (Number.isNaN(workflowRunID)) {
throw new Error(
`GITHUB_RUN_ID must define a non NaN workflow run ID. Current value is ${workflowRunIdString}`
);
}
if (workflowRunID < 0) {
throw new Error(
`GITHUB_RUN_ID must be a non-negative integer. Current value is ${workflowRunIdString}`
);
}
return workflowRunID;
}
/**
* Get the workflow run attempt number.
*/
export function getWorkflowRunAttempt(): number {
const workflowRunAttemptString = getRequiredEnvParam("GITHUB_RUN_ATTEMPT");
const workflowRunAttempt = parseInt(workflowRunAttemptString, 10);
if (Number.isNaN(workflowRunAttempt)) {
throw new Error(
`GITHUB_RUN_ATTEMPT must define a non NaN workflow run attempt. Current value is ${workflowRunAttemptString}`
);
}
if (workflowRunAttempt <= 0) {
throw new Error(
`GITHUB_RUN_ATTEMPT must be a positive integer. Current value is ${workflowRunAttemptString}`
);
}
return workflowRunAttempt;
}

View File

@@ -27,7 +27,7 @@ test("analyze action with RAM & threads from environment variables", async (t) =
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
process.env["GITHUB_API_URL"] = "https://api.github.com";
sinon
.stub(actionsUtil, "createStatusReportBase")
.stub(api, "createStatusReportBase")
.resolves({} as actionsUtil.StatusReportBase);
sinon.stub(api, "sendStatusReport").resolves(true);
sinon.stub(actionsUtil, "isAnalyzingDefaultBranch").resolves(true);

View File

@@ -27,7 +27,7 @@ test("analyze action with RAM & threads from action inputs", async (t) => {
process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository";
process.env["GITHUB_API_URL"] = "https://api.github.com";
sinon
.stub(actionsUtil, "createStatusReportBase")
.stub(api, "createStatusReportBase")
.resolves({} as actionsUtil.StatusReportBase);
sinon.stub(api, "sendStatusReport").resolves(true);
const gitHubVersion: util.GitHubVersion = {

View File

@@ -61,7 +61,7 @@ export async function sendStatusReport(
error,
stats?.analyze_failure_language
);
const statusReportBase = await actionsUtil.createStatusReportBase(
const statusReportBase = await api.createStatusReportBase(
"finish",
status,
startedAt,
@@ -182,11 +182,7 @@ async function run() {
try {
if (
!(await api.sendStatusReport(
await actionsUtil.createStatusReportBase(
"finish",
"starting",
startedAt
)
await api.createStatusReportBase("finish", "starting", startedAt)
))
) {
return;

View File

@@ -1,15 +1,25 @@
import * as os from "os";
import * as core from "@actions/core";
import * as githubUtils from "@actions/github/lib/utils";
import * as retry from "@octokit/plugin-retry";
import consoleLogLevel from "console-log-level";
import {
ActionName,
ActionStatus,
StatusReportBase,
getActionVersion,
getOptionalInput,
getRef,
getRequiredInput,
getWorkflowEventName,
getWorkflowRunAttempt,
getWorkflowRunID,
} from "./actions-util";
import { EnvVar } from "./environment";
import {
getCachedCodeQlVersion,
getRequiredEnvParam,
GITHUB_DOTCOM_URL,
GitHubVariant,
@@ -17,6 +27,7 @@ import {
isHTTPError,
isInTestMode,
parseGitHubUrl,
parseMatrixInput,
} from "./util";
const GITHUB_ENTERPRISE_VERSION_HEADER = "x-github-enterprise-version";
@@ -125,6 +136,99 @@ export async function getGitHubVersion(): Promise<GitHubVersion> {
return cachedGitHubVersion;
}
/**
* Compose a StatusReport.
*
* @param actionName The name of the action, e.g. 'init', 'finish', 'upload-sarif'
* @param status The status. Must be 'success', 'failure', or 'starting'
* @param startedAt The time this action started executing.
* @param cause Cause of failure (only supply if status is 'failure')
* @param exception Exception (only supply if status is 'failure')
*/
export async function createStatusReportBase(
actionName: ActionName,
status: ActionStatus,
actionStartedAt: Date,
cause?: string,
exception?: string
): 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[EnvVar.WORKFLOW_STARTED_AT];
if (workflowStartedAt === undefined) {
workflowStartedAt = actionStartedAt.toISOString();
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[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(EnvVar.TESTING_ENVIRONMENT, testingEnvironment);
}
const statusReport: StatusReportBase = {
job_run_uuid: jobRunUUID,
workflow_run_id: workflowRunID,
workflow_run_attempt: workflowRunAttempt,
workflow_name: workflowName,
job_name: jobName,
analysis_key,
commit_oid: commitOid,
ref,
action_name: actionName,
action_ref: actionRef,
action_oid: "unknown", // TODO decide if it's possible to fill this in
started_at: workflowStartedAt,
action_started_at: actionStartedAt.toISOString(),
status,
testing_environment: testingEnvironment,
runner_os: runnerOs,
action_version: getActionVersion(),
};
// Add optional parameters
if (cause) {
statusReport.cause = cause;
}
if (exception) {
statusReport.exception = exception;
}
if (
status === "success" ||
status === "failure" ||
status === "aborted" ||
status === "user-error"
) {
statusReport.completed_at = new Date().toISOString();
}
const matrix = getRequiredInput("matrix");
if (matrix) {
statusReport.matrix_vars = matrix;
}
if ("RUNNER_ARCH" in process.env) {
// RUNNER_ARCH is available only in GHES 3.4 and later
// Values other than X86, X64, ARM, or ARM64 are discarded server side
statusReport.runner_arch = process.env["RUNNER_ARCH"];
}
if (runnerOs === "Windows" || runnerOs === "macOS") {
statusReport.runner_os_release = os.release();
}
if (codeQlCliVersion !== undefined) {
statusReport.codeql_version = codeQlCliVersion;
}
return statusReport;
}
const GENERIC_403_MSG =
"The repo on which this action is running is not opted-in to CodeQL code scanning.";
const GENERIC_404_MSG =
@@ -212,3 +316,81 @@ export async function sendStatusReport<S extends StatusReportBase>(
return true;
}
}
/**
* Get the path of the currently executing workflow relative to the repository root.
*/
export async function getWorkflowRelativePath(): Promise<string> {
const repo_nwo = getRequiredEnvParam("GITHUB_REPOSITORY").split("/");
const owner = repo_nwo[0];
const repo = repo_nwo[1];
const run_id = Number(getRequiredEnvParam("GITHUB_RUN_ID"));
const apiClient = getApiClient();
const runsResponse = await apiClient.request(
"GET /repos/:owner/:repo/actions/runs/:run_id?exclude_pull_requests=true",
{
owner,
repo,
run_id,
}
);
const workflowUrl = runsResponse.data.workflow_url;
const workflowResponse = await apiClient.request(`GET ${workflowUrl}`);
return workflowResponse.data.path;
}
/**
* Get the analysis key parameter for the current job.
*
* This will combine the workflow path and current job name.
* Computing this the first time requires making requests to
* the GitHub API, but after that the result will be cached.
*/
export async function getAnalysisKey(): Promise<string> {
const analysisKeyEnvVar = "CODEQL_ACTION_ANALYSIS_KEY";
let analysisKey = process.env[analysisKeyEnvVar];
if (analysisKey !== undefined) {
return analysisKey;
}
const workflowPath = await getWorkflowRelativePath();
const jobName = getRequiredEnvParam("GITHUB_JOB");
analysisKey = `${workflowPath}:${jobName}`;
core.exportVariable(analysisKeyEnvVar, analysisKey);
return analysisKey;
}
export async function getAutomationID(): Promise<string> {
const analysis_key = await getAnalysisKey();
const environment = getRequiredInput("matrix");
return computeAutomationID(analysis_key, environment);
}
export function computeAutomationID(
analysis_key: string,
environment: string | undefined
): string {
let automationID = `${analysis_key}/`;
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 {
// In code scanning we just handle the string values,
// the rest get converted to the empty string
automationID += `${entry[0]}:/`;
}
}
}
return automationID;
}

View File

@@ -1,14 +1,17 @@
import * as core from "@actions/core";
import {
createStatusReportBase,
getActionsStatus,
getActionVersion,
getOptionalInput,
getTemporaryDirectory,
StatusReportBase,
} from "./actions-util";
import { getGitHubVersion, sendStatusReport } from "./api-client";
import {
createStatusReportBase,
getGitHubVersion,
sendStatusReport,
} from "./api-client";
import { determineAutobuildLanguages, runAutobuild } from "./autobuild";
import * as configUtils from "./config-utils";
import { EnvVar } from "./environment";

View File

@@ -7,13 +7,16 @@
import * as core from "@actions/core";
import {
createStatusReportBase,
getActionsStatus,
getTemporaryDirectory,
printDebugLogs,
StatusReportBase,
} from "./actions-util";
import { getGitHubVersion, sendStatusReport } from "./api-client";
import {
createStatusReportBase,
getGitHubVersion,
sendStatusReport,
} from "./api-client";
import * as debugArtifacts from "./debug-artifacts";
import { Features } from "./feature-flags";
import * as initActionPostHelper from "./init-action-post-helper";

View File

@@ -4,7 +4,6 @@ import * as core from "@actions/core";
import { v4 as uuidV4 } from "uuid";
import {
createStatusReportBase,
getActionsStatus,
getActionVersion,
getOptionalInput,
@@ -12,7 +11,11 @@ import {
getTemporaryDirectory,
StatusReportBase,
} from "./actions-util";
import { getGitHubVersion, sendStatusReport } from "./api-client";
import {
createStatusReportBase,
getGitHubVersion,
sendStatusReport,
} from "./api-client";
import { CodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { getMlPoweredJsQueriesStatus } from "./config-utils";

View File

@@ -1,13 +1,16 @@
import * as core from "@actions/core";
import {
createStatusReportBase,
getActionsStatus,
getOptionalInput,
getRequiredInput,
getTemporaryDirectory,
} from "./actions-util";
import { getGitHubVersion, sendStatusReport } from "./api-client";
import {
createStatusReportBase,
getGitHubVersion,
sendStatusReport,
} from "./api-client";
import { CommandInvocationError } from "./codeql";
import * as configUtils from "./config-utils";
import { Language, resolveAlias } from "./languages";

View File

@@ -16,7 +16,6 @@ import { Logger } from "./logging";
import { parseRepositoryNwo, RepositoryNwo } from "./repository";
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.
@@ -81,7 +80,7 @@ function getAutomationID(
return automationID;
}
return actionsUtil.computeAutomationID(analysis_key, environment);
return api.computeAutomationID(analysis_key, environment);
}
// Upload the given payload.
@@ -169,11 +168,11 @@ export async function uploadFromActions(
parseRepositoryNwo(util.getRequiredEnvParam("GITHUB_REPOSITORY")),
await actionsUtil.getCommitOid(checkoutPath),
await actionsUtil.getRef(),
await actionsUtil.getAnalysisKey(),
await api.getAnalysisKey(),
category,
util.getRequiredEnvParam("GITHUB_WORKFLOW"),
workflow.getWorkflowRunID(),
workflow.getWorkflowRunAttempt(),
actionsUtil.getWorkflowRunID(),
actionsUtil.getWorkflowRunAttempt(),
checkoutPath,
actionsUtil.getRequiredInput("matrix"),
logger

View File

@@ -2,7 +2,7 @@ import * as core from "@actions/core";
import * as actionsUtil from "./actions-util";
import { getActionVersion } from "./actions-util";
import { sendStatusReport } from "./api-client";
import { createStatusReportBase, sendStatusReport } from "./api-client";
import { getActionsLogger } from "./logging";
import { parseRepositoryNwo } from "./repository";
import * as upload_lib from "./upload-lib";
@@ -21,7 +21,7 @@ async function sendSuccessStatusReport(
startedAt: Date,
uploadStats: upload_lib.UploadStatusReport
) {
const statusReportBase = await actionsUtil.createStatusReportBase(
const statusReportBase = await createStatusReportBase(
"upload-sarif",
"success",
startedAt
@@ -38,11 +38,7 @@ async function run() {
initializeEnvironment(getActionVersion());
if (
!(await sendStatusReport(
await actionsUtil.createStatusReportBase(
"upload-sarif",
"starting",
startedAt
)
await createStatusReportBase("upload-sarif", "starting", startedAt)
))
) {
return;
@@ -74,7 +70,7 @@ async function run() {
core.setFailed(message);
console.log(error);
await sendStatusReport(
await actionsUtil.createStatusReportBase(
await createStatusReportBase(
"upload-sarif",
actionsUtil.getActionsStatus(error),
startedAt,

View File

@@ -225,7 +225,7 @@ export async function getWorkflow(logger: Logger): Promise<Workflow> {
* Get the absolute path of the currently executing workflow.
*/
async function getWorkflowAbsolutePath(logger: Logger): Promise<string> {
const relativePath = await getWorkflowRelativePath();
const relativePath = await api.getWorkflowRelativePath();
const absolutePath = path.join(
getRequiredEnvParam("GITHUB_WORKSPACE"),
relativePath
@@ -245,69 +245,6 @@ async function getWorkflowAbsolutePath(logger: Logger): Promise<string> {
);
}
/**
* Get the path of the currently executing workflow relative to the repository root.
*/
export async function getWorkflowRelativePath(): Promise<string> {
const repo_nwo = getRequiredEnvParam("GITHUB_REPOSITORY").split("/");
const owner = repo_nwo[0];
const repo = repo_nwo[1];
const run_id = Number(getRequiredEnvParam("GITHUB_RUN_ID"));
const apiClient = api.getApiClient();
const runsResponse = await apiClient.request(
"GET /repos/:owner/:repo/actions/runs/:run_id?exclude_pull_requests=true",
{
owner,
repo,
run_id,
}
);
const workflowUrl = runsResponse.data.workflow_url;
const workflowResponse = await apiClient.request(`GET ${workflowUrl}`);
return workflowResponse.data.path;
}
/**
* Get the workflow run ID.
*/
export function getWorkflowRunID(): number {
const workflowRunIdString = getRequiredEnvParam("GITHUB_RUN_ID");
const workflowRunID = parseInt(workflowRunIdString, 10);
if (Number.isNaN(workflowRunID)) {
throw new Error(
`GITHUB_RUN_ID must define a non NaN workflow run ID. Current value is ${workflowRunIdString}`
);
}
if (workflowRunID < 0) {
throw new Error(
`GITHUB_RUN_ID must be a non-negative integer. Current value is ${workflowRunIdString}`
);
}
return workflowRunID;
}
/**
* Get the workflow run attempt number.
*/
export function getWorkflowRunAttempt(): number {
const workflowRunAttemptString = getRequiredEnvParam("GITHUB_RUN_ATTEMPT");
const workflowRunAttempt = parseInt(workflowRunAttemptString, 10);
if (Number.isNaN(workflowRunAttempt)) {
throw new Error(
`GITHUB_RUN_ATTEMPT must define a non NaN workflow run attempt. Current value is ${workflowRunAttemptString}`
);
}
if (workflowRunAttempt <= 0) {
throw new Error(
`GITHUB_RUN_ATTEMPT must be a positive integer. Current value is ${workflowRunAttemptString}`
);
}
return workflowRunAttempt;
}
function getStepsCallingAction(
job: WorkflowJob,
actionName: string