import * as core from "@actions/core"; /** * This constant is set in `ava.setup.mjs` for tests. */ declare const __CODEQL_ACTION_TEST_ENV__: string | undefined; /** * Environment variables used by Default Setup to communicate the private registry proxy configuration. */ export enum RegistryProxyVars { PROXY_HOST = "CODEQL_PROXY_HOST", PROXY_PORT = "CODEQL_PROXY_PORT", PROXY_CA_CERTIFICATE = "CODEQL_PROXY_CA_CERTIFICATE", PROXY_URLS = "CODEQL_PROXY_URLS", } /** * Environment variables used by the CodeQL Action. * * We recommend prefixing environment variables with `CODEQL_ACTION_` * to reduce the risk that they are overwritten by other steps. */ export enum EnvVar { /** Whether the `analyze` Action completes successfully. */ ANALYZE_DID_COMPLETE_SUCCESSFULLY = "CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY", /** Whether the `autobuild` Action completes successfully. */ AUTOBUILD_DID_COMPLETE_SUCCESSFULLY = "CODEQL_ACTION_AUTOBUILD_DID_COMPLETE_SUCCESSFULLY", /** * The verbosity level of the CLI. One of the following: `errors`, `warnings`, `progress`, * `progress+`, `progress++`, `progress+++`. */ CLI_VERBOSITY = "CODEQL_VERBOSITY", /** * Set by Default Setup to the base branch of the PR being analysed, if analysing a PR. * This is needed because the `pull_request` context is not available for `dynamic` events. */ CODE_SCANNING_BASE_BRANCH = "CODE_SCANNING_BASE_BRANCH", /** * Set by Default Setup to the full ref being analysed, if analysing a PR. * This is needed because the `pull_request` context is not available for `dynamic` events. */ CODE_SCANNING_REF = "CODE_SCANNING_REF", /** Whether the CodeQL Action has invoked the Go autobuilder. */ DID_AUTOBUILD_GOLANG = "CODEQL_ACTION_DID_AUTOBUILD_GOLANG", /** * Whether the CodeQL Action is using its own deprecated and non-standard way of scanning for * multiple languages. */ FEATURE_MULTI_LANGUAGE = "CODEQL_ACTION_FEATURE_MULTI_LANGUAGE", /** Whether the CodeQL Action is using its own sandwiched workflow mechanism. */ FEATURE_SANDWICH = "CODEQL_ACTION_FEATURE_SANDWICH", /** * Whether the CodeQL Action might combine SARIF output from several `interpret-results` runs for * the same language. */ FEATURE_SARIF_COMBINE = "CODEQL_ACTION_FEATURE_SARIF_COMBINE", /** Whether the CodeQL Action will upload SARIF, not the CLI. */ FEATURE_WILL_UPLOAD = "CODEQL_ACTION_FEATURE_WILL_UPLOAD", /** Whether the CodeQL Action has already warned the user about low disk space. */ HAS_WARNED_ABOUT_DISK_SPACE = "CODEQL_ACTION_HAS_WARNED_ABOUT_DISK_SPACE", /** * Whether a step in this job has already set up CodeQL. Steps that run afterwards may be holding * a path into the toolcache, so we must not delete anything from it. */ HAS_SET_UP_CODEQL = "CODEQL_ACTION_HAS_SET_UP_CODEQL", /** Whether the `setup-codeql` action has been run. */ SETUP_CODEQL_ACTION_HAS_RUN = "CODEQL_ACTION_SETUP_CODEQL_HAS_RUN", /** Whether the init action has been run. */ INIT_ACTION_HAS_RUN = "CODEQL_ACTION_INIT_HAS_RUN", /** Whether the deprecation warning for file coverage on PRs has been logged. */ DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION = "CODEQL_ACTION_DID_LOG_FILE_COVERAGE_ON_PRS_DEPRECATION", /** * Set to `true` to opt out of the upcoming change that skips file coverage * information on pull requests. */ FILE_COVERAGE_ON_PRS = "CODEQL_ACTION_FILE_COVERAGE_ON_PRS", /** Whether the error for a deprecated version of the CodeQL Action was logged. */ LOG_VERSION_DEPRECATION = "CODEQL_ACTION_DID_LOG_VERSION_DEPRECATION", /** UUID representing the current job run. */ JOB_RUN_UUID = "CODEQL_ACTION_JOB_RUN_UUID", /** Status for the entire job, submitted to the status report in `init-post` */ JOB_STATUS = "CODEQL_ACTION_JOB_STATUS", /** The value of the `output` input for the analyze action. */ SARIF_RESULTS_OUTPUT_DIR = "CODEQL_ACTION_SARIF_RESULTS_OUTPUT_DIR", /** * What percentage of the total amount of RAM over 8 GB that the Action should reserve for the * system. */ SCALING_RESERVED_RAM_PERCENTAGE = "CODEQL_ACTION_SCALING_RESERVED_RAM_PERCENTAGE", /** Whether to suppress the warning if the current CLI will soon be unsupported. */ SUPPRESS_DEPRECATED_SOON_WARNING = "CODEQL_ACTION_SUPPRESS_DEPRECATED_SOON_WARNING", /** Used to dictate or persist the temporary directory used by the CodeQL Action. */ TEMP = "CODEQL_ACTION_TEMP", /** Whether to disable uploading SARIF results or status reports to the GitHub API */ TEST_MODE = "CODEQL_ACTION_TEST_MODE", TESTING_ENVIRONMENT = "CODEQL_ACTION_TESTING_ENVIRONMENT", /** Semver of the CodeQL Action as specified in `package.json`. */ VERSION = "CODEQL_ACTION_VERSION", /** * The time at which the first action (normally init) started executing. * If a workflow invokes a different action without first invoking the init * action (i.e. the upload action is being used by a third-party integrator) * then this variable will be assigned the start time of the action invoked * rather that the init action. */ WORKFLOW_STARTED_AT = "CODEQL_WORKFLOW_STARTED_AT", /** * The path where we initially discovered the Go binary in the system path. * We check this later to ensure that it hasn't been tampered with by a late e.g. `setup-go` step. */ GO_BINARY_LOCATION = "CODEQL_ACTION_GO_BINARY", /** * Used as an alternative to the `dependency-caching` input for the `init` Action. * Useful for experiments where it is easier to set an environment variable than * change the inputs to the Action. */ DEPENDENCY_CACHING = "CODEQL_ACTION_DEPENDENCY_CACHING", /** * An optional string to add into the cache key used by dependency caching. * Useful for testing purposes where multiple caches may be stored in the same repository. */ DEPENDENCY_CACHING_PREFIX = "CODEQL_ACTION_DEPENDENCY_CACHE_PREFIX", /** Used by the Java extractor option to enable minimizing dependency JARs. */ JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS = "CODEQL_EXTRACTOR_JAVA_OPTION_MINIMIZE_DEPENDENCY_JARS", /** * Whether to enable experimental extractors for CodeQL. */ EXPERIMENTAL_FEATURES = "CODEQL_ENABLE_EXPERIMENTAL_FEATURES", /** * Whether and where to dump the processed SARIF file that would be uploaded, regardless of * whether the upload is disabled. This is intended for testing and debugging purposes. */ SARIF_DUMP_DIR = "CODEQL_ACTION_SARIF_DUMP_DIR", /** * Whether to skip generating a failed SARIF file. Intended for testing purposes. * This setting is more specific than `CODEQL_ACTION_TEST_MODE` and * `CODEQL_ACTION_SKIP_SARIF_UPLOAD`, which imply this option. */ SKIP_GENERATE_FAILED_SARIF = "CODEQL_ACTION_SKIP_GENERATE_FAILED_SARIF", /** * Whether to skip uploading SARIF results to GitHub. Intended for testing purposes. * This setting is more specific than `CODEQL_ACTION_TEST_MODE`, which implies this option. */ SKIP_SARIF_UPLOAD = "CODEQL_ACTION_SKIP_SARIF_UPLOAD", /** * Whether to skip workflow validation. Intended for internal use, where we know that * the workflow is valid and validation is not necessary. */ SKIP_WORKFLOW_VALIDATION = "CODEQL_ACTION_SKIP_WORKFLOW_VALIDATION", /** * Whether to tolerate failure to determine the git version (only applicable in test mode). * Intended for use in environments where git may not be installed, such as Docker containers. */ TOLERATE_MISSING_GIT_VERSION = "CODEQL_ACTION_TOLERATE_MISSING_GIT_VERSION", /** * Used to store the analysis key used by the CodeQL Action. This is normally populated by * `getAnalysisKey`, but can also be set manually for testing and non-standard applications. */ ANALYSIS_KEY = "CODEQL_ACTION_ANALYSIS_KEY", /** Used by Code Scanning Risk Assessment to communicate the assessment ID to the CodeQL Action. */ RISK_ASSESSMENT_ID = "CODEQL_ACTION_RISK_ASSESSMENT_ID", } /** * 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", GITHUB_WORKSPACE = "GITHUB_WORKSPACE", RUNNER_ENVIRONMENT = "RUNNER_ENVIRONMENT", RUNNER_NAME = "RUNNER_NAME", RUNNER_OS = "RUNNER_OS", RUNNER_TEMP = "RUNNER_TEMP", RUNNER_TOOL_CACHE = "RUNNER_TOOL_CACHE", } /** Environment variables which are not specific to CodeQL. */ export enum SystemEnvVar { /** * Used by Node and related tools to indicate what kind of environment we are running in. */ NODE_ENV = "NODE_ENV", } /** A type representing all known environment variables. */ export type KnownEnvVar = | EnvVar | ActionsEnvVars | RegistryProxyVars | SystemEnvVar; /** * Gets an environment variable, but throws an error if it is not set. */ function getRequiredEnvVar(env: NodeJS.ProcessEnv, paramName: string): string { const value = env[paramName]; if (value === undefined || value.length === 0) { throw new Error(`${paramName} environment variable must be set`); } return value; } /** * Get an environment parameter, but throw an error if it is not set. * * @deprecated Use `getRequired` of a `ReadOnlyEnv` or `Env` instance instead. */ export function getRequiredEnvParam(paramName: string): string { return getRequiredEnvVar(process.env, paramName); } /** * Gets an environment variable, but returns `undefined` if it is not set or empty. */ function getOptionalEnvVarFrom( env: NodeJS.ProcessEnv, paramName: string, ): string | undefined { const value = env[paramName]; if (value?.trim().length === 0) { return undefined; } return value; } /** * Get an environment variable, but return `undefined` if it is not set or empty. * * @deprecated Use `getOptional` of a `ReadOnlyEnv` or `Env` instance instead. */ export function getOptionalEnvVar(paramName: string): string | undefined { return getOptionalEnvVarFrom(process.env, paramName); } /** * An abstraction around read-only environment variables, to allow abstracting away from `process.env` * in tests, while clearly signalling in regular code that the consumer of the `ReadOnlyEnv` instance * will only read from it. */ export class ReadOnlyEnv { constructor(protected readonly vars: Record) {} /** Clones the object while detaching the underlying environment from the original. */ public clone(): this { return Object.create(this, { vars: { value: { ...this.vars } } }) as this; } /** Gets a copy of the underlying environment. */ public get(): Record { return { ...this.vars }; } /** Tries to get the value for `name` and throws if there isn't one. */ public getRequired(name: string): string { return getRequiredEnvVar(this.vars, name); } /** Gets the value for `name`, or `undefined` if it isn't set or empty. */ public getOptional(name: string): string | undefined { return getOptionalEnvVarFrom(this.vars, name); } /** Gets the entries of the underlying `ProcessEnv`. */ public entries(): Array<[string, T]> { return Object.entries(this.vars); } /** * Gets a value indicating whether we should skip uploads of * all kinds (SARIF results, status reports, DBs, ...). * * This is not guaranteed to be set in all test environments. */ public isSkippingUploadsInTests(): boolean { return this.getOptional(EnvVar.TEST_MODE) === "true"; } /** * Gets a value indicative of whether we are in a testing environment * by testing whether the value of the `NODE_ENV` variable is "test". * This is expected to be the case if e.g. `ava` is running the tests * or if this instance was constructed by `getTestEnv`. */ public isTestingEnv(): boolean { return __CODEQL_ACTION_TEST_ENV__ === "unit-test"; } } /** * A wrapper around an environment, to allow abstracting away from `process.env` in tests. * Use `ReadOnlyEnv` instead if you only plan to read from the environment. * This type allows writing to the environment. */ export class Env< T extends string | undefined = string | undefined, > extends ReadOnlyEnv { private changed: boolean = false; /** Sets an environment variable. */ public set(name: string, value: T): void { this.vars[name] = value; this.changed = true; } /** Sets all environment variables given by `vars`. */ public setAll(vars: Record): void { for (const [key, val] of Object.entries(vars)) { this.set(key, val); } } /** * Wrapper around `core.exportVariable` which does not call `core.exportVariable` * when running unit tests. This is important, because otherwise `core.exportVariable` * sets environment variables for other steps in a workflow when we run unit tests in CI. * * @param name The name of the environment variable to set and export. * @param val The value to set and export for the environment variable. */ public export(name: string, val: T): void { // Setting the environment variable for this instance is always OK, including // in tests, since we use fresh `Env` instances whenever needed. This allows // tests to pass that rely on that part of the `core.exportVariable` behaviour. this.set(name, val); // Call `core.exportVariable` whenever we are not in a test environment. if (!this.isTestingEnv()) { core.exportVariable(name, val); } } /** Gets a value indicating whether `set` was called at least once. */ public hasChanged(): boolean { return this.changed; } } /** Gets an `Env` instance for `env`, which is `process.env` by default. */ export function getEnv(env: NodeJS.ProcessEnv = process.env): Env { return new Env(env); } /** * Returns whether we are in test mode. This is used by CodeQL Action PR checks. * * In test mode, we skip several uploads (SARIF results, status reports, DBs, ...). * * @deprecated * The purpose of this function is ambiguous. Use `isSkippingUploadsInTests` on * a `ReadOnlyEnv` instance instead for equivalent behaviour. Use `isTestingEnv` * to determine if we are running in a unit test. */ export function isInTestMode(): boolean { return getEnv().isSkippingUploadsInTests(); } /** * Wrapper around `core.exportVariable` which does not call `core.exportVariable` * when running unit tests. This is important, because otherwise `core.exportVariable` * sets environment variables for other steps in a workflow when we run unit tests in CI. * * @deprecated Use `export` on an `Env` instance instead. */ export function exportEnvVar(name: string, val: any): void { const env = getEnv(); if (typeof val === "string") { env.export(name, val); } else { env.export(name, JSON.stringify(val)); } }