mirror of
https://github.com/github/codeql-action.git
synced 2026-11-03 03:29:41 +00:00
Compare commits
34 Commits
mbg/ci/val
...
default-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c050421152 | ||
|
|
750fe8feea | ||
|
|
adfcd5ffb8 | ||
|
|
2ef3b62201 | ||
|
|
b35c6c6aae | ||
|
|
ac9f257611 | ||
|
|
52cf185226 | ||
|
|
95dd6bfbc4 | ||
|
|
120993f723 | ||
|
|
f1396bd6e2 | ||
|
|
8d18caadc8 | ||
|
|
410d706e43 | ||
|
|
fb9d45340b | ||
|
|
ce1de8daea | ||
|
|
9410c4a524 | ||
|
|
80d7b74bf4 | ||
|
|
efea0445d3 | ||
|
|
8f2596eca1 | ||
|
|
1fb93080c2 | ||
|
|
be54d791a1 | ||
|
|
4a6767cdd0 | ||
|
|
5c1005e0ae | ||
|
|
c29909a87f | ||
|
|
44f1c73c56 | ||
|
|
7def52843a | ||
|
|
774bc191bf | ||
|
|
dc2ed81cc7 | ||
|
|
02283e9542 | ||
|
|
86bc4448d3 | ||
|
|
9c72af5b2b | ||
|
|
929f5c0db4 | ||
|
|
a76419a632 | ||
|
|
071544a67a | ||
|
|
8bdea824e2 |
@@ -4,7 +4,7 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
|
||||
|
||||
## [UNRELEASED]
|
||||
|
||||
No user facing changes.
|
||||
- Update default CodeQL bundle version to [2.27.4](https://github.com/github/codeql-action/releases/tag/codeql-bundle-v2.27.4). [#4284](https://github.com/github/codeql-action/pull/4284)
|
||||
|
||||
## 4.39.1 - 21 Oct 2026
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"bundleVersion": "codeql-bundle-v2.27.3",
|
||||
"cliVersion": "2.27.3",
|
||||
"priorBundleVersion": "codeql-bundle-v2.27.2",
|
||||
"priorCliVersion": "2.27.2"
|
||||
"bundleVersion": "codeql-bundle-v2.27.4",
|
||||
"cliVersion": "2.27.4",
|
||||
"priorBundleVersion": "codeql-bundle-v2.27.3",
|
||||
"priorCliVersion": "2.27.3"
|
||||
}
|
||||
|
||||
571
lib/entry-points.js
generated
571
lib/entry-points.js
generated
File diff suppressed because it is too large
Load Diff
@@ -11,8 +11,8 @@ import {
|
||||
} from "./actions-util";
|
||||
import { computeAutomationID } from "./api-client";
|
||||
import { EnvVar } from "./environment";
|
||||
import { RecordingLogger, setupTests } from "./testing-utils";
|
||||
import { initializeEnvironment, withTmpDir } from "./util";
|
||||
import { getTestEnv, RecordingLogger, setupTests } from "./testing-utils";
|
||||
import { withTmpDir } from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
@@ -227,8 +227,9 @@ test.serial(
|
||||
);
|
||||
|
||||
test.serial("initializeEnvironment", (t) => {
|
||||
initializeEnvironment("1.2.3");
|
||||
t.deepEqual(process.env[EnvVar.VERSION], "1.2.3");
|
||||
const env = getTestEnv();
|
||||
env.initialise("1.2.3");
|
||||
t.deepEqual(env.getOptional(EnvVar.VERSION), "1.2.3");
|
||||
});
|
||||
|
||||
test.serial("fixCodeQualityCategory", (t) => {
|
||||
|
||||
@@ -233,7 +233,7 @@ async function run(action: ActionState<["Base", "Logger", "Env", "Actions"]>) {
|
||||
let databaseUploadResults: DatabaseUploadResult[] = [];
|
||||
|
||||
try {
|
||||
util.initializeEnvironment(actionsUtil.getActionVersion());
|
||||
action.env.initialise(actionsUtil.getActionVersion());
|
||||
|
||||
// Make inputs accessible in the `post` step, details at
|
||||
// https://github.com/github/codeql-action/issues/2553
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ProxyAgent } from "undici";
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as api from "./api-client";
|
||||
import { DO_NOT_RETRY_STATUSES } from "./api-client";
|
||||
import { ActionsEnvVars, RegistryProxyVars } from "./environment";
|
||||
import { ActionsEnvVars, RegistryProxyVars, getEnv } from "./environment";
|
||||
import { GITHUB_DOTCOM_API_URL, GITHUB_DOTCOM_URL } from "./github";
|
||||
import {
|
||||
callee,
|
||||
@@ -21,7 +21,7 @@ import * as util from "./util";
|
||||
setupTests(test);
|
||||
|
||||
test.beforeEach(() => {
|
||||
util.initializeEnvironment(actionsUtil.getActionVersion());
|
||||
getEnv().initialise(actionsUtil.getActionVersion());
|
||||
});
|
||||
|
||||
test.serial("getApiClient", async (t) => {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getGitHubVersion } from "./api-client";
|
||||
import { determineAutobuildLanguages, runAutobuild } from "./autobuild";
|
||||
import { getCodeQL } from "./codeql";
|
||||
import { Config, getConfig } from "./config-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import { EnvVar, type Env } from "./environment";
|
||||
import { Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import {
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
checkDiskUsage,
|
||||
checkGitHubVersionInRange,
|
||||
ConfigurationError,
|
||||
initializeEnvironment,
|
||||
wrapError,
|
||||
} from "./util";
|
||||
|
||||
@@ -38,6 +37,7 @@ interface AutobuildStatusReport extends StatusReportBase {
|
||||
}
|
||||
|
||||
async function sendCompletedStatusReport(
|
||||
env: Env,
|
||||
config: Config | undefined,
|
||||
logger: Logger,
|
||||
startedAt: Date,
|
||||
@@ -45,7 +45,7 @@ async function sendCompletedStatusReport(
|
||||
failingLanguage?: string,
|
||||
cause?: Error,
|
||||
) {
|
||||
initializeEnvironment(getActionVersion());
|
||||
env.initialise(getActionVersion());
|
||||
|
||||
const status = getActionsStatus(cause, failingLanguage);
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
@@ -129,6 +129,7 @@ async function run({
|
||||
`We were unable to automatically build your code. Please replace the call to the autobuild action with your custom build steps. ${error.message}`,
|
||||
);
|
||||
await sendCompletedStatusReport(
|
||||
env,
|
||||
config,
|
||||
logger,
|
||||
startedAt,
|
||||
@@ -141,7 +142,13 @@ async function run({
|
||||
|
||||
env.export(EnvVar.AUTOBUILD_DID_COMPLETE_SUCCESSFULLY, "true");
|
||||
|
||||
await sendCompletedStatusReport(config, logger, startedAt, languages ?? []);
|
||||
await sendCompletedStatusReport(
|
||||
env,
|
||||
config,
|
||||
logger,
|
||||
startedAt,
|
||||
languages ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
/** Defines the `autobuild` Action. */
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
import type { Config } from "./config-utils";
|
||||
import * as defaults from "./defaults.json";
|
||||
import { DocUrl } from "./doc-url";
|
||||
import { getEnv } from "./environment";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { ToolsSource } from "./setup-codeql";
|
||||
import {
|
||||
@@ -38,14 +39,13 @@ import {
|
||||
} from "./testing-utils";
|
||||
import { ToolsDownloadStatusReport } from "./tools-download";
|
||||
import * as util from "./util";
|
||||
import { initializeEnvironment } from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
let stubConfig: Config;
|
||||
|
||||
test.beforeEach(() => {
|
||||
initializeEnvironment("1.2.3");
|
||||
getEnv().initialise("1.2.3");
|
||||
|
||||
stubConfig = createTestConfig({
|
||||
languages: [BuiltInLanguage.cpp],
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as path from "path";
|
||||
import * as core from "@actions/core";
|
||||
import * as toolrunner from "@actions/exec/lib/toolrunner";
|
||||
import * as yaml from "js-yaml";
|
||||
import * as semver from "semver";
|
||||
|
||||
import {
|
||||
CommandInvocationError,
|
||||
@@ -19,6 +20,7 @@ import type { ActionConfig } from "./config/action-config";
|
||||
import { appendExtraQueryExclusions, type Config } from "./config-utils";
|
||||
import { DocUrl } from "./doc-url";
|
||||
import { EnvVar, getEnv, Env, ReadOnlyEnv } from "./environment";
|
||||
import { getErrorMessage, isDiskConfigurationError } from "./errors";
|
||||
import {
|
||||
CodeQLDefaultVersionInfo,
|
||||
Feature,
|
||||
@@ -34,7 +36,9 @@ import { ToolsDownloadStatusReport } from "./tools-download";
|
||||
import { ToolsFeature, isSupportedToolsFeature } from "./tools-features";
|
||||
import { shouldEnableIndirectTracing } from "./tracer-config";
|
||||
import * as util from "./util";
|
||||
import { BuildMode, CleanupLevel, getErrorMessage } from "./util";
|
||||
import { BuildMode, CleanupLevel } from "./util";
|
||||
|
||||
export { isDiskConfigurationError } from "./errors";
|
||||
|
||||
type Options = Array<string | number | boolean>;
|
||||
|
||||
@@ -70,6 +74,11 @@ export interface CodeQL {
|
||||
* Get a string containing the semver version of the CodeQL executable.
|
||||
*/
|
||||
getVersion(): Promise<VersionInfo>;
|
||||
/**
|
||||
* Determines whether the CLI installation represented by this {@link CodeQL} instance
|
||||
* has at least the {@link requiredVersion}.
|
||||
*/
|
||||
isVersionAtLeast(requiredVersion: string): Promise<boolean>;
|
||||
/**
|
||||
* Print version information about CodeQL.
|
||||
*/
|
||||
@@ -275,26 +284,6 @@ const GHES_MOST_RECENT_DEPRECATION_DATE = "2026-10-16";
|
||||
/** The CLI verbosity level to use for extraction in debug mode. */
|
||||
const EXTRACTION_DEBUG_MODE_VERBOSITY = "progress++";
|
||||
|
||||
/**
|
||||
* Decides whether `e` is a disk-related error outside of our control
|
||||
* that should be classified as a `ConfigurationError`.
|
||||
*
|
||||
* @param e The error to check.
|
||||
* @returns True if the error should be treated as a `ConfigurationError` or false if not.
|
||||
*/
|
||||
export function isDiskConfigurationError(e: unknown): boolean {
|
||||
if (!(e instanceof Error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
// out of disk space
|
||||
e.message.includes("ENOSPC") ||
|
||||
// access denied
|
||||
e.message.includes("EACCES")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up CodeQL CLI access.
|
||||
*
|
||||
@@ -403,9 +392,9 @@ export function setCodeQL(codeql: Partial<CodeQL>): void {
|
||||
cachedCodeQL = createStubCodeQL(codeql);
|
||||
}
|
||||
|
||||
function resolveFunction<T>(
|
||||
function resolveFunction<K extends keyof CodeQL, T extends CodeQL[K]>(
|
||||
partialCodeql: Partial<CodeQL>,
|
||||
methodName: string,
|
||||
methodName: K,
|
||||
defaultImplementation?: T,
|
||||
): T {
|
||||
if (typeof partialCodeql[methodName] !== "function") {
|
||||
@@ -415,7 +404,7 @@ function resolveFunction<T>(
|
||||
const dummyMethod = () => {
|
||||
throw new Error(`CodeQL ${methodName} method not correctly defined`);
|
||||
};
|
||||
return dummyMethod as T;
|
||||
return dummyMethod as unknown as T;
|
||||
}
|
||||
return partialCodeql[methodName] as T;
|
||||
}
|
||||
@@ -432,6 +421,19 @@ export function createStubCodeQL(partialCodeql: Partial<CodeQL>): CodeQL {
|
||||
getVersion: resolveFunction(partialCodeql, "getVersion", async () => ({
|
||||
version: "1.0.0",
|
||||
})),
|
||||
isVersionAtLeast: resolveFunction(
|
||||
partialCodeql,
|
||||
"isVersionAtLeast",
|
||||
async (requiredVersion) => {
|
||||
return (
|
||||
!!partialCodeql.getVersion &&
|
||||
semver.gte(
|
||||
(await partialCodeql.getVersion()).version,
|
||||
requiredVersion,
|
||||
)
|
||||
);
|
||||
},
|
||||
),
|
||||
printVersion: resolveFunction(partialCodeql, "printVersion"),
|
||||
supportsFeature: resolveFunction(
|
||||
partialCodeql,
|
||||
@@ -525,6 +527,9 @@ export async function getCodeQLForCmd(
|
||||
}
|
||||
return result;
|
||||
},
|
||||
async isVersionAtLeast(requiredVersion: string) {
|
||||
return semver.gte((await this.getVersion()).version, requiredVersion);
|
||||
},
|
||||
async printVersion() {
|
||||
// Reuse the cached version information rather than invoking the CLI again.
|
||||
core.info(JSON.stringify(await this.getVersion(), null, 2));
|
||||
@@ -1062,7 +1067,7 @@ export async function getCodeQLForCmd(
|
||||
// CodeQL object is created.
|
||||
if (
|
||||
checkVersion &&
|
||||
!(await util.codeQlVersionAtLeast(codeql, CODEQL_MINIMUM_VERSION))
|
||||
!(await codeql.isVersionAtLeast(CODEQL_MINIMUM_VERSION))
|
||||
) {
|
||||
throw new util.ConfigurationError(
|
||||
`Expected a CodeQL CLI with version at least ${CODEQL_MINIMUM_VERSION} but got version ${
|
||||
@@ -1072,7 +1077,7 @@ export async function getCodeQLForCmd(
|
||||
} else if (
|
||||
checkVersion &&
|
||||
env.getOptional(EnvVar.SUPPRESS_DEPRECATED_SOON_WARNING) !== "true" &&
|
||||
!(await util.codeQlVersionAtLeast(codeql, CODEQL_NEXT_MINIMUM_VERSION))
|
||||
!(await codeql.isVersionAtLeast(CODEQL_NEXT_MINIMUM_VERSION))
|
||||
) {
|
||||
const result = await codeql.getVersion();
|
||||
core.warning(
|
||||
|
||||
@@ -82,7 +82,6 @@ import {
|
||||
GitHubVersion,
|
||||
ConfigurationError,
|
||||
BuildMode,
|
||||
codeQlVersionAtLeast,
|
||||
cloneObject,
|
||||
isDefined,
|
||||
checkDiskUsage,
|
||||
@@ -654,10 +653,7 @@ async function runnerHasSufficientMemory(
|
||||
logger: Logger,
|
||||
): Promise<boolean> {
|
||||
if (
|
||||
await codeQlVersionAtLeast(
|
||||
codeql,
|
||||
CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE,
|
||||
)
|
||||
await codeql.isVersionAtLeast(CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE)
|
||||
) {
|
||||
logger.debug(
|
||||
`Skipping memory check for overlay analysis because CodeQL version is at least ${CODEQL_VERSION_REDUCED_OVERLAY_MEMORY_USAGE}.`,
|
||||
@@ -901,7 +897,7 @@ async function validateOverlayDatabaseMode(
|
||||
);
|
||||
return new Failure(OverlayDisabledReason.IncompatibleBuildMode);
|
||||
}
|
||||
if (!(await codeQlVersionAtLeast(codeql, CODEQL_OVERLAY_MINIMUM_VERSION))) {
|
||||
if (!(await codeql.isVersionAtLeast(CODEQL_OVERLAY_MINIMUM_VERSION))) {
|
||||
logger.warning(
|
||||
`Cannot build an ${overlayDatabaseMode} database because ` +
|
||||
`the CodeQL CLI is older than ${CODEQL_OVERLAY_MINIMUM_VERSION}. ` +
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
cleanupAndUploadDatabases,
|
||||
uploadBundledDatabase,
|
||||
} from "./database-upload";
|
||||
import { getEnv } from "./environment";
|
||||
import { Feature } from "./feature-flags";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
@@ -31,18 +32,12 @@ import {
|
||||
setupActionsVars,
|
||||
setupTests,
|
||||
} from "./testing-utils";
|
||||
import {
|
||||
CleanupLevel,
|
||||
GitHubVariant,
|
||||
HTTPError,
|
||||
initializeEnvironment,
|
||||
withTmpDir,
|
||||
} from "./util";
|
||||
import { CleanupLevel, GitHubVariant, HTTPError, withTmpDir } from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test.beforeEach(() => {
|
||||
initializeEnvironment("1.2.3");
|
||||
getEnv().initialise("1.2.3");
|
||||
});
|
||||
|
||||
const testRepoName: RepositoryNwo = { owner: "github", repo: "example" };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"bundleVersion": "codeql-bundle-v2.27.3",
|
||||
"cliVersion": "2.27.3",
|
||||
"priorBundleVersion": "codeql-bundle-v2.27.2",
|
||||
"priorCliVersion": "2.27.2"
|
||||
"bundleVersion": "codeql-bundle-v2.27.4",
|
||||
"cliVersion": "2.27.4",
|
||||
"priorBundleVersion": "codeql-bundle-v2.27.3",
|
||||
"priorCliVersion": "2.27.3"
|
||||
}
|
||||
|
||||
@@ -397,6 +397,18 @@ export class Env extends ReadOnlyEnv {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set some initial environment variables that we can set even without
|
||||
* knowing what version of CodeQL we're running.
|
||||
*/
|
||||
public initialise(version: string) {
|
||||
this.export(EnvVar.FEATURE_MULTI_LANGUAGE, "false");
|
||||
this.export(EnvVar.FEATURE_SANDWICH, "false");
|
||||
this.export(EnvVar.FEATURE_SARIF_COMBINE, "true");
|
||||
this.export(EnvVar.FEATURE_WILL_UPLOAD, "true");
|
||||
this.export(EnvVar.VERSION, version);
|
||||
}
|
||||
|
||||
/** Gets a value indicating whether `set` was called at least once. */
|
||||
public hasChanged(): boolean {
|
||||
return this.changed;
|
||||
|
||||
88
src/errors/index.ts
Normal file
88
src/errors/index.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import * as json from "../json";
|
||||
|
||||
/**
|
||||
* If {@link error} is not already an instance of {@link Error}, then this function constructs
|
||||
* an {@link Error} with a string representation of {@link error} as the message.
|
||||
*/
|
||||
export function wrapError(error: unknown): Error {
|
||||
return error instanceof Error ? error : new Error(String(error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an appropriate message for the error.
|
||||
*
|
||||
* If the error is an {@link Error} instance, this returns the error message without
|
||||
* an `Error: ` prefix.
|
||||
*/
|
||||
export function getErrorMessage(error: unknown): string {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximates whether {@link error} is like a {@link NodeJS.ErrnoException}.
|
||||
* We only typically care about the `code` property, so that's what we check for here.
|
||||
*/
|
||||
export function isErrnoException(
|
||||
error: unknown,
|
||||
): error is NodeJS.ErrnoException {
|
||||
return (
|
||||
typeof error === "object" &&
|
||||
error !== null &&
|
||||
"code" in error &&
|
||||
typeof error.code === "string"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether `e` is a disk-related error outside of our control
|
||||
* that should be classified as a `ConfigurationError`.
|
||||
*
|
||||
* @param e The error to check.
|
||||
* @returns True if the error should be treated as a `ConfigurationError` or false if not.
|
||||
*/
|
||||
export function isDiskConfigurationError(e: unknown): boolean {
|
||||
if (!(e instanceof Error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
// out of disk space
|
||||
e.message.includes("ENOSPC") ||
|
||||
// access denied
|
||||
e.message.includes("EACCES")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* An Error class that indicates an error that occurred due to
|
||||
* a misconfiguration of the action or the CodeQL CLI.
|
||||
*/
|
||||
export class ConfigurationError extends Error {}
|
||||
|
||||
/** Minimal representation of errors resulting from HTTP requests. */
|
||||
export class HTTPError extends Error {
|
||||
public status: number;
|
||||
|
||||
constructor(message: string, status: number) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to convert {@link arg} to a {@link HTTPError}, but returns `undefined` if
|
||||
* {@link arg} is not like a {@link HTTPError}.
|
||||
*/
|
||||
export function asHTTPError(arg: any): HTTPError | undefined {
|
||||
if (!json.isObject<any>(arg) || !json.isString(arg.message)) {
|
||||
return undefined;
|
||||
}
|
||||
if (Number.isInteger(arg.status)) {
|
||||
return new HTTPError(arg.message, arg.status as number);
|
||||
}
|
||||
// See https://github.com/actions/toolkit/blob/acb230b99a46ed33a3f04a758cd68b47b9a82908/packages/tool-cache/src/tool-cache.ts#L19
|
||||
if (Number.isInteger(arg.httpStatusCode)) {
|
||||
return new HTTPError(arg.message, arg.httpStatusCode as number);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import * as path from "path";
|
||||
import test from "ava";
|
||||
|
||||
import * as defaults from "./defaults.json";
|
||||
import { getEnv } from "./environment";
|
||||
import {
|
||||
Feature,
|
||||
featureConfig,
|
||||
@@ -26,12 +27,12 @@ import {
|
||||
setupTests,
|
||||
stubFeatureFlagApiEndpoint,
|
||||
} from "./testing-utils";
|
||||
import { GitHubVariant, initializeEnvironment, withTmpDir } from "./util";
|
||||
import { GitHubVariant, withTmpDir } from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test.beforeEach(() => {
|
||||
initializeEnvironment("1.2.3");
|
||||
getEnv().initialise("1.2.3");
|
||||
});
|
||||
|
||||
test.serial(
|
||||
|
||||
@@ -560,7 +560,7 @@ class OfflineFeatures implements FeatureEnablement {
|
||||
// Never use this feature if the CLI version explicitly can't support it.
|
||||
const minimumVersion = config.minimumVersion;
|
||||
if (codeql && minimumVersion) {
|
||||
if (!(await util.codeQlVersionAtLeast(codeql, minimumVersion))) {
|
||||
if (!(await codeql.isVersionAtLeast(minimumVersion))) {
|
||||
this.logger.debug(
|
||||
`Feature ${feature} is disabled because the CodeQL CLI version is older than the minimum ` +
|
||||
`version ${minimumVersion}.`,
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
LoggedMessage,
|
||||
setupTests,
|
||||
} from "../testing-utils";
|
||||
import { GitHubVariant, initializeEnvironment, withTmpDir } from "../util";
|
||||
import { getEnv, GitHubVariant, withTmpDir } from "../util";
|
||||
|
||||
import {
|
||||
assertAllFeaturesHaveDefaultValues,
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
setupTests(test);
|
||||
|
||||
test.beforeEach(() => {
|
||||
initializeEnvironment("1.2.3");
|
||||
getEnv().initialise("1.2.3");
|
||||
});
|
||||
|
||||
test("OfflineFeatures makes no API requests", async (t) => {
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
import { ActionsEnvVars, type ReadOnlyEnv } from "./environment";
|
||||
import { ConfigurationError } from "./util";
|
||||
import { ConfigurationError } from "./errors";
|
||||
|
||||
export enum GitHubVariant {
|
||||
/** [GitHub.com](https://github.com) */
|
||||
DOTCOM = "GitHub.com",
|
||||
/** [GitHub Enterprise Server](https://docs.github.com/en/enterprise-server@latest/admin/overview/about-github-enterprise-server) */
|
||||
GHES = "GitHub Enterprise Server",
|
||||
/** [GitHub Enterprise Cloud with data residency](https://docs.github.com/en/enterprise-cloud@latest/admin/data-residency/about-github-enterprise-cloud-with-data-residency) */
|
||||
GHEC_DR = "GitHub Enterprise Cloud with data residency",
|
||||
}
|
||||
|
||||
export type GitHubVersion =
|
||||
| { type: GitHubVariant.DOTCOM }
|
||||
| { type: GitHubVariant.GHEC_DR }
|
||||
| { type: GitHubVariant.GHES; version: string };
|
||||
|
||||
/** A string representing a GitHub instance address. */
|
||||
export type InstanceURL = string;
|
||||
|
||||
@@ -89,12 +89,10 @@ import {
|
||||
checkDiskUsage,
|
||||
checkForTimeout,
|
||||
checkGitHubVersionInRange,
|
||||
codeQlVersionAtLeast,
|
||||
DEFAULT_DEBUG_ARTIFACT_NAME,
|
||||
DEFAULT_DEBUG_DATABASE_NAME,
|
||||
getCodeQLMemoryLimit,
|
||||
getThreadsFlagValue,
|
||||
initializeEnvironment,
|
||||
ConfigurationError,
|
||||
wrapError,
|
||||
checkActionVersion,
|
||||
@@ -227,7 +225,7 @@ async function run(
|
||||
let toolsVersion: string;
|
||||
|
||||
try {
|
||||
initializeEnvironment(getActionVersion());
|
||||
actionState.env.initialise(getActionVersion());
|
||||
|
||||
// Make inputs accessible in the `post` step.
|
||||
persistInputs();
|
||||
@@ -685,7 +683,7 @@ async function run(
|
||||
`${EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS} is already set to '${process.env[EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS]}', so the Action will not override it.`,
|
||||
);
|
||||
} else if (
|
||||
(await codeQlVersionAtLeast(codeql, CODEQL_VERSION_JAR_MINIMIZATION)) &&
|
||||
(await codeql.isVersionAtLeast(CODEQL_VERSION_JAR_MINIMIZATION)) &&
|
||||
config.dependencyCachingEnabled &&
|
||||
config.buildMode === BuildMode.None &&
|
||||
config.languages.includes(BuiltInLanguage.java)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import fs from "fs";
|
||||
|
||||
import type { ExecOptions } from "@actions/exec";
|
||||
import * as toolrunner from "@actions/exec/lib/toolrunner";
|
||||
import * as io from "@actions/io";
|
||||
import test from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
@@ -344,38 +347,6 @@ test.serial(
|
||||
}),
|
||||
);
|
||||
|
||||
test("checkRunnerSupportsTracedAnalysis doesn't perform Xcode check on self-hosted runners", async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
env.set(ActionsEnvVars.RUNNER_ENVIRONMENT, "self-hosted");
|
||||
const features = createFeatures([Feature.CheckUnsupportedTracedAnalysis]);
|
||||
|
||||
const codeql = await getCodeQLWithTracedStub(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
await checkRunnerSupportsTracedAnalysis(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "26.0.0",
|
||||
env,
|
||||
features,
|
||||
}),
|
||||
createTestConfig({ tempDir: tmpDir, languages: [BuiltInLanguage.go] }),
|
||||
codeql,
|
||||
);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Skipping traced analysis compatibility check for Xcode because we are on a self-hosted runner.",
|
||||
]);
|
||||
}));
|
||||
|
||||
// `addDiagnostic` changes global state and we must stub it, so this test must be serial.
|
||||
test.serial(
|
||||
"checkRunnerSupportsTracedAnalysis doesn't add a diagnostic if Xcode version is supported",
|
||||
@@ -426,6 +397,86 @@ test.serial(
|
||||
}),
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"checkRunnerSupportsTracedAnalysis attempts fallbacks if needed",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
const features = createFeatures([Feature.CheckUnsupportedTracedAnalysis]);
|
||||
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.throws(new Error("Not found"));
|
||||
const which = sinon.stub(io, "which").resolves("/path/to/xcrun");
|
||||
const exec = sinon.stub<[], Promise<number>>().callsFake(async () => {
|
||||
throw new Error("Error to force next fallback");
|
||||
});
|
||||
const runner = sinon.createStubInstance(toolrunner.ToolRunner, { exec });
|
||||
const toolrunnerStub = sinon
|
||||
.stub(toolrunner, "ToolRunner")
|
||||
.callsFake(() => {
|
||||
return runner;
|
||||
});
|
||||
|
||||
const codeql = await getCodeQLWithTracedStub(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic");
|
||||
|
||||
const config = createTestConfig({
|
||||
tempDir: tmpDir,
|
||||
languages: [BuiltInLanguage.swift],
|
||||
});
|
||||
await checkRunnerSupportsTracedAnalysis(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "26.0.0",
|
||||
env,
|
||||
features,
|
||||
}),
|
||||
config,
|
||||
codeql,
|
||||
);
|
||||
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
t.is(which.callCount, 2);
|
||||
t.deepEqual(which.args[0], ["xcrun", true]);
|
||||
t.deepEqual(which.args[1], ["xcrun", true]);
|
||||
|
||||
t.is(toolrunnerStub.callCount, 2);
|
||||
t.is(exec.callCount, 2);
|
||||
|
||||
t.is(toolrunnerStub.args[0][0], "/path/to/xcrun");
|
||||
t.deepEqual(toolrunnerStub.args[0][1], ["xcodebuild", "-version"]);
|
||||
|
||||
t.is(toolrunnerStub.args[1][0], "/path/to/xcrun");
|
||||
t.deepEqual(toolrunnerStub.args[1][1], [
|
||||
"--sdk",
|
||||
"macosx",
|
||||
"--show-sdk-version",
|
||||
]);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Unable to determine Xcode version: Not found",
|
||||
"Unable to determine Xcode version from xcodebuild: Error to force next fallback",
|
||||
"Unable to determine Xcode SDK version: Error to force next fallback",
|
||||
]);
|
||||
checkUnexpectedLogMessages(t, logger.messages, [
|
||||
"Traced analysis is not supported on Xcode 27",
|
||||
]);
|
||||
|
||||
t.is(addDiagnostic.callCount, 0);
|
||||
}),
|
||||
);
|
||||
|
||||
// `addDiagnostic` changes global state and we must stub it, so this test must be serial.
|
||||
test.serial(
|
||||
"checkRunnerSupportsTracedAnalysis logs and adds diagnostic if Xcode version is unsupported",
|
||||
@@ -496,6 +547,103 @@ test.serial(
|
||||
}),
|
||||
);
|
||||
|
||||
// `addDiagnostic` changes global state and we must stub it, so this test must be serial.
|
||||
test.serial(
|
||||
"checkRunnerSupportsTracedAnalysis logs and adds diagnostic if Xcode SDK version is unsupported",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
const features = createFeatures([Feature.CheckUnsupportedTracedAnalysis]);
|
||||
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.throws(new Error("Not found"));
|
||||
const which = sinon.stub(io, "which").resolves("/path/to/xcrun");
|
||||
|
||||
let stdoutListener: ((data: Buffer) => void) | undefined = undefined;
|
||||
const exec = sinon.stub<[], Promise<number>>().callsFake(async () => {
|
||||
if (stdoutListener !== undefined) {
|
||||
stdoutListener(Buffer.from("27.5"));
|
||||
return 0;
|
||||
} else {
|
||||
throw new Error("Error to force next fallback");
|
||||
}
|
||||
});
|
||||
const runner = sinon.createStubInstance(toolrunner.ToolRunner, {
|
||||
exec,
|
||||
});
|
||||
const toolrunnerStub = sinon
|
||||
.stub(toolrunner, "ToolRunner")
|
||||
.callsFake((_cmd, args: string[], opts: ExecOptions) => {
|
||||
if (args[0] !== "xcodebuild") {
|
||||
stdoutListener = opts.listeners?.stdout;
|
||||
}
|
||||
return runner;
|
||||
});
|
||||
|
||||
const codeql = await getCodeQLWithTracedStub(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic");
|
||||
|
||||
const config = createTestConfig({
|
||||
tempDir: tmpDir,
|
||||
languages: [BuiltInLanguage.swift],
|
||||
});
|
||||
await checkRunnerSupportsTracedAnalysis(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "26.0.0",
|
||||
env,
|
||||
features,
|
||||
}),
|
||||
config,
|
||||
codeql,
|
||||
);
|
||||
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
t.is(which.callCount, 2);
|
||||
t.is(toolrunnerStub.callCount, 2);
|
||||
t.is(exec.callCount, 2);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Unable to determine Xcode version from xcodebuild",
|
||||
"Traced analysis is not supported on Xcode SDK 27",
|
||||
]);
|
||||
|
||||
t.is(addDiagnostic.callCount, 1);
|
||||
t.like(addDiagnostic.args[0], [
|
||||
config,
|
||||
BuiltInLanguage.swift,
|
||||
{
|
||||
attributes: {
|
||||
languages: [BuiltInLanguage.swift],
|
||||
xcodeSdkVersion: "27.5.0",
|
||||
},
|
||||
severity: "warning",
|
||||
source: {
|
||||
id: "codeql-action/unsupported-traced-analysis-xcode-sdk",
|
||||
name: "Traced analysis is not supported on this version of Xcode SDK",
|
||||
},
|
||||
visibility: {
|
||||
cliSummaryTable: true,
|
||||
statusPage: true,
|
||||
telemetry: true,
|
||||
},
|
||||
} satisfies Partial<diagnostics.DiagnosticMessage>,
|
||||
]);
|
||||
}),
|
||||
);
|
||||
|
||||
// `addDiagnostic` changes global state and we must stub it, so this test must be serial.
|
||||
test.serial(
|
||||
"checkRunnerSupportsTracedAnalysis throws if Xcode version is unsupported and FF is enabled",
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import * as semver from "semver";
|
||||
|
||||
import type { ActionState, Logger } from "../action-common";
|
||||
import { isSelfHostedRunner } from "../actions-util";
|
||||
import type { CodeQL } from "../codeql";
|
||||
import type { ActionConfig } from "../config/action-config";
|
||||
import { addDiagnostic, makeDiagnostic } from "../diagnostics";
|
||||
import { DocUrl } from "../doc-url";
|
||||
import { Feature } from "../feature-flags";
|
||||
import { darwinVersion, xcodeVersion } from "../platform";
|
||||
import {
|
||||
darwinVersion,
|
||||
xcodeVersionFromLink,
|
||||
xcodeSdkVersion,
|
||||
xcodeVersionFromXcodeBuild,
|
||||
} from "../platform";
|
||||
import { ConfigurationError } from "../util";
|
||||
|
||||
import { checkSwiftCompatibility } from "./swift";
|
||||
@@ -18,6 +22,27 @@ export const UNSUPPORTED_DARWIN_FOR_TRACED = 27;
|
||||
/** Xcode 27 and above do not support traced extraction. */
|
||||
export const UNSUPPORTED_XCODE_FOR_TRACED = 27;
|
||||
|
||||
/** Names of products that we check the versions of here. */
|
||||
type ProductName = "macOS" | "Xcode" | "Xcode SDK";
|
||||
|
||||
/** A version along with the product it relates to. */
|
||||
type ProductVersion = {
|
||||
product: ProductName;
|
||||
version: semver.SemVer;
|
||||
};
|
||||
|
||||
/** Gets the appropriate attribute name for the diagnostic based on {@link product}. */
|
||||
function diagnosticAttributeName(product: ProductName): string {
|
||||
switch (product) {
|
||||
case "macOS":
|
||||
return "darwinVersion";
|
||||
case "Xcode":
|
||||
return "xcodeVersion";
|
||||
case "Xcode SDK":
|
||||
return "xcodeSdkVersion";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a diagnostic indicating that `version` of `product` is unsupported for traced analysis.
|
||||
* Depending on `skipUnsupportedTracedAnalysis`, this function then either throws a {@link ConfigurationError}
|
||||
@@ -27,28 +52,30 @@ export const UNSUPPORTED_XCODE_FOR_TRACED = 27;
|
||||
* @param config The CodeQL Action configuration.
|
||||
* @param tracedLanguages Traced languages that are configured for analysis.
|
||||
* @param abortUnsupportedTracedAnalysis Whether this is a fatal error.
|
||||
* @param product The product that the version is unsupported of.
|
||||
* @param version The unsupported version.
|
||||
* @param productVersion A description of the product and its version that is unsupported.
|
||||
*/
|
||||
function handleUnsupportedVersion(
|
||||
logger: Logger,
|
||||
config: ActionConfig<"dbLocation" | "tempDir">,
|
||||
tracedLanguages: string[],
|
||||
abortUnsupportedTracedAnalysis: boolean,
|
||||
product: "macOS" | "Xcode",
|
||||
version: semver.SemVer,
|
||||
productVersion: ProductVersion,
|
||||
) {
|
||||
const { product, version } = productVersion;
|
||||
const unsupportedVersion =
|
||||
product === "macOS"
|
||||
? UNSUPPORTED_DARWIN_FOR_TRACED
|
||||
: UNSUPPORTED_XCODE_FOR_TRACED;
|
||||
: // We use the same constant for both Xcode and the Xcode SDK since we treat them
|
||||
// as equivalent indicators for whether traced analysis is unsupported.
|
||||
UNSUPPORTED_XCODE_FOR_TRACED;
|
||||
|
||||
const baseMessage = [
|
||||
`Traced analysis is not supported on ${product} ${unsupportedVersion} or above.`,
|
||||
`Configure your analysis to run on macOS ${UNSUPPORTED_DARWIN_FOR_TRACED - 1} or below`,
|
||||
`and Xcode ${UNSUPPORTED_XCODE_FOR_TRACED - 1} or below.`,
|
||||
].join(" ");
|
||||
|
||||
const attributeName = product === "macOS" ? "darwinVersion" : "xcodeVersion";
|
||||
const attributeName = diagnosticAttributeName(product);
|
||||
|
||||
// Create a diagnostic that will show up on the TSP.
|
||||
for (const language of tracedLanguages) {
|
||||
@@ -56,7 +83,7 @@ function handleUnsupportedVersion(
|
||||
config,
|
||||
language,
|
||||
makeDiagnostic(
|
||||
`codeql-action/unsupported-traced-analysis-${product.toLowerCase()}`,
|
||||
`codeql-action/unsupported-traced-analysis-${product.toLowerCase().replaceAll(" ", "-")}`,
|
||||
`Traced analysis is not supported on this version of ${product}`,
|
||||
{
|
||||
attributes: {
|
||||
@@ -177,8 +204,7 @@ export async function checkRunnerSupportsTracedAnalysis(
|
||||
config,
|
||||
tracedLanguages,
|
||||
abortUnsupportedTracedAnalysis,
|
||||
"macOS",
|
||||
version,
|
||||
{ product: "macOS", version },
|
||||
);
|
||||
|
||||
// Return early so that we don't end up producing more-or-less the same log message/diagnostic
|
||||
@@ -186,32 +212,39 @@ export async function checkRunnerSupportsTracedAnalysis(
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if we are on a self-hosted runner since the current Xcode version detection
|
||||
// is only validated on hosted runners.
|
||||
if (isSelfHostedRunner(action.env)) {
|
||||
action.logger.debug(
|
||||
"Skipping traced analysis compatibility check for Xcode because we are on a self-hosted runner.",
|
||||
);
|
||||
return;
|
||||
// Determining whether the Xcode version is supported only makes sense on macOS, so we only do it
|
||||
// after determining that we are running on macOS. We first try to obtain the version of Xcode itself
|
||||
// starting with the cheapest approach and then call out to an external program. If neither works, we try
|
||||
// to obtain the version from the SDK instead. Since either Xcode or the SDK at version 27 or above is
|
||||
// incompatible, either is fine as we only check the major version below.
|
||||
const xcodeVer =
|
||||
xcodeVersionFromLink(action.logger) ??
|
||||
(await xcodeVersionFromXcodeBuild(action.logger));
|
||||
let xcodeProduct: ProductVersion | undefined = undefined;
|
||||
|
||||
if (xcodeVer !== undefined) {
|
||||
xcodeProduct = { product: "Xcode", version: xcodeVer };
|
||||
} else {
|
||||
// Fallback to checking the SDK version, which may be available even if a full Xcode installation is not.
|
||||
const xcodeSdkVer = await xcodeSdkVersion(action.logger);
|
||||
xcodeProduct =
|
||||
xcodeSdkVer !== undefined
|
||||
? { product: "Xcode SDK", version: xcodeSdkVer }
|
||||
: undefined;
|
||||
}
|
||||
|
||||
// Determining whether the Xcode version is supported only makes sense on macOS, so we only do it
|
||||
// after determining that we are running on macOS.
|
||||
const xcodeVer = xcodeVersion(action.logger);
|
||||
|
||||
if (
|
||||
xcodeVer !== undefined &&
|
||||
xcodeVer.major >= UNSUPPORTED_XCODE_FOR_TRACED
|
||||
xcodeProduct !== undefined &&
|
||||
xcodeProduct.version.major >= UNSUPPORTED_XCODE_FOR_TRACED
|
||||
) {
|
||||
handleUnsupportedVersion(
|
||||
action.logger,
|
||||
config,
|
||||
tracedLanguages,
|
||||
abortUnsupportedTracedAnalysis,
|
||||
"Xcode",
|
||||
xcodeVer,
|
||||
xcodeProduct,
|
||||
);
|
||||
} else if (xcodeVer === undefined) {
|
||||
} else if (xcodeProduct === undefined) {
|
||||
action.logger.info(
|
||||
"Unable to determine Xcode version. Skipping Xcode compatibility check.",
|
||||
);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import fs from "fs";
|
||||
|
||||
import type { ExecOptions } from "@actions/exec/lib/interfaces";
|
||||
import * as toolrunner from "@actions/exec/lib/toolrunner";
|
||||
import * as io from "@actions/io";
|
||||
import test from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
@@ -8,7 +11,9 @@ import {
|
||||
getBundlePlatform,
|
||||
darwinVersion,
|
||||
XCODE_SELECT_LINK_PATH,
|
||||
xcodeVersion,
|
||||
xcodeVersionFromLink,
|
||||
xcodeSdkVersion,
|
||||
xcodeVersionFromXcodeBuild,
|
||||
} from "./platform";
|
||||
import {
|
||||
checkExpectedLogMessages,
|
||||
@@ -71,46 +76,52 @@ test("darwinVersion returns semver if parsing succeeds", (t) => {
|
||||
}
|
||||
});
|
||||
|
||||
test.serial("xcodeVersion returns undefined if symlink doesn't exist", (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
test.serial(
|
||||
"xcodeVersionFromLink returns undefined if symlink doesn't exist",
|
||||
(t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.throws(new Error("ENOENT: no such file or directory"));
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.throws(new Error("ENOENT: no such file or directory"));
|
||||
|
||||
t.is(xcodeVersion(logger), undefined);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
t.is(xcodeVersionFromLink(logger), undefined);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Unable to determine Xcode version: ENOENT: no such file or directory",
|
||||
]);
|
||||
});
|
||||
|
||||
test.serial("xcodeVersion returns undefined if file is not a symlink", (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.throws(new Error("EINVAL: invalid argument"));
|
||||
|
||||
t.is(xcodeVersion(logger), undefined);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Unable to determine Xcode version: EINVAL: invalid argument",
|
||||
]);
|
||||
});
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Unable to determine Xcode version: ENOENT: no such file or directory",
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"xcodeVersion returns undefined if resolving the symlink returns nothing",
|
||||
"xcodeVersionFromLink returns undefined if file is not a symlink",
|
||||
(t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.throws(new Error("EINVAL: invalid argument"));
|
||||
|
||||
t.is(xcodeVersionFromLink(logger), undefined);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Unable to determine Xcode version: EINVAL: invalid argument",
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"xcodeVersionFromLink returns undefined if resolving the symlink returns nothing",
|
||||
(t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const readlinkSync = sinon.stub(fs, "readlinkSync").returns("");
|
||||
|
||||
t.is(xcodeVersion(logger), undefined);
|
||||
t.is(xcodeVersionFromLink(logger), undefined);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
@@ -121,7 +132,7 @@ test.serial(
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"xcodeVersion returns undefined if resolved path doesn't include pattern",
|
||||
"xcodeVersionFromLink returns undefined if resolved path doesn't include pattern",
|
||||
(t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
@@ -129,7 +140,7 @@ test.serial(
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode.app/Contents/Developer");
|
||||
|
||||
t.is(xcodeVersion(logger), undefined);
|
||||
t.is(xcodeVersionFromLink(logger), undefined);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
@@ -139,30 +150,33 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial("xcodeVersion returns undefined if match can't be parsed", (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
test.serial(
|
||||
"xcodeVersionFromLink returns undefined if match can't be parsed",
|
||||
(t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_00.0.app/Contents/Developer");
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_00.0.app/Contents/Developer");
|
||||
|
||||
t.is(xcodeVersion(logger), undefined);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
t.is(xcodeVersionFromLink(logger), undefined);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Couldn't parse '00.0' as a semantic version.",
|
||||
]);
|
||||
});
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Couldn't parse '00.0' as a semantic version.",
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial("xcodeVersion returns version from resolved path", (t) => {
|
||||
test.serial("xcodeVersionFromLink returns version from resolved path", (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_16.4.app/Contents/Developer");
|
||||
|
||||
const result = xcodeVersion(logger);
|
||||
const result = xcodeVersionFromLink(logger);
|
||||
|
||||
if (t.truthy(result)) {
|
||||
t.is(result.major, 16);
|
||||
@@ -173,26 +187,29 @@ test.serial("xcodeVersion returns version from resolved path", (t) => {
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
});
|
||||
|
||||
test.serial("xcodeVersion returns major version from resolved path", (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
test.serial(
|
||||
"xcodeVersionFromLink returns major version from resolved path",
|
||||
(t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_16.app/Contents/Developer");
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_16.app/Contents/Developer");
|
||||
|
||||
const result = xcodeVersion(logger);
|
||||
const result = xcodeVersionFromLink(logger);
|
||||
|
||||
if (t.truthy(result)) {
|
||||
t.is(result.major, 16);
|
||||
t.is(result.minor, 0);
|
||||
}
|
||||
if (t.truthy(result)) {
|
||||
t.is(result.major, 16);
|
||||
t.is(result.minor, 0);
|
||||
}
|
||||
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
});
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"xcodeVersion allows more version components in resolved path",
|
||||
"xcodeVersionFromLink allows more version components in resolved path",
|
||||
(t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
@@ -200,7 +217,7 @@ test.serial(
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_16.5.9.app/Contents/Developer");
|
||||
|
||||
const result = xcodeVersion(logger);
|
||||
const result = xcodeVersionFromLink(logger);
|
||||
|
||||
if (t.truthy(result)) {
|
||||
t.is(result.major, 16);
|
||||
@@ -211,3 +228,135 @@ test.serial(
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"xcodeVersionFromXcodeBuild returns undefined for non-zero exit code",
|
||||
async (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const which = sinon.stub(io, "which").resolves("/path/to/xcrun");
|
||||
const exec = sinon.stub<[], Promise<number>>().resolves(1);
|
||||
const runner = sinon.createStubInstance(toolrunner.ToolRunner, { exec });
|
||||
const toolrunnerStub = sinon.stub(toolrunner, "ToolRunner").returns(runner);
|
||||
|
||||
await t.notThrowsAsync(async () => {
|
||||
const result = await xcodeVersionFromXcodeBuild(logger);
|
||||
|
||||
t.is(result, undefined);
|
||||
|
||||
t.is(which.callCount, 1);
|
||||
t.deepEqual(which.args[0], ["xcrun", true]);
|
||||
|
||||
t.is(toolrunnerStub.callCount, 1);
|
||||
t.is(exec.callCount, 1);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"xcodebuild returned exit code 1",
|
||||
]);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"xcodeVersionFromXcodeBuild extracts version from xcrun stdout",
|
||||
async (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const which = sinon.stub(io, "which").resolves("/path/to/xcrun");
|
||||
|
||||
let stdoutListener: ((data: Buffer) => void) | undefined = undefined;
|
||||
const exec = sinon.stub<[], Promise<number>>().callsFake(async () => {
|
||||
if (stdoutListener !== undefined) {
|
||||
stdoutListener(Buffer.from("Xcode 26.5"));
|
||||
stdoutListener(Buffer.from("Build version 26F9"));
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
const runner = sinon.createStubInstance(toolrunner.ToolRunner, { exec });
|
||||
|
||||
const toolrunnerStub = sinon
|
||||
.stub(toolrunner, "ToolRunner")
|
||||
.callsFake((_cmd, _args, opts: ExecOptions) => {
|
||||
stdoutListener = opts.listeners?.stdout;
|
||||
return runner;
|
||||
});
|
||||
|
||||
await t.notThrowsAsync(async () => {
|
||||
const result = await xcodeVersionFromXcodeBuild(logger);
|
||||
|
||||
t.not(result, undefined);
|
||||
t.is(result?.major, 26);
|
||||
t.is(result?.minor, 5);
|
||||
|
||||
t.is(which.callCount, 1);
|
||||
t.deepEqual(which.args[0], ["xcrun", true]);
|
||||
|
||||
t.is(toolrunnerStub.callCount, 1);
|
||||
t.is(exec.callCount, 1);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"xcodeSdkVersion returns undefined for non-zero exit code",
|
||||
async (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const which = sinon.stub(io, "which").resolves("/path/to/xcrun");
|
||||
const exec = sinon.stub<[], Promise<number>>().resolves(1);
|
||||
const runner = sinon.createStubInstance(toolrunner.ToolRunner, { exec });
|
||||
const toolrunnerStub = sinon.stub(toolrunner, "ToolRunner").returns(runner);
|
||||
|
||||
await t.notThrowsAsync(async () => {
|
||||
const result = await xcodeSdkVersion(logger);
|
||||
|
||||
t.is(result, undefined);
|
||||
|
||||
t.is(which.callCount, 1);
|
||||
t.deepEqual(which.args[0], ["xcrun", true]);
|
||||
|
||||
t.is(toolrunnerStub.callCount, 1);
|
||||
t.is(exec.callCount, 1);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"xcrun returned exit code 1",
|
||||
]);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial("xcodeSdkVersion extracts version from xcrun stdout", async (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const which = sinon.stub(io, "which").resolves("/path/to/xcrun");
|
||||
|
||||
let stdoutListener: ((data: Buffer) => void) | undefined = undefined;
|
||||
const exec = sinon.stub<[], Promise<number>>().callsFake(async () => {
|
||||
if (stdoutListener !== undefined) {
|
||||
stdoutListener(Buffer.from("26.5"));
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
const runner = sinon.createStubInstance(toolrunner.ToolRunner, { exec });
|
||||
|
||||
const toolrunnerStub = sinon
|
||||
.stub(toolrunner, "ToolRunner")
|
||||
.callsFake((_cmd, _args, opts: ExecOptions) => {
|
||||
stdoutListener = opts.listeners?.stdout;
|
||||
return runner;
|
||||
});
|
||||
|
||||
await t.notThrowsAsync(async () => {
|
||||
const result = await xcodeSdkVersion(logger);
|
||||
|
||||
t.not(result, undefined);
|
||||
t.is(result?.major, 26);
|
||||
t.is(result?.minor, 5);
|
||||
|
||||
t.is(which.callCount, 1);
|
||||
t.deepEqual(which.args[0], ["xcrun", true]);
|
||||
|
||||
t.is(toolrunnerStub.callCount, 1);
|
||||
t.is(exec.callCount, 1);
|
||||
});
|
||||
});
|
||||
|
||||
138
src/platform.ts
138
src/platform.ts
@@ -1,5 +1,7 @@
|
||||
import fs from "fs";
|
||||
|
||||
import * as toolrunner from "@actions/exec/lib/toolrunner";
|
||||
import * as io from "@actions/io";
|
||||
import * as semver from "semver";
|
||||
|
||||
import type { ActionState, Logger } from "./action-common";
|
||||
@@ -68,12 +70,15 @@ export const XCODE_APP_FILENAME_PATTERN = new RegExp(
|
||||
);
|
||||
|
||||
/**
|
||||
* Tries to determine the version of Xcode that is installed.
|
||||
* Tries to determine the version of Xcode that is installed from the resolved symbolic link
|
||||
* at {@link XCODE_SELECT_LINK_PATH}.
|
||||
*
|
||||
* @param logger The logger to use.
|
||||
* @returns The Xcode version or `undefined` if it couldn't be determined.
|
||||
*/
|
||||
export function xcodeVersion(logger: Logger): semver.SemVer | undefined {
|
||||
export function xcodeVersionFromLink(
|
||||
logger: Logger,
|
||||
): semver.SemVer | undefined {
|
||||
try {
|
||||
// Read what the symbolic link points to. This will throw if `XCODE_SELECT_LINK_PATH` does not
|
||||
// exist (`ENOENT`) or is not a symbolic link (`EINVAL`).
|
||||
@@ -109,3 +114,132 @@ export function xcodeVersion(logger: Logger): semver.SemVer | undefined {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to determine the version of Xcode that is installed by running `xcrun xcodebuild -version`.
|
||||
*
|
||||
* @param logger The logger to use.
|
||||
* @returns The Xcode version or `undefined` if it couldn't be determined.
|
||||
*/
|
||||
export async function xcodeVersionFromXcodeBuild(
|
||||
logger: Logger,
|
||||
): Promise<semver.SemVer | undefined> {
|
||||
try {
|
||||
const xcrun = await io.which("xcrun", true);
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
|
||||
const exitCode = await new toolrunner.ToolRunner(
|
||||
xcrun,
|
||||
["xcodebuild", "-version"],
|
||||
{
|
||||
silent: true,
|
||||
ignoreReturnCode: true,
|
||||
listeners: {
|
||||
stdout: (data: Buffer) => {
|
||||
stdout += data.toString("utf-8");
|
||||
},
|
||||
stderr: (data: Buffer) => {
|
||||
stderr += data.toString("utf-8");
|
||||
},
|
||||
},
|
||||
},
|
||||
).exec();
|
||||
|
||||
if (exitCode !== 0) {
|
||||
logger.debug(`xcodebuild returned exit code ${exitCode}`);
|
||||
logger.debug(`stdout:\n${stdout}`);
|
||||
logger.debug(`stderr:\n${stderr}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// The output contains a couple of lines, but we only care about the product version.
|
||||
const match = stdout.match(/^Xcode (\d+(\.\d+)?)/);
|
||||
|
||||
if (match === null) {
|
||||
logger.debug(
|
||||
`Output doesn't appear to contain the Xcode product version.`,
|
||||
);
|
||||
logger.debug(`stdout:\n${stdout}`);
|
||||
logger.debug(`stderr:\n${stderr}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// We expect the output to only be a version string. Try to parse it into a semver.
|
||||
const version = semver.coerce(match[1].trim());
|
||||
|
||||
if (version === null) {
|
||||
logger.debug(
|
||||
`xcodebuild's output couldn't be parsed as a version:\n${stdout}`,
|
||||
);
|
||||
logger.debug(`stderr:\n${stderr}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return version;
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`Unable to determine Xcode version from xcodebuild: ${getErrorMessage(err)}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to determine the version of the Xcode SDK that is installed by running
|
||||
* `xcrun --sdk macosx --show-sdk-version`.
|
||||
*
|
||||
* @param logger The logger to use.
|
||||
* @returns The Xcode SDK version or `undefined` if it couldn't be determined.
|
||||
*/
|
||||
export async function xcodeSdkVersion(
|
||||
logger: Logger,
|
||||
): Promise<semver.SemVer | undefined> {
|
||||
try {
|
||||
const xcrun = await io.which("xcrun", true);
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
|
||||
const exitCode = await new toolrunner.ToolRunner(
|
||||
xcrun,
|
||||
["--sdk", "macosx", "--show-sdk-version"],
|
||||
{
|
||||
silent: true,
|
||||
ignoreReturnCode: true,
|
||||
listeners: {
|
||||
stdout: (data: Buffer) => {
|
||||
stdout += data.toString("utf-8");
|
||||
},
|
||||
stderr: (data: Buffer) => {
|
||||
stderr += data.toString("utf-8");
|
||||
},
|
||||
},
|
||||
},
|
||||
).exec();
|
||||
|
||||
if (exitCode !== 0) {
|
||||
logger.debug(`xcrun returned exit code ${exitCode}`);
|
||||
logger.debug(`stdout:\n${stdout}`);
|
||||
logger.debug(`stderr:\n${stderr}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// We expect the output to only be a version string. Try to parse it into a semver.
|
||||
const version = semver.coerce(stdout.trim());
|
||||
|
||||
if (version === null) {
|
||||
logger.debug(
|
||||
`xcrun's output couldn't be parsed as a version:\n${stdout}`,
|
||||
);
|
||||
logger.debug(`stderr:\n${stderr}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return version;
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`Unable to determine Xcode SDK version: ${getErrorMessage(err)}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ import {
|
||||
checkDiskUsage,
|
||||
checkForTimeout,
|
||||
checkGitHubVersionInRange,
|
||||
initializeEnvironment,
|
||||
ConfigurationError,
|
||||
wrapError,
|
||||
checkActionVersion,
|
||||
@@ -108,7 +107,7 @@ async function run(
|
||||
let toolsVersion: string;
|
||||
|
||||
try {
|
||||
initializeEnvironment(getActionVersion());
|
||||
actionState.env.initialise(getActionVersion());
|
||||
|
||||
const apiDetails = {
|
||||
auth: getRequiredInput("token"),
|
||||
|
||||
@@ -11,7 +11,13 @@ import * as sinon from "sinon";
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as api from "./api-client";
|
||||
import * as diagnostics from "./diagnostics";
|
||||
import { ActionsEnvVars, Env, EnvVar, ReadOnlyEnv } from "./environment";
|
||||
import {
|
||||
ActionsEnvVars,
|
||||
Env,
|
||||
EnvVar,
|
||||
getEnv,
|
||||
ReadOnlyEnv,
|
||||
} from "./environment";
|
||||
import { Feature } from "./feature-flags";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { getCacheRestoreKeyPrefix } from "./overlay/caching";
|
||||
@@ -37,18 +43,12 @@ import {
|
||||
setupTests,
|
||||
} from "./testing-utils";
|
||||
import * as toolsDownload from "./tools-download";
|
||||
import {
|
||||
getErrorMessage,
|
||||
GitHubVariant,
|
||||
HTTPError,
|
||||
initializeEnvironment,
|
||||
withTmpDir,
|
||||
} from "./util";
|
||||
import { getErrorMessage, GitHubVariant, HTTPError, withTmpDir } from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test.beforeEach(() => {
|
||||
initializeEnvironment("1.2.3");
|
||||
getEnv().initialise("1.2.3");
|
||||
});
|
||||
|
||||
function stubDownloadAndExtract() {
|
||||
|
||||
@@ -6,7 +6,6 @@ import * as sinon from "sinon";
|
||||
import * as actionsUtil from "../actions-util";
|
||||
import { ActionsEnvVars } from "../environment";
|
||||
import { callee, getTestEnv, setupTests } from "../testing-utils";
|
||||
import { initializeEnvironment } from "../util";
|
||||
|
||||
import { getCodeQLActionRepository } from "./repository";
|
||||
|
||||
@@ -16,7 +15,7 @@ test.serial("getCodeQLActionRepository", async (t) => {
|
||||
const initialEnv = getTestEnv({
|
||||
[ActionsEnvVars.RUNNER_TEMP]: path.dirname(__dirname),
|
||||
});
|
||||
initializeEnvironment("1.2.3", initialEnv);
|
||||
initialEnv.initialise("1.2.3");
|
||||
|
||||
const target = callee(getCodeQLActionRepository)
|
||||
.withArgs()
|
||||
|
||||
@@ -2,13 +2,12 @@ import { ActionState } from "../action-common";
|
||||
import { isRunningLocalAction } from "../actions-util";
|
||||
import * as api from "../api-client";
|
||||
import { ActionsEnvVars } from "../environment";
|
||||
import { GITHUB_DOTCOM_URL } from "../github";
|
||||
import { GITHUB_DOTCOM_URL, type InstanceURL } from "../github";
|
||||
import {
|
||||
isRepositoryNwoStr,
|
||||
parseRepositoryNwo,
|
||||
type RepositoryNwoStr,
|
||||
} from "../repository";
|
||||
import { type InstanceURL } from "../util";
|
||||
|
||||
/** The NWO of the standard CodeQL Action repository. */
|
||||
export const CODEQL_DEFAULT_ACTION_REPOSITORY = "github/codeql-action";
|
||||
|
||||
@@ -10,6 +10,7 @@ import { https } from "follow-redirects";
|
||||
import nock from "nock";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import * as errors from "./errors";
|
||||
import * as tar from "./tar";
|
||||
import { RecordingLogger, setupTests } from "./testing-utils";
|
||||
import { downloadAndExtract } from "./tools-download";
|
||||
@@ -163,7 +164,7 @@ test.serial(
|
||||
test.serial(
|
||||
"downloadAndExtract handles an unknown status as a non-HTTP error",
|
||||
async (t) => {
|
||||
const asHTTPError = sinon.spy(util, "asHTTPError");
|
||||
const asHTTPError = sinon.spy(errors, "asHTTPError");
|
||||
await util.withTmpDir(async (tmpDir) => {
|
||||
sinon.stub(process, "platform").value("linux");
|
||||
const archivePath = path.join(tmpDir, "codeql-bundle.tar.zst");
|
||||
|
||||
@@ -10,17 +10,18 @@ import * as analyses from "./analyses";
|
||||
import { AnalysisKind, CodeQuality, CodeScanning } from "./analyses";
|
||||
import * as api from "./api-client";
|
||||
import * as diffUtils from "./diff-informed-analysis-utils";
|
||||
import { getEnv } from "./environment";
|
||||
import { Logger } from "./logging";
|
||||
import * as sarif from "./sarif";
|
||||
import { RecordingLogger, setupTests } from "./testing-utils";
|
||||
import * as uploadLib from "./upload-lib";
|
||||
import { UploadPayload } from "./upload-lib/types";
|
||||
import { GitHubVariant, initializeEnvironment, withTmpDir } from "./util";
|
||||
import { GitHubVariant, withTmpDir } from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test.beforeEach(() => {
|
||||
initializeEnvironment("1.2.3");
|
||||
getEnv().initialise("1.2.3");
|
||||
});
|
||||
|
||||
test.serial("validateSarifFileSchema - valid", (t) => {
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
ConfigurationError,
|
||||
checkActionVersion,
|
||||
checkDiskUsage,
|
||||
initializeEnvironment,
|
||||
shouldSkipSarifUpload,
|
||||
wrapError,
|
||||
} from "./util";
|
||||
@@ -55,11 +54,13 @@ async function sendSuccessStatusReport(
|
||||
}
|
||||
}
|
||||
|
||||
async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
|
||||
async function run(action: ActionState<["Base", "Env", "Logger"]>) {
|
||||
// To capture errors appropriately, keep as much code within the try-catch as
|
||||
// possible, and only use safe functions outside.
|
||||
const logger = action.logger;
|
||||
|
||||
try {
|
||||
initializeEnvironment(getActionVersion());
|
||||
action.env.initialise(getActionVersion());
|
||||
|
||||
const gitHubVersion = await getGitHubVersion();
|
||||
checkActionVersion(getActionVersion(), gitHubVersion);
|
||||
@@ -79,7 +80,7 @@ async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
|
||||
const startingStatusReportBase = await createStatusReportBase(
|
||||
ActionName.UploadSarif,
|
||||
"starting",
|
||||
startedAt,
|
||||
action.startedAt,
|
||||
undefined,
|
||||
await checkDiskUsage(logger),
|
||||
logger,
|
||||
@@ -137,7 +138,7 @@ async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
|
||||
// so we can't wait for that here.
|
||||
}
|
||||
await sendSuccessStatusReport(
|
||||
startedAt,
|
||||
action.startedAt,
|
||||
codeScanningResult?.statusReport || {},
|
||||
logger,
|
||||
);
|
||||
@@ -153,7 +154,7 @@ async function run({ startedAt, logger }: ActionState<["Base", "Logger"]>) {
|
||||
const errorStatusReportBase = await createStatusReportBase(
|
||||
ActionName.UploadSarif,
|
||||
getActionsStatus(error),
|
||||
startedAt,
|
||||
action.startedAt,
|
||||
undefined,
|
||||
await checkDiskUsage(logger),
|
||||
logger,
|
||||
|
||||
104
src/util.ts
104
src/util.ts
@@ -20,20 +20,22 @@ import {
|
||||
getRequiredEnvParam,
|
||||
exportEnvVar,
|
||||
getEnv,
|
||||
type Env,
|
||||
type ReadOnlyEnv,
|
||||
} from "./environment";
|
||||
import * as json from "./json";
|
||||
import { Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { ConfigurationError, getErrorMessage, wrapError } from "./errors";
|
||||
import { GitHubVariant, type GitHubVersion } from "./github";
|
||||
import type { Language } from "./languages";
|
||||
import type { Logger } from "./logging";
|
||||
|
||||
// Re-export for backwards compatibility to avoid updating a lot of imports elsewhere.
|
||||
export * from "./errors";
|
||||
export {
|
||||
getRequiredEnvParam,
|
||||
getOptionalEnvVar,
|
||||
getEnv,
|
||||
isInTestMode,
|
||||
} from "./environment";
|
||||
export { GitHubVariant, type GitHubVersion } from "./github";
|
||||
|
||||
/**
|
||||
* The name of the file containing the base database OIDs, as stored in the
|
||||
@@ -47,9 +49,6 @@ const BASE_DATABASE_OIDS_FILE_NAME = "base-database-oids.json";
|
||||
*/
|
||||
const BROKEN_VERSIONS = ["0.0.0-20211207"];
|
||||
|
||||
/** A string representing a GitHub instance. */
|
||||
export type InstanceURL = string;
|
||||
|
||||
/**
|
||||
* Default name of the debugging artifact.
|
||||
*/
|
||||
@@ -470,20 +469,6 @@ const CODEQL_ACTION_WARNED_ABOUT_VERSION_ENV_VAR =
|
||||
|
||||
let hasBeenWarnedAboutVersion = false;
|
||||
|
||||
export enum GitHubVariant {
|
||||
/** [GitHub.com](https://github.com) */
|
||||
DOTCOM = "GitHub.com",
|
||||
/** [GitHub Enterprise Server](https://docs.github.com/en/enterprise-server@latest/admin/overview/about-github-enterprise-server) */
|
||||
GHES = "GitHub Enterprise Server",
|
||||
/** [GitHub Enterprise Cloud with data residency](https://docs.github.com/en/enterprise-cloud@latest/admin/data-residency/about-github-enterprise-cloud-with-data-residency) */
|
||||
GHEC_DR = "GitHub Enterprise Cloud with data residency",
|
||||
}
|
||||
|
||||
export type GitHubVersion =
|
||||
| { type: GitHubVariant.DOTCOM }
|
||||
| { type: GitHubVariant.GHEC_DR }
|
||||
| { type: GitHubVariant.GHES; version: string };
|
||||
|
||||
export function checkGitHubVersionInRange(
|
||||
version: GitHubVersion,
|
||||
logger: Logger,
|
||||
@@ -552,54 +537,6 @@ export function assertNever(value: never): never {
|
||||
throw new ExhaustivityCheckingError(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set some initial environment variables that we can set even without
|
||||
* knowing what version of CodeQL we're running.
|
||||
*/
|
||||
export function initializeEnvironment(version: string, env: Env = getEnv()) {
|
||||
env.export(EnvVar.FEATURE_MULTI_LANGUAGE, "false");
|
||||
env.export(EnvVar.FEATURE_SANDWICH, "false");
|
||||
env.export(EnvVar.FEATURE_SARIF_COMBINE, "true");
|
||||
env.export(EnvVar.FEATURE_WILL_UPLOAD, "true");
|
||||
env.export(EnvVar.VERSION, version);
|
||||
}
|
||||
|
||||
export class HTTPError extends Error {
|
||||
public status: number;
|
||||
|
||||
constructor(message: string, status: number) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An Error class that indicates an error that occurred due to
|
||||
* a misconfiguration of the action or the CodeQL CLI.
|
||||
*/
|
||||
export class ConfigurationError extends Error {}
|
||||
|
||||
export function asHTTPError(arg: any): HTTPError | undefined {
|
||||
if (!json.isObject<any>(arg) || !json.isString(arg.message)) {
|
||||
return undefined;
|
||||
}
|
||||
if (Number.isInteger(arg.status)) {
|
||||
return new HTTPError(arg.message, arg.status as number);
|
||||
}
|
||||
// See https://github.com/actions/toolkit/blob/acb230b99a46ed33a3f04a758cd68b47b9a82908/packages/tool-cache/src/tool-cache.ts#L19
|
||||
if (Number.isInteger(arg.httpStatusCode)) {
|
||||
return new HTTPError(arg.message, arg.httpStatusCode as number);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function codeQlVersionAtLeast(
|
||||
codeql: CodeQL,
|
||||
requiredVersion: string,
|
||||
): Promise<boolean> {
|
||||
return semver.gte((await codeql.getVersion()).version, requiredVersion);
|
||||
}
|
||||
|
||||
export function getBaseDatabaseOidsFilePath(
|
||||
config: ActionConfig<"dbLocation">,
|
||||
): string {
|
||||
@@ -886,35 +823,6 @@ export function parseMatrixInput(
|
||||
}
|
||||
}
|
||||
|
||||
export function wrapError(error: unknown): Error {
|
||||
return error instanceof Error ? error : new Error(String(error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an appropriate message for the error.
|
||||
*
|
||||
* If the error is an `Error` instance, this returns the error message without
|
||||
* an `Error: ` prefix.
|
||||
*/
|
||||
export function getErrorMessage(error: unknown): string {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approximates whether {@link error} is like a {@link NodeJS.ErrnoException}.
|
||||
* We only typically care about the `code` property, so that's what we check for here.
|
||||
*/
|
||||
export function isErrnoException(
|
||||
error: unknown,
|
||||
): error is NodeJS.ErrnoException {
|
||||
return (
|
||||
typeof error === "object" &&
|
||||
error !== null &&
|
||||
"code" in error &&
|
||||
typeof error.code === "string"
|
||||
);
|
||||
}
|
||||
|
||||
export function prettyPrintPack(pack: Pack) {
|
||||
return `${pack.name}${pack.version ? `@${pack.version}` : ""}${
|
||||
pack.path ? `:${pack.path}` : ""
|
||||
|
||||
Reference in New Issue
Block a user