mirror of
https://github.com/github/codeql-action.git
synced 2026-11-05 04:29:40 +00:00
Add xcodeVersionFromXcodeBuild
This commit is contained in:
60
lib/entry-points.js
generated
60
lib/entry-points.js
generated
@@ -183478,6 +183478,64 @@ function xcodeVersionFromLink(logger2) {
|
||||
return void 0;
|
||||
}
|
||||
}
|
||||
async function xcodeVersionFromXcodeBuild(logger2) {
|
||||
try {
|
||||
const xcrun = await io4.which("xcrun", true);
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
const exitCode = await new toolrunner3.ToolRunner(
|
||||
xcrun,
|
||||
["xcodebuild", "-version"],
|
||||
{
|
||||
silent: true,
|
||||
ignoreReturnCode: true,
|
||||
listeners: {
|
||||
stdout: (data) => {
|
||||
stdout += data.toString("utf-8");
|
||||
},
|
||||
stderr: (data) => {
|
||||
stderr += data.toString("utf-8");
|
||||
}
|
||||
}
|
||||
}
|
||||
).exec();
|
||||
if (exitCode !== 0) {
|
||||
logger2.debug(`xcodebuild returned exit code ${exitCode}`);
|
||||
logger2.debug(`stdout:
|
||||
${stdout}`);
|
||||
logger2.debug(`stderr:
|
||||
${stderr}`);
|
||||
return void 0;
|
||||
}
|
||||
const match2 = stdout.match(/^Xcode (\d+(\.\d+)?)/);
|
||||
if (match2 === null) {
|
||||
logger2.debug(
|
||||
`Output doesn't appear to contain the Xcode product version.`
|
||||
);
|
||||
logger2.debug(`stdout:
|
||||
${stdout}`);
|
||||
logger2.debug(`stderr:
|
||||
${stderr}`);
|
||||
return void 0;
|
||||
}
|
||||
const version = semver6.coerce(match2[1].trim());
|
||||
if (version === null) {
|
||||
logger2.debug(
|
||||
`xcodebuild's output couldn't be parsed as a version:
|
||||
${stdout}`
|
||||
);
|
||||
logger2.debug(`stderr:
|
||||
${stderr}`);
|
||||
return void 0;
|
||||
}
|
||||
return version;
|
||||
} catch (err) {
|
||||
logger2.debug(
|
||||
`Unable to determine Xcode version from xcodebuild: ${getErrorMessage(err)}`
|
||||
);
|
||||
return void 0;
|
||||
}
|
||||
}
|
||||
async function xcodeVersionFromSdk(logger2) {
|
||||
try {
|
||||
const xcrun = await io4.which("xcrun", true);
|
||||
@@ -194063,7 +194121,7 @@ async function checkRunnerSupportsTracedAnalysis(action, config, codeql) {
|
||||
);
|
||||
return;
|
||||
}
|
||||
const xcodeVer = xcodeVersionFromLink(action.logger) ?? await xcodeVersionFromSdk(action.logger);
|
||||
const xcodeVer = xcodeVersionFromLink(action.logger) ?? await xcodeVersionFromXcodeBuild(action.logger) ?? await xcodeVersionFromSdk(action.logger);
|
||||
if (xcodeVer !== void 0 && xcodeVer.major >= UNSUPPORTED_XCODE_FOR_TRACED) {
|
||||
handleUnsupportedVersion(
|
||||
action.logger,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import fs from "fs";
|
||||
|
||||
import * as toolrunner from "@actions/exec/lib/toolrunner";
|
||||
import * as io from "@actions/io";
|
||||
import test from "ava";
|
||||
import * as sinon from "sinon";
|
||||
@@ -428,7 +429,7 @@ test.serial(
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"checkRunnerSupportsTracedAnalysis uses Xcode SDK version as fallback",
|
||||
"checkRunnerSupportsTracedAnalysis attempts fallbacks if needed",
|
||||
async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
@@ -439,7 +440,16 @@ test.serial(
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.throws(new Error("Not found"));
|
||||
const which = sinon.stub(io, "which").throws(new Error("Tool 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",
|
||||
@@ -468,11 +478,27 @@ test.serial(
|
||||
|
||||
t.is(readlinkSync.callCount, 1);
|
||||
t.deepEqual(readlinkSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
t.is(which.callCount, 1);
|
||||
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: Tool not found",
|
||||
"Unable to determine Xcode version: Not found",
|
||||
"Unable to determine Xcode version from xcodebuild: Error to force next fallback",
|
||||
"Unable to determine Xcode version: Error to force next fallback",
|
||||
]);
|
||||
checkUnexpectedLogMessages(t, logger.messages, [
|
||||
"Traced analysis is not supported on Xcode 27",
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
darwinVersion,
|
||||
xcodeVersionFromLink,
|
||||
xcodeVersionFromSdk,
|
||||
xcodeVersionFromXcodeBuild,
|
||||
} from "../platform";
|
||||
import { ConfigurationError } from "../util";
|
||||
|
||||
@@ -201,10 +202,12 @@ export async function checkRunnerSupportsTracedAnalysis(
|
||||
|
||||
// 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. If that doesn't work, we obtain the version from the SDK instead.
|
||||
// Since either Xcode or the SDK at version 27 or above is incompatible, the major version of either is fine.
|
||||
// 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)) ??
|
||||
(await xcodeVersionFromSdk(action.logger));
|
||||
|
||||
if (
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
XCODE_SELECT_LINK_PATH,
|
||||
xcodeVersionFromLink,
|
||||
xcodeVersionFromSdk,
|
||||
xcodeVersionFromXcodeBuild,
|
||||
} from "./platform";
|
||||
import {
|
||||
checkExpectedLogMessages,
|
||||
@@ -228,6 +229,74 @@ test.serial(
|
||||
},
|
||||
);
|
||||
|
||||
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(
|
||||
"xcodeVersionFromSdk returns undefined for non-zero exit code",
|
||||
async (t) => {
|
||||
|
||||
@@ -115,6 +115,76 @@ export function xcodeVersionFromLink(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Xcode that is installed by running `xcrun --sdk macosx --show-sdk-version`.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user