mirror of
https://github.com/github/codeql-action.git
synced 2026-10-21 02:01:27 +00:00
Add function to determine Xcode version based on symlink
This commit is contained in:
56
lib/entry-points.js
generated
56
lib/entry-points.js
generated
@@ -27288,7 +27288,7 @@ var require_coerce = __commonJS({
|
||||
var SemVer = require_semver();
|
||||
var parse4 = require_parse2();
|
||||
var { safeRe: re, t } = require_re();
|
||||
var coerce3 = (version, options) => {
|
||||
var coerce4 = (version, options) => {
|
||||
if (version instanceof SemVer) {
|
||||
return version;
|
||||
}
|
||||
@@ -27323,7 +27323,7 @@ var require_coerce = __commonJS({
|
||||
const build2 = options.includePrerelease && match2[6] ? `+${match2[6]}` : "";
|
||||
return parse4(`${major}.${minor}.${patch}${prerelease}${build2}`, options);
|
||||
};
|
||||
module2.exports = coerce3;
|
||||
module2.exports = coerce4;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -28409,7 +28409,7 @@ var require_semver2 = __commonJS({
|
||||
var gte7 = require_gte();
|
||||
var lte2 = require_lte();
|
||||
var cmp = require_cmp();
|
||||
var coerce3 = require_coerce();
|
||||
var coerce4 = require_coerce();
|
||||
var truncate = require_truncate();
|
||||
var Comparator = require_comparator();
|
||||
var Range2 = require_range();
|
||||
@@ -28448,7 +28448,7 @@ var require_semver2 = __commonJS({
|
||||
gte: gte7,
|
||||
lte: lte2,
|
||||
cmp,
|
||||
coerce: coerce3,
|
||||
coerce: coerce4,
|
||||
truncate,
|
||||
Comparator,
|
||||
Range: Range2,
|
||||
@@ -34547,8 +34547,8 @@ var require_semver3 = __commonJS({
|
||||
r2 = new Range2(r2, options);
|
||||
return r1.intersects(r2);
|
||||
}
|
||||
exports2.coerce = coerce3;
|
||||
function coerce3(version, options) {
|
||||
exports2.coerce = coerce4;
|
||||
function coerce4(version, options) {
|
||||
if (version instanceof SemVer) {
|
||||
return version;
|
||||
}
|
||||
@@ -34719,7 +34719,7 @@ var require_cacheUtils = __commonJS({
|
||||
var crypto3 = __importStar2(require("crypto"));
|
||||
var fs33 = __importStar2(require("fs"));
|
||||
var path30 = __importStar2(require("path"));
|
||||
var semver12 = __importStar2(require_semver3());
|
||||
var semver13 = __importStar2(require_semver3());
|
||||
var util4 = __importStar2(require("util"));
|
||||
var constants_1 = require_constants7();
|
||||
var versionSalt = "1.0";
|
||||
@@ -34812,7 +34812,7 @@ var require_cacheUtils = __commonJS({
|
||||
function getCompressionMethod() {
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
const versionOutput = yield getVersion("zstd", ["--quiet"]);
|
||||
const version = semver12.clean(versionOutput);
|
||||
const version = semver13.clean(versionOutput);
|
||||
core29.debug(`zstd version: ${version}`);
|
||||
if (versionOutput === "") {
|
||||
return constants_1.CompressionMethod.Gzip;
|
||||
@@ -37599,7 +37599,7 @@ var require_common = __commonJS({
|
||||
function setup(env) {
|
||||
createDebug.debug = createDebug;
|
||||
createDebug.default = createDebug;
|
||||
createDebug.coerce = coerce3;
|
||||
createDebug.coerce = coerce4;
|
||||
createDebug.disable = disable;
|
||||
createDebug.enable = enable;
|
||||
createDebug.enabled = enabled;
|
||||
@@ -37754,7 +37754,7 @@ var require_common = __commonJS({
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function coerce3(val) {
|
||||
function coerce4(val) {
|
||||
if (val instanceof Error) {
|
||||
return val.stack || val.message;
|
||||
}
|
||||
@@ -106361,7 +106361,7 @@ var require_manifest = __commonJS({
|
||||
exports2._findMatch = _findMatch;
|
||||
exports2._getOsVersion = _getOsVersion;
|
||||
exports2._readLinuxVersionFile = _readLinuxVersionFile;
|
||||
var semver12 = __importStar2(require_semver2());
|
||||
var semver13 = __importStar2(require_semver2());
|
||||
var core_1 = require_core();
|
||||
var os8 = require("os");
|
||||
var cp = require("child_process");
|
||||
@@ -106375,7 +106375,7 @@ var require_manifest = __commonJS({
|
||||
for (const candidate of candidates) {
|
||||
const version = candidate.version;
|
||||
(0, core_1.debug)(`check ${version} satisfies ${versionSpec}`);
|
||||
if (semver12.satisfies(version, versionSpec) && (!stable || candidate.stable === stable)) {
|
||||
if (semver13.satisfies(version, versionSpec) && (!stable || candidate.stable === stable)) {
|
||||
file = candidate.files.find((item) => {
|
||||
(0, core_1.debug)(`${item.arch}===${archFilter} && ${item.platform}===${platFilter}`);
|
||||
let chk = item.arch === archFilter && item.platform === platFilter;
|
||||
@@ -106384,7 +106384,7 @@ var require_manifest = __commonJS({
|
||||
if (osVersion === item.platform_version) {
|
||||
chk = true;
|
||||
} else {
|
||||
chk = semver12.satisfies(osVersion, item.platform_version);
|
||||
chk = semver13.satisfies(osVersion, item.platform_version);
|
||||
}
|
||||
}
|
||||
return chk;
|
||||
@@ -106644,7 +106644,7 @@ var require_tool_cache = __commonJS({
|
||||
var os8 = __importStar2(require("os"));
|
||||
var path30 = __importStar2(require("path"));
|
||||
var httpm = __importStar2(require_lib());
|
||||
var semver12 = __importStar2(require_semver2());
|
||||
var semver13 = __importStar2(require_semver2());
|
||||
var stream2 = __importStar2(require("stream"));
|
||||
var util4 = __importStar2(require("util"));
|
||||
var assert_1 = require("assert");
|
||||
@@ -106917,7 +106917,7 @@ var require_tool_cache = __commonJS({
|
||||
}
|
||||
function cacheDir2(sourceDir, tool, version, arch2) {
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
version = semver12.clean(version) || version;
|
||||
version = semver13.clean(version) || version;
|
||||
arch2 = arch2 || os8.arch();
|
||||
core29.debug(`Caching tool ${tool} ${version} ${arch2}`);
|
||||
core29.debug(`source dir: ${sourceDir}`);
|
||||
@@ -106935,7 +106935,7 @@ var require_tool_cache = __commonJS({
|
||||
}
|
||||
function cacheFile(sourceFile, targetFile, tool, version, arch2) {
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
version = semver12.clean(version) || version;
|
||||
version = semver13.clean(version) || version;
|
||||
arch2 = arch2 || os8.arch();
|
||||
core29.debug(`Caching tool ${tool} ${version} ${arch2}`);
|
||||
core29.debug(`source file: ${sourceFile}`);
|
||||
@@ -106965,7 +106965,7 @@ var require_tool_cache = __commonJS({
|
||||
}
|
||||
let toolPath = "";
|
||||
if (versionSpec) {
|
||||
versionSpec = semver12.clean(versionSpec) || "";
|
||||
versionSpec = semver13.clean(versionSpec) || "";
|
||||
const cachePath = path30.join(_getCacheDirectory(), toolName, versionSpec, arch2);
|
||||
core29.debug(`checking cache: ${cachePath}`);
|
||||
if (fs33.existsSync(cachePath) && fs33.existsSync(`${cachePath}.complete`)) {
|
||||
@@ -107045,7 +107045,7 @@ var require_tool_cache = __commonJS({
|
||||
}
|
||||
function _createToolPath(tool, version, arch2) {
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
const folderPath = path30.join(_getCacheDirectory(), tool, semver12.clean(version) || version, arch2 || "");
|
||||
const folderPath = path30.join(_getCacheDirectory(), tool, semver13.clean(version) || version, arch2 || "");
|
||||
core29.debug(`destination ${folderPath}`);
|
||||
const markerPath = `${folderPath}.complete`;
|
||||
yield io9.rmRF(folderPath);
|
||||
@@ -107055,15 +107055,15 @@ var require_tool_cache = __commonJS({
|
||||
});
|
||||
}
|
||||
function _completeToolPath(tool, version, arch2) {
|
||||
const folderPath = path30.join(_getCacheDirectory(), tool, semver12.clean(version) || version, arch2 || "");
|
||||
const folderPath = path30.join(_getCacheDirectory(), tool, semver13.clean(version) || version, arch2 || "");
|
||||
const markerPath = `${folderPath}.complete`;
|
||||
fs33.writeFileSync(markerPath, "");
|
||||
core29.debug("finished caching tool");
|
||||
}
|
||||
function isExplicitVersion(versionSpec) {
|
||||
const c = semver12.clean(versionSpec) || "";
|
||||
const c = semver13.clean(versionSpec) || "";
|
||||
core29.debug(`isExplicit: ${c}`);
|
||||
const valid4 = semver12.valid(c) != null;
|
||||
const valid4 = semver13.valid(c) != null;
|
||||
core29.debug(`explicit? ${valid4}`);
|
||||
return valid4;
|
||||
}
|
||||
@@ -107071,14 +107071,14 @@ var require_tool_cache = __commonJS({
|
||||
let version = "";
|
||||
core29.debug(`evaluating ${versions.length} versions`);
|
||||
versions = versions.sort((a, b) => {
|
||||
if (semver12.gt(a, b)) {
|
||||
if (semver13.gt(a, b)) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
});
|
||||
for (let i = versions.length - 1; i >= 0; i--) {
|
||||
const potential = versions[i];
|
||||
const satisfied = semver12.satisfies(potential, versionSpec);
|
||||
const satisfied = semver13.satisfies(potential, versionSpec);
|
||||
if (satisfied) {
|
||||
version = potential;
|
||||
break;
|
||||
@@ -175719,7 +175719,7 @@ function macOSVersion(action) {
|
||||
if (action.platform !== "darwin") {
|
||||
return void 0;
|
||||
}
|
||||
const version = semver7.parse(action.osRelease);
|
||||
const version = semver6.parse(action.osRelease);
|
||||
if (version === null) {
|
||||
return action.osRelease;
|
||||
}
|
||||
@@ -186138,7 +186138,7 @@ var fs30 = __toESM(require("fs"));
|
||||
var path25 = __toESM(require("path"));
|
||||
var core19 = __toESM(require_core());
|
||||
var io7 = __toESM(require_io());
|
||||
var semver11 = __toESM(require_semver2());
|
||||
var semver12 = __toESM(require_semver2());
|
||||
|
||||
// src/config/inputs.ts
|
||||
async function getToolsInput(action, repositoryProperties) {
|
||||
@@ -186175,6 +186175,10 @@ async function getToolsInput(action, repositoryProperties) {
|
||||
}
|
||||
|
||||
// src/languages/swift.ts
|
||||
var semver11 = __toESM(require_semver2());
|
||||
var XCODE_APP_FILENAME_PATTERN = new RegExp(
|
||||
/(?<filename>Xcode_(?<majorMinor>\d+.\d+).app)/
|
||||
);
|
||||
var SWIFT_TRACED_UNSUPPORTED_MACOS = 27;
|
||||
var SWIFT_TRACED_UNSUPPORTED_XCODE = 27;
|
||||
async function isSwiftCompatible(action, config, codeql) {
|
||||
@@ -186708,7 +186712,7 @@ async function run3(actionState) {
|
||||
) {
|
||||
const publicPreview = "2.22.1";
|
||||
const actualVer = (await codeql.getVersion()).version;
|
||||
if (semver11.lt(actualVer, publicPreview)) {
|
||||
if (semver12.lt(actualVer, publicPreview)) {
|
||||
actionState.env.export("CODEQL_ENABLE_EXPERIMENTAL_FEATURES" /* EXPERIMENTAL_FEATURES */, "true");
|
||||
logger2.info("Experimental Rust analysis enabled");
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
} from "./status-report";
|
||||
import { getEnv, getErrorMessage, wrapError } from "./util";
|
||||
|
||||
export type { Logger } from "./logging";
|
||||
|
||||
/** Base state that is available to an Action on startup. */
|
||||
export interface BaseState {
|
||||
/** The name of the Action. */
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import fs from "fs";
|
||||
|
||||
import test from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
@@ -18,12 +20,136 @@ import {
|
||||
import { ToolsFeature } from "../tools-features";
|
||||
import { withTmpDir } from "../util";
|
||||
|
||||
import { isSwiftCompatible } from "./swift";
|
||||
import {
|
||||
isSwiftCompatible,
|
||||
XCODE_SELECT_LINK_PATH,
|
||||
xcodeVersion,
|
||||
} from "./swift";
|
||||
|
||||
import { BuiltInLanguage } from ".";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test.serial("xcodeVersion returns undefined if symlink doesn't exist", (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const statSync = sinon.stub(fs, "statSync").throws(new Error("ENOENT"));
|
||||
|
||||
t.is(xcodeVersion(logger), undefined);
|
||||
t.is(statSync.callCount, 1);
|
||||
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"Unable to determine Xcode version: ENOENT",
|
||||
]);
|
||||
});
|
||||
|
||||
test.serial("xcodeVersion returns undefined if file is not a symlink", (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const statSync = sinon
|
||||
.stub(fs, "statSync")
|
||||
.returns({ isSymbolicLink: () => false } as fs.Stats);
|
||||
|
||||
t.is(xcodeVersion(logger), undefined);
|
||||
t.is(statSync.callCount, 1);
|
||||
t.deepEqual(statSync.args[0], [XCODE_SELECT_LINK_PATH]);
|
||||
|
||||
checkExpectedLogMessages(t, logger.messages, [
|
||||
"exists, but is not a symbolic link",
|
||||
]);
|
||||
});
|
||||
|
||||
test.serial(
|
||||
"xcodeVersion returns undefined if resolving the symlink returns nothing",
|
||||
(t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const statSync = sinon
|
||||
.stub(fs, "statSync")
|
||||
.returns({ isSymbolicLink: () => true } as fs.Stats);
|
||||
const readlinkSync = sinon.stub(fs, "readlinkSync").returns("");
|
||||
|
||||
t.is(xcodeVersion(logger), undefined);
|
||||
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, [
|
||||
"unexpectedly returned nothing",
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"xcodeVersion returns undefined if resolved path doesn't include pattern",
|
||||
(t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const statSync = sinon
|
||||
.stub(fs, "statSync")
|
||||
.returns({ isSymbolicLink: () => true } as fs.Stats);
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode.app/Contents/Developer");
|
||||
|
||||
t.is(xcodeVersion(logger), undefined);
|
||||
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, [
|
||||
"does not contain expected pattern",
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial("xcodeVersion returns undefined if match can't be parsed", (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const statSync = sinon
|
||||
.stub(fs, "statSync")
|
||||
.returns({ isSymbolicLink: () => true } as fs.Stats);
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_00.0.app/Contents/Developer");
|
||||
|
||||
t.is(xcodeVersion(logger), undefined);
|
||||
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, [
|
||||
"Couldn't parse '00.0' as a semantic version.",
|
||||
]);
|
||||
});
|
||||
|
||||
test.serial("xcodeVersion returns version from resolved path", (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
|
||||
const statSync = sinon
|
||||
.stub(fs, "statSync")
|
||||
.returns({ isSymbolicLink: () => true } as fs.Stats);
|
||||
const readlinkSync = sinon
|
||||
.stub(fs, "readlinkSync")
|
||||
.returns("/Applications/Xcode_16.4.app/Contents/Developer");
|
||||
|
||||
const result = xcodeVersion(logger);
|
||||
|
||||
if (t.truthy(result)) {
|
||||
t.is(result.major, 16);
|
||||
t.is(result.minor, 4);
|
||||
}
|
||||
|
||||
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]);
|
||||
});
|
||||
|
||||
test("isSwiftCompatible doesn't throw for non-Swift languages", async (t) => {
|
||||
for (const language of Object.values(BuiltInLanguage)) {
|
||||
if (language === BuiltInLanguage.swift) {
|
||||
|
||||
@@ -1,20 +1,88 @@
|
||||
import { ActionState } from "../action-common";
|
||||
import * as fs from "fs";
|
||||
|
||||
import * as 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 } from "../platform";
|
||||
import { ToolsFeature } from "../tools-features";
|
||||
import { ConfigurationError } from "../util";
|
||||
import { ConfigurationError, getErrorMessage } from "../util";
|
||||
|
||||
import { BuiltInLanguage } from ".";
|
||||
|
||||
/** The static path we check for a symbolic link to the (dynamic) Xcode location. */
|
||||
export const XCODE_SELECT_LINK_PATH = "/private/var/db/xcode_select_link";
|
||||
|
||||
/** The pattern we expect to find in the Xcode path. */
|
||||
export const XCODE_APP_FILENAME_PATTERN = new RegExp(
|
||||
/(?<filename>Xcode_(?<majorMinor>\d+.\d+).app)/,
|
||||
);
|
||||
|
||||
/** 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;
|
||||
|
||||
/**
|
||||
* Tries to determine the version of Xcode that is installed.
|
||||
*
|
||||
* @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 {
|
||||
try {
|
||||
// Stat the expected symbolic link to check that it exists and is a symbolic link.
|
||||
// The `readlinkSync` call below returns an empty string in either case and so
|
||||
// this check allows us to distinguish between the two cases.
|
||||
const stats = fs.statSync(XCODE_SELECT_LINK_PATH);
|
||||
|
||||
if (!stats.isSymbolicLink()) {
|
||||
logger.warning(
|
||||
`${XCODE_SELECT_LINK_PATH} exists, but is not a symbolic link.`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Read what the symbolic link points to.
|
||||
const xcodePath = fs.readlinkSync(XCODE_SELECT_LINK_PATH);
|
||||
|
||||
if (xcodePath === "") {
|
||||
logger.warning(
|
||||
`Resolving ${XCODE_SELECT_LINK_PATH} unexpectedly returned nothing.`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Try to extract the version from the path.
|
||||
const matchResult = xcodePath.match(XCODE_APP_FILENAME_PATTERN);
|
||||
|
||||
if (matchResult?.groups === undefined) {
|
||||
logger.warning(
|
||||
`Xcode path '${xcodePath}' does not contain expected pattern.`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const majorMinor = matchResult.groups["majorMinor"];
|
||||
const version = semver.coerce(majorMinor);
|
||||
|
||||
if (version === null) {
|
||||
logger.warning(`Couldn't parse '${majorMinor}' as a semantic version.`);
|
||||
return undefined;
|
||||
}
|
||||
return version;
|
||||
} catch (err) {
|
||||
logger.warning(
|
||||
`Unable to determine Xcode version: ${getErrorMessage(err)}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether we can run a Swift analysis on the current runner.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user