mirror of
https://github.com/github/codeql-action.git
synced 2026-11-05 04:29:40 +00:00
Merge remote-tracking branch 'origin/main' into mbg/refactor-util/errors
This commit is contained in:
7
.github/workflows/pr-checks.yml
vendored
7
.github/workflows/pr-checks.yml
vendored
@@ -126,10 +126,15 @@ jobs:
|
||||
if: ${{ !cancelled() && steps.install-deps.outcome == 'success' }}
|
||||
run: .github/workflows/script/verify-pr-checks.sh
|
||||
|
||||
- name: Typecheck pr-checks scripts
|
||||
if: ${{ !cancelled() && steps.install-deps.outcome == 'success' }}
|
||||
working-directory: pr-checks
|
||||
run: npm run typecheck
|
||||
|
||||
- name: Run pr-checks tests
|
||||
if: ${{ !cancelled() && steps.install-deps.outcome == 'success' }}
|
||||
working-directory: pr-checks
|
||||
run: npx tsx --test
|
||||
run: npm run test
|
||||
|
||||
- name: Run `pr-checks/changenotes.ts` to ensure that all unreleased change notes are valid
|
||||
if: ${{ !cancelled() && steps.install-deps.outcome == 'success' }}
|
||||
|
||||
291
lib/entry-points.js
generated
291
lib/entry-points.js
generated
@@ -28347,7 +28347,7 @@ var require_io = __commonJS({
|
||||
exports2.mv = mv;
|
||||
exports2.rmRF = rmRF;
|
||||
exports2.mkdirP = mkdirP;
|
||||
exports2.which = which9;
|
||||
exports2.which = which10;
|
||||
exports2.findInPath = findInPath;
|
||||
var assert_1 = require("assert");
|
||||
var path30 = __importStar2(require("path"));
|
||||
@@ -28423,13 +28423,13 @@ var require_io = __commonJS({
|
||||
yield ioUtil.mkdir(fsPath, { recursive: true });
|
||||
});
|
||||
}
|
||||
function which9(tool, check) {
|
||||
function which10(tool, check) {
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
if (!tool) {
|
||||
throw new Error("parameter 'tool' is required");
|
||||
}
|
||||
if (check) {
|
||||
const result = yield which9(tool, false);
|
||||
const result = yield which10(tool, false);
|
||||
if (!result) {
|
||||
if (ioUtil.IS_WINDOWS) {
|
||||
throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.`);
|
||||
@@ -28610,11 +28610,11 @@ var require_toolrunner = __commonJS({
|
||||
var events = __importStar2(require("events"));
|
||||
var child = __importStar2(require("child_process"));
|
||||
var path30 = __importStar2(require("path"));
|
||||
var io9 = __importStar2(require_io());
|
||||
var io10 = __importStar2(require_io());
|
||||
var ioUtil = __importStar2(require_io_util());
|
||||
var timers_1 = require("timers");
|
||||
var IS_WINDOWS = process.platform === "win32";
|
||||
var ToolRunner7 = class extends events.EventEmitter {
|
||||
var ToolRunner8 = class extends events.EventEmitter {
|
||||
constructor(toolPath, args, options) {
|
||||
super();
|
||||
if (!toolPath) {
|
||||
@@ -28826,7 +28826,7 @@ var require_toolrunner = __commonJS({
|
||||
if (!ioUtil.isRooted(this.toolPath) && (this.toolPath.includes("/") || IS_WINDOWS && this.toolPath.includes("\\"))) {
|
||||
this.toolPath = path30.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath);
|
||||
}
|
||||
this.toolPath = yield io9.which(this.toolPath, true);
|
||||
this.toolPath = yield io10.which(this.toolPath, true);
|
||||
return new Promise((resolve15, reject) => __awaiter2(this, void 0, void 0, function* () {
|
||||
this._debug(`exec tool: ${this.toolPath}`);
|
||||
this._debug("arguments:");
|
||||
@@ -28923,7 +28923,7 @@ var require_toolrunner = __commonJS({
|
||||
});
|
||||
}
|
||||
};
|
||||
exports2.ToolRunner = ToolRunner7;
|
||||
exports2.ToolRunner = ToolRunner8;
|
||||
function argStringToArray(argString) {
|
||||
const args = [];
|
||||
let inQuotes = false;
|
||||
@@ -42426,7 +42426,7 @@ var require_cacheUtils = __commonJS({
|
||||
var core29 = __importStar2(require_core());
|
||||
var exec3 = __importStar2(require_exec());
|
||||
var glob2 = __importStar2(require_glob());
|
||||
var io9 = __importStar2(require_io());
|
||||
var io10 = __importStar2(require_io());
|
||||
var crypto3 = __importStar2(require("crypto"));
|
||||
var fs34 = __importStar2(require("fs"));
|
||||
var path30 = __importStar2(require("path"));
|
||||
@@ -42452,7 +42452,7 @@ var require_cacheUtils = __commonJS({
|
||||
tempDirectory = path30.join(baseLocation, "actions", "temp");
|
||||
}
|
||||
const dest = path30.join(tempDirectory, crypto3.randomUUID());
|
||||
yield io9.mkdirP(dest);
|
||||
yield io10.mkdirP(dest);
|
||||
return dest;
|
||||
});
|
||||
}
|
||||
@@ -42541,7 +42541,7 @@ var require_cacheUtils = __commonJS({
|
||||
return constants_1.GnuTarPathOnWindows;
|
||||
}
|
||||
const versionOutput = yield getVersion("tar");
|
||||
return versionOutput.toLowerCase().includes("gnu tar") ? io9.which("tar") : "";
|
||||
return versionOutput.toLowerCase().includes("gnu tar") ? io10.which("tar") : "";
|
||||
});
|
||||
}
|
||||
function assertDefined(name, value) {
|
||||
@@ -113305,7 +113305,7 @@ var require_tar = __commonJS({
|
||||
exports2.createTar = createTar;
|
||||
var core29 = __importStar2(require_core());
|
||||
var exec_1 = require_exec();
|
||||
var io9 = __importStar2(require_io());
|
||||
var io10 = __importStar2(require_io());
|
||||
var fs_1 = require("fs");
|
||||
var path30 = __importStar2(require("path"));
|
||||
var utils = __importStar2(require_cacheUtils());
|
||||
@@ -113325,12 +113325,12 @@ var require_tar = __commonJS({
|
||||
break;
|
||||
}
|
||||
case "darwin": {
|
||||
const gnuTar = yield io9.which("gtar", false);
|
||||
const gnuTar = yield io10.which("gtar", false);
|
||||
if (gnuTar) {
|
||||
return { path: gnuTar, type: constants_1.ArchiveToolType.GNU };
|
||||
} else {
|
||||
return {
|
||||
path: yield io9.which("tar", true),
|
||||
path: yield io10.which("tar", true),
|
||||
type: constants_1.ArchiveToolType.BSD
|
||||
};
|
||||
}
|
||||
@@ -113339,7 +113339,7 @@ var require_tar = __commonJS({
|
||||
break;
|
||||
}
|
||||
return {
|
||||
path: yield io9.which("tar", true),
|
||||
path: yield io10.which("tar", true),
|
||||
type: constants_1.ArchiveToolType.GNU
|
||||
};
|
||||
});
|
||||
@@ -113468,7 +113468,7 @@ var require_tar = __commonJS({
|
||||
} finally {
|
||||
if (tempDirectory) {
|
||||
try {
|
||||
yield io9.rmRF(tempDirectory);
|
||||
yield io10.rmRF(tempDirectory);
|
||||
} catch (error3) {
|
||||
core29.debug(`Failed to delete temporary tar directory: ${error3}`);
|
||||
}
|
||||
@@ -113484,7 +113484,7 @@ var require_tar = __commonJS({
|
||||
function extractTar3(archivePath, compressionMethod) {
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
const workingDirectory = getWorkingDirectory();
|
||||
yield io9.mkdirP(workingDirectory);
|
||||
yield io10.mkdirP(workingDirectory);
|
||||
yield execReadCommands(archivePath, compressionMethod, "extract");
|
||||
});
|
||||
}
|
||||
@@ -114348,7 +114348,7 @@ var require_tool_cache = __commonJS({
|
||||
exports2.isExplicitVersion = isExplicitVersion;
|
||||
exports2.evaluateVersions = evaluateVersions;
|
||||
var core29 = __importStar2(require_core());
|
||||
var io9 = __importStar2(require_io());
|
||||
var io10 = __importStar2(require_io());
|
||||
var crypto3 = __importStar2(require("crypto"));
|
||||
var fs34 = __importStar2(require("fs"));
|
||||
var mm = __importStar2(require_manifest());
|
||||
@@ -114375,7 +114375,7 @@ var require_tool_cache = __commonJS({
|
||||
function downloadTool3(url2, dest, auth2, headers) {
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
dest = dest || path30.join(_getTempDirectory(), crypto3.randomUUID());
|
||||
yield io9.mkdirP(path30.dirname(dest));
|
||||
yield io10.mkdirP(path30.dirname(dest));
|
||||
core29.debug(`Downloading ${url2}`);
|
||||
core29.debug(`Destination ${dest}`);
|
||||
const maxAttempts = 3;
|
||||
@@ -114428,7 +114428,7 @@ var require_tool_cache = __commonJS({
|
||||
if (!succeeded) {
|
||||
core29.debug("download failed");
|
||||
try {
|
||||
yield io9.rmRF(dest);
|
||||
yield io10.rmRF(dest);
|
||||
} catch (err) {
|
||||
core29.debug(`Failed to delete '${dest}'. ${err.message}`);
|
||||
}
|
||||
@@ -114483,7 +114483,7 @@ var require_tool_cache = __commonJS({
|
||||
silent: true
|
||||
};
|
||||
try {
|
||||
const powershellPath = yield io9.which("powershell", true);
|
||||
const powershellPath = yield io10.which("powershell", true);
|
||||
yield (0, exec_1.exec)(`"${powershellPath}"`, args, options);
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
@@ -114550,7 +114550,7 @@ var require_tool_cache = __commonJS({
|
||||
if (core29.isDebug()) {
|
||||
args.push("-v");
|
||||
}
|
||||
const xarPath = yield io9.which("xar", true);
|
||||
const xarPath = yield io10.which("xar", true);
|
||||
yield (0, exec_1.exec)(`"${xarPath}"`, _unique(args));
|
||||
return dest;
|
||||
});
|
||||
@@ -114573,7 +114573,7 @@ var require_tool_cache = __commonJS({
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
const escapedFile = file.replace(/'/g, "''").replace(/"|\n|\r/g, "");
|
||||
const escapedDest = dest.replace(/'/g, "''").replace(/"|\n|\r/g, "");
|
||||
const pwshPath = yield io9.which("pwsh", false);
|
||||
const pwshPath = yield io10.which("pwsh", false);
|
||||
if (pwshPath) {
|
||||
const pwshCommand = [
|
||||
`$ErrorActionPreference = 'Stop' ;`,
|
||||
@@ -114609,7 +114609,7 @@ var require_tool_cache = __commonJS({
|
||||
"-Command",
|
||||
powershellCommand
|
||||
];
|
||||
const powershellPath = yield io9.which("powershell", true);
|
||||
const powershellPath = yield io10.which("powershell", true);
|
||||
core29.debug(`Using powershell at path: ${powershellPath}`);
|
||||
yield (0, exec_1.exec)(`"${powershellPath}"`, args);
|
||||
}
|
||||
@@ -114617,7 +114617,7 @@ var require_tool_cache = __commonJS({
|
||||
}
|
||||
function extractZipNix(file, dest) {
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
const unzipPath = yield io9.which("unzip", true);
|
||||
const unzipPath = yield io10.which("unzip", true);
|
||||
const args = [file];
|
||||
if (!core29.isDebug()) {
|
||||
args.unshift("-q");
|
||||
@@ -114638,7 +114638,7 @@ var require_tool_cache = __commonJS({
|
||||
const destPath = yield _createToolPath(tool, version, arch2);
|
||||
for (const itemName of fs34.readdirSync(sourceDir)) {
|
||||
const s = path30.join(sourceDir, itemName);
|
||||
yield io9.cp(s, destPath, { recursive: true });
|
||||
yield io10.cp(s, destPath, { recursive: true });
|
||||
}
|
||||
_completeToolPath(tool, version, arch2);
|
||||
return destPath;
|
||||
@@ -114656,7 +114656,7 @@ var require_tool_cache = __commonJS({
|
||||
const destFolder = yield _createToolPath(tool, version, arch2);
|
||||
const destPath = path30.join(destFolder, targetFile);
|
||||
core29.debug(`destination file ${destPath}`);
|
||||
yield io9.cp(sourceFile, destPath);
|
||||
yield io10.cp(sourceFile, destPath);
|
||||
_completeToolPath(tool, version, arch2);
|
||||
return destFolder;
|
||||
});
|
||||
@@ -114750,7 +114750,7 @@ var require_tool_cache = __commonJS({
|
||||
if (!dest) {
|
||||
dest = path30.join(_getTempDirectory(), crypto3.randomUUID());
|
||||
}
|
||||
yield io9.mkdirP(dest);
|
||||
yield io10.mkdirP(dest);
|
||||
return dest;
|
||||
});
|
||||
}
|
||||
@@ -114759,9 +114759,9 @@ var require_tool_cache = __commonJS({
|
||||
const folderPath = path30.join(_getCacheDirectory(), tool, semver13.clean(version) || version, arch2 || "");
|
||||
core29.debug(`destination ${folderPath}`);
|
||||
const markerPath = `${folderPath}.complete`;
|
||||
yield io9.rmRF(folderPath);
|
||||
yield io9.rmRF(markerPath);
|
||||
yield io9.mkdirP(folderPath);
|
||||
yield io10.rmRF(folderPath);
|
||||
yield io10.rmRF(markerPath);
|
||||
yield io10.mkdirP(folderPath);
|
||||
return folderPath;
|
||||
});
|
||||
}
|
||||
@@ -152391,13 +152391,13 @@ var require_io2 = __commonJS({
|
||||
});
|
||||
}
|
||||
exports2.mkdirP = mkdirP;
|
||||
function which9(tool, check) {
|
||||
function which10(tool, check) {
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
if (!tool) {
|
||||
throw new Error("parameter 'tool' is required");
|
||||
}
|
||||
if (check) {
|
||||
const result = yield which9(tool, false);
|
||||
const result = yield which10(tool, false);
|
||||
if (!result) {
|
||||
if (ioUtil.IS_WINDOWS) {
|
||||
throw new Error(`Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.`);
|
||||
@@ -152414,7 +152414,7 @@ var require_io2 = __commonJS({
|
||||
return "";
|
||||
});
|
||||
}
|
||||
exports2.which = which9;
|
||||
exports2.which = which10;
|
||||
function findInPath(tool) {
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
if (!tool) {
|
||||
@@ -152565,11 +152565,11 @@ var require_toolrunner2 = __commonJS({
|
||||
var events = __importStar2(require("events"));
|
||||
var child = __importStar2(require("child_process"));
|
||||
var path30 = __importStar2(require("path"));
|
||||
var io9 = __importStar2(require_io2());
|
||||
var io10 = __importStar2(require_io2());
|
||||
var ioUtil = __importStar2(require_io_util2());
|
||||
var timers_1 = require("timers");
|
||||
var IS_WINDOWS = process.platform === "win32";
|
||||
var ToolRunner7 = class extends events.EventEmitter {
|
||||
var ToolRunner8 = class extends events.EventEmitter {
|
||||
constructor(toolPath, args, options) {
|
||||
super();
|
||||
if (!toolPath) {
|
||||
@@ -152781,7 +152781,7 @@ var require_toolrunner2 = __commonJS({
|
||||
if (!ioUtil.isRooted(this.toolPath) && (this.toolPath.includes("/") || IS_WINDOWS && this.toolPath.includes("\\"))) {
|
||||
this.toolPath = path30.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath);
|
||||
}
|
||||
this.toolPath = yield io9.which(this.toolPath, true);
|
||||
this.toolPath = yield io10.which(this.toolPath, true);
|
||||
return new Promise((resolve15, reject) => __awaiter2(this, void 0, void 0, function* () {
|
||||
this._debug(`exec tool: ${this.toolPath}`);
|
||||
this._debug("arguments:");
|
||||
@@ -152878,7 +152878,7 @@ var require_toolrunner2 = __commonJS({
|
||||
});
|
||||
}
|
||||
};
|
||||
exports2.ToolRunner = ToolRunner7;
|
||||
exports2.ToolRunner = ToolRunner8;
|
||||
function argStringToArray(argString) {
|
||||
const args = [];
|
||||
let inQuotes = false;
|
||||
@@ -180459,13 +180459,13 @@ var SarifScanOrder = [
|
||||
var fs19 = __toESM(require("fs"));
|
||||
var path16 = __toESM(require("path"));
|
||||
var import_perf_hooks5 = require("perf_hooks");
|
||||
var io5 = __toESM(require_io());
|
||||
var io6 = __toESM(require_io());
|
||||
|
||||
// src/codeql.ts
|
||||
var fs18 = __toESM(require("fs"));
|
||||
var path15 = __toESM(require("path"));
|
||||
var core12 = __toESM(require_core());
|
||||
var toolrunner3 = __toESM(require_toolrunner());
|
||||
var toolrunner4 = __toESM(require_toolrunner());
|
||||
var semver11 = __toESM(require_semver2());
|
||||
|
||||
// src/cli-errors.ts
|
||||
@@ -183432,6 +183432,8 @@ var semver7 = __toESM(require_semver2());
|
||||
|
||||
// src/platform.ts
|
||||
var import_fs = __toESM(require("fs"));
|
||||
var toolrunner3 = __toESM(require_toolrunner());
|
||||
var io4 = __toESM(require_io());
|
||||
var semver6 = __toESM(require_semver2());
|
||||
function getBundlePlatform(platform2 = process.platform, arch2 = process.arch) {
|
||||
switch (platform2) {
|
||||
@@ -183459,7 +183461,7 @@ var XCODE_SELECT_LINK_PATH = "/private/var/db/xcode_select_link";
|
||||
var XCODE_APP_FILENAME_PATTERN = new RegExp(
|
||||
/(?<filename>Xcode_(?<majorMinor>\d+(\.\d+)?).*\.app)/
|
||||
);
|
||||
function xcodeVersion(logger2) {
|
||||
function xcodeVersionFromLink(logger2) {
|
||||
try {
|
||||
const xcodePath = import_fs.default.readlinkSync(XCODE_SELECT_LINK_PATH);
|
||||
if (xcodePath.trim() === "") {
|
||||
@@ -183487,6 +183489,111 @@ function xcodeVersion(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 xcodeSdkVersion(logger2) {
|
||||
try {
|
||||
const xcrun = await io4.which("xcrun", true);
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
const exitCode = await new toolrunner3.ToolRunner(
|
||||
xcrun,
|
||||
["--sdk", "macosx", "--show-sdk-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(`xcrun returned exit code ${exitCode}`);
|
||||
logger2.debug(`stdout:
|
||||
${stdout}`);
|
||||
logger2.debug(`stderr:
|
||||
${stderr}`);
|
||||
return void 0;
|
||||
}
|
||||
const version = semver6.coerce(stdout.trim());
|
||||
if (version === null) {
|
||||
logger2.debug(
|
||||
`xcrun'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 SDK version: ${getErrorMessage(err)}`
|
||||
);
|
||||
return void 0;
|
||||
}
|
||||
}
|
||||
|
||||
// src/per-language-bundles.ts
|
||||
var MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION = "2.27.1";
|
||||
@@ -183670,13 +183777,13 @@ var import_child_process = require("child_process");
|
||||
var fs14 = __toESM(require("fs"));
|
||||
var stream = __toESM(require("stream"));
|
||||
var import_toolrunner = __toESM(require_toolrunner());
|
||||
var io4 = __toESM(require_io());
|
||||
var io5 = __toESM(require_io());
|
||||
var toolcache = __toESM(require_tool_cache());
|
||||
var semver8 = __toESM(require_semver2());
|
||||
var MIN_REQUIRED_BSD_TAR_VERSION = "3.4.3";
|
||||
var MIN_REQUIRED_GNU_TAR_VERSION = "1.31";
|
||||
async function getTarVersion() {
|
||||
const tar = await io4.which("tar", true);
|
||||
const tar = await io5.which("tar", true);
|
||||
let stdout = "";
|
||||
const exitCode = await new import_toolrunner.ToolRunner(tar, ["--version"], {
|
||||
listeners: {
|
||||
@@ -183693,13 +183800,13 @@ async function getTarVersion() {
|
||||
if (!match2?.[1]) {
|
||||
throw new Error("Failed to parse output of tar --version.");
|
||||
}
|
||||
return { type: "gnu", version: match2[1] };
|
||||
return { type: "gnu", version: match2[1], path: tar };
|
||||
} else if (stdout.includes("bsdtar")) {
|
||||
const match2 = stdout.match(/bsdtar ([0-9.]+)/);
|
||||
if (!match2?.[1]) {
|
||||
throw new Error("Failed to parse output of tar --version.");
|
||||
}
|
||||
return { type: "bsd", version: match2[1] };
|
||||
return { type: "bsd", version: match2[1], path: tar };
|
||||
} else {
|
||||
throw new Error("Unknown tar version");
|
||||
}
|
||||
@@ -183766,10 +183873,10 @@ async function extractTarZst(tar, dest, tarVersion, logger2) {
|
||||
args.push("--overwrite");
|
||||
}
|
||||
args.push("-f", tar instanceof stream.Readable ? "-" : tar, "-C", dest);
|
||||
process.stdout.write(`[command]tar ${args.join(" ")}
|
||||
process.stdout.write(`[command]${tarVersion.path} ${args.join(" ")}
|
||||
`);
|
||||
await new Promise((resolve15, reject) => {
|
||||
const tarProcess = (0, import_child_process.spawn)("tar", args, { stdio: "pipe" });
|
||||
const tarProcess = (0, import_child_process.spawn)(tarVersion.path, args, { stdio: "pipe" });
|
||||
let stdout = "";
|
||||
tarProcess.stdout?.on("data", (data) => {
|
||||
stdout += data.toString();
|
||||
@@ -183796,11 +183903,11 @@ async function extractTarZst(tar, dest, tarVersion, logger2) {
|
||||
if (code !== 0) {
|
||||
reject(
|
||||
new CommandInvocationError(
|
||||
"tar",
|
||||
tarVersion.path,
|
||||
args,
|
||||
code ?? void 0,
|
||||
stdout,
|
||||
stderr
|
||||
stderr,
|
||||
stdout
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -185158,7 +185265,7 @@ async function getCodeQLForCmd(logger2, cmd, checkVersion, env = getEnv()) {
|
||||
])
|
||||
);
|
||||
}
|
||||
await new toolrunner3.ToolRunner(cmd, args, {
|
||||
await new toolrunner4.ToolRunner(cmd, args, {
|
||||
env: env.getDefined()
|
||||
}).exec();
|
||||
},
|
||||
@@ -185179,7 +185286,7 @@ async function getCodeQLForCmd(logger2, cmd, checkVersion, env = getEnv()) {
|
||||
if (automationDetailsId !== void 0) {
|
||||
args.push("--sarif-category", automationDetailsId);
|
||||
}
|
||||
await new toolrunner3.ToolRunner(cmd, args, {
|
||||
await new toolrunner4.ToolRunner(cmd, args, {
|
||||
env: env.getDefined()
|
||||
}).exec();
|
||||
},
|
||||
@@ -185208,13 +185315,13 @@ async function getCodeQLForCmd(logger2, cmd, checkVersion, env = getEnv()) {
|
||||
if (automationDetailsId !== void 0) {
|
||||
args.push("--sarif-category", automationDetailsId);
|
||||
}
|
||||
await new toolrunner3.ToolRunner(cmd, args, {
|
||||
await new toolrunner4.ToolRunner(cmd, args, {
|
||||
env: env.getDefined()
|
||||
}).exec();
|
||||
},
|
||||
async resolveExtractor(language) {
|
||||
let extractorPath = "";
|
||||
await new toolrunner3.ToolRunner(
|
||||
await new toolrunner4.ToolRunner(
|
||||
cmd,
|
||||
[
|
||||
"resolve",
|
||||
@@ -186157,7 +186264,7 @@ async function runFinalize(features, outputDir, threadsFlag, memoryFlag, codeql,
|
||||
async function warnIfGoInstalledAfterInit(config, logger2) {
|
||||
const goInitPath = process.env["CODEQL_ACTION_GO_BINARY" /* GO_BINARY_LOCATION */];
|
||||
if (process.env["CODEQL_ACTION_DID_AUTOBUILD_GOLANG" /* DID_AUTOBUILD_GOLANG */] !== "true" && goInitPath !== void 0) {
|
||||
const goBinaryPath = await io5.which("go", true);
|
||||
const goBinaryPath = await io6.which("go", true);
|
||||
if (goInitPath !== goBinaryPath) {
|
||||
logger2.warning(
|
||||
`Expected \`which go\` to return ${goInitPath}, but got ${goBinaryPath}: please ensure that the correct version of Go is installed before the \`codeql-action/init\` Action is used.`
|
||||
@@ -187506,9 +187613,9 @@ async function addFingerprints(sarifLog, sourceRoot, logger2) {
|
||||
// src/init.ts
|
||||
var fs22 = __toESM(require("fs"));
|
||||
var path18 = __toESM(require("path"));
|
||||
var toolrunner4 = __toESM(require_toolrunner());
|
||||
var toolrunner5 = __toESM(require_toolrunner());
|
||||
var github3 = __toESM(require_github());
|
||||
var io6 = __toESM(require_io());
|
||||
var io7 = __toESM(require_io());
|
||||
async function initCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, rawLanguages, otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, features, logger2) {
|
||||
logger2.startGroup("Setup CodeQL tools");
|
||||
const { codeql, toolsDownloadStatusReport, toolsSource, toolsVersion } = await setupCodeQL(
|
||||
@@ -187621,7 +187728,7 @@ async function checkInstallPython311(languages, codeql) {
|
||||
"../python-setup",
|
||||
"check_python12.ps1"
|
||||
);
|
||||
await new toolrunner4.ToolRunner(await io6.which("powershell", true), [
|
||||
await new toolrunner5.ToolRunner(await io7.which("powershell", true), [
|
||||
script
|
||||
]).exec();
|
||||
}
|
||||
@@ -190349,10 +190456,10 @@ var Minimatch = class {
|
||||
let ai = 0;
|
||||
let bi = 0;
|
||||
let result = [];
|
||||
let which9 = "";
|
||||
let which10 = "";
|
||||
while (ai < a.length && bi < b.length) {
|
||||
if (a[ai] === b[bi]) {
|
||||
result.push(which9 === "b" ? b[bi] : a[ai]);
|
||||
result.push(which10 === "b" ? b[bi] : a[ai]);
|
||||
ai++;
|
||||
bi++;
|
||||
} else if (emptyGSMatch && a[ai] === "**" && b[bi] === a[ai + 1]) {
|
||||
@@ -190362,16 +190469,16 @@ var Minimatch = class {
|
||||
result.push(b[bi]);
|
||||
bi++;
|
||||
} else if (a[ai] === "*" && b[bi] && (this.options.dot || !b[bi].startsWith(".")) && b[bi] !== "**") {
|
||||
if (which9 === "b")
|
||||
if (which10 === "b")
|
||||
return false;
|
||||
which9 = "a";
|
||||
which10 = "a";
|
||||
result.push(a[ai]);
|
||||
ai++;
|
||||
bi++;
|
||||
} else if (b[bi] === "*" && a[ai] && (this.options.dot || !a[ai].startsWith(".")) && a[ai] !== "**") {
|
||||
if (which9 === "a")
|
||||
if (which10 === "a")
|
||||
return false;
|
||||
which9 = "b";
|
||||
which10 = "b";
|
||||
result.push(b[bi]);
|
||||
ai++;
|
||||
bi++;
|
||||
@@ -193867,7 +193974,7 @@ async function runWrapper3() {
|
||||
var fs31 = __toESM(require("fs"));
|
||||
var path25 = __toESM(require("path"));
|
||||
var core19 = __toESM(require_core());
|
||||
var io7 = __toESM(require_io());
|
||||
var io8 = __toESM(require_io());
|
||||
var semver12 = __toESM(require_semver2());
|
||||
|
||||
// src/config/inputs.ts
|
||||
@@ -193922,20 +194029,35 @@ async function checkSwiftCompatibility(config, codeql, darwinVersion2) {
|
||||
// src/languages/traced.ts
|
||||
var UNSUPPORTED_DARWIN_FOR_TRACED = 27;
|
||||
var UNSUPPORTED_XCODE_FOR_TRACED = 27;
|
||||
function handleUnsupportedVersion(logger2, config, tracedLanguages, abortUnsupportedTracedAnalysis, product, version) {
|
||||
const unsupportedVersion = product === "macOS" ? UNSUPPORTED_DARWIN_FOR_TRACED : UNSUPPORTED_XCODE_FOR_TRACED;
|
||||
function diagnosticAttributeName(product) {
|
||||
switch (product) {
|
||||
case "macOS":
|
||||
return "darwinVersion";
|
||||
case "Xcode":
|
||||
return "xcodeVersion";
|
||||
case "Xcode SDK":
|
||||
return "xcodeSdkVersion";
|
||||
}
|
||||
}
|
||||
function handleUnsupportedVersion(logger2, config, tracedLanguages, abortUnsupportedTracedAnalysis, productVersion) {
|
||||
const { product, version } = productVersion;
|
||||
const unsupportedVersion = product === "macOS" ? UNSUPPORTED_DARWIN_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);
|
||||
for (const language of tracedLanguages) {
|
||||
addDiagnostic(
|
||||
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: {
|
||||
@@ -194016,28 +194138,27 @@ async function checkRunnerSupportsTracedAnalysis(action, config, codeql) {
|
||||
config,
|
||||
tracedLanguages,
|
||||
abortUnsupportedTracedAnalysis,
|
||||
"macOS",
|
||||
version
|
||||
{ product: "macOS", version }
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (isSelfHostedRunner(action.env)) {
|
||||
action.logger.debug(
|
||||
"Skipping traced analysis compatibility check for Xcode because we are on a self-hosted runner."
|
||||
);
|
||||
return;
|
||||
const xcodeVer = xcodeVersionFromLink(action.logger) ?? await xcodeVersionFromXcodeBuild(action.logger);
|
||||
let xcodeProduct = void 0;
|
||||
if (xcodeVer !== void 0) {
|
||||
xcodeProduct = { product: "Xcode", version: xcodeVer };
|
||||
} else {
|
||||
const xcodeSdkVer = await xcodeSdkVersion(action.logger);
|
||||
xcodeProduct = xcodeSdkVer !== void 0 ? { product: "Xcode SDK", version: xcodeSdkVer } : void 0;
|
||||
}
|
||||
const xcodeVer = xcodeVersion(action.logger);
|
||||
if (xcodeVer !== void 0 && xcodeVer.major >= UNSUPPORTED_XCODE_FOR_TRACED) {
|
||||
if (xcodeProduct !== void 0 && xcodeProduct.version.major >= UNSUPPORTED_XCODE_FOR_TRACED) {
|
||||
handleUnsupportedVersion(
|
||||
action.logger,
|
||||
config,
|
||||
tracedLanguages,
|
||||
abortUnsupportedTracedAnalysis,
|
||||
"Xcode",
|
||||
xcodeVer
|
||||
xcodeProduct
|
||||
);
|
||||
} else if (xcodeVer === void 0) {
|
||||
} else if (xcodeProduct === void 0) {
|
||||
action.logger.info(
|
||||
"Unable to determine Xcode version. Skipping Xcode compatibility check."
|
||||
);
|
||||
@@ -194655,7 +194776,7 @@ async function run3(actionState) {
|
||||
}
|
||||
if (config.languages.includes("go" /* go */) && process.platform === "linux") {
|
||||
try {
|
||||
const goBinaryPath = await io7.which("go", true);
|
||||
const goBinaryPath = await io8.which("go", true);
|
||||
const fileOutput = await getFileType(goBinaryPath);
|
||||
if (fileOutput.includes("statically linked") && !await codeql.supportsFeature(
|
||||
"indirectTracingSupportsStaticBinaries" /* IndirectTracingSupportsStaticBinaries */
|
||||
@@ -196010,8 +196131,8 @@ function generateCertificateAuthority() {
|
||||
// src/start-proxy/environment.ts
|
||||
var fs33 = __toESM(require("fs"));
|
||||
var path28 = __toESM(require("path"));
|
||||
var toolrunner5 = __toESM(require_toolrunner());
|
||||
var io8 = __toESM(require_io());
|
||||
var toolrunner6 = __toESM(require_toolrunner());
|
||||
var io9 = __toESM(require_io());
|
||||
function checkEnvVar(logger2, name) {
|
||||
const value = process.env[name];
|
||||
if (isDefined2(value)) {
|
||||
@@ -196101,9 +196222,9 @@ function checkJdkSettings(logger2, jdkHome) {
|
||||
}
|
||||
async function showJavaSettings(logger2) {
|
||||
try {
|
||||
const java = await io8.which("java", true);
|
||||
const java = await io9.which("java", true);
|
||||
let output = "";
|
||||
await new toolrunner5.ToolRunner(
|
||||
await new toolrunner6.ToolRunner(
|
||||
java,
|
||||
["-XshowSettings:all", "-XshowSettings:security:all", "-version"],
|
||||
{
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"test": "npm run ava -- src/",
|
||||
"test-debug": "npm run test -- --timeout=20m",
|
||||
"transpile": "tsc --build --verbose tsconfig.json",
|
||||
"update-pr-checks": "./pr-checks/sync.sh"
|
||||
"update-pr-checks": "./pr-checks/sync.sh",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
"private": true,
|
||||
"description": "Dependencies for codeql-action scripts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "npx tsx --test",
|
||||
"checks": "npm run typecheck && npm run test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^2.0.3",
|
||||
"@actions/github": "^8.0.1",
|
||||
|
||||
@@ -9,5 +9,12 @@
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["./**/*.ts", "../src/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"../src/testdata/**",
|
||||
"../src/**/*.test.ts",
|
||||
"../src/testing-utils.ts",
|
||||
"../src/json/testing-util.ts",
|
||||
"../src/feature-flags/testing-util.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ test("extractTarZst rejects if the input stream errors", async (t) => {
|
||||
const promise = extractTarZst(
|
||||
archive,
|
||||
path.join(tmpDir, "dest"),
|
||||
{ type: "gnu", version: "1.34" },
|
||||
{ type: "gnu", version: "1.34", path: "tar" },
|
||||
new RecordingLogger(),
|
||||
);
|
||||
|
||||
|
||||
14
src/tar.ts
14
src/tar.ts
@@ -17,6 +17,8 @@ const MIN_REQUIRED_GNU_TAR_VERSION = "1.31";
|
||||
export type TarVersion = {
|
||||
type: "gnu" | "bsd";
|
||||
version: string;
|
||||
/** Path to the `tar` executable that reported this version. */
|
||||
path: string;
|
||||
};
|
||||
|
||||
async function getTarVersion(): Promise<TarVersion> {
|
||||
@@ -39,14 +41,14 @@ async function getTarVersion(): Promise<TarVersion> {
|
||||
throw new Error("Failed to parse output of tar --version.");
|
||||
}
|
||||
|
||||
return { type: "gnu", version: match[1] };
|
||||
return { type: "gnu", version: match[1], path: tar };
|
||||
} else if (stdout.includes("bsdtar")) {
|
||||
const match = stdout.match(/bsdtar ([0-9.]+)/);
|
||||
if (!match?.[1]) {
|
||||
throw new Error("Failed to parse output of tar --version.");
|
||||
}
|
||||
|
||||
return { type: "bsd", version: match[1] };
|
||||
return { type: "bsd", version: match[1], path: tar };
|
||||
} else {
|
||||
throw new Error("Unknown tar version");
|
||||
}
|
||||
@@ -171,10 +173,10 @@ export async function extractTarZst(
|
||||
|
||||
args.push("-f", tar instanceof stream.Readable ? "-" : tar, "-C", dest);
|
||||
|
||||
process.stdout.write(`[command]tar ${args.join(" ")}\n`);
|
||||
process.stdout.write(`[command]${tarVersion.path} ${args.join(" ")}\n`);
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const tarProcess = spawn("tar", args, { stdio: "pipe" });
|
||||
const tarProcess = spawn(tarVersion.path, args, { stdio: "pipe" });
|
||||
|
||||
let stdout = "";
|
||||
tarProcess.stdout?.on("data", (data: Buffer) => {
|
||||
@@ -210,11 +212,11 @@ export async function extractTarZst(
|
||||
if (code !== 0) {
|
||||
reject(
|
||||
new CommandInvocationError(
|
||||
"tar",
|
||||
tarVersion.path,
|
||||
args,
|
||||
code ?? undefined,
|
||||
stdout,
|
||||
stderr,
|
||||
stdout,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ test.serial(
|
||||
destination,
|
||||
undefined,
|
||||
{},
|
||||
{ type: "gnu", version: "1.34" },
|
||||
{ type: "gnu", version: "1.34", path: "tar" },
|
||||
new RecordingLogger(),
|
||||
);
|
||||
|
||||
@@ -104,7 +104,7 @@ test.serial(
|
||||
destination,
|
||||
undefined,
|
||||
{},
|
||||
{ type: "gnu", version: "1.34" },
|
||||
{ type: "gnu", version: "1.34", path: "tar" },
|
||||
new RecordingLogger(),
|
||||
),
|
||||
{
|
||||
@@ -148,7 +148,7 @@ test.serial(
|
||||
destination,
|
||||
undefined,
|
||||
{},
|
||||
{ type: "gnu", version: "1.34" },
|
||||
{ type: "gnu", version: "1.34", path: "tar" },
|
||||
new RecordingLogger(),
|
||||
);
|
||||
|
||||
@@ -188,7 +188,7 @@ test.serial(
|
||||
destination,
|
||||
undefined,
|
||||
{},
|
||||
{ type: "gnu", version: "1.34" },
|
||||
{ type: "gnu", version: "1.34", path: "tar" },
|
||||
new RecordingLogger(),
|
||||
);
|
||||
|
||||
@@ -236,7 +236,7 @@ test.serial(
|
||||
path.join(tmpDir, "codeql"),
|
||||
undefined,
|
||||
{},
|
||||
{ type: "gnu", version: "1.34" },
|
||||
{ type: "gnu", version: "1.34", path: "tar" },
|
||||
new RecordingLogger(),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user