Files
codeql-action/src/setup-codeql-action.ts

223 lines
6.5 KiB
TypeScript

import * as core from "@actions/core";
import { Action, ActionState, runInActions } from "./action-common";
import {
getActionVersion,
getOptionalInput,
getRequiredInput,
getTemporaryDirectory,
} from "./actions-util";
import { AnalysisKind, getAnalysisKinds } from "./analyses";
import { getGitHubVersion } from "./api-client";
import { CodeQL } from "./codeql";
import { ComputedInput, getToolsInput } from "./config/inputs";
import { getRawLanguagesNoAutodetect } from "./config-utils";
import { ActionsEnvVars, EnvVar } from "./environment";
import { initFeatures } from "./feature-flags";
import { loadRepositoryProperties } from "./feature-flags/properties";
import { initCodeQL } from "./init";
import { Logger } from "./logging";
import { getRepositoryNwo } from "./repository";
import { ToolsSource } from "./setup-codeql";
import {
ActionName,
InitStatusReport,
createStatusReportBase,
getActionsStatus,
sendStatusReport,
} from "./status-report";
import { createInitToolsDownloadFields } from "./status-report/tools-download";
import { ToolsDownloadStatusReport } from "./tools-download";
import {
checkDiskUsage,
checkForTimeout,
checkGitHubVersionInRange,
initializeEnvironment,
ConfigurationError,
wrapError,
checkActionVersion,
} from "./util";
/**
* Helper function to send a full status report for this action.
*/
async function sendCompletedStatusReport(
startedAt: Date,
toolsInput: ComputedInput | undefined,
toolsDownloadStatusReport: ToolsDownloadStatusReport | undefined,
toolsFeatureFlagsValid: boolean | undefined,
toolsSource: ToolsSource,
toolsVersion: string,
logger: Logger,
error?: Error,
): Promise<void> {
const statusReportBase = await createStatusReportBase(
ActionName.SetupCodeQL,
getActionsStatus(error),
startedAt,
undefined,
await checkDiskUsage(logger),
logger,
error?.message,
error?.stack,
);
if (statusReportBase === undefined) {
return;
}
const initStatusReport: InitStatusReport = {
...statusReportBase,
tools_input: toolsInput?.value || "",
tools_resolved_version: toolsVersion,
tools_source: toolsSource || ToolsSource.Unknown,
workflow_languages: "",
};
if (toolsInput !== undefined) {
initStatusReport.computed_inputs.tools = toolsInput;
}
const initToolsDownloadFields = createInitToolsDownloadFields(
toolsDownloadStatusReport,
toolsFeatureFlagsValid,
);
await sendStatusReport({ ...initStatusReport, ...initToolsDownloadFields });
}
/** The main behaviour of this action. */
async function run(
actionState: ActionState<["Base", "Logger", "Env", "Actions"]>,
): Promise<void> {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.
const { logger, startedAt } = actionState;
let codeql: CodeQL;
let toolsInput: ComputedInput | undefined;
let toolsDownloadStatusReport: ToolsDownloadStatusReport | undefined;
let toolsFeatureFlagsValid: boolean | undefined;
let toolsSource: ToolsSource;
let toolsVersion: string;
try {
initializeEnvironment(getActionVersion());
const apiDetails = {
auth: getRequiredInput("token"),
externalRepoAuth: getOptionalInput("external-repository-token"),
url: actionState.env.getRequired(ActionsEnvVars.GITHUB_SERVER_URL),
apiURL: actionState.env.getRequired(ActionsEnvVars.GITHUB_API_URL),
};
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);
checkActionVersion(getActionVersion(), gitHubVersion);
const repositoryNwo = getRepositoryNwo();
const features = initFeatures(
gitHubVersion,
repositoryNwo,
getTemporaryDirectory(),
logger,
);
// Fetch the values of known repository properties that affect us.
const repositoryPropertiesResult = await loadRepositoryProperties(
repositoryNwo,
logger,
);
const repositoryProperties = repositoryPropertiesResult.orElse({});
const actionStateWithFeatures = { ...actionState, features };
const statusReportBase = await createStatusReportBase(
ActionName.SetupCodeQL,
"starting",
startedAt,
undefined,
await checkDiskUsage(logger),
logger,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
// Get the computed `tools` input.
toolsInput = await getToolsInput(
actionStateWithFeatures,
repositoryProperties,
);
const codeQLDefaultVersionInfo =
await features.getEnabledDefaultCliVersions(gitHubVersion.type);
toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid;
const rawLanguages = getRawLanguagesNoAutodetect(
getOptionalInput("languages"),
);
const analysisKinds = await getAnalysisKinds(logger, features);
const initCodeQLResult = await initCodeQL(
toolsInput?.value,
apiDetails,
getTemporaryDirectory(),
gitHubVersion.type,
codeQLDefaultVersionInfo,
rawLanguages,
analysisKinds.length === 1 &&
analysisKinds[0] === AnalysisKind.CodeScanning,
features,
logger,
);
codeql = initCodeQLResult.codeql;
toolsDownloadStatusReport = initCodeQLResult.toolsDownloadStatusReport;
toolsVersion = initCodeQLResult.toolsVersion;
toolsSource = initCodeQLResult.toolsSource;
core.setOutput("codeql-path", codeql.getPath());
core.setOutput("codeql-version", (await codeql.getVersion()).version);
core.exportVariable(EnvVar.SETUP_CODEQL_ACTION_HAS_RUN, "true");
} catch (unwrappedError) {
const error = wrapError(unwrappedError);
core.setFailed(error.message);
const statusReportBase = await createStatusReportBase(
ActionName.SetupCodeQL,
error instanceof ConfigurationError ? "user-error" : "failure",
startedAt,
undefined,
await checkDiskUsage(logger),
logger,
error.message,
error.stack,
);
if (statusReportBase !== undefined) {
await sendStatusReport(statusReportBase);
}
return;
}
await sendCompletedStatusReport(
startedAt,
toolsInput,
toolsDownloadStatusReport,
toolsFeatureFlagsValid,
toolsSource,
toolsVersion,
logger,
);
}
/** Defines the `setup-codeql` Action. */
const setupCodeQL: Action = {
name: ActionName.SetupCodeQL,
run,
};
/** Run the action and catch any unhandled errors. */
export async function runWrapper(): Promise<void> {
await runInActions(setupCodeQL);
await checkForTimeout();
}