mirror of
https://github.com/github/codeql-action.git
synced 2026-10-29 17:17:15 +00:00
Refactor non-Swift-specific parts out into traced.ts
This commit is contained in:
121
lib/entry-points.js
generated
121
lib/entry-points.js
generated
@@ -42967,11 +42967,11 @@ var require_tracingPolicy = __commonJS({
|
||||
var import_log = require_log3();
|
||||
var import_core_util = require_commonjs4();
|
||||
var import_restError = require_restError3();
|
||||
var import_util61 = require_internal3();
|
||||
var import_util62 = require_internal3();
|
||||
var tracingPolicyName2 = "tracingPolicy";
|
||||
function tracingPolicy2(options = {}) {
|
||||
const userAgentPromise = (0, import_userAgent.getUserAgentValue)(options.userAgentPrefix);
|
||||
const sanitizer = new import_util61.Sanitizer({
|
||||
const sanitizer = new import_util62.Sanitizer({
|
||||
additionalAllowedQueryParameters: options.additionalAllowedQueryParameters
|
||||
});
|
||||
const tracingClient = tryCreateTracingClient();
|
||||
@@ -46597,11 +46597,11 @@ var require_response2 = __commonJS({
|
||||
});
|
||||
module2.exports = __toCommonJS2(response_exports);
|
||||
var import_core_rest_pipeline = require_commonjs6();
|
||||
var import_util61 = require_util9();
|
||||
var import_util62 = require_util9();
|
||||
var originalResponse = /* @__PURE__ */ Symbol("Original FullOperationResponse");
|
||||
function toCompatResponse2(response, options) {
|
||||
let request3 = (0, import_util61.toWebResourceLike)(response.request);
|
||||
let headers = (0, import_util61.toHttpHeadersLike)(response.headers);
|
||||
let request3 = (0, import_util62.toWebResourceLike)(response.request);
|
||||
let headers = (0, import_util62.toHttpHeadersLike)(response.headers);
|
||||
if (options?.createProxy) {
|
||||
return new Proxy(response, {
|
||||
get(target, prop, receiver) {
|
||||
@@ -46642,7 +46642,7 @@ var require_response2 = __commonJS({
|
||||
return {
|
||||
...compatResponse,
|
||||
headers,
|
||||
request: (0, import_util61.toPipelineRequest)(compatResponse.request)
|
||||
request: (0, import_util62.toPipelineRequest)(compatResponse.request)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -46749,7 +46749,7 @@ var require_requestPolicyFactoryPolicy = __commonJS({
|
||||
requestPolicyFactoryPolicyName: () => requestPolicyFactoryPolicyName2
|
||||
});
|
||||
module2.exports = __toCommonJS2(requestPolicyFactoryPolicy_exports);
|
||||
var import_util61 = require_util9();
|
||||
var import_util62 = require_util9();
|
||||
var import_response = require_response2();
|
||||
var HttpPipelineLogLevel2 = /* @__PURE__ */ ((HttpPipelineLogLevel22) => {
|
||||
HttpPipelineLogLevel22[HttpPipelineLogLevel22["ERROR"] = 1] = "ERROR";
|
||||
@@ -46773,14 +46773,14 @@ var require_requestPolicyFactoryPolicy = __commonJS({
|
||||
async sendRequest(request3, next) {
|
||||
let httpPipeline = {
|
||||
async sendRequest(httpRequest) {
|
||||
const response2 = await next((0, import_util61.toPipelineRequest)(httpRequest));
|
||||
const response2 = await next((0, import_util62.toPipelineRequest)(httpRequest));
|
||||
return (0, import_response.toCompatResponse)(response2, { createProxy: true });
|
||||
}
|
||||
};
|
||||
for (const factory of orderedFactories) {
|
||||
httpPipeline = factory.create(httpPipeline, mockRequestPolicyOptions);
|
||||
}
|
||||
const webResourceLike = (0, import_util61.toWebResourceLike)(request3, { createProxy: true });
|
||||
const webResourceLike = (0, import_util62.toWebResourceLike)(request3, { createProxy: true });
|
||||
const response = await httpPipeline.sendRequest(webResourceLike);
|
||||
return (0, import_response.toPipelineResponse)(response);
|
||||
}
|
||||
@@ -46815,12 +46815,12 @@ var require_httpClientAdapter = __commonJS({
|
||||
});
|
||||
module2.exports = __toCommonJS2(httpClientAdapter_exports);
|
||||
var import_response = require_response2();
|
||||
var import_util61 = require_util9();
|
||||
var import_util62 = require_util9();
|
||||
function convertHttpClient2(requestPolicyClient) {
|
||||
return {
|
||||
sendRequest: async (request3) => {
|
||||
const response = await requestPolicyClient.sendRequest(
|
||||
(0, import_util61.toWebResourceLike)(request3, { createProxy: true })
|
||||
(0, import_util62.toWebResourceLike)(request3, { createProxy: true })
|
||||
);
|
||||
return (0, import_response.toPipelineResponse)(response);
|
||||
}
|
||||
@@ -46858,7 +46858,7 @@ var require_commonjs9 = __commonJS({
|
||||
disableKeepAlivePolicyName: () => import_disableKeepAlivePolicy.disableKeepAlivePolicyName,
|
||||
requestPolicyFactoryPolicyName: () => import_requestPolicyFactoryPolicy.requestPolicyFactoryPolicyName,
|
||||
toCompatResponse: () => import_response.toCompatResponse,
|
||||
toHttpHeadersLike: () => import_util61.toHttpHeadersLike
|
||||
toHttpHeadersLike: () => import_util62.toHttpHeadersLike
|
||||
});
|
||||
module2.exports = __toCommonJS2(src_exports);
|
||||
var import_extendedClient = require_extendedClient();
|
||||
@@ -46866,7 +46866,7 @@ var require_commonjs9 = __commonJS({
|
||||
var import_requestPolicyFactoryPolicy = require_requestPolicyFactoryPolicy();
|
||||
var import_disableKeepAlivePolicy = require_disableKeepAlivePolicy();
|
||||
var import_httpClientAdapter = require_httpClientAdapter();
|
||||
var import_util61 = require_util9();
|
||||
var import_util62 = require_util9();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -186219,55 +186219,62 @@ async function getToolsInput(action, repositoryProperties) {
|
||||
}
|
||||
|
||||
// src/languages/swift.ts
|
||||
var SWIFT_TRACED_UNSUPPORTED_MACOS = 27;
|
||||
var SWIFT_TRACED_UNSUPPORTED_XCODE = 27;
|
||||
function handleUnsupportedVersion(logger2, config, skipUnsupportedTracedAnalysis, product, version) {
|
||||
const baseMessage = [
|
||||
`Traced Swift analysis is not supported on ${product} ${SWIFT_TRACED_UNSUPPORTED_MACOS} or above.`,
|
||||
`Configure your analysis to run on macOS ${SWIFT_TRACED_UNSUPPORTED_MACOS - 1} or below`,
|
||||
`and XCode ${SWIFT_TRACED_UNSUPPORTED_XCODE - 1} or below.`
|
||||
].join(" ");
|
||||
const attributeName = product === "macOS" ? "macOSVersion" : "xcodeVersion";
|
||||
addDiagnostic(
|
||||
config,
|
||||
"swift" /* swift */,
|
||||
makeDiagnostic(
|
||||
`codeql-action/unsupported-traced-swift-analysis-${product.toLowerCase()}`,
|
||||
`Traced Swift analysis is not supported on this version of ${product}`,
|
||||
{
|
||||
attributes: {
|
||||
languages: config.languages,
|
||||
[attributeName]: version.toString()
|
||||
},
|
||||
markdownMessage: baseMessage,
|
||||
severity: skipUnsupportedTracedAnalysis ? "error" : "warning",
|
||||
visibility: {
|
||||
cliSummaryTable: true,
|
||||
statusPage: true,
|
||||
telemetry: true
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
if (skipUnsupportedTracedAnalysis) {
|
||||
throw new ConfigurationError(baseMessage);
|
||||
} else {
|
||||
logger2.warning(baseMessage);
|
||||
}
|
||||
}
|
||||
async function isSwiftCompatible(action, config, codeql) {
|
||||
async function isSwiftCompatible(config, codeql, macosVersion) {
|
||||
if (!config.languages.includes("swift" /* swift */)) {
|
||||
return;
|
||||
}
|
||||
if (await codeql.supportsFeature("swiftSupportsAllPlatforms" /* SwiftSupportsAllPlatforms */)) {
|
||||
return;
|
||||
}
|
||||
const version = macOSVersion(action);
|
||||
if (version === void 0) {
|
||||
if (macosVersion === void 0) {
|
||||
throw new ConfigurationError(
|
||||
`Swift analysis is only supported on macOS runner images. Please migrate to a macOS runner.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// src/languages/traced.ts
|
||||
var UNSUPPORTED_MACOS_FOR_TRACED = 27;
|
||||
var UNSUPPORTED_XCODE_FOR_TRACED = 27;
|
||||
function handleUnsupportedVersion(logger2, config, skipUnsupportedTracedAnalysis, product, version) {
|
||||
const baseMessage = [
|
||||
`Traced analysis is not supported on ${product} ${UNSUPPORTED_MACOS_FOR_TRACED} or above.`,
|
||||
`Configure your analysis to run on macOS ${UNSUPPORTED_MACOS_FOR_TRACED - 1} or below`,
|
||||
`and XCode ${UNSUPPORTED_XCODE_FOR_TRACED - 1} or below.`
|
||||
].join(" ");
|
||||
const attributeName = product === "macOS" ? "macOSVersion" : "xcodeVersion";
|
||||
for (const language of config.languages) {
|
||||
addDiagnostic(
|
||||
config,
|
||||
language,
|
||||
makeDiagnostic(
|
||||
`codeql-action/unsupported-traced-analysis-${product.toLowerCase()}`,
|
||||
`Traced analysis is not supported on this version of ${product}`,
|
||||
{
|
||||
attributes: {
|
||||
languages: config.languages,
|
||||
[attributeName]: version.toString()
|
||||
},
|
||||
markdownMessage: baseMessage,
|
||||
severity: skipUnsupportedTracedAnalysis ? "error" : "warning",
|
||||
visibility: {
|
||||
cliSummaryTable: true,
|
||||
statusPage: true,
|
||||
telemetry: true
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
if (skipUnsupportedTracedAnalysis) {
|
||||
throw new ConfigurationError(baseMessage);
|
||||
} else {
|
||||
logger2.warning(baseMessage);
|
||||
}
|
||||
}
|
||||
async function checkRunnerSupportsTracedAnalysis(action, config, codeql) {
|
||||
const version = macOSVersion(action);
|
||||
await isSwiftCompatible(config, codeql, version);
|
||||
const skipUnsupportedTracedAnalysis = await action.features.getValue(
|
||||
"skip_unsupported_traced_analysis" /* SkipUnsupportedTracedAnalysis */
|
||||
);
|
||||
@@ -186275,7 +186282,7 @@ async function isSwiftCompatible(action, config, codeql) {
|
||||
action.logger.warning(
|
||||
`Unable to determine version of macOS, got: ${version}`
|
||||
);
|
||||
} else if (version.major >= SWIFT_TRACED_UNSUPPORTED_MACOS) {
|
||||
} else if (version !== void 0 && version.major >= UNSUPPORTED_MACOS_FOR_TRACED) {
|
||||
handleUnsupportedVersion(
|
||||
action.logger,
|
||||
config,
|
||||
@@ -186285,7 +186292,7 @@ async function isSwiftCompatible(action, config, codeql) {
|
||||
);
|
||||
}
|
||||
const xcodeVer = xcodeVersion(action.logger);
|
||||
if (xcodeVer !== void 0 && xcodeVer.major >= SWIFT_TRACED_UNSUPPORTED_XCODE) {
|
||||
if (xcodeVer !== void 0 && xcodeVer.major >= UNSUPPORTED_XCODE_FOR_TRACED) {
|
||||
handleUnsupportedVersion(
|
||||
action.logger,
|
||||
config,
|
||||
@@ -186815,7 +186822,11 @@ async function run3(actionState) {
|
||||
enableFileCoverageInformation: fileCoverageResult.enabled,
|
||||
logger: logger2
|
||||
});
|
||||
await isSwiftCompatible(actionStateWithFeatures, config, codeql);
|
||||
await checkRunnerSupportsTracedAnalysis(
|
||||
actionStateWithFeatures,
|
||||
config,
|
||||
codeql
|
||||
);
|
||||
if (repositoryPropertiesResult.isFailure()) {
|
||||
addNoLanguageDiagnostic(
|
||||
config,
|
||||
|
||||
@@ -62,7 +62,7 @@ import {
|
||||
runDatabaseInitCluster,
|
||||
} from "./init";
|
||||
import { JavaEnvVars, BuiltInLanguage } from "./languages";
|
||||
import { isSwiftCompatible } from "./languages/swift";
|
||||
import { checkRunnerSupportsTracedAnalysis } from "./languages/traced";
|
||||
import { Logger, withGroupAsync } from "./logging";
|
||||
import {
|
||||
downloadOverlayBaseDatabaseFromCache,
|
||||
@@ -419,7 +419,11 @@ async function run(
|
||||
logger,
|
||||
});
|
||||
|
||||
await isSwiftCompatible(actionStateWithFeatures, config, codeql);
|
||||
await checkRunnerSupportsTracedAnalysis(
|
||||
actionStateWithFeatures,
|
||||
config,
|
||||
codeql,
|
||||
);
|
||||
|
||||
if (repositoryPropertiesResult.isFailure()) {
|
||||
addNoLanguageDiagnostic(
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
import fs from "fs";
|
||||
|
||||
import test from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import { getCodeQLForTesting } from "../codeql";
|
||||
import * as diagnostics from "../diagnostics";
|
||||
import { ActionsEnvVars } from "../environment";
|
||||
import { Feature } from "../feature-flags";
|
||||
import { XCODE_SELECT_LINK_PATH } from "../platform";
|
||||
import {
|
||||
checkExpectedLogMessages,
|
||||
checkUnexpectedLogMessages,
|
||||
createFeatures,
|
||||
createTestConfig,
|
||||
getTestEnv,
|
||||
initAllState,
|
||||
makeVersionInfo,
|
||||
RecordingLogger,
|
||||
setupTests,
|
||||
@@ -37,9 +28,11 @@ test("isSwiftCompatible doesn't throw for non-Swift languages", async (t) => {
|
||||
const codeql = await getCodeQLForTesting();
|
||||
await t.notThrowsAsync(
|
||||
isSwiftCompatible(
|
||||
initAllState(),
|
||||
createTestConfig({ languages: [language] }),
|
||||
codeql,
|
||||
// A value of `undefined` should make `isSwiftCompatible` throw for Swift by default
|
||||
// since it means we are on a platform other than macOS
|
||||
undefined,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -59,9 +52,11 @@ test("isSwiftCompatible doesn't throw for Swift if CLI supports swiftSupportsAll
|
||||
|
||||
await t.notThrowsAsync(
|
||||
isSwiftCompatible(
|
||||
initAllState({ platform: "darwin", env, logger }),
|
||||
createTestConfig({ languages: [BuiltInLanguage.swift] }),
|
||||
codeql,
|
||||
// A value of `undefined` should make `isSwiftCompatible` throw for Swift by default
|
||||
// since it means we are on a platform other than macOS
|
||||
undefined,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -82,370 +77,13 @@ test("isSwiftCompatible doesn't throw for Swift on darwin", async (t) =>
|
||||
|
||||
await t.notThrowsAsync(
|
||||
isSwiftCompatible(
|
||||
initAllState({ platform: "darwin", env, logger }),
|
||||
createTestConfig({
|
||||
tempDir: tmpDir,
|
||||
languages: [BuiltInLanguage.swift],
|
||||
}),
|
||||
codeql,
|
||||
// A value other than `undefined` means we are on macOS.
|
||||
"some-macos-version",
|
||||
),
|
||||
);
|
||||
}));
|
||||
|
||||
const nonDarwinPlatforms: NodeJS.Platform[] = ["linux", "win32"];
|
||||
for (const nonDarwinPlatform of nonDarwinPlatforms) {
|
||||
test(`isSwiftCompatible throws for Swift on ${nonDarwinPlatform}`, async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const codeql = await getCodeQLForTesting(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
await t.throwsAsync(
|
||||
isSwiftCompatible(
|
||||
initAllState({ platform: nonDarwinPlatform, env, logger }),
|
||||
createTestConfig({ languages: [BuiltInLanguage.swift] }),
|
||||
codeql,
|
||||
),
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
test("isSwiftCompatible warns if version string is not a semver", async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const codeql = await getCodeQLForTesting("codeql-for-testing", logger, env);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
await isSwiftCompatible(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "unexpected",
|
||||
env,
|
||||
}),
|
||||
createTestConfig({ languages: [BuiltInLanguage.swift] }),
|
||||
codeql,
|
||||
);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Unable to determine version of macOS, got: unexpected",
|
||||
]);
|
||||
}));
|
||||
|
||||
// `addDiagnostic` changes global state and we must stub it, so this test must be serial.
|
||||
test.serial(
|
||||
"isSwiftCompatible logs and adds diagnostic if macOS version is unsupported",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const codeql = await getCodeQLForTesting(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic");
|
||||
|
||||
const config = createTestConfig({ languages: [BuiltInLanguage.swift] });
|
||||
await isSwiftCompatible(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "27.0.0",
|
||||
env,
|
||||
}),
|
||||
config,
|
||||
codeql,
|
||||
);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Traced Swift analysis is not supported on macOS 27",
|
||||
]);
|
||||
|
||||
t.is(addDiagnostic.callCount, 1);
|
||||
t.like(addDiagnostic.args[0], [
|
||||
config,
|
||||
BuiltInLanguage.swift,
|
||||
{
|
||||
attributes: {
|
||||
languages: [BuiltInLanguage.swift],
|
||||
macOSVersion: "27.0.0",
|
||||
},
|
||||
severity: "warning",
|
||||
source: {
|
||||
id: "codeql-action/unsupported-traced-swift-analysis-macos",
|
||||
name: "Traced Swift analysis is not supported on this version of macOS",
|
||||
},
|
||||
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(
|
||||
"isSwiftCompatible throws if macOS version is unsupported and FF is enabled",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const features = createFeatures([Feature.SkipUnsupportedTracedAnalysis]);
|
||||
|
||||
const codeql = await getCodeQLForTesting(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic");
|
||||
|
||||
const config = createTestConfig({ languages: [BuiltInLanguage.swift] });
|
||||
await t.throwsAsync(
|
||||
isSwiftCompatible(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "27.0.0",
|
||||
env,
|
||||
features,
|
||||
}),
|
||||
config,
|
||||
codeql,
|
||||
),
|
||||
);
|
||||
|
||||
t.is(addDiagnostic.callCount, 1);
|
||||
t.like(addDiagnostic.args[0], [
|
||||
config,
|
||||
BuiltInLanguage.swift,
|
||||
{
|
||||
attributes: {
|
||||
languages: [BuiltInLanguage.swift],
|
||||
macOSVersion: "27.0.0",
|
||||
},
|
||||
severity: "error",
|
||||
source: {
|
||||
id: "codeql-action/unsupported-traced-swift-analysis-macos",
|
||||
name: "Traced Swift analysis is not supported on this version of macOS",
|
||||
},
|
||||
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(
|
||||
"isSwiftCompatible doesn't add a diagnostic if Xcode version is supported",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const statSync = sinon
|
||||
.stub(fs, "statSync")
|
||||
.returns({ isSymbolicLink: () => true } as fs.Stats);
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_26.0.app/Contents/Developer");
|
||||
|
||||
const codeql = await getCodeQLForTesting(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic");
|
||||
|
||||
const config = createTestConfig({ languages: [BuiltInLanguage.swift] });
|
||||
await isSwiftCompatible(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "26.0.0",
|
||||
env,
|
||||
}),
|
||||
config,
|
||||
codeql,
|
||||
);
|
||||
|
||||
t.is(statSync.callCount, 1);
|
||||
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
checkUnexpectedLogMessages(t, logger.messages, [
|
||||
"Traced Swift 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(
|
||||
"isSwiftCompatible logs and adds diagnostic if Xcode version is unsupported",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const statSync = sinon
|
||||
.stub(fs, "statSync")
|
||||
.returns({ isSymbolicLink: () => true } as fs.Stats);
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_27.0.app/Contents/Developer");
|
||||
|
||||
const codeql = await getCodeQLForTesting(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic");
|
||||
|
||||
const config = createTestConfig({ languages: [BuiltInLanguage.swift] });
|
||||
await isSwiftCompatible(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "26.0.0",
|
||||
env,
|
||||
}),
|
||||
config,
|
||||
codeql,
|
||||
);
|
||||
|
||||
t.is(statSync.callCount, 1);
|
||||
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Traced Swift analysis is not supported on Xcode 27",
|
||||
]);
|
||||
|
||||
t.is(addDiagnostic.callCount, 1);
|
||||
t.like(addDiagnostic.args[0], [
|
||||
config,
|
||||
BuiltInLanguage.swift,
|
||||
{
|
||||
attributes: {
|
||||
languages: [BuiltInLanguage.swift],
|
||||
xcodeVersion: "27.0.0",
|
||||
},
|
||||
severity: "warning",
|
||||
source: {
|
||||
id: "codeql-action/unsupported-traced-swift-analysis-xcode",
|
||||
name: "Traced Swift analysis is not supported on this version of Xcode",
|
||||
},
|
||||
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(
|
||||
"isSwiftCompatible throws if Xcode version is unsupported and FF is enabled",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const statSync = sinon
|
||||
.stub(fs, "statSync")
|
||||
.returns({ isSymbolicLink: () => true } as fs.Stats);
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_27.0.app/Contents/Developer");
|
||||
|
||||
const features = createFeatures([Feature.SkipUnsupportedTracedAnalysis]);
|
||||
|
||||
const codeql = await getCodeQLForTesting(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic");
|
||||
|
||||
const config = createTestConfig({ languages: [BuiltInLanguage.swift] });
|
||||
await t.throwsAsync(
|
||||
isSwiftCompatible(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "26.0.0",
|
||||
env,
|
||||
features,
|
||||
}),
|
||||
config,
|
||||
codeql,
|
||||
),
|
||||
);
|
||||
|
||||
t.is(statSync.callCount, 1);
|
||||
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
t.is(addDiagnostic.callCount, 1);
|
||||
t.like(addDiagnostic.args[0], [
|
||||
config,
|
||||
BuiltInLanguage.swift,
|
||||
{
|
||||
attributes: {
|
||||
languages: [BuiltInLanguage.swift],
|
||||
xcodeVersion: "27.0.0",
|
||||
},
|
||||
severity: "error",
|
||||
source: {
|
||||
id: "codeql-action/unsupported-traced-swift-analysis-xcode",
|
||||
name: "Traced Swift analysis is not supported on this version of Xcode",
|
||||
},
|
||||
visibility: {
|
||||
cliSummaryTable: true,
|
||||
statusPage: true,
|
||||
telemetry: true,
|
||||
},
|
||||
} satisfies Partial<diagnostics.DiagnosticMessage>,
|
||||
]);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,95 +1,25 @@
|
||||
import * as semver from "semver";
|
||||
import type { SemVer } from "semver";
|
||||
|
||||
import { ActionState, Logger } from "../action-common";
|
||||
import { CodeQL } from "../codeql";
|
||||
import { Config } from "../config-utils";
|
||||
import { addDiagnostic, makeDiagnostic } from "../diagnostics";
|
||||
import { Feature } from "../feature-flags";
|
||||
import { macOSVersion, xcodeVersion } from "../platform";
|
||||
import type { ActionConfig } from "../config/action-config";
|
||||
import { ToolsFeature } from "../tools-features";
|
||||
import { ConfigurationError } from "../util";
|
||||
|
||||
import { BuiltInLanguage } from ".";
|
||||
|
||||
/** macOS 27 and above do not support traced extraction for Swift. */
|
||||
export const SWIFT_TRACED_UNSUPPORTED_MACOS = 27;
|
||||
|
||||
/** Xcode 27 and above do not support traced extraction for Swift. */
|
||||
export const SWIFT_TRACED_UNSUPPORTED_XCODE = 27;
|
||||
|
||||
/**
|
||||
* Creates a diagnostic indicating that `version` of `product` is unsupported for traced Swift analysis.
|
||||
* Depending on `skipUnsupportedTracedAnalysis`, this function then either throws a {@link ConfigurationError}
|
||||
* or logs the problem as a warning.
|
||||
*
|
||||
* @param logger The logger to use.
|
||||
* @param config The CodeQL Action configuration.
|
||||
* @param skipUnsupportedTracedAnalysis Whether this is a fatal error.
|
||||
* @param product The product that the version is unsupported of.
|
||||
* @param version The unsupported version.
|
||||
*/
|
||||
function handleUnsupportedVersion(
|
||||
logger: Logger,
|
||||
config: Config,
|
||||
skipUnsupportedTracedAnalysis: boolean,
|
||||
product: "macOS" | "Xcode",
|
||||
version: semver.SemVer,
|
||||
) {
|
||||
const baseMessage = [
|
||||
`Traced Swift analysis is not supported on ${product} ${SWIFT_TRACED_UNSUPPORTED_MACOS} or above.`,
|
||||
`Configure your analysis to run on macOS ${SWIFT_TRACED_UNSUPPORTED_MACOS - 1} or below`,
|
||||
`and XCode ${SWIFT_TRACED_UNSUPPORTED_XCODE - 1} or below.`,
|
||||
].join(" ");
|
||||
|
||||
const attributeName = product === "macOS" ? "macOSVersion" : "xcodeVersion";
|
||||
|
||||
// Create a diagnostic that will show up on the TSP.
|
||||
addDiagnostic(
|
||||
config,
|
||||
BuiltInLanguage.swift,
|
||||
makeDiagnostic(
|
||||
`codeql-action/unsupported-traced-swift-analysis-${product.toLowerCase()}`,
|
||||
`Traced Swift analysis is not supported on this version of ${product}`,
|
||||
{
|
||||
attributes: {
|
||||
languages: config.languages,
|
||||
[attributeName]: version.toString(),
|
||||
},
|
||||
markdownMessage: baseMessage,
|
||||
severity: skipUnsupportedTracedAnalysis ? "error" : "warning",
|
||||
visibility: {
|
||||
cliSummaryTable: true,
|
||||
statusPage: true,
|
||||
telemetry: true,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Throw an error to abort the analysis if the FF is enabled or log the message.
|
||||
if (skipUnsupportedTracedAnalysis) {
|
||||
// ConfigurationErrors are converted to the "aborted" status by the exception handler
|
||||
// in `init-action.ts` that guards the call to `isSwiftCompatible`.
|
||||
throw new ConfigurationError(baseMessage);
|
||||
} else {
|
||||
// This will also show up as a workflow annotation.
|
||||
logger.warning(baseMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether we can run a Swift analysis on the current runner.
|
||||
*
|
||||
* @param action The Action state.
|
||||
* @param config The Action configuration.
|
||||
* @param codeql The {@link CodeQL} CLI instance.
|
||||
* @param macosVersion The version of macOS, if any.
|
||||
*
|
||||
* @throws {ConfigurationError} If Swift analysis is not possible on the current runner.
|
||||
* @returns True if we can run a Swift analysis.
|
||||
*/
|
||||
export async function isSwiftCompatible(
|
||||
action: ActionState<["Base", "Logger", "FeatureFlags"]>,
|
||||
config: Config,
|
||||
config: ActionConfig<"languages">,
|
||||
codeql: CodeQL,
|
||||
macosVersion: SemVer | string | undefined,
|
||||
) {
|
||||
// The checks are not relevant if we are not trying to analyse Swift.
|
||||
if (!config.languages.includes(BuiltInLanguage.swift)) {
|
||||
@@ -103,48 +33,10 @@ export async function isSwiftCompatible(
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to get the macOS version.
|
||||
const version = macOSVersion(action);
|
||||
|
||||
// If `version` is undefined, then we are not on macOS.
|
||||
if (version === undefined) {
|
||||
// If `macosVersion` is undefined, then we are not on macOS and Swift analysis is unsupported.
|
||||
if (macosVersion === undefined) {
|
||||
throw new ConfigurationError(
|
||||
`Swift analysis is only supported on macOS runner images. Please migrate to a macOS runner.`,
|
||||
);
|
||||
}
|
||||
|
||||
const skipUnsupportedTracedAnalysis = await action.features.getValue(
|
||||
Feature.SkipUnsupportedTracedAnalysis,
|
||||
);
|
||||
if (typeof version === "string") {
|
||||
// If we got a string, we are on macOS but couldn't parse the version string.
|
||||
action.logger.warning(
|
||||
`Unable to determine version of macOS, got: ${version}`,
|
||||
);
|
||||
} else if (version.major >= SWIFT_TRACED_UNSUPPORTED_MACOS) {
|
||||
handleUnsupportedVersion(
|
||||
action.logger,
|
||||
config,
|
||||
skipUnsupportedTracedAnalysis,
|
||||
"macOS",
|
||||
version,
|
||||
);
|
||||
}
|
||||
|
||||
// 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 >= SWIFT_TRACED_UNSUPPORTED_XCODE
|
||||
) {
|
||||
handleUnsupportedVersion(
|
||||
action.logger,
|
||||
config,
|
||||
skipUnsupportedTracedAnalysis,
|
||||
"Xcode",
|
||||
xcodeVer,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
386
src/languages/traced.test.ts
Normal file
386
src/languages/traced.test.ts
Normal file
@@ -0,0 +1,386 @@
|
||||
import fs from "fs";
|
||||
|
||||
import test from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import { getCodeQLForTesting } from "../codeql";
|
||||
import * as diagnostics from "../diagnostics";
|
||||
import { ActionsEnvVars } from "../environment";
|
||||
import { Feature } from "../feature-flags";
|
||||
import { XCODE_SELECT_LINK_PATH } from "../platform";
|
||||
import {
|
||||
checkExpectedLogMessages,
|
||||
checkUnexpectedLogMessages,
|
||||
createFeatures,
|
||||
createTestConfig,
|
||||
getTestEnv,
|
||||
initAllState,
|
||||
makeVersionInfo,
|
||||
RecordingLogger,
|
||||
setupTests,
|
||||
} from "../testing-utils";
|
||||
import { withTmpDir } from "../util";
|
||||
|
||||
import { checkRunnerSupportsTracedAnalysis } from "./traced";
|
||||
|
||||
import { BuiltInLanguage } from ".";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
const nonDarwinPlatforms: NodeJS.Platform[] = ["linux", "win32"];
|
||||
for (const nonDarwinPlatform of nonDarwinPlatforms) {
|
||||
test(`checkRunnerSupportsTracedAnalysis throws for Swift on ${nonDarwinPlatform}`, async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const codeql = await getCodeQLForTesting(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
await t.throwsAsync(
|
||||
checkRunnerSupportsTracedAnalysis(
|
||||
initAllState({ platform: nonDarwinPlatform, env, logger }),
|
||||
createTestConfig({ languages: [BuiltInLanguage.swift] }),
|
||||
codeql,
|
||||
),
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
test("checkRunnerSupportsTracedAnalysis warns if version string is not a semver", async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const codeql = await getCodeQLForTesting("codeql-for-testing", logger, env);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
await checkRunnerSupportsTracedAnalysis(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "unexpected",
|
||||
env,
|
||||
}),
|
||||
createTestConfig({ languages: [BuiltInLanguage.go] }),
|
||||
codeql,
|
||||
);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Unable to determine version of macOS, got: unexpected",
|
||||
]);
|
||||
}));
|
||||
|
||||
// `addDiagnostic` changes global state and we must stub it, so this test must be serial.
|
||||
test.serial(
|
||||
"checkRunnerSupportsTracedAnalysis logs and adds diagnostic if macOS version is unsupported",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const codeql = await getCodeQLForTesting(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic");
|
||||
|
||||
const config = createTestConfig({ languages: [BuiltInLanguage.swift] });
|
||||
await checkRunnerSupportsTracedAnalysis(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "27.0.0",
|
||||
env,
|
||||
}),
|
||||
config,
|
||||
codeql,
|
||||
);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Traced analysis is not supported on macOS 27",
|
||||
]);
|
||||
|
||||
t.is(addDiagnostic.callCount, 1);
|
||||
t.like(addDiagnostic.args[0], [
|
||||
config,
|
||||
BuiltInLanguage.swift,
|
||||
{
|
||||
attributes: {
|
||||
languages: [BuiltInLanguage.swift],
|
||||
macOSVersion: "27.0.0",
|
||||
},
|
||||
severity: "warning",
|
||||
source: {
|
||||
id: "codeql-action/unsupported-traced-analysis-macos",
|
||||
name: "Traced analysis is not supported on this version of macOS",
|
||||
},
|
||||
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 macOS version is unsupported and FF is enabled",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const features = createFeatures([Feature.SkipUnsupportedTracedAnalysis]);
|
||||
|
||||
const codeql = await getCodeQLForTesting(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic");
|
||||
|
||||
const config = createTestConfig({ languages: [BuiltInLanguage.swift] });
|
||||
await t.throwsAsync(
|
||||
checkRunnerSupportsTracedAnalysis(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "27.0.0",
|
||||
env,
|
||||
features,
|
||||
}),
|
||||
config,
|
||||
codeql,
|
||||
),
|
||||
);
|
||||
|
||||
t.is(addDiagnostic.callCount, 1);
|
||||
t.like(addDiagnostic.args[0], [
|
||||
config,
|
||||
BuiltInLanguage.swift,
|
||||
{
|
||||
attributes: {
|
||||
languages: [BuiltInLanguage.swift],
|
||||
macOSVersion: "27.0.0",
|
||||
},
|
||||
severity: "error",
|
||||
source: {
|
||||
id: "codeql-action/unsupported-traced-analysis-macos",
|
||||
name: "Traced analysis is not supported on this version of macOS",
|
||||
},
|
||||
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 doesn't add a diagnostic if Xcode version is supported",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const statSync = sinon
|
||||
.stub(fs, "statSync")
|
||||
.returns({ isSymbolicLink: () => true } as fs.Stats);
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_26.0.app/Contents/Developer");
|
||||
|
||||
const codeql = await getCodeQLForTesting(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic");
|
||||
|
||||
const config = createTestConfig({ languages: [BuiltInLanguage.swift] });
|
||||
await checkRunnerSupportsTracedAnalysis(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "26.0.0",
|
||||
env,
|
||||
}),
|
||||
config,
|
||||
codeql,
|
||||
);
|
||||
|
||||
t.is(statSync.callCount, 1);
|
||||
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
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",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const statSync = sinon
|
||||
.stub(fs, "statSync")
|
||||
.returns({ isSymbolicLink: () => true } as fs.Stats);
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_27.0.app/Contents/Developer");
|
||||
|
||||
const codeql = await getCodeQLForTesting(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic");
|
||||
|
||||
const config = createTestConfig({ languages: [BuiltInLanguage.swift] });
|
||||
await checkRunnerSupportsTracedAnalysis(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "26.0.0",
|
||||
env,
|
||||
}),
|
||||
config,
|
||||
codeql,
|
||||
);
|
||||
|
||||
t.is(statSync.callCount, 1);
|
||||
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Traced analysis is not supported on Xcode 27",
|
||||
]);
|
||||
|
||||
t.is(addDiagnostic.callCount, 1);
|
||||
t.like(addDiagnostic.args[0], [
|
||||
config,
|
||||
BuiltInLanguage.swift,
|
||||
{
|
||||
attributes: {
|
||||
languages: [BuiltInLanguage.swift],
|
||||
xcodeVersion: "27.0.0",
|
||||
},
|
||||
severity: "warning",
|
||||
source: {
|
||||
id: "codeql-action/unsupported-traced-analysis-xcode",
|
||||
name: "Traced analysis is not supported on this version of Xcode",
|
||||
},
|
||||
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",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
const env = getTestEnv();
|
||||
env.set(ActionsEnvVars.RUNNER_TEMP, tmpDir);
|
||||
|
||||
const statSync = sinon
|
||||
.stub(fs, "statSync")
|
||||
.returns({ isSymbolicLink: () => true } as fs.Stats);
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_27.0.app/Contents/Developer");
|
||||
|
||||
const features = createFeatures([Feature.SkipUnsupportedTracedAnalysis]);
|
||||
|
||||
const codeql = await getCodeQLForTesting(
|
||||
"codeql-for-testing",
|
||||
logger,
|
||||
env,
|
||||
);
|
||||
sinon.stub(codeql, "getVersion").resolves(makeVersionInfo("2.27.0"));
|
||||
|
||||
const addDiagnostic = sinon.stub(diagnostics, "addDiagnostic");
|
||||
|
||||
const config = createTestConfig({ languages: [BuiltInLanguage.swift] });
|
||||
await t.throwsAsync(
|
||||
checkRunnerSupportsTracedAnalysis(
|
||||
initAllState({
|
||||
logger,
|
||||
platform: "darwin",
|
||||
osRelease: "26.0.0",
|
||||
env,
|
||||
features,
|
||||
}),
|
||||
config,
|
||||
codeql,
|
||||
),
|
||||
);
|
||||
|
||||
t.is(statSync.callCount, 1);
|
||||
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
t.is(addDiagnostic.callCount, 1);
|
||||
t.like(addDiagnostic.args[0], [
|
||||
config,
|
||||
BuiltInLanguage.swift,
|
||||
{
|
||||
attributes: {
|
||||
languages: [BuiltInLanguage.swift],
|
||||
xcodeVersion: "27.0.0",
|
||||
},
|
||||
severity: "error",
|
||||
source: {
|
||||
id: "codeql-action/unsupported-traced-analysis-xcode",
|
||||
name: "Traced analysis is not supported on this version of Xcode",
|
||||
},
|
||||
visibility: {
|
||||
cliSummaryTable: true,
|
||||
statusPage: true,
|
||||
telemetry: true,
|
||||
},
|
||||
} satisfies Partial<diagnostics.DiagnosticMessage>,
|
||||
]);
|
||||
}),
|
||||
);
|
||||
134
src/languages/traced.ts
Normal file
134
src/languages/traced.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import * as semver from "semver";
|
||||
|
||||
import type { ActionState, Logger } from "../action-common";
|
||||
import type { CodeQL } from "../codeql";
|
||||
import type { ActionConfig } from "../config/action-config";
|
||||
import { addDiagnostic, makeDiagnostic } from "../diagnostics";
|
||||
import { Feature } from "../feature-flags";
|
||||
import { macOSVersion, xcodeVersion } from "../platform";
|
||||
import { ConfigurationError } from "../util";
|
||||
|
||||
import { isSwiftCompatible } from "./swift";
|
||||
|
||||
/** macOS 27 and above do not support traced extraction. */
|
||||
export const UNSUPPORTED_MACOS_FOR_TRACED = 27;
|
||||
|
||||
/** Xcode 27 and above do not support traced extraction. */
|
||||
export const UNSUPPORTED_XCODE_FOR_TRACED = 27;
|
||||
|
||||
/**
|
||||
* Creates a diagnostic indicating that `version` of `product` is unsupported for traced analysis.
|
||||
* Depending on `skipUnsupportedTracedAnalysis`, this function then either throws a {@link ConfigurationError}
|
||||
* or logs the problem as a warning.
|
||||
*
|
||||
* @param logger The logger to use.
|
||||
* @param config The CodeQL Action configuration.
|
||||
* @param skipUnsupportedTracedAnalysis Whether this is a fatal error.
|
||||
* @param product The product that the version is unsupported of.
|
||||
* @param version The unsupported version.
|
||||
*/
|
||||
function handleUnsupportedVersion(
|
||||
logger: Logger,
|
||||
config: ActionConfig<"languages" | "dbLocation" | "tempDir">,
|
||||
skipUnsupportedTracedAnalysis: boolean,
|
||||
product: "macOS" | "Xcode",
|
||||
version: semver.SemVer,
|
||||
) {
|
||||
const baseMessage = [
|
||||
`Traced analysis is not supported on ${product} ${UNSUPPORTED_MACOS_FOR_TRACED} or above.`,
|
||||
`Configure your analysis to run on macOS ${UNSUPPORTED_MACOS_FOR_TRACED - 1} or below`,
|
||||
`and XCode ${UNSUPPORTED_XCODE_FOR_TRACED - 1} or below.`,
|
||||
].join(" ");
|
||||
|
||||
const attributeName = product === "macOS" ? "macOSVersion" : "xcodeVersion";
|
||||
|
||||
// Create a diagnostic that will show up on the TSP.
|
||||
for (const language of config.languages) {
|
||||
addDiagnostic(
|
||||
config,
|
||||
language,
|
||||
makeDiagnostic(
|
||||
`codeql-action/unsupported-traced-analysis-${product.toLowerCase()}`,
|
||||
`Traced analysis is not supported on this version of ${product}`,
|
||||
{
|
||||
attributes: {
|
||||
languages: config.languages,
|
||||
[attributeName]: version.toString(),
|
||||
},
|
||||
markdownMessage: baseMessage,
|
||||
severity: skipUnsupportedTracedAnalysis ? "error" : "warning",
|
||||
visibility: {
|
||||
cliSummaryTable: true,
|
||||
statusPage: true,
|
||||
telemetry: true,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Throw an error to abort the analysis if the FF is enabled or log the message.
|
||||
if (skipUnsupportedTracedAnalysis) {
|
||||
// ConfigurationErrors are converted to the "aborted" status by the exception handler
|
||||
// in `init-action.ts` that guards the call to `checkRunnerSupportsTracedAnalysis`.
|
||||
throw new ConfigurationError(baseMessage);
|
||||
} else {
|
||||
// This will also show up as a workflow annotation.
|
||||
logger.warning(baseMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether we can run a traced analysis on the current runner.
|
||||
*
|
||||
* @throws {ConfigurationError} If traced analysis is not possible on the current runner.
|
||||
*/
|
||||
export async function checkRunnerSupportsTracedAnalysis(
|
||||
action: ActionState<["Base", "Logger", "FeatureFlags"]>,
|
||||
config: ActionConfig<"languages" | "dbLocation" | "tempDir">,
|
||||
codeql: CodeQL,
|
||||
) {
|
||||
// Try to get the macOS version.
|
||||
const version = macOSVersion(action);
|
||||
|
||||
// Perform some initial, Swift-specific checks.
|
||||
await isSwiftCompatible(config, codeql, version);
|
||||
|
||||
const skipUnsupportedTracedAnalysis = await action.features.getValue(
|
||||
Feature.SkipUnsupportedTracedAnalysis,
|
||||
);
|
||||
if (typeof version === "string") {
|
||||
// If we got a string, we are on macOS but couldn't parse the version string.
|
||||
action.logger.warning(
|
||||
`Unable to determine version of macOS, got: ${version}`,
|
||||
);
|
||||
} else if (
|
||||
version !== undefined &&
|
||||
version.major >= UNSUPPORTED_MACOS_FOR_TRACED
|
||||
) {
|
||||
handleUnsupportedVersion(
|
||||
action.logger,
|
||||
config,
|
||||
skipUnsupportedTracedAnalysis,
|
||||
"macOS",
|
||||
version,
|
||||
);
|
||||
}
|
||||
|
||||
// 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
|
||||
) {
|
||||
handleUnsupportedVersion(
|
||||
action.logger,
|
||||
config,
|
||||
skipUnsupportedTracedAnalysis,
|
||||
"Xcode",
|
||||
xcodeVer,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user