GeezerVison is showing github.com — Merge pull request #3993 from github/mbg/env-abstraction · github/codeql-action@ae4c8d0
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/entry-points.js +24-16Lines changed: 24 additions & 16 deletions Collapse file Copy file name to clipboardExpand all lines: src/actions-util.ts +34-10Lines changed: 34 additions & 10 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -21,6 +21,28 @@ import { | 21 | 21 | */ | 22 | 22 | declare const __CODEQL_ACTION_VERSION__: string; | 23 | 23 |
| | 24 | +/** | | 25 | + * Enumerates known GitHub Actions environment variables that we expect | | 26 | + * to be set in a GitHub Actions environment. | | 27 | + */ | | 28 | +export enum ActionsEnvVars { | | 29 | + GITHUB_ACTION_REPOSITORY = "GITHUB_ACTION_REPOSITORY", | | 30 | + GITHUB_API_URL = "GITHUB_API_URL", | | 31 | + GITHUB_EVENT_NAME = "GITHUB_EVENT_NAME", | | 32 | + GITHUB_EVENT_PATH = "GITHUB_EVENT_PATH", | | 33 | + GITHUB_JOB = "GITHUB_JOB", | | 34 | + GITHUB_REF = "GITHUB_REF", | | 35 | + GITHUB_REPOSITORY = "GITHUB_REPOSITORY", | | 36 | + GITHUB_RUN_ATTEMPT = "GITHUB_RUN_ATTEMPT", | | 37 | + GITHUB_RUN_ID = "GITHUB_RUN_ID", | | 38 | + GITHUB_SERVER_URL = "GITHUB_SERVER_URL", | | 39 | + GITHUB_SHA = "GITHUB_SHA", | | 40 | + GITHUB_WORKFLOW = "GITHUB_WORKFLOW", | | 41 | + RUNNER_NAME = "RUNNER_NAME", | | 42 | + RUNNER_OS = "RUNNER_OS", | | 43 | + RUNNER_TEMP = "RUNNER_TEMP", | | 44 | +} | | 45 | + | 24 | 46 | /** | 25 | 47 | * Abstracts over GitHub Actions functions so that we do not have to stub | 26 | 48 | * global functions in tests. | @@ -65,7 +87,7 @@ export function getTemporaryDirectory(): string { | 65 | 87 | const value = process.env["CODEQL_ACTION_TEMP"]; | 66 | 88 | return value !== undefined && value !== "" | 67 | 89 | ? value | 68 | | - : getRequiredEnvParam("RUNNER_TEMP"); | | 90 | + : getRequiredEnvParam(ActionsEnvVars.RUNNER_TEMP); | 69 | 91 | } | 70 | 92 |
| 71 | 93 | const PR_DIFF_RANGE_JSON_FILENAME = "pr-diff-range.json"; | @@ -84,7 +106,7 @@ export function getActionVersion(): string { | 84 | 106 | * This will be "dynamic" for default setup workflow runs. | 85 | 107 | */ | 86 | 108 | export function getWorkflowEventName() { | 87 | | - return getRequiredEnvParam("GITHUB_EVENT_NAME"); | | 109 | + return getRequiredEnvParam(ActionsEnvVars.GITHUB_EVENT_NAME); | 88 | 110 | } | 89 | 111 |
| 90 | 112 | /** | @@ -104,14 +126,14 @@ export function isRunningLocalAction(): boolean { | 104 | 126 | * This can be used to get the Action's name or tell if we're running a local Action. | 105 | 127 | */ | 106 | 128 | function getRelativeScriptPath(): string { | 107 | | - const runnerTemp = getRequiredEnvParam("RUNNER_TEMP"); | | 129 | + const runnerTemp = getRequiredEnvParam(ActionsEnvVars.RUNNER_TEMP); | 108 | 130 | const actionsDirectory = path.join(path.dirname(runnerTemp), "_actions"); | 109 | 131 | return path.relative(actionsDirectory, __filename); | 110 | 132 | } | 111 | 133 |
| 112 | 134 | /** Returns the contents of `GITHUB_EVENT_PATH` as a JSON object. */ | 113 | 135 | export function getWorkflowEvent(): any { | 114 | | - const eventJsonFile = getRequiredEnvParam("GITHUB_EVENT_PATH"); | | 136 | + const eventJsonFile = getRequiredEnvParam(ActionsEnvVars.GITHUB_EVENT_PATH); | 115 | 137 | try { | 116 | 138 | return JSON.parse(fs.readFileSync(eventJsonFile, "utf-8")); | 117 | 139 | } catch (e) { | @@ -181,16 +203,16 @@ export function getUploadValue(input: string | undefined): UploadKind { | 181 | 203 | * Get the workflow run ID. | 182 | 204 | */ | 183 | 205 | export function getWorkflowRunID(): number { | 184 | | - const workflowRunIdString = getRequiredEnvParam("GITHUB_RUN_ID"); | | 206 | + const workflowRunIdString = getRequiredEnvParam(ActionsEnvVars.GITHUB_RUN_ID); | 185 | 207 | const workflowRunID = parseInt(workflowRunIdString, 10); | 186 | 208 | if (Number.isNaN(workflowRunID)) { | 187 | 209 | throw new Error( | 188 | | - `GITHUB_RUN_ID must define a non NaN workflow run ID. Current value is ${workflowRunIdString}`, | | 210 | + `${ActionsEnvVars.GITHUB_RUN_ID} must define a non NaN workflow run ID. Current value is ${workflowRunIdString}`, | 189 | 211 | ); | 190 | 212 | } | 191 | 213 | if (workflowRunID < 0) { | 192 | 214 | throw new Error( | 193 | | - `GITHUB_RUN_ID must be a non-negative integer. Current value is ${workflowRunIdString}`, | | 215 | + `${ActionsEnvVars.GITHUB_RUN_ID} must be a non-negative integer. Current value is ${workflowRunIdString}`, | 194 | 216 | ); | 195 | 217 | } | 196 | 218 | return workflowRunID; | @@ -200,16 +222,18 @@ export function getWorkflowRunID(): number { | 200 | 222 | * Get the workflow run attempt number. | 201 | 223 | */ | 202 | 224 | export function getWorkflowRunAttempt(): number { | 203 | | - const workflowRunAttemptString = getRequiredEnvParam("GITHUB_RUN_ATTEMPT"); | | 225 | + const workflowRunAttemptString = getRequiredEnvParam( | | 226 | + ActionsEnvVars.GITHUB_RUN_ATTEMPT, | | 227 | + ); | 204 | 228 | const workflowRunAttempt = parseInt(workflowRunAttemptString, 10); | 205 | 229 | if (Number.isNaN(workflowRunAttempt)) { | 206 | 230 | throw new Error( | 207 | | - `GITHUB_RUN_ATTEMPT must define a non NaN workflow run attempt. Current value is ${workflowRunAttemptString}`, | | 231 | + `${ActionsEnvVars.GITHUB_RUN_ATTEMPT} must define a non NaN workflow run attempt. Current value is ${workflowRunAttemptString}`, | 208 | 232 | ); | 209 | 233 | } | 210 | 234 | if (workflowRunAttempt <= 0) { | 211 | 235 | throw new Error( | 212 | | - `GITHUB_RUN_ATTEMPT must be a positive integer. Current value is ${workflowRunAttemptString}`, | | 236 | + `${ActionsEnvVars.GITHUB_RUN_ATTEMPT} must be a positive integer. Current value is ${workflowRunAttemptString}`, | 213 | 237 | ); | 214 | 238 | } | 215 | 239 | return workflowRunAttempt; | |
Collapse file Copy file name to clipboardExpand all lines: src/api-client.ts +7-3Lines changed: 7 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -2,7 +2,11 @@ import * as core from "@actions/core"; | 2 | 2 | import * as githubUtils from "@actions/github/lib/utils"; | 3 | 3 | import * as retry from "@octokit/plugin-retry"; | 4 | 4 |
| 5 | | -import { getActionVersion, getRequiredInput } from "./actions-util"; | | 5 | +import { | | 6 | + ActionsEnvVars, | | 7 | + getActionVersion, | | 8 | + getRequiredInput, | | 9 | +} from "./actions-util"; | 6 | 10 | import { EnvVar } from "./environment"; | 7 | 11 | import { Logger } from "./logging"; | 8 | 12 | import { getRepositoryNwo, RepositoryNwo } from "./repository"; | @@ -70,8 +74,8 @@ function createApiClientWithDetails( | 70 | 74 | export function getApiDetails(): GitHubApiDetails { | 71 | 75 | return { | 72 | 76 | auth: getRequiredInput("token"), | 73 | | - url: getRequiredEnvParam("GITHUB_SERVER_URL"), | 74 | | - apiURL: getRequiredEnvParam("GITHUB_API_URL"), | | 77 | + url: getRequiredEnvParam(ActionsEnvVars.GITHUB_SERVER_URL), | | 78 | + apiURL: getRequiredEnvParam(ActionsEnvVars.GITHUB_API_URL), | 75 | 79 | }; | 76 | 80 | } | 77 | 81 |
| |
Collapse file Copy file name to clipboardExpand all lines: src/environment.ts +8Lines changed: 8 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -160,3 +160,11 @@ export enum EnvVar { | 160 | 160 | /** Used by Code Scanning Risk Assessment to communicate the assessment ID to the CodeQL Action. */ | 161 | 161 | RISK_ASSESSMENT_ID = "CODEQL_ACTION_RISK_ASSESSMENT_ID", | 162 | 162 | } | | 163 | + | | 164 | +/** A wrapper around an environment, to allow abstracting away from `process.env` in tests. */ | | 165 | +export interface Env { | | 166 | + /** Tries to get the value for `name` and throws if there isn't one. */ | | 167 | + getRequired(name: string): string; | | 168 | + /** Gets the value for `name`, or `undefined` if it isn't set or empty. */ | | 169 | + getOptional(name: string): string | undefined; | | 170 | +} |
Collapse file Copy file name to clipboardExpand all lines: src/testing-utils.ts +9-2Lines changed: 9 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -10,14 +10,15 @@ import test, { | 10 | 10 | import nock from "nock"; | 11 | 11 | import * as sinon from "sinon"; | 12 | 12 |
| 13 | | -import { ActionsEnv, getActionVersion } from "./actions-util"; | | 13 | +import { ActionsEnv, ActionsEnvVars, getActionVersion } from "./actions-util"; | 14 | 14 | import { AnalysisKind } from "./analyses"; | 15 | 15 | import * as apiClient from "./api-client"; | 16 | 16 | import { GitHubApiDetails } from "./api-client"; | 17 | 17 | import { CachingKind } from "./caching-utils"; | 18 | 18 | import * as codeql from "./codeql"; | 19 | 19 | import { Config } from "./config-utils"; | 20 | 20 | import * as defaults from "./defaults.json"; | | 21 | +import { Env } from "./environment"; | 21 | 22 | import { | 22 | 23 | CodeQLDefaultVersionInfo, | 23 | 24 | Feature, | @@ -29,6 +30,7 @@ import { OverlayDatabaseMode } from "./overlay/overlay-database-mode"; | 29 | 30 | import { | 30 | 31 | DEFAULT_DEBUG_ARTIFACT_NAME, | 31 | 32 | DEFAULT_DEBUG_DATABASE_NAME, | | 33 | + getEnv, | 32 | 34 | GitHubVariant, | 33 | 35 | GitHubVersion, | 34 | 36 | HTTPError, | @@ -172,6 +174,11 @@ export function makeMacro<Args extends unknown[]>( | 172 | 174 | return wrapper; | 173 | 175 | } | 174 | 176 |
| | 177 | +export function getTestEnv(): Env { | | 178 | + const testEnv: NodeJS.ProcessEnv = {}; | | 179 | + return getEnv(testEnv); | | 180 | +} | | 181 | + | 175 | 182 | /** | 176 | 183 | * Gets an `ActionsEnv` instance for use in tests. | 177 | 184 | */ | @@ -200,7 +207,7 @@ export const DEFAULT_ACTIONS_VARS = { | 200 | 207 | GITHUB_WORKFLOW: "test-workflow", | 201 | 208 | RUNNER_NAME: "my-runner", | 202 | 209 | RUNNER_OS: "Linux", | 203 | | -} as const satisfies Record<string, string>; | | 210 | +} as const satisfies Partial<Record<ActionsEnvVars, string>>; | 204 | 211 |
| 205 | 212 | /** Partial mappings from GitHub Actions environment variables to values. */ | 206 | 213 | export type ActionVarOverrides = Partial< | |
Collapse file Copy file name to clipboardExpand all lines: src/util.ts +35-7Lines changed: 35 additions & 7 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -13,7 +13,7 @@ import * as apiCompatibility from "./api-compatibility.json"; | 13 | 13 | import type { CodeQL, VersionInfo } from "./codeql"; | 14 | 14 | import type { Pack } from "./config/db-config"; | 15 | 15 | import type { Config } from "./config-utils"; | 16 | | -import { EnvVar } from "./environment"; | | 16 | +import { Env, EnvVar } from "./environment"; | 17 | 17 | import * as json from "./json"; | 18 | 18 | import { Language } from "./languages"; | 19 | 19 | import { Logger } from "./logging"; | @@ -566,28 +566,56 @@ export function initializeEnvironment(version: string) { | 566 | 566 | core.exportVariable(EnvVar.VERSION, version); | 567 | 567 | } | 568 | 568 |
| | 569 | +/** Gets an `Env` instance for `env`, which is `process.env` by default. */ | | 570 | +export function getEnv(env: NodeJS.ProcessEnv = process.env): Env { | | 571 | + return { | | 572 | + getRequired: (name) => getRequiredEnvVar(env, name), | | 573 | + getOptional: (name) => getOptionalEnvVarFrom(env, name), | | 574 | + }; | | 575 | +} | | 576 | + | 569 | 577 | /** | 570 | | - * Get an environment parameter, but throw an error if it is not set. | | 578 | + * Gets an environment variable, but throws an error if it is not set. | 571 | 579 | */ | 572 | | -export function getRequiredEnvParam(paramName: string): string { | 573 | | - const value = process.env[paramName]; | | 580 | +export function getRequiredEnvVar( | | 581 | + env: NodeJS.ProcessEnv, | | 582 | + paramName: string, | | 583 | +): string { | | 584 | + const value = env[paramName]; | 574 | 585 | if (value === undefined || value.length === 0) { | 575 | 586 | throw new Error(`${paramName} environment variable must be set`); | 576 | 587 | } | 577 | 588 | return value; | 578 | 589 | } | 579 | 590 |
| 580 | 591 | /** | 581 | | - * Get an environment variable, but return `undefined` if it is not set or empty. | | 592 | + * Get an environment parameter, but throw an error if it is not set. | 582 | 593 | */ | 583 | | -export function getOptionalEnvVar(paramName: string): string | undefined { | 584 | | - const value = process.env[paramName]; | | 594 | +export function getRequiredEnvParam(paramName: string): string { | | 595 | + return getRequiredEnvVar(process.env, paramName); | | 596 | +} | | 597 | + | | 598 | +/** | | 599 | + * Gets an environment variable, but returns `undefined` if it is not set or empty. | | 600 | + */ | | 601 | +export function getOptionalEnvVarFrom( | | 602 | + env: NodeJS.ProcessEnv, | | 603 | + paramName: string, | | 604 | +): string | undefined { | | 605 | + const value = env[paramName]; | 585 | 606 | if (value?.trim().length === 0) { | 586 | 607 | return undefined; | 587 | 608 | } | 588 | 609 | return value; | 589 | 610 | } | 590 | 611 |
| | 612 | +/** | | 613 | + * Get an environment variable, but return `undefined` if it is not set or empty. | | 614 | + */ | | 615 | +export function getOptionalEnvVar(paramName: string): string | undefined { | | 616 | + return getOptionalEnvVarFrom(process.env, paramName); | | 617 | +} | | 618 | + | 591 | 619 | export class HTTPError extends Error { | 592 | 620 | public status: number; | 593 | 621 |
| |
|