Merge branch 'main' into dependabot/npm_and_yarn/npm-minor-10933408d3

This commit is contained in:
Michael B. Gale
2026-07-09 16:17:22 +01:00
committed by GitHub
15 changed files with 319 additions and 220 deletions

View File

@@ -4,7 +4,7 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
## [UNRELEASED]
No user facing changes.
- _Upcoming breaking change_: Add a deprecation warning for customers using CodeQL version 2.20.6 and earlier. These versions of CodeQL were discontinued on 1 July 2026 alongside GitHub Enterprise Server 3.16, and will be unsupported by the next minor release of the CodeQL Action. [#3956](https://github.com/github/codeql-action/pull/3956)
## 4.37.0 - 08 Jul 2026

167
lib/entry-points.js generated
View File

@@ -141462,6 +141462,61 @@ var toolrunner = __toESM(require_toolrunner());
var github = __toESM(require_github());
var io2 = __toESM(require_io());
// src/environment.ts
function getRequiredEnvVar(env, paramName) {
const value = env[paramName];
if (value === void 0 || value.length === 0) {
throw new Error(`${paramName} environment variable must be set`);
}
return value;
}
function getRequiredEnvParam(paramName) {
return getRequiredEnvVar(process.env, paramName);
}
function getOptionalEnvVarFrom(env, paramName) {
const value = env[paramName];
if (value?.trim().length === 0) {
return void 0;
}
return value;
}
function getOptionalEnvVar(paramName) {
return getOptionalEnvVarFrom(process.env, paramName);
}
var ReadOnlyEnv = class {
constructor(vars) {
this.vars = vars;
}
vars;
/** Tries to get the value for `name` and throws if there isn't one. */
getRequired(name) {
return getRequiredEnvVar(this.vars, name);
}
/** Gets the value for `name`, or `undefined` if it isn't set or empty. */
getOptional(name) {
return getOptionalEnvVarFrom(this.vars, name);
}
/** Gets the entries of the underlying `ProcessEnv`. */
entries() {
return Object.entries(this.vars);
}
};
var Env = class extends ReadOnlyEnv {
changed = false;
/** Sets an environment variable. */
set(name, value) {
this.vars[name] = value;
this.changed = true;
}
/** Gets a value indicating whether `set` was called at least once. */
hasChanged() {
return this.changed;
}
};
function getEnv(env = process.env) {
return new Env(env);
}
// src/util.ts
var fs = __toESM(require("fs"));
var fsPromises = __toESM(require("fs/promises"));
@@ -144432,7 +144487,7 @@ var semver = __toESM(require_semver2());
// src/api-compatibility.json
var maximumVersion = "3.22";
var minimumVersion = "3.16";
var minimumVersion = "3.17";
// src/json/index.ts
function parseString(data) {
@@ -144775,32 +144830,6 @@ function initializeEnvironment(version) {
core2.exportVariable("CODEQL_ACTION_FEATURE_WILL_UPLOAD" /* FEATURE_WILL_UPLOAD */, "true");
core2.exportVariable("CODEQL_ACTION_VERSION" /* VERSION */, version);
}
function getEnv(env = process.env) {
return {
getRequired: (name) => getRequiredEnvVar(env, name),
getOptional: (name) => getOptionalEnvVarFrom(env, name)
};
}
function getRequiredEnvVar(env, paramName) {
const value = env[paramName];
if (value === void 0 || value.length === 0) {
throw new Error(`${paramName} environment variable must be set`);
}
return value;
}
function getRequiredEnvParam(paramName) {
return getRequiredEnvVar(process.env, paramName);
}
function getOptionalEnvVarFrom(env, paramName) {
const value = env[paramName];
if (value?.trim().length === 0) {
return void 0;
}
return value;
}
function getOptionalEnvVar(paramName) {
return getOptionalEnvVarFrom(process.env, paramName);
}
var HTTPError = class extends Error {
status;
constructor(message, status) {
@@ -145150,31 +145179,30 @@ var getOptionalInput = function(name) {
const value = core3.getInput(name);
return value.length > 0 ? value : void 0;
};
function getTemporaryDirectory() {
const value = process.env["CODEQL_ACTION_TEMP"];
return value !== void 0 && value !== "" ? value : getRequiredEnvParam("RUNNER_TEMP" /* RUNNER_TEMP */);
function getTemporaryDirectory(env = getEnv()) {
return env.getOptional("CODEQL_ACTION_TEMP" /* TEMP */) ?? env.getRequired("RUNNER_TEMP" /* RUNNER_TEMP */);
}
var PR_DIFF_RANGE_JSON_FILENAME = "pr-diff-range.json";
function getDiffRangesJsonFilePath() {
return path2.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME);
function getDiffRangesJsonFilePath(env = getEnv()) {
return path2.join(getTemporaryDirectory(env), PR_DIFF_RANGE_JSON_FILENAME);
}
function getActionVersion() {
return "4.37.1";
}
function getWorkflowEventName() {
return getRequiredEnvParam("GITHUB_EVENT_NAME" /* GITHUB_EVENT_NAME */);
function getWorkflowEventName(env = getEnv()) {
return env.getRequired("GITHUB_EVENT_NAME" /* GITHUB_EVENT_NAME */);
}
function isRunningLocalAction() {
const relativeScriptPath = getRelativeScriptPath();
function isRunningLocalAction(env = getEnv()) {
const relativeScriptPath = getRelativeScriptPath(env);
return relativeScriptPath.startsWith("..") || path2.isAbsolute(relativeScriptPath);
}
function getRelativeScriptPath() {
const runnerTemp = getRequiredEnvParam("RUNNER_TEMP" /* RUNNER_TEMP */);
function getRelativeScriptPath(env) {
const runnerTemp = env.getRequired("RUNNER_TEMP" /* RUNNER_TEMP */);
const actionsDirectory = path2.join(path2.dirname(runnerTemp), "_actions");
return path2.relative(actionsDirectory, __filename);
}
function getWorkflowEvent() {
const eventJsonFile = getRequiredEnvParam("GITHUB_EVENT_PATH" /* GITHUB_EVENT_PATH */);
function getWorkflowEvent(env = getEnv()) {
const eventJsonFile = env.getRequired("GITHUB_EVENT_PATH" /* GITHUB_EVENT_PATH */);
try {
return JSON.parse(fs2.readFileSync(eventJsonFile, "utf-8"));
} catch (e) {
@@ -145230,8 +145258,8 @@ function getUploadValue(input) {
return "always";
}
}
function getWorkflowRunID() {
const workflowRunIdString = getRequiredEnvParam("GITHUB_RUN_ID" /* GITHUB_RUN_ID */);
function getWorkflowRunID(env = getEnv()) {
const workflowRunIdString = env.getRequired("GITHUB_RUN_ID" /* GITHUB_RUN_ID */);
const workflowRunID = parseInt(workflowRunIdString, 10);
if (Number.isNaN(workflowRunID)) {
throw new Error(
@@ -145245,8 +145273,8 @@ function getWorkflowRunID() {
}
return workflowRunID;
}
function getWorkflowRunAttempt() {
const workflowRunAttemptString = getRequiredEnvParam(
function getWorkflowRunAttempt(env = getEnv()) {
const workflowRunAttemptString = env.getRequired(
"GITHUB_RUN_ATTEMPT" /* GITHUB_RUN_ATTEMPT */
);
const workflowRunAttempt = parseInt(workflowRunAttemptString, 10);
@@ -145299,14 +145327,14 @@ var getFileType = async (filePath) => {
throw e;
}
};
function isSelfHostedRunner() {
return process.env.RUNNER_ENVIRONMENT === "self-hosted";
function isSelfHostedRunner(env = getEnv()) {
return env.getOptional("RUNNER_ENVIRONMENT" /* RUNNER_ENVIRONMENT */) === "self-hosted";
}
function isDynamicWorkflow() {
return getWorkflowEventName() === "dynamic";
function isDynamicWorkflow(env = getEnv()) {
return getWorkflowEventName(env) === "dynamic";
}
function isDefaultSetup() {
return isDynamicWorkflow();
function isDefaultSetup(env = getEnv()) {
return isDynamicWorkflow(env);
}
function prettyPrintInvocation(cmd, args) {
return [cmd, ...args].map((x) => x.includes(" ") ? `'${x}'` : x).join(" ");
@@ -145370,8 +145398,9 @@ async function runTool(cmd, args = [], opts = {}) {
return stdout;
}
var persistedInputsKey = "persisted_inputs";
var persistInputs = function() {
const inputEnvironmentVariables = Object.entries(process.env).filter(
var persistInputs = function(env = getEnv()) {
const entries = env.entries();
const inputEnvironmentVariables = entries.filter(
([name]) => name.startsWith("INPUT_")
);
core3.saveState(persistedInputsKey, JSON.stringify(inputEnvironmentVariables));
@@ -145384,7 +145413,7 @@ var restoreInputs = function() {
}
}
};
function getPullRequestBranches() {
function getPullRequestBranches(env = getEnv()) {
const pullRequest = github.context.payload.pull_request;
if (pullRequest) {
return {
@@ -145395,8 +145424,10 @@ function getPullRequestBranches() {
head: pullRequest.head.label
};
}
const codeScanningRef = process.env.CODE_SCANNING_REF;
const codeScanningBaseBranch = process.env.CODE_SCANNING_BASE_BRANCH;
const codeScanningRef = env.getOptional("CODE_SCANNING_REF" /* CODE_SCANNING_REF */);
const codeScanningBaseBranch = env.getOptional(
"CODE_SCANNING_BASE_BRANCH" /* CODE_SCANNING_BASE_BRANCH */
);
if (codeScanningRef && codeScanningBaseBranch) {
return {
base: codeScanningBaseBranch,
@@ -145407,8 +145438,8 @@ function getPullRequestBranches() {
}
return void 0;
}
function isAnalyzingPullRequest() {
return getPullRequestBranches() !== void 0;
function isAnalyzingPullRequest(env = getEnv()) {
return getPullRequestBranches(env) !== void 0;
}
var qualityCategoryMapping = {
"c#": "csharp",
@@ -145420,8 +145451,8 @@ var qualityCategoryMapping = {
typescript: "javascript-typescript",
kotlin: "java-kotlin"
};
function fixCodeQualityCategory(logger, category) {
if (category !== void 0 && isDefaultSetup() && category.startsWith("/language:")) {
function fixCodeQualityCategory(logger, category, env = getEnv()) {
if (category !== void 0 && isDefaultSetup(env) && category.startsWith("/language:")) {
const language = category.substring("/language:".length);
const mappedLanguage = qualityCategoryMapping[language];
if (mappedLanguage) {
@@ -145605,15 +145636,15 @@ function createApiClientWithDetails(apiDetails, { allowExternal = false } = {})
})
);
}
function getApiDetails() {
function getApiDetails(env = getEnv()) {
return {
auth: getRequiredInput("token"),
url: getRequiredEnvParam("GITHUB_SERVER_URL" /* GITHUB_SERVER_URL */),
apiURL: getRequiredEnvParam("GITHUB_API_URL" /* GITHUB_API_URL */)
url: env.getRequired("GITHUB_SERVER_URL" /* GITHUB_SERVER_URL */),
apiURL: env.getRequired("GITHUB_API_URL" /* GITHUB_API_URL */)
};
}
function getApiClient() {
return createApiClientWithDetails(getApiDetails());
function getApiClient(env = getEnv()) {
return createApiClientWithDetails(getApiDetails(env));
}
function getApiClientWithExternalAuth(apiDetails) {
return createApiClientWithDetails(apiDetails, { allowExternal: true });
@@ -151013,9 +151044,9 @@ async function getCombinedTracerConfig(codeql, config) {
// src/codeql.ts
var cachedCodeQL = void 0;
var CODEQL_MINIMUM_VERSION = "2.19.4";
var CODEQL_NEXT_MINIMUM_VERSION = "2.19.4";
var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15";
var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09";
var CODEQL_NEXT_MINIMUM_VERSION = "2.20.7";
var GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.16";
var GHES_MOST_RECENT_DEPRECATION_DATE = "2026-07-01";
var EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++";
async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, features, logger, checkVersion) {
try {
@@ -151648,7 +151679,7 @@ async function setupCppAutobuild(codeql, logger) {
logger
);
if (await features.getValue("cpp_dependency_installation_enabled" /* CppDependencyInstallation */, codeql)) {
if (process.env["RUNNER_ENVIRONMENT"] === "self-hosted" && process.env[envVar] !== "true") {
if (process.env["RUNNER_ENVIRONMENT" /* RUNNER_ENVIRONMENT */] === "self-hosted" && process.env[envVar] !== "true") {
logger.info(
`Disabling ${featureName} as we are on a self-hosted runner.${getWorkflowEventName() !== "dynamic" ? ` To override this, set the ${envVar} environment variable to 'true' in your workflow. See ${"https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow" /* DEFINE_ENV_VARIABLES */} for more information.` : ""}`
);

View File

@@ -7,12 +7,13 @@ import * as github from "@actions/github";
import * as io from "@actions/io";
import type { Config } from "./config-utils";
import { Env, EnvVar, ActionsEnvVars } from "./environment";
import { Logger } from "./logging";
import {
doesDirectoryExist,
getCodeQLDatabasePath,
getRequiredEnvParam,
ConfigurationError,
getEnv,
} from "./util";
/**
@@ -21,28 +22,6 @@ 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.
@@ -83,17 +62,21 @@ export const getOptionalInput = function (name: string): string | undefined {
return value.length > 0 ? value : undefined;
};
export function getTemporaryDirectory(): string {
const value = process.env["CODEQL_ACTION_TEMP"];
return value !== undefined && value !== ""
? value
: getRequiredEnvParam(ActionsEnvVars.RUNNER_TEMP);
/**
* Gets the temporary directory used by the CodeQL Action. This will either be the temporary
* directory that has been set in `CODEQL_ACTION_TEMP` by e.g. a previous step, or the
* value of `RUNNER_TEMP` otherwise.
*/
export function getTemporaryDirectory(env: Env = getEnv()): string {
return (
env.getOptional(EnvVar.TEMP) ?? env.getRequired(ActionsEnvVars.RUNNER_TEMP)
);
}
const PR_DIFF_RANGE_JSON_FILENAME = "pr-diff-range.json";
export function getDiffRangesJsonFilePath(): string {
return path.join(getTemporaryDirectory(), PR_DIFF_RANGE_JSON_FILENAME);
export function getDiffRangesJsonFilePath(env: Env = getEnv()): string {
return path.join(getTemporaryDirectory(env), PR_DIFF_RANGE_JSON_FILENAME);
}
export function getActionVersion(): string {
@@ -105,16 +88,16 @@ export function getActionVersion(): string {
*
* This will be "dynamic" for default setup workflow runs.
*/
export function getWorkflowEventName() {
return getRequiredEnvParam(ActionsEnvVars.GITHUB_EVENT_NAME);
export function getWorkflowEventName(env: Env = getEnv()) {
return env.getRequired(ActionsEnvVars.GITHUB_EVENT_NAME);
}
/**
* Returns whether the current workflow is executing a local copy of the Action, e.g. we're running
* a workflow on the codeql-action repo itself.
*/
export function isRunningLocalAction(): boolean {
const relativeScriptPath = getRelativeScriptPath();
export function isRunningLocalAction(env: Env = getEnv()): boolean {
const relativeScriptPath = getRelativeScriptPath(env);
return (
relativeScriptPath.startsWith("..") || path.isAbsolute(relativeScriptPath)
);
@@ -125,15 +108,15 @@ 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(ActionsEnvVars.RUNNER_TEMP);
function getRelativeScriptPath(env: Env): string {
const runnerTemp = env.getRequired(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(ActionsEnvVars.GITHUB_EVENT_PATH);
export function getWorkflowEvent(env: Env = getEnv()): any {
const eventJsonFile = env.getRequired(ActionsEnvVars.GITHUB_EVENT_PATH);
try {
return JSON.parse(fs.readFileSync(eventJsonFile, "utf-8"));
} catch (e) {
@@ -202,8 +185,8 @@ export function getUploadValue(input: string | undefined): UploadKind {
/**
* Get the workflow run ID.
*/
export function getWorkflowRunID(): number {
const workflowRunIdString = getRequiredEnvParam(ActionsEnvVars.GITHUB_RUN_ID);
export function getWorkflowRunID(env: Env = getEnv()): number {
const workflowRunIdString = env.getRequired(ActionsEnvVars.GITHUB_RUN_ID);
const workflowRunID = parseInt(workflowRunIdString, 10);
if (Number.isNaN(workflowRunID)) {
throw new Error(
@@ -221,8 +204,8 @@ export function getWorkflowRunID(): number {
/**
* Get the workflow run attempt number.
*/
export function getWorkflowRunAttempt(): number {
const workflowRunAttemptString = getRequiredEnvParam(
export function getWorkflowRunAttempt(env: Env = getEnv()): number {
const workflowRunAttemptString = env.getRequired(
ActionsEnvVars.GITHUB_RUN_ATTEMPT,
);
const workflowRunAttempt = parseInt(workflowRunAttemptString, 10);
@@ -290,18 +273,18 @@ export const getFileType = async (filePath: string): Promise<string> => {
}
};
export function isSelfHostedRunner() {
return process.env.RUNNER_ENVIRONMENT === "self-hosted";
export function isSelfHostedRunner(env: Env = getEnv()) {
return env.getOptional(ActionsEnvVars.RUNNER_ENVIRONMENT) === "self-hosted";
}
/** Determines whether the workflow trigger is `dynamic`. */
export function isDynamicWorkflow(): boolean {
return getWorkflowEventName() === "dynamic";
export function isDynamicWorkflow(env: Env = getEnv()): boolean {
return getWorkflowEventName(env) === "dynamic";
}
/** Determines whether we are running in default setup. */
export function isDefaultSetup(): boolean {
return isDynamicWorkflow();
export function isDefaultSetup(env: Env = getEnv()): boolean {
return isDynamicWorkflow(env);
}
export function prettyPrintInvocation(cmd: string, args: string[]): string {
@@ -399,9 +382,10 @@ const persistedInputsKey = "persisted_inputs";
* This would be simplified if actions/runner#3514 is addressed.
* https://github.com/actions/runner/issues/3514
*/
export const persistInputs = function () {
const inputEnvironmentVariables = Object.entries(process.env).filter(
([name]) => name.startsWith("INPUT_"),
export const persistInputs = function (env: Env = getEnv()) {
const entries = env.entries();
const inputEnvironmentVariables = entries.filter(([name]) =>
name.startsWith("INPUT_"),
);
core.saveState(persistedInputsKey, JSON.stringify(inputEnvironmentVariables));
};
@@ -429,7 +413,9 @@ export interface PullRequestBranches {
* @returns the base and head branches of the pull request, or undefined if
* we are not analyzing a pull request.
*/
export function getPullRequestBranches(): PullRequestBranches | undefined {
export function getPullRequestBranches(
env: Env = getEnv(),
): PullRequestBranches | undefined {
const pullRequest = github.context.payload.pull_request;
if (pullRequest) {
return {
@@ -443,8 +429,10 @@ export function getPullRequestBranches(): PullRequestBranches | undefined {
// PR analysis under Default Setup does not have the pull_request context,
// but it should set CODE_SCANNING_REF and CODE_SCANNING_BASE_BRANCH.
const codeScanningRef = process.env.CODE_SCANNING_REF;
const codeScanningBaseBranch = process.env.CODE_SCANNING_BASE_BRANCH;
const codeScanningRef = env.getOptional(EnvVar.CODE_SCANNING_REF);
const codeScanningBaseBranch = env.getOptional(
EnvVar.CODE_SCANNING_BASE_BRANCH,
);
if (codeScanningRef && codeScanningBaseBranch) {
return {
base: codeScanningBaseBranch,
@@ -459,8 +447,8 @@ export function getPullRequestBranches(): PullRequestBranches | undefined {
/**
* Returns whether we are analyzing a pull request.
*/
export function isAnalyzingPullRequest(): boolean {
return getPullRequestBranches() !== undefined;
export function isAnalyzingPullRequest(env: Env = getEnv()): boolean {
return getPullRequestBranches(env) !== undefined;
}
/**
@@ -484,13 +472,14 @@ const qualityCategoryMapping: Record<string, string> = {
export function fixCodeQualityCategory(
logger: Logger,
category?: string,
env: Env = getEnv(),
): string | undefined {
// The `category` should always be set by Default Setup. We perform this check
// to avoid potential issues if Code Quality supports Advanced Setup in the future
// and before this workaround is removed.
if (
category !== undefined &&
isDefaultSetup() &&
isDefaultSetup(env) &&
category.startsWith("/language:")
) {
const language = category.substring("/language:".length);

View File

@@ -6,7 +6,8 @@ import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as api from "./api-client";
import { DO_NOT_RETRY_STATUSES } from "./api-client";
import { setupTests } from "./testing-utils";
import { ActionsEnvVars } from "./environment";
import { getTestEnv, setupTests } from "./testing-utils";
import * as util from "./util";
setupTests(test);
@@ -20,16 +21,13 @@ test.serial("getApiClient", async (t) => {
const githubStub: sinon.SinonStub = sinon.stub();
pluginStub.returns(githubStub);
sinon.stub(actionsUtil, "getRequiredInput").withArgs("token").returns("xyz");
const requiredEnvParamStub = sinon.stub(util, "getRequiredEnvParam");
requiredEnvParamStub
.withArgs("GITHUB_SERVER_URL")
.returns("http://github.localhost");
requiredEnvParamStub
.withArgs("GITHUB_API_URL")
.returns("http://api.github.localhost");
const env = getTestEnv();
env.set(ActionsEnvVars.GITHUB_SERVER_URL, "http://github.localhost");
env.set(ActionsEnvVars.GITHUB_API_URL, "http://api.github.localhost");
api.getApiClient();
sinon.stub(actionsUtil, "getRequiredInput").withArgs("token").returns("xyz");
api.getApiClient(env);
t.assert(
githubStub.calledOnceWithExactly({

View File

@@ -2,12 +2,8 @@ import * as core from "@actions/core";
import * as githubUtils from "@actions/github/lib/utils";
import * as retry from "@octokit/plugin-retry";
import {
ActionsEnvVars,
getActionVersion,
getRequiredInput,
} from "./actions-util";
import { EnvVar } from "./environment";
import { getActionVersion, getRequiredInput } from "./actions-util";
import { EnvVar, ReadOnlyEnv, ActionsEnvVars, getEnv } from "./environment";
import { Logger } from "./logging";
import { getRepositoryNwo, RepositoryNwo } from "./repository";
import {
@@ -71,16 +67,16 @@ function createApiClientWithDetails(
);
}
export function getApiDetails(): GitHubApiDetails {
export function getApiDetails(env: ReadOnlyEnv = getEnv()): GitHubApiDetails {
return {
auth: getRequiredInput("token"),
url: getRequiredEnvParam(ActionsEnvVars.GITHUB_SERVER_URL),
apiURL: getRequiredEnvParam(ActionsEnvVars.GITHUB_API_URL),
url: env.getRequired(ActionsEnvVars.GITHUB_SERVER_URL),
apiURL: env.getRequired(ActionsEnvVars.GITHUB_API_URL),
};
}
export function getApiClient() {
return createApiClientWithDetails(getApiDetails());
export function getApiClient(env: ReadOnlyEnv = getEnv()) {
return createApiClientWithDetails(getApiDetails(env));
}
export function getApiClientWithExternalAuth(

View File

@@ -1 +1 @@
{"maximumVersion": "3.22", "minimumVersion": "3.16"}
{"maximumVersion": "3.22", "minimumVersion": "3.17"}

View File

@@ -5,7 +5,7 @@ import { getGitHubVersion } from "./api-client";
import { CodeQL, getCodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { DocUrl } from "./doc-url";
import { EnvVar } from "./environment";
import { ActionsEnvVars, EnvVar } from "./environment";
import { Feature, featureConfig, initFeatures } from "./feature-flags";
import { BuiltInLanguage, Language } from "./languages";
import { Logger } from "./logging";
@@ -126,7 +126,7 @@ export async function setupCppAutobuild(codeql: CodeQL, logger: Logger) {
if (await features.getValue(Feature.CppDependencyInstallation, codeql)) {
// disable autoinstall on self-hosted runners unless explicitly requested
if (
process.env["RUNNER_ENVIRONMENT"] === "self-hosted" &&
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] === "self-hosted" &&
process.env[envVar] !== "true"
) {
logger.info(

View File

@@ -272,17 +272,17 @@ const CODEQL_MINIMUM_VERSION = "2.19.4";
/**
* This version will shortly become the oldest version of CodeQL that the Action will run with.
*/
const CODEQL_NEXT_MINIMUM_VERSION = "2.19.4";
const CODEQL_NEXT_MINIMUM_VERSION = "2.20.7";
/**
* This is the version of GHES that was most recently deprecated.
*/
const GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.15";
const GHES_VERSION_MOST_RECENTLY_DEPRECATED = "3.16";
/**
* This is the deprecation date for the version of GHES that was most recently deprecated.
*/
const GHES_MOST_RECENT_DEPRECATION_DATE = "2026-04-09";
const GHES_MOST_RECENT_DEPRECATION_DATE = "2026-07-01";
/** The CLI verbosity level to use for extraction in debug mode. */
const EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++";

View File

@@ -1,7 +1,7 @@
import test from "ava";
import sinon from "sinon";
import { ActionsEnvVars } from "../actions-util";
import { ActionsEnvVars } from "../environment";
import * as errors from "../error-messages";
import { Feature } from "../feature-flags";
import { callee, getTestEnv } from "../testing-utils";

View File

@@ -1,6 +1,5 @@
import { ActionState } from "../action-common";
import { ActionsEnvVars } from "../actions-util";
import { Env } from "../environment";
import { Env, ActionsEnvVars } from "../environment";
import * as errorMessages from "../error-messages";
import { Feature } from "../feature-flags";
import { ConfigurationError, Failure, Result, Success } from "../util";

View File

@@ -17,6 +17,18 @@ export enum EnvVar {
*/
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",
/**
* `PersistedVersionInfo` for the CodeQL CLI, so later Actions steps can reuse it instead of
* invoking `codeql version` again.
@@ -83,6 +95,9 @@ export enum EnvVar {
/** 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",
@@ -161,10 +176,122 @@ export enum EnvVar {
RISK_ASSESSMENT_ID = "CODEQL_ACTION_RISK_ASSESSMENT_ID",
}
/** A wrapper around an environment, to allow abstracting away from `process.env` in tests. */
export interface Env {
/** Tries to get the value for `name` and throws if there isn't one. */
getRequired(name: string): string;
/** Gets the value for `name`, or `undefined` if it isn't set or empty. */
getOptional(name: string): string | undefined;
/**
* 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_ENVIRONMENT = "RUNNER_ENVIRONMENT",
RUNNER_NAME = "RUNNER_NAME",
RUNNER_OS = "RUNNER_OS",
RUNNER_TEMP = "RUNNER_TEMP",
}
/** A type representing all known environment variables. */
export type KnownEnvVar = EnvVar | ActionsEnvVars;
/**
* 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<T extends string | undefined = string | undefined> {
constructor(protected readonly vars: Record<string, T>) {}
/** 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);
}
}
/**
* 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<T> {
private changed: boolean = false;
/** Sets an environment variable. */
public set(name: string, value: T): void {
this.vars[name] = value;
this.changed = true;
}
/** 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);
}

View File

@@ -8,6 +8,7 @@ import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import { createStubCodeQL } from "./codeql";
import { ActionsEnvVars } from "./environment";
import { Feature } from "./feature-flags";
import {
checkPacksForOverlayCompatibility,
@@ -84,7 +85,7 @@ for (const { runnerEnv, ErrorConstructor, message } of [
`cleanupDatabaseClusterDirectory throws a ${ErrorConstructor.name} when cleanup fails on ${runnerEnv} runner`,
async (t) => {
await withTmpDir(async (tmpDir: string) => {
process.env["RUNNER_ENVIRONMENT"] = runnerEnv;
process.env[ActionsEnvVars.RUNNER_ENVIRONMENT] = runnerEnv;
const dbLocation = path.resolve(tmpDir, "dbs");
fs.mkdirSync(dbLocation, { recursive: true });

View File

@@ -7,6 +7,7 @@ import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as api from "./api-client";
import { EnvVar } from "./environment";
import { Feature } from "./feature-flags";
import { getRunnerLogger } from "./logging";
import { getCacheRestoreKeyPrefix } from "./overlay/caching";
@@ -637,8 +638,8 @@ test.serial(
async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
process.env["CODE_SCANNING_REF"] = "refs/heads/feature-branch";
process.env["CODE_SCANNING_BASE_BRANCH"] = "main";
process.env[EnvVar.CODE_SCANNING_REF] = "refs/heads/feature-branch";
process.env[EnvVar.CODE_SCANNING_BASE_BRANCH] = "main";
sinon.stub(api, "getAutomationID").resolves("test/");
const listStub = sinon.stub(api, "listActionsCaches").resolves([

View File

@@ -11,7 +11,7 @@ import nock from "nock";
import * as sinon from "sinon";
import { ActionState, StateFeature } from "./action-common";
import { ActionsEnv, ActionsEnvVars, getActionVersion } from "./actions-util";
import { ActionsEnv, getActionVersion } from "./actions-util";
import { AnalysisKind } from "./analyses";
import * as apiClient from "./api-client";
import { GitHubApiDetails } from "./api-client";
@@ -19,7 +19,7 @@ import { CachingKind } from "./caching-utils";
import * as codeql from "./codeql";
import { Config } from "./config-utils";
import * as defaults from "./defaults.json";
import { Env } from "./environment";
import { Env, ActionsEnvVars } from "./environment";
import {
CodeQLDefaultVersionInfo,
Feature,
@@ -331,11 +331,15 @@ export type ActionVarOverrides = Partial<
* excluding some that are expected to be set to paths. See `setupActionsVars`.
*
* @param overrides Overrides for the defaults.
* @param env The environment to set the variables for.
*/
export function setupBaseActionsVars(overrides?: ActionVarOverrides) {
export function setupBaseActionsVars(
overrides?: ActionVarOverrides,
env: Env = getEnv(),
) {
const vars = { ...DEFAULT_ACTIONS_VARS, ...overrides };
for (const [key, value] of Object.entries(vars)) {
process.env[key] = value;
env.set(key, value);
}
}

View File

@@ -13,11 +13,14 @@ import * as apiCompatibility from "./api-compatibility.json";
import type { CodeQL, VersionInfo } from "./codeql";
import type { Pack } from "./config/db-config";
import type { Config } from "./config-utils";
import { Env, EnvVar } from "./environment";
import { EnvVar, getRequiredEnvParam } from "./environment";
import * as json from "./json";
import { Language } from "./languages";
import { Logger } from "./logging";
// Re-export for backwards compatibility to avoid updating a lot of imports elsewhere.
export { getRequiredEnvParam, getOptionalEnvVar, getEnv } from "./environment";
/**
* The name of the file containing the base database OIDs, as stored in the
* root of the database location.
@@ -566,56 +569,6 @@ export function initializeEnvironment(version: string) {
core.exportVariable(EnvVar.VERSION, version);
}
/** Gets an `Env` instance for `env`, which is `process.env` by default. */
export function getEnv(env: NodeJS.ProcessEnv = process.env): Env {
return {
getRequired: (name) => getRequiredEnvVar(env, name),
getOptional: (name) => getOptionalEnvVarFrom(env, name),
};
}
/**
* Gets an environment variable, but throws an error if it is not set.
*/
export 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.
*/
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.
*/
export 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.
*/
export function getOptionalEnvVar(paramName: string): string | undefined {
return getOptionalEnvVarFrom(process.env, paramName);
}
export class HTTPError extends Error {
public status: number;