Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,418 changes: 1,207 additions & 1,211 deletions lib/entry-points.js

Large diffs are not rendered by default.

89 changes: 89 additions & 0 deletions src/action-common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as core from "@actions/core";

import { ActionsEnv, getActionsEnv } from "./actions-util";
import { Env } from "./environment";
import { FeatureEnablement } from "./feature-flags";
import { getActionsLogger, Logger } from "./logging";
import { ActionName, sendUnhandledErrorStatusReport } from "./status-report";
import { getEnv, getErrorMessage } from "./util";

/** Common state that is always available in `ActionState`. */
export interface BaseState {
/** The name of the Action. */
name: ActionName;
/** When the Action was started. */
startedAt: Date;
}

/** Describes different state features that an Action may have. */
export interface FeatureState {
Logger: {
/** The logger that is in use. */
logger: Logger;
};
Env: {
/** Information about environment variables. */
env: Env;
};
Actions: {
/** Access to Actions-related functionality. */
actions: ActionsEnv;
};
FeatureFlags: {
/** Information about enabled feature flags. */
features: FeatureEnablement;
};
}

/** Identifies a type of state an Action may have. */
export type StateFeature = keyof FeatureState;

/** Constructs the union of all state types identifies by `Fs`. */
export type FieldsOf<Fs extends readonly StateFeature[]> = Fs extends []
? BaseState
: Fs extends [
infer Head extends StateFeature,
...infer Tail extends StateFeature[],
]
? FeatureState[Head] & FieldsOf<Tail>
: never;

/** Describes the state of an Action that has access to the state corresponding to `Fs`. */
export type ActionState<Fs extends readonly StateFeature[]> = FieldsOf<Fs>;

/** The type of an Action's main entry point. This is a function that is provided
* with a basic `ActionState` object with features that are always available.
* Each Action can then augment the `state` further if additional features are required.
*/
export type ActionMain = (
state: ActionState<["Logger", "Env", "Actions"]>,
) => Promise<void>;

/** A specification for a CodeQL Action step. */
export interface Action {
/** The name of the Action. */
name: ActionName;
/** The entry point for the Action. */
run: ActionMain;
}

/** A generic entry point that sets up the basic environment for the `action` and runs it. */
export async function runInActions(action: Action) {
const startedAt = new Date();
const logger = getActionsLogger();
const env = getEnv();
const actionsEnv = getActionsEnv();

try {
await action.run({
name: action.name,
startedAt,
logger,
env,
actions: actionsEnv,
});
} catch (error) {
core.setFailed(`${action.name} action failed: ${getErrorMessage(error)}`);
await sendUnhandledErrorStatusReport(action.name, startedAt, error, logger);
}
}
44 changes: 34 additions & 10 deletions src/actions-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@ import {
*/
declare const __CODEQL_ACTION_VERSION__: string;

/**
* Enumerates known GitHub Actions environment variables that we expect
* to be set in a GitHub Actions environment.
*/
export enum ActionsEnvVars {
GITHUB_ACTION_REPOSITORY = "GITHUB_ACTION_REPOSITORY",
GITHUB_API_URL = "GITHUB_API_URL",
GITHUB_EVENT_NAME = "GITHUB_EVENT_NAME",
GITHUB_EVENT_PATH = "GITHUB_EVENT_PATH",
GITHUB_JOB = "GITHUB_JOB",
GITHUB_REF = "GITHUB_REF",
GITHUB_REPOSITORY = "GITHUB_REPOSITORY",
GITHUB_RUN_ATTEMPT = "GITHUB_RUN_ATTEMPT",
GITHUB_RUN_ID = "GITHUB_RUN_ID",
GITHUB_SERVER_URL = "GITHUB_SERVER_URL",
GITHUB_SHA = "GITHUB_SHA",
GITHUB_WORKFLOW = "GITHUB_WORKFLOW",
RUNNER_NAME = "RUNNER_NAME",
RUNNER_OS = "RUNNER_OS",
RUNNER_TEMP = "RUNNER_TEMP",
}

/**
* Abstracts over GitHub Actions functions so that we do not have to stub
* global functions in tests.
Expand Down Expand Up @@ -65,7 +87,7 @@ export function getTemporaryDirectory(): string {
const value = process.env["CODEQL_ACTION_TEMP"];
return value !== undefined && value !== ""
? value
: getRequiredEnvParam("RUNNER_TEMP");
: getRequiredEnvParam(ActionsEnvVars.RUNNER_TEMP);
}

const PR_DIFF_RANGE_JSON_FILENAME = "pr-diff-range.json";
Expand All @@ -84,7 +106,7 @@ export function getActionVersion(): string {
* This will be "dynamic" for default setup workflow runs.
*/
export function getWorkflowEventName() {
return getRequiredEnvParam("GITHUB_EVENT_NAME");
return getRequiredEnvParam(ActionsEnvVars.GITHUB_EVENT_NAME);
}

/**
Expand All @@ -104,14 +126,14 @@ export function isRunningLocalAction(): boolean {
* This can be used to get the Action's name or tell if we're running a local Action.
*/
function getRelativeScriptPath(): string {
const runnerTemp = getRequiredEnvParam("RUNNER_TEMP");
const runnerTemp = getRequiredEnvParam(ActionsEnvVars.RUNNER_TEMP);
const actionsDirectory = path.join(path.dirname(runnerTemp), "_actions");
return path.relative(actionsDirectory, __filename);
}

/** Returns the contents of `GITHUB_EVENT_PATH` as a JSON object. */
export function getWorkflowEvent(): any {
const eventJsonFile = getRequiredEnvParam("GITHUB_EVENT_PATH");
const eventJsonFile = getRequiredEnvParam(ActionsEnvVars.GITHUB_EVENT_PATH);
try {
return JSON.parse(fs.readFileSync(eventJsonFile, "utf-8"));
} catch (e) {
Expand Down Expand Up @@ -181,16 +203,16 @@ export function getUploadValue(input: string | undefined): UploadKind {
* Get the workflow run ID.
*/
export function getWorkflowRunID(): number {
const workflowRunIdString = getRequiredEnvParam("GITHUB_RUN_ID");
const workflowRunIdString = getRequiredEnvParam(ActionsEnvVars.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}`,
`${ActionsEnvVars.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}`,
`${ActionsEnvVars.GITHUB_RUN_ID} must be a non-negative integer. Current value is ${workflowRunIdString}`,
);
}
return workflowRunID;
Expand All @@ -200,16 +222,18 @@ export function getWorkflowRunID(): number {
* Get the workflow run attempt number.
*/
export function getWorkflowRunAttempt(): number {
const workflowRunAttemptString = getRequiredEnvParam("GITHUB_RUN_ATTEMPT");
const workflowRunAttemptString = getRequiredEnvParam(
ActionsEnvVars.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}`,
`${ActionsEnvVars.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}`,
`${ActionsEnvVars.GITHUB_RUN_ATTEMPT} must be a positive integer. Current value is ${workflowRunAttemptString}`,
);
}
return workflowRunAttempt;
Expand Down
25 changes: 9 additions & 16 deletions src/analyze-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { performance } from "perf_hooks";

import * as core from "@actions/core";

import { Action, ActionState, runInActions } from "./action-common";
import * as actionsUtil from "./actions-util";
import * as analyses from "./analyses";
import {
Expand Down Expand Up @@ -40,7 +41,6 @@ import {
createStatusReportBase,
DatabaseCreationTimings,
getActionsStatus,
sendUnhandledErrorStatusReport,
StatusReportBase,
} from "./status-report";
import {
Expand Down Expand Up @@ -212,7 +212,7 @@ async function runAutobuildIfLegacyGoWorkflow(config: Config, logger: Logger) {
await runAutobuild(config, BuiltInLanguage.go, logger);
}

async function run(startedAt: Date) {
async function run({ startedAt, logger }: ActionState<["Logger"]>) {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.

Expand All @@ -228,7 +228,6 @@ async function run(startedAt: Date) {
let didUploadTrapCaches = false;
let dependencyCacheResults: DependencyCacheUploadStatusReport | undefined;
let databaseUploadResults: DatabaseUploadResult[] = [];
const logger = getActionsLogger();

try {
util.initializeEnvironment(actionsUtil.getActionVersion());
Expand Down Expand Up @@ -523,19 +522,13 @@ async function run(startedAt: Date) {
}
}

/** Defines the `analyze` Action. */
const analyze: Action = {
name: ActionName.Analyze,
run,
};

export async function runWrapper() {
const startedAt = new Date();
const logger = getActionsLogger();
try {
await run(startedAt);
} catch (error) {
core.setFailed(`analyze action failed: ${util.getErrorMessage(error)}`);
await sendUnhandledErrorStatusReport(
ActionName.Analyze,
startedAt,
error,
logger,
);
}
await runInActions(analyze);
await util.checkForTimeout();
}
10 changes: 7 additions & 3 deletions src/api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import * as core from "@actions/core";
import * as githubUtils from "@actions/github/lib/utils";
import * as retry from "@octokit/plugin-retry";

import { getActionVersion, getRequiredInput } from "./actions-util";
import {
ActionsEnvVars,
getActionVersion,
getRequiredInput,
} from "./actions-util";
import { EnvVar } from "./environment";
import { Logger } from "./logging";
import { getRepositoryNwo, RepositoryNwo } from "./repository";
Expand Down Expand Up @@ -70,8 +74,8 @@ function createApiClientWithDetails(
export function getApiDetails(): GitHubApiDetails {
return {
auth: getRequiredInput("token"),
url: getRequiredEnvParam("GITHUB_SERVER_URL"),
apiURL: getRequiredEnvParam("GITHUB_API_URL"),
url: getRequiredEnvParam(ActionsEnvVars.GITHUB_SERVER_URL),
apiURL: getRequiredEnvParam(ActionsEnvVars.GITHUB_API_URL),
};
}

Expand Down
28 changes: 10 additions & 18 deletions src/autobuild-action.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as core from "@actions/core";

import { Action, ActionState, runInActions } from "./action-common";
import {
getActionVersion,
getOptionalInput,
Expand All @@ -11,13 +12,12 @@ import { getCodeQL } from "./codeql";
import { Config, getConfig } from "./config-utils";
import { EnvVar } from "./environment";
import { Language } from "./languages";
import { Logger, getActionsLogger } from "./logging";
import { Logger } from "./logging";
import {
StatusReportBase,
getActionsStatus,
createStatusReportBase,
sendStatusReport,
sendUnhandledErrorStatusReport,
ActionName,
} from "./status-report";
import { endTracingForCluster } from "./tracer-config";
Expand All @@ -26,7 +26,6 @@ import {
checkDiskUsage,
checkGitHubVersionInRange,
ConfigurationError,
getErrorMessage,
initializeEnvironment,
wrapError,
} from "./util";
Expand Down Expand Up @@ -69,11 +68,10 @@ async function sendCompletedStatusReport(
}
}

async function run(startedAt: Date) {
async function run({ startedAt, logger }: ActionState<["Logger"]>) {
// To capture errors appropriately, keep as much code within the try-catch as
// possible, and only use safe functions outside.

const logger = getActionsLogger();
let config: Config | undefined;
let currentLanguage: Language | undefined;
let languages: Language[] | undefined;
Expand Down Expand Up @@ -142,18 +140,12 @@ async function run(startedAt: Date) {
await sendCompletedStatusReport(config, logger, startedAt, languages ?? []);
}

/** Defines the `autobuild` Action. */
const autobuild: Action = {
name: ActionName.Autobuild,
run,
};

export async function runWrapper() {
const startedAt = new Date();
const logger = getActionsLogger();
try {
await run(startedAt);
} catch (error) {
core.setFailed(`autobuild action failed. ${getErrorMessage(error)}`);
await sendUnhandledErrorStatusReport(
ActionName.Autobuild,
startedAt,
error,
logger,
);
}
await runInActions(autobuild);
}
Loading
Loading