diff --git a/lib/entry-points.js b/lib/entry-points.js index 807c1fbfa..de3ea96af 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -34719,7 +34719,7 @@ var require_cacheUtils = __commonJS({ var crypto3 = __importStar2(require("crypto")); var fs34 = __importStar2(require("fs")); var path30 = __importStar2(require("path")); - var semver13 = __importStar2(require_semver3()); + var semver12 = __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 = semver13.clean(versionOutput); + const version = semver12.clean(versionOutput); core29.debug(`zstd version: ${version}`); if (versionOutput === "") { return constants_1.CompressionMethod.Gzip; @@ -42967,11 +42967,11 @@ var require_tracingPolicy = __commonJS({ var import_log = require_log3(); var import_core_util = require_commonjs4(); var import_restError = require_restError3(); - var import_util60 = require_internal3(); + var import_util61 = require_internal3(); var tracingPolicyName2 = "tracingPolicy"; function tracingPolicy2(options = {}) { const userAgentPromise = (0, import_userAgent.getUserAgentValue)(options.userAgentPrefix); - const sanitizer = new import_util60.Sanitizer({ + const sanitizer = new import_util61.Sanitizer({ additionalAllowedQueryParameters: options.additionalAllowedQueryParameters }); const tracingClient = tryCreateTracingClient(); @@ -46597,11 +46597,11 @@ var require_response2 = __commonJS({ }); module2.exports = __toCommonJS2(response_exports); var import_core_rest_pipeline = require_commonjs6(); - var import_util60 = require_util9(); + var import_util61 = require_util9(); var originalResponse = /* @__PURE__ */ Symbol("Original FullOperationResponse"); function toCompatResponse2(response, options) { - let request3 = (0, import_util60.toWebResourceLike)(response.request); - let headers = (0, import_util60.toHttpHeadersLike)(response.headers); + let request3 = (0, import_util61.toWebResourceLike)(response.request); + let headers = (0, import_util61.toHttpHeadersLike)(response.headers); if (options?.createProxy) { return new Proxy(response, { get(target, prop, receiver) { @@ -46642,7 +46642,7 @@ var require_response2 = __commonJS({ return { ...compatResponse, headers, - request: (0, import_util60.toPipelineRequest)(compatResponse.request) + request: (0, import_util61.toPipelineRequest)(compatResponse.request) }; } } @@ -46749,7 +46749,7 @@ var require_requestPolicyFactoryPolicy = __commonJS({ requestPolicyFactoryPolicyName: () => requestPolicyFactoryPolicyName2 }); module2.exports = __toCommonJS2(requestPolicyFactoryPolicy_exports); - var import_util60 = require_util9(); + var import_util61 = require_util9(); var import_response = require_response2(); var HttpPipelineLogLevel2 = /* @__PURE__ */ ((HttpPipelineLogLevel22) => { HttpPipelineLogLevel22[HttpPipelineLogLevel22["ERROR"] = 1] = "ERROR"; @@ -46773,14 +46773,14 @@ var require_requestPolicyFactoryPolicy = __commonJS({ async sendRequest(request3, next) { let httpPipeline = { async sendRequest(httpRequest) { - const response2 = await next((0, import_util60.toPipelineRequest)(httpRequest)); + const response2 = await next((0, import_util61.toPipelineRequest)(httpRequest)); return (0, import_response.toCompatResponse)(response2, { createProxy: true }); } }; for (const factory of orderedFactories) { httpPipeline = factory.create(httpPipeline, mockRequestPolicyOptions); } - const webResourceLike = (0, import_util60.toWebResourceLike)(request3, { createProxy: true }); + const webResourceLike = (0, import_util61.toWebResourceLike)(request3, { createProxy: true }); const response = await httpPipeline.sendRequest(webResourceLike); return (0, import_response.toPipelineResponse)(response); } @@ -46815,12 +46815,12 @@ var require_httpClientAdapter = __commonJS({ }); module2.exports = __toCommonJS2(httpClientAdapter_exports); var import_response = require_response2(); - var import_util60 = require_util9(); + var import_util61 = require_util9(); function convertHttpClient2(requestPolicyClient) { return { sendRequest: async (request3) => { const response = await requestPolicyClient.sendRequest( - (0, import_util60.toWebResourceLike)(request3, { createProxy: true }) + (0, import_util61.toWebResourceLike)(request3, { createProxy: true }) ); return (0, import_response.toPipelineResponse)(response); } @@ -46858,7 +46858,7 @@ var require_commonjs9 = __commonJS({ disableKeepAlivePolicyName: () => import_disableKeepAlivePolicy.disableKeepAlivePolicyName, requestPolicyFactoryPolicyName: () => import_requestPolicyFactoryPolicy.requestPolicyFactoryPolicyName, toCompatResponse: () => import_response.toCompatResponse, - toHttpHeadersLike: () => import_util60.toHttpHeadersLike + toHttpHeadersLike: () => import_util61.toHttpHeadersLike }); module2.exports = __toCommonJS2(src_exports); var import_extendedClient = require_extendedClient(); @@ -46866,7 +46866,7 @@ var require_commonjs9 = __commonJS({ var import_requestPolicyFactoryPolicy = require_requestPolicyFactoryPolicy(); var import_disableKeepAlivePolicy = require_disableKeepAlivePolicy(); var import_httpClientAdapter = require_httpClientAdapter(); - var import_util60 = require_util9(); + var import_util61 = require_util9(); } }); @@ -106361,7 +106361,7 @@ var require_manifest = __commonJS({ exports2._findMatch = _findMatch; exports2._getOsVersion = _getOsVersion; exports2._readLinuxVersionFile = _readLinuxVersionFile; - var semver13 = __importStar2(require_semver2()); + var semver12 = __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 (semver13.satisfies(version, versionSpec) && (!stable || candidate.stable === stable)) { + if (semver12.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 = semver13.satisfies(osVersion, item.platform_version); + chk = semver12.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 semver13 = __importStar2(require_semver2()); + var semver12 = __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 = semver13.clean(version) || version; + version = semver12.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 = semver13.clean(version) || version; + version = semver12.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 = semver13.clean(versionSpec) || ""; + versionSpec = semver12.clean(versionSpec) || ""; const cachePath = path30.join(_getCacheDirectory(), toolName, versionSpec, arch2); core29.debug(`checking cache: ${cachePath}`); if (fs34.existsSync(cachePath) && fs34.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, semver13.clean(version) || version, arch2 || ""); + const folderPath = path30.join(_getCacheDirectory(), tool, semver12.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, semver13.clean(version) || version, arch2 || ""); + const folderPath = path30.join(_getCacheDirectory(), tool, semver12.clean(version) || version, arch2 || ""); const markerPath = `${folderPath}.complete`; fs34.writeFileSync(markerPath, ""); core29.debug("finished caching tool"); } function isExplicitVersion(versionSpec) { - const c = semver13.clean(versionSpec) || ""; + const c = semver12.clean(versionSpec) || ""; core29.debug(`isExplicit: ${c}`); - const valid4 = semver13.valid(c) != null; + const valid4 = semver12.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 (semver13.gt(a, b)) { + if (semver12.gt(a, b)) { return 1; } return -1; }); for (let i = versions.length - 1; i >= 0; i--) { const potential = versions[i]; - const satisfied = semver13.satisfies(potential, versionSpec); + const satisfied = semver12.satisfies(potential, versionSpec); if (satisfied) { version = potential; break; @@ -166068,7 +166068,7 @@ __export(entry_points_exports, { module.exports = __toCommonJS(entry_points_exports); // src/analyze-action.ts -var fs24 = __toESM(require("fs")); +var fs25 = __toESM(require("fs")); var import_path5 = __toESM(require("path")); var import_perf_hooks6 = require("perf_hooks"); var core14 = __toESM(require_core()); @@ -172680,13 +172680,13 @@ var SarifScanOrder = [ ]; // src/analyze.ts -var fs18 = __toESM(require("fs")); +var fs19 = __toESM(require("fs")); var path16 = __toESM(require("path")); var import_perf_hooks5 = require("perf_hooks"); var io5 = __toESM(require_io()); // src/codeql.ts -var fs17 = __toESM(require("fs")); +var fs18 = __toESM(require("fs")); var path15 = __toESM(require("path")); var core12 = __toESM(require_core()); var toolrunner3 = __toESM(require_toolrunner()); @@ -175366,7 +175366,7 @@ async function logGeneratedFilesTelemetry(config, duration, generatedFilesCount) } // src/setup-codeql.ts -var fs15 = __toESM(require("fs")); +var fs16 = __toESM(require("fs")); var path13 = __toESM(require("path")); var import_perf_hooks4 = require("perf_hooks"); var toolcache3 = __toESM(require_tool_cache()); @@ -175702,6 +175702,7 @@ async function getCodeQlVersionsForOverlayBaseDatabases(rawLanguages, logger2) { var semver7 = __toESM(require_semver2()); // src/platform.ts +var import_fs = __toESM(require("fs")); var semver6 = __toESM(require_semver2()); function getBundlePlatform(platform2 = process.platform, arch2 = process.arch) { switch (platform2) { @@ -175725,6 +175726,47 @@ function macOSVersion(action) { } return version; } +var XCODE_SELECT_LINK_PATH = "/private/var/db/xcode_select_link"; +var XCODE_APP_FILENAME_PATTERN = new RegExp( + /(?Xcode_(?\d+.\d+).app)/ +); +function xcodeVersion(logger2) { + try { + const stats = import_fs.default.statSync(XCODE_SELECT_LINK_PATH); + if (!stats.isSymbolicLink()) { + logger2.warning( + `${XCODE_SELECT_LINK_PATH} exists, but is not a symbolic link.` + ); + return void 0; + } + const xcodePath = import_fs.default.readlinkSync(XCODE_SELECT_LINK_PATH); + if (xcodePath === "") { + logger2.warning( + `Resolving ${XCODE_SELECT_LINK_PATH} unexpectedly returned nothing.` + ); + return void 0; + } + const matchResult = xcodePath.match(XCODE_APP_FILENAME_PATTERN); + if (matchResult?.groups === void 0) { + logger2.warning( + `Xcode path '${xcodePath}' does not contain expected pattern.` + ); + return void 0; + } + const majorMinor = matchResult.groups["majorMinor"]; + const version = semver6.coerce(majorMinor); + if (version === null) { + logger2.warning(`Couldn't parse '${majorMinor}' as a semantic version.`); + return void 0; + } + return version; + } catch (err) { + logger2.warning( + `Unable to determine Xcode version: ${getErrorMessage(err)}` + ); + return void 0; + } +} // src/per-language-bundles.ts var MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION = "2.27.1"; @@ -175910,7 +175952,7 @@ async function getCodeQLAssetDownloadURL(action, apiDetails, tagName, assetName, // src/tar.ts var import_child_process = require("child_process"); -var fs13 = __toESM(require("fs")); +var fs14 = __toESM(require("fs")); var stream = __toESM(require("stream")); var import_toolrunner = __toESM(require_toolrunner()); var io4 = __toESM(require_io()); @@ -175983,7 +176025,7 @@ async function isZstdAvailable(logger2) { } } async function extract(tarPath, dest, compressionMethod, tarVersion, logger2) { - fs13.mkdirSync(dest, { recursive: true }); + fs14.mkdirSync(dest, { recursive: true }); switch (compressionMethod) { case "gzip": return await toolcache.extractTar(tarPath, dest); @@ -176069,7 +176111,7 @@ function inferCompressionMethod(tarPath) { } // src/tools-download.ts -var fs14 = __toESM(require("fs")); +var fs15 = __toESM(require("fs")); var os5 = __toESM(require("os")); var path12 = __toESM(require("path")); var import_perf_hooks3 = require("perf_hooks"); @@ -176155,7 +176197,7 @@ async function downloadAndExtract(codeqlURL, compressionMethod, dest, authorizat }; } async function downloadAndExtractZstdWithStreaming(codeqlURL, dest, authorization, headers, tarVersion, logger2) { - fs14.mkdirSync(dest, { recursive: true }); + fs15.mkdirSync(dest, { recursive: true }); const agent = new import_http_client.HttpClient().getAgent(codeqlURL); headers = Object.assign( { "User-Agent": "CodeQL Action" }, @@ -176214,7 +176256,7 @@ function getToolcacheDirectory(version) { } function isToolcacheOnWorkspaceFilesystem(logger2) { try { - return fs14.statSync(getRequiredEnvParam("RUNNER_TOOL_CACHE")).dev === fs14.statSync(getRequiredEnvParam("GITHUB_WORKSPACE")).dev; + return fs15.statSync(getRequiredEnvParam("RUNNER_TOOL_CACHE")).dev === fs15.statSync(getRequiredEnvParam("GITHUB_WORKSPACE")).dev; } catch (e) { logger2.debug( `Could not determine whether the toolcache is on the same filesystem as the workspace: ${getErrorMessage(e)}` @@ -176236,7 +176278,7 @@ async function deleteToolcacheBundles({ return { deletedVersions: [], failed: true }; } try { - if ((await fs14.promises.lstat(toolDirectory)).isSymbolicLink()) { + if ((await fs15.promises.lstat(toolDirectory)).isSymbolicLink()) { logger2.info( `Not deleting the CodeQL tools from the toolcache since '${toolDirectory}' is a symlink.` ); @@ -176255,7 +176297,7 @@ async function deleteToolcacheBundles({ return { deletedVersions: [], failed: true }; } try { - const entries = await fs14.promises.readdir(toolDirectory, { + const entries = await fs15.promises.readdir(toolDirectory, { withFileTypes: true }); const deletedVersions = []; @@ -176269,7 +176311,7 @@ async function deleteToolcacheBundles({ } const versionDirectory = path12.join(toolDirectory, entry.name); try { - await fs14.promises.rm(versionDirectory, { + await fs15.promises.rm(versionDirectory, { force: true, recursive: true }); @@ -176294,7 +176336,7 @@ async function deleteToolcacheBundles({ } function writeToolcacheMarkerFile(extractedPath, logger2) { const markerFilePath = `${extractedPath}.complete`; - fs14.writeFileSync(markerFilePath, ""); + fs15.writeFileSync(markerFilePath, ""); logger2.info(`Created toolcache marker file ${markerFilePath}`); } @@ -176346,7 +176388,7 @@ async function findOverridingToolsInCache(humanReadableVersion, logger2) { const candidates = toolcache3.findAllVersions("CodeQL").filter(isGoodVersion).map((version) => ({ folder: toolcache3.find("CodeQL", version), version - })).filter(({ folder }) => fs15.existsSync(path13.join(folder, "pinned-version"))); + })).filter(({ folder }) => fs16.existsSync(path13.join(folder, "pinned-version"))); if (candidates.length === 1) { const candidate = candidates[0]; logger2.debug( @@ -177008,7 +177050,7 @@ function isReservedToolsValue(tools) { } // src/tracer-config.ts -var fs16 = __toESM(require("fs")); +var fs17 = __toESM(require("fs")); var path14 = __toESM(require("path")); async function shouldEnableIndirectTracing(codeql, config) { if (config.buildMode === "none" /* None */) { @@ -177028,14 +177070,14 @@ async function endTracingForCluster(codeql, config, logger2) { config.dbLocation, "temp/tracingEnvironment/end-tracing.json" ); - if (!fs16.existsSync(envVariablesFile)) { + if (!fs17.existsSync(envVariablesFile)) { throw new Error( `Environment file for ending tracing not found: ${envVariablesFile}` ); } try { const endTracingEnvVariables = JSON.parse( - fs16.readFileSync(envVariablesFile, "utf8") + fs17.readFileSync(envVariablesFile, "utf8") ); for (const [key, value] of Object.entries(endTracingEnvVariables)) { if (value !== null) { @@ -177056,7 +177098,7 @@ async function getTracerConfigForCluster(logger2, config) { "temp/tracingEnvironment/start-tracing.json" ); try { - const tracingEnvVariables = JSON.parse(fs16.readFileSync(filePath, "utf8")); + const tracingEnvVariables = JSON.parse(fs17.readFileSync(filePath, "utf8")); return { env: tracingEnvVariables }; @@ -177178,7 +177220,7 @@ async function getCodeQLForCmd(logger2, cmd, checkVersion, env = getEnv()) { "tools", "tracing-config.lua" ); - return fs17.existsSync(tracingConfigPath); + return fs18.existsSync(tracingConfigPath); }, async isScannedLanguage(language) { return !await this.isTracedLanguage(language); @@ -177465,7 +177507,7 @@ async function getCodeQLForCmd(logger2, cmd, checkVersion, env = getEnv()) { }, async diagnosticsExport(sarifFile, automationDetailsId, config, diagnosticDir) { let configPath = getGeneratedCodeScanningConfigPath(config); - if (diagnosticDir !== void 0 && !fs17.existsSync(configPath)) { + if (diagnosticDir !== void 0 && !fs18.existsSync(configPath)) { configPath = void 0; } const args = [ @@ -177642,7 +177684,7 @@ async function writeCodeScanningConfigFile(config, logger2) { logger2.startGroup("Augmented user configuration file contents"); logger2.info(dump(augmentedConfig)); logger2.endGroup(); - fs17.writeFileSync(codeScanningConfigFile, dump(augmentedConfig)); + fs18.writeFileSync(codeScanningConfigFile, dump(augmentedConfig)); return codeScanningConfigFile; } var TRAP_CACHE_SIZE_MB = 1024; @@ -178138,7 +178180,7 @@ function dbIsFinalized(config, language, logger2) { const dbPath = getCodeQLDatabasePath(config, language); try { const dbInfo = load( - fs18.readFileSync(path16.resolve(dbPath, "codeql-database.yml"), "utf8") + fs19.readFileSync(path16.resolve(dbPath, "codeql-database.yml"), "utf8") ); return !("inProgress" in dbInfo); } catch { @@ -178222,8 +178264,8 @@ function writeDiffRangeDataExtensionPack(logger2, ranges, checkoutPath) { ranges = [{ path: "", startLine: 0, endLine: 0 }]; } const diffRangeDir = path16.join(getTemporaryDirectory(), "pr-diff-range"); - fs18.mkdirSync(diffRangeDir, { recursive: true }); - fs18.writeFileSync( + fs19.mkdirSync(diffRangeDir, { recursive: true }); + fs19.writeFileSync( path16.join(diffRangeDir, "qlpack.yml"), ` name: codeql-action/pr-diff-range @@ -178240,7 +178282,7 @@ dataExtensions: checkoutPath ); const extensionFilePath = path16.join(diffRangeDir, "pr-diff-range.yml"); - fs18.writeFileSync(extensionFilePath, extensionContents); + fs19.writeFileSync(extensionFilePath, extensionContents); logger2.debug( `Wrote pr-diff-range extension pack to ${extensionFilePath}: ${extensionContents}` @@ -178385,7 +178427,7 @@ async function runQueries(sarifFolder, memoryFlag, threadsFlag, diffRangePackDir } function getPerQueryAlertCounts(sarifPath) { const sarifObject = JSON.parse( - fs18.readFileSync(sarifPath, "utf8") + fs19.readFileSync(sarifPath, "utf8") ); const perQueryAlertCounts = {}; for (const sarifRun of sarifObject.runs) { @@ -178403,13 +178445,13 @@ async function runQueries(sarifFolder, memoryFlag, threadsFlag, diffRangePackDir } async function runFinalize(features, outputDir, threadsFlag, memoryFlag, codeql, config, logger2) { try { - await fs18.promises.rm(outputDir, { force: true, recursive: true }); + await fs19.promises.rm(outputDir, { force: true, recursive: true }); } catch (error3) { if (error3?.code !== "ENOENT") { throw error3; } } - await fs18.promises.mkdir(outputDir, { recursive: true }); + await fs19.promises.mkdir(outputDir, { recursive: true }); const timings = await finalizeDatabaseCreation( codeql, features, @@ -178453,7 +178495,7 @@ async function warnIfGoInstalledAfterInit(config, logger2) { } // src/database-upload.ts -var fs19 = __toESM(require("fs")); +var fs20 = __toESM(require("fs")); async function cleanupAndUploadDatabases(action, repositoryNwo, codeql, config, apiDetails, checkoutPath) { const logger2 = action.logger; if (getRequiredInput("upload-database") !== "true") { @@ -178490,7 +178532,7 @@ async function cleanupAndUploadDatabases(action, repositoryNwo, codeql, config, const bundledDb = await bundleDb(config, language, codeql, language, { includeDiagnostics: false }); - bundledDbSize = fs19.statSync(bundledDb).size; + bundledDbSize = fs20.statSync(bundledDb).size; const commitOid = await getCommitOid(action.env, checkoutPath); const maxAttempts = 4; let uploadDurationMs; @@ -178568,7 +178610,7 @@ async function recordClearCleanupSizes(codeql, config, reports, logger2) { const bundledDb = await bundleDb(config, language, codeql, language, { includeDiagnostics: false }); - report.clear_cleanup_zipped_size_bytes = fs19.statSync(bundledDb).size; + report.clear_cleanup_zipped_size_bytes = fs20.statSync(bundledDb).size; logger2.debug( `Database for ${language} is ${report.clear_cleanup_zipped_size_bytes} bytes zipped at the '${"clear" /* Clear */}' cleanup level (vs. ${report.zipped_upload_size_bytes ?? "unknown"} bytes at the '${"overlay" /* Overlay */}' level).` ); @@ -178591,7 +178633,7 @@ async function uploadBundledDatabase(repositoryNwo, language, commitOid, bundled if (uploadsBaseUrl.endsWith("/")) { uploadsBaseUrl = uploadsBaseUrl.slice(0, -1); } - const bundledDbReadStream = fs19.createReadStream(bundledDb); + const bundledDbReadStream = fs20.createReadStream(bundledDb); try { const startTime = performance.now(); await client.request( @@ -178643,7 +178685,7 @@ __export(upload_lib_exports, { waitForProcessing: () => waitForProcessing, writePostProcessedFiles: () => writePostProcessedFiles }); -var fs23 = __toESM(require("fs")); +var fs24 = __toESM(require("fs")); var path19 = __toESM(require("path")); var url = __toESM(require("url")); var import_zlib = __toESM(require("zlib")); @@ -178651,7 +178693,7 @@ var core13 = __toESM(require_core()); var jsonschema2 = __toESM(require_lib2()); // src/fingerprints.ts -var fs20 = __toESM(require("fs")); +var fs21 = __toESM(require("fs")); var import_path4 = __toESM(require("path")); // node_modules/long/index.js @@ -179639,7 +179681,7 @@ async function hash(callback, filepath) { } updateHash(current); }; - const readStream = fs20.createReadStream(filepath, "utf8"); + const readStream = fs21.createReadStream(filepath, "utf8"); for await (const data of readStream) { for (let i = 0; i < data.length; ++i) { processCharacter(data.charCodeAt(i)); @@ -179714,11 +179756,11 @@ function resolveUriToFile(location, artifacts, sourceRoot, logger2) { if (!import_path4.default.isAbsolute(uri)) { uri = srcRootPrefix + uri; } - if (!fs20.existsSync(uri)) { + if (!fs21.existsSync(uri)) { logger2.debug(`Unable to compute fingerprint for non-existent file: ${uri}`); return void 0; } - if (fs20.statSync(uri).isDirectory()) { + if (fs21.statSync(uri).isDirectory()) { logger2.debug(`Unable to compute fingerprint for directory: ${uri}`); return void 0; } @@ -179773,7 +179815,7 @@ async function addFingerprints(sarifLog, sourceRoot, logger2) { } // src/init.ts -var fs21 = __toESM(require("fs")); +var fs22 = __toESM(require("fs")); var path18 = __toESM(require("path")); var toolrunner4 = __toESM(require_toolrunner()); var github3 = __toESM(require_github()); @@ -179808,7 +179850,7 @@ async function initConfig2(actionState, inputs) { }); } async function runDatabaseInitCluster(databaseInitEnvironment, codeql, config, sourceRoot, processName, qlconfigFile) { - fs21.mkdirSync(config.dbLocation, { recursive: true }); + fs22.mkdirSync(config.dbLocation, { recursive: true }); await wrapEnvironment( databaseInitEnvironment, async () => await codeql.databaseInitCluster( @@ -179843,24 +179885,24 @@ async function checkPacksForOverlayCompatibility(codeql, config, logger2) { function checkPackForOverlayCompatibility(packDir, codeQlOverlayVersion, logger2) { try { let qlpackPath = path18.join(packDir, "qlpack.yml"); - if (!fs21.existsSync(qlpackPath)) { + if (!fs22.existsSync(qlpackPath)) { qlpackPath = path18.join(packDir, "codeql-pack.yml"); } const qlpackContents = load( - fs21.readFileSync(qlpackPath, "utf8") + fs22.readFileSync(qlpackPath, "utf8") ); if (!qlpackContents.buildMetadata) { return true; } const packInfoPath = path18.join(packDir, ".packinfo"); - if (!fs21.existsSync(packInfoPath)) { + if (!fs22.existsSync(packInfoPath)) { logger2.warning( `The query pack at ${packDir} does not have a .packinfo file, so it cannot support overlay analysis. Recompiling the query pack with the latest CodeQL CLI should solve this problem.` ); return false; } const packInfoFileContents = JSON.parse( - fs21.readFileSync(packInfoPath, "utf8") + fs22.readFileSync(packInfoPath, "utf8") ); const packOverlayVersion = packInfoFileContents.overlayVersion; if (typeof packOverlayVersion !== "number") { @@ -179895,8 +179937,8 @@ async function checkInstallPython311(languages, codeql) { ]).exec(); } } -function cleanupDatabaseClusterDirectory(config, logger2, options = {}, rmSync5 = fs21.rmSync) { - if (fs21.existsSync(config.dbLocation) && (fs21.statSync(config.dbLocation).isFile() || fs21.readdirSync(config.dbLocation).length > 0)) { +function cleanupDatabaseClusterDirectory(config, logger2, options = {}, rmSync5 = fs22.rmSync) { + if (fs22.existsSync(config.dbLocation) && (fs22.statSync(config.dbLocation).isFile() || fs22.readdirSync(config.dbLocation).length > 0)) { if (!options.disableExistingDirectoryWarning) { logger2.warning( `The database cluster directory ${config.dbLocation} must be empty. Attempting to clean it up.` @@ -180001,7 +180043,7 @@ To opt out of this change, ${envVarOptOut}`; } // src/sarif/index.ts -var fs22 = __toESM(require("fs")); +var fs23 = __toESM(require("fs")); var InvalidSarifUploadError = class extends Error { }; function getToolNames(sarifFile) { @@ -180017,7 +180059,7 @@ function getToolNames(sarifFile) { } function readSarifFile(sarifFilePath) { try { - return JSON.parse(fs22.readFileSync(sarifFilePath, "utf8")); + return JSON.parse(fs23.readFileSync(sarifFilePath, "utf8")); } catch (err) { throw new Error( `Parsing SARIF file at '${sarifFilePath}' failed: ${getErrorMessage(err)}` @@ -180158,8 +180200,8 @@ async function combineSarifFilesUsingCLI(sarifFiles, gitHubVersion, features, lo codeQL = initCodeQLResult.codeql; } const baseTempDir = path19.resolve(tempDir, "combined-sarif"); - fs23.mkdirSync(baseTempDir, { recursive: true }); - const outputDirectory = fs23.mkdtempSync(path19.resolve(baseTempDir, "output-")); + fs24.mkdirSync(baseTempDir, { recursive: true }); + const outputDirectory = fs24.mkdtempSync(path19.resolve(baseTempDir, "output-")); const outputFile = path19.resolve(outputDirectory, "combined-sarif.sarif"); await codeQL.mergeResults(sarifFiles, outputFile, { mergeRunsFromEqualCategory: true @@ -180204,7 +180246,7 @@ async function uploadPayload(payload, repositoryNwo, logger2, analysis) { `SARIF upload disabled by an environment variable. Saving to ${payloadSaveFile}` ); logger2.info(`Payload: ${JSON.stringify(payload, null, 2)}`); - fs23.writeFileSync(payloadSaveFile, JSON.stringify(payload, null, 2)); + fs24.writeFileSync(payloadSaveFile, JSON.stringify(payload, null, 2)); return "dummy-sarif-id"; } const client = getApiClient(); @@ -180238,7 +180280,7 @@ async function uploadPayload(payload, repositoryNwo, logger2, analysis) { function findSarifFilesInDir(sarifPath, isSarif) { const sarifFiles = []; const walkSarifFiles = (dir) => { - const entries = fs23.readdirSync(dir, { withFileTypes: true }); + const entries = fs24.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { if (entry.isFile() && isSarif(entry.name)) { sarifFiles.push(path19.resolve(dir, entry.name)); @@ -180251,11 +180293,11 @@ function findSarifFilesInDir(sarifPath, isSarif) { return sarifFiles; } function getSarifFilePaths(sarifPath, isSarif) { - if (!fs23.existsSync(sarifPath)) { + if (!fs24.existsSync(sarifPath)) { throw new ConfigurationError(`Path does not exist: ${sarifPath}`); } let sarifFiles; - if (fs23.lstatSync(sarifPath).isDirectory()) { + if (fs24.lstatSync(sarifPath).isDirectory()) { sarifFiles = findSarifFilesInDir(sarifPath, isSarif); if (sarifFiles.length === 0) { throw new ConfigurationError( @@ -180268,7 +180310,7 @@ function getSarifFilePaths(sarifPath, isSarif) { return sarifFiles; } async function getGroupedSarifFilePaths(logger2, sarifPath) { - const stats = fs23.statSync(sarifPath, { throwIfNoEntry: false }); + const stats = fs24.statSync(sarifPath, { throwIfNoEntry: false }); if (stats === void 0) { throw new ConfigurationError(`Path does not exist: ${sarifPath}`); } @@ -180403,7 +180445,7 @@ function buildPayload(commitOid, ref, analysisKey, analysisName, zippedSarif, wo payloadObj.base_sha = mergeBaseCommitOid; } else if (process.env.GITHUB_EVENT_PATH) { const githubEvent = JSON.parse( - fs23.readFileSync(process.env.GITHUB_EVENT_PATH, "utf8") + fs24.readFileSync(process.env.GITHUB_EVENT_PATH, "utf8") ); payloadObj.base_ref = `refs/heads/${githubEvent.pull_request.base.ref}`; payloadObj.base_sha = githubEvent.pull_request.base.sha; @@ -180541,9 +180583,9 @@ async function uploadPostProcessedFiles(logger2, checkoutPath, uploadTarget, pos }; } function dumpSarifFile(sarifPayload, outputDir, logger2, uploadTarget) { - if (!fs23.existsSync(outputDir)) { - fs23.mkdirSync(outputDir, { recursive: true }); - } else if (!fs23.lstatSync(outputDir).isDirectory()) { + if (!fs24.existsSync(outputDir)) { + fs24.mkdirSync(outputDir, { recursive: true }); + } else if (!fs24.lstatSync(outputDir).isDirectory()) { throw new ConfigurationError( `The path that processed SARIF files should be written to exists, but is not a directory: ${outputDir}` ); @@ -180553,7 +180595,7 @@ function dumpSarifFile(sarifPayload, outputDir, logger2, uploadTarget) { `upload${uploadTarget.sarifExtension}` ); logger2.info(`Writing processed SARIF file to ${outputFile}`); - fs23.writeFileSync(outputFile, sarifPayload); + fs24.writeFileSync(outputFile, sarifPayload); } var STATUS_CHECK_INITIAL_BACKOFF_MILLISECONDS = 5 * 1e3; var STATUS_CHECK_BACKOFF_MULTIPLIER = 2; @@ -180800,7 +180842,7 @@ function doesGoExtractionOutputExist(config) { "trap", "go" /* go */ ); - return fs24.existsSync(trapDirectory) && fs24.readdirSync(trapDirectory).some( + return fs25.existsSync(trapDirectory) && fs25.readdirSync(trapDirectory).some( (fileName) => [ ".trap", ".trap.gz", @@ -181103,18 +181145,18 @@ async function runWrapper() { } // src/analyze-action-post.ts -var fs28 = __toESM(require("fs")); +var fs29 = __toESM(require("fs")); var core16 = __toESM(require_core()); // src/debug-artifacts.ts -var fs27 = __toESM(require("fs")); +var fs28 = __toESM(require("fs")); var path23 = __toESM(require("path")); var artifact = __toESM(require_artifact2()); var artifactLegacy = __toESM(require_artifact_client2()); var core15 = __toESM(require_core()); // node_modules/archiver/lib/core.js -var import_fs = require("fs"); +var import_fs2 = require("fs"); // node_modules/is-stream/index.js function isStream(stream2, { checkOpen = true } = {}) { @@ -181122,7 +181164,7 @@ function isStream(stream2, { checkOpen = true } = {}) { } // node_modules/readdir-glob/dist/index.mjs -var fs25 = __toESM(require("fs"), 1); +var fs26 = __toESM(require("fs"), 1); var import_events = require("events"); // node_modules/readdir-glob/node_modules/balanced-match/dist/esm/index.js @@ -182999,7 +183041,7 @@ minimatch.unescape = unescape2; var import_path6 = require("path"); function readdir2(dir, strict) { return new Promise((resolve$1, reject) => { - fs25.readdir(dir, { withFileTypes: true }, (err, files) => { + fs26.readdir(dir, { withFileTypes: true }, (err, files) => { if (err) switch (err.code) { case "ENOTDIR": if (strict) reject(err); @@ -183022,7 +183064,7 @@ function readdir2(dir, strict) { } function getStat(file, followSymlinks) { return new Promise((resolve$1) => { - const statFunc = followSymlinks ? fs25.stat : fs25.lstat; + const statFunc = followSymlinks ? fs26.stat : fs26.lstat; statFunc(file, (err, stats) => { if (err) switch (err.code) { case "ENOENT": @@ -183192,7 +183234,7 @@ var import_async = __toESM(require_async(), 1); var import_path7 = require("path"); // node_modules/archiver/lib/error.js -var import_util37 = __toESM(require("util"), 1); +var import_util38 = __toESM(require("util"), 1); var ERROR_CODES = { ABORTED: "archive was aborted", DIRECTORYDIRPATHREQUIRED: "diretory dirpath argument must be a non-empty string value", @@ -183217,7 +183259,7 @@ function ArchiverError(code, data) { this.code = code; this.data = data; } -import_util37.default.inherits(ArchiverError, Error); +import_util38.default.inherits(ArchiverError, Error); // node_modules/archiver/lib/core.js var import_readable_stream2 = __toESM(require_ours(), 1); @@ -183330,7 +183372,7 @@ var Archiver = class extends import_readable_stream2.Transform { data.sourcePath = filepath; task.data = data; this._entriesCount++; - if (data.stats && data.stats instanceof import_fs.Stats) { + if (data.stats && data.stats instanceof import_fs2.Stats) { task = this._updateQueueTaskWithStats(task, data.stats); if (task) { if (data.stats.size) { @@ -183578,7 +183620,7 @@ var Archiver = class extends import_readable_stream2.Transform { callback(); return; } - (0, import_fs.lstat)( + (0, import_fs2.lstat)( task.filepath, function(err, stats) { if (this._state.aborted) { @@ -183640,7 +183682,7 @@ var Archiver = class extends import_readable_stream2.Transform { task.data.type = "file"; task.data.sourceType = "stream"; task.source = new import_lazystream.Readable(function() { - return (0, import_fs.createReadStream)(task.filepath); + return (0, import_fs2.createReadStream)(task.filepath); }); } else if (stats.isDirectory() && this._supportsDirectory) { task.data.name = trailingSlashIt(task.data.name); @@ -183649,7 +183691,7 @@ var Archiver = class extends import_readable_stream2.Transform { task.data.sourceType = "buffer"; task.source = Buffer.concat([]); } else if (stats.isSymbolicLink() && this._supportsSymlink) { - const linkPath = (0, import_fs.readlinkSync)(task.filepath); + const linkPath = (0, import_fs2.readlinkSync)(task.filepath); const dirName = (0, import_path7.dirname)(task.filepath); task.data.type = "symlink"; task.data.linkname = (0, import_path7.relative)( @@ -185492,7 +185534,7 @@ var ZipArchive = class extends Archiver { }; // src/artifact-scanner.ts -var fs26 = __toESM(require("fs")); +var fs27 = __toESM(require("fs")); var os7 = __toESM(require("os")); var path22 = __toESM(require("path")); var exec = __toESM(require_exec()); @@ -185539,7 +185581,7 @@ function isAuthToken(value, patterns = GITHUB_TOKEN_PATTERNS) { function scanFileForTokens(filePath, relativePath2, logger2) { const findings = []; try { - const content = fs26.readFileSync(filePath, "utf8"); + const content = fs27.readFileSync(filePath, "utf8"); for (const { type, pattern } of GITHUB_TOKEN_PATTERNS) { const matches = content.match(pattern); if (matches) { @@ -185572,7 +185614,7 @@ async function scanArchiveFile(archivePath, relativeArchivePath, extractDir, log findings: [] }; try { - const tempExtractDir = fs26.mkdtempSync( + const tempExtractDir = fs27.mkdtempSync( path22.join(extractDir, `extract-${depth}-`) ); const fileName = path22.basename(archivePath).toLowerCase(); @@ -185606,7 +185648,7 @@ async function scanArchiveFile(archivePath, relativeArchivePath, extractDir, log path22.basename(archivePath, ".gz") ); await exec.exec("gunzip", ["-c", archivePath], { - outStream: fs26.createWriteStream(outputFile), + outStream: fs27.createWriteStream(outputFile), silent: true }); } else if (fileName.endsWith(".zip")) { @@ -185627,7 +185669,7 @@ async function scanArchiveFile(archivePath, relativeArchivePath, extractDir, log ); result.scannedFiles += scanResult.scannedFiles; result.findings.push(...scanResult.findings); - fs26.rmSync(tempExtractDir, { recursive: true, force: true }); + fs27.rmSync(tempExtractDir, { recursive: true, force: true }); } catch (e) { logger2.debug( `Could not extract or scan archive file ${archivePath}: ${getErrorMessage(e)}` @@ -185662,7 +185704,7 @@ async function scanDirectory(dirPath, baseRelativePath, logger2, depth = 0) { scannedFiles: 0, findings: [] }; - const entries = fs26.readdirSync(dirPath, { withFileTypes: true }); + const entries = fs27.readdirSync(dirPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path22.join(dirPath, entry.name); const relativePath2 = path22.join(baseRelativePath, entry.name); @@ -185697,10 +185739,10 @@ async function scanArtifactsForTokens(filesToScan, logger2) { scannedFiles: 0, findings: [] }; - const tempScanDir = fs26.mkdtempSync(path22.join(os7.tmpdir(), "artifact-scan-")); + const tempScanDir = fs27.mkdtempSync(path22.join(os7.tmpdir(), "artifact-scan-")); try { for (const filePath of filesToScan) { - const stats = fs26.statSync(filePath); + const stats = fs27.statSync(filePath); const fileName = path22.basename(filePath); if (stats.isDirectory()) { const dirResult = await scanDirectory(filePath, fileName, logger2); @@ -185738,7 +185780,7 @@ async function scanArtifactsForTokens(filesToScan, logger2) { } } finally { try { - fs26.rmSync(tempScanDir, { recursive: true, force: true }); + fs27.rmSync(tempScanDir, { recursive: true, force: true }); } catch (e) { logger2.debug( `Could not clean up temporary scan directory: ${getErrorMessage(e)}` @@ -185760,10 +185802,10 @@ async function uploadCombinedSarifArtifacts(logger2, gitHubVariant) { ); const baseTempDir = path23.resolve(tempDir, "combined-sarif"); const toUpload = []; - if (fs27.existsSync(baseTempDir)) { - const outputDirs = fs27.readdirSync(baseTempDir); + if (fs28.existsSync(baseTempDir)) { + const outputDirs = fs28.readdirSync(baseTempDir); for (const outputDir of outputDirs) { - const sarifFiles = fs27.readdirSync(path23.resolve(baseTempDir, outputDir)).filter((f) => path23.extname(f) === ".sarif"); + const sarifFiles = fs28.readdirSync(path23.resolve(baseTempDir, outputDir)).filter((f) => path23.extname(f) === ".sarif"); for (const sarifFile of sarifFiles) { toUpload.push(path23.resolve(baseTempDir, outputDir, sarifFile)); } @@ -185790,17 +185832,17 @@ async function uploadCombinedSarifArtifacts(logger2, gitHubVariant) { function tryPrepareSarifDebugArtifact(config, language, logger2) { try { const analyzeActionOutputDir = process.env["CODEQL_ACTION_SARIF_RESULTS_OUTPUT_DIR" /* SARIF_RESULTS_OUTPUT_DIR */]; - if (analyzeActionOutputDir !== void 0 && fs27.existsSync(analyzeActionOutputDir) && fs27.lstatSync(analyzeActionOutputDir).isDirectory()) { + if (analyzeActionOutputDir !== void 0 && fs28.existsSync(analyzeActionOutputDir) && fs28.lstatSync(analyzeActionOutputDir).isDirectory()) { const sarifFile = path23.resolve( analyzeActionOutputDir, `${language}.sarif` ); - if (fs27.existsSync(sarifFile)) { + if (fs28.existsSync(sarifFile)) { const sarifInDbLocation = path23.resolve( config.dbLocation, `${language}.sarif` ); - fs27.copyFileSync(sarifFile, sarifInDbLocation); + fs28.copyFileSync(sarifFile, sarifInDbLocation); return sarifInDbLocation; } } @@ -185968,10 +186010,10 @@ async function createPartialDatabaseBundle(config, language) { core15.info( `${config.debugDatabaseName}-${language} is not finalized. Uploading partial database bundle at ${databaseBundlePath}...` ); - if (fs27.existsSync(databaseBundlePath)) { - await fs27.promises.rm(databaseBundlePath, { force: true }); + if (fs28.existsSync(databaseBundlePath)) { + await fs28.promises.rm(databaseBundlePath, { force: true }); } - const output = fs27.createWriteStream(databaseBundlePath); + const output = fs28.createWriteStream(databaseBundlePath); const zip = new ZipArchive(); zip.on("error", (err) => { throw err; @@ -186021,9 +186063,9 @@ async function runWrapper2() { getCsharpTempDependencyDir() ]; for (const tempDependencyDir of tempDependencyDirs) { - if (fs28.existsSync(tempDependencyDir)) { + if (fs29.existsSync(tempDependencyDir)) { try { - fs28.rmSync(tempDependencyDir, { recursive: true }); + fs29.rmSync(tempDependencyDir, { recursive: true }); } catch (error3) { logger2.info( `Failed to remove temporary dependencies directory: ${getErrorMessage(error3)}` @@ -186138,7 +186180,7 @@ var fs31 = __toESM(require("fs")); var path25 = __toESM(require("path")); var core19 = __toESM(require_core()); var io7 = __toESM(require_io()); -var semver12 = __toESM(require_semver2()); +var semver11 = __toESM(require_semver2()); // src/config/inputs.ts async function getToolsInput(action, repositoryProperties) { @@ -186175,51 +186217,8 @@ async function getToolsInput(action, repositoryProperties) { } // src/languages/swift.ts -var fs29 = __toESM(require("fs")); -var semver11 = __toESM(require_semver2()); -var XCODE_SELECT_LINK_PATH = "/private/var/db/xcode_select_link"; -var XCODE_APP_FILENAME_PATTERN = new RegExp( - /(?Xcode_(?\d+.\d+).app)/ -); var SWIFT_TRACED_UNSUPPORTED_MACOS = 27; var SWIFT_TRACED_UNSUPPORTED_XCODE = 27; -function xcodeVersion(logger2) { - try { - const stats = fs29.statSync(XCODE_SELECT_LINK_PATH); - if (!stats.isSymbolicLink()) { - logger2.warning( - `${XCODE_SELECT_LINK_PATH} exists, but is not a symbolic link.` - ); - return void 0; - } - const xcodePath = fs29.readlinkSync(XCODE_SELECT_LINK_PATH); - if (xcodePath === "") { - logger2.warning( - `Resolving ${XCODE_SELECT_LINK_PATH} unexpectedly returned nothing.` - ); - return void 0; - } - const matchResult = xcodePath.match(XCODE_APP_FILENAME_PATTERN); - if (matchResult?.groups === void 0) { - logger2.warning( - `Xcode path '${xcodePath}' does not contain expected pattern.` - ); - return void 0; - } - const majorMinor = matchResult.groups["majorMinor"]; - const version = semver11.coerce(majorMinor); - if (version === null) { - logger2.warning(`Couldn't parse '${majorMinor}' as a semantic version.`); - return void 0; - } - return version; - } catch (err) { - logger2.warning( - `Unable to determine Xcode version: ${getErrorMessage(err)}` - ); - return void 0; - } -} function handleUnsupportedVersion(logger2, config, skipUnsupportedTracedAnalysis, product, version) { const baseMessage = [ `Traced Swift analysis is not supported on ${product} ${SWIFT_TRACED_UNSUPPORTED_MACOS} or above.`, @@ -186769,7 +186768,7 @@ async function run3(actionState) { ) { const publicPreview = "2.22.1"; const actualVer = (await codeql.getVersion()).version; - if (semver12.lt(actualVer, publicPreview)) { + if (semver11.lt(actualVer, publicPreview)) { actionState.env.export("CODEQL_ENABLE_EXPERIMENTAL_FEATURES" /* EXPERIMENTAL_FEATURES */, "true"); logger2.info("Experimental Rust analysis enabled"); } diff --git a/src/languages/swift.test.ts b/src/languages/swift.test.ts index 7df9bbe80..ccc47fcc7 100644 --- a/src/languages/swift.test.ts +++ b/src/languages/swift.test.ts @@ -7,6 +7,7 @@ import { getCodeQLForTesting } from "../codeql"; import * as diagnostics from "../diagnostics"; import { ActionsEnvVars } from "../environment"; import { Feature } from "../feature-flags"; +import { XCODE_SELECT_LINK_PATH } from "../platform"; import { checkExpectedLogMessages, checkUnexpectedLogMessages, @@ -21,136 +22,12 @@ import { import { ToolsFeature } from "../tools-features"; import { withTmpDir } from "../util"; -import { - isSwiftCompatible, - XCODE_SELECT_LINK_PATH, - xcodeVersion, -} from "./swift"; +import { isSwiftCompatible } 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) { diff --git a/src/languages/swift.ts b/src/languages/swift.ts index a35f9c30e..1ae37d9dd 100644 --- a/src/languages/swift.ts +++ b/src/languages/swift.ts @@ -1,5 +1,3 @@ -import * as fs from "fs"; - import * as semver from "semver"; import { ActionState, Logger } from "../action-common"; @@ -7,82 +5,18 @@ import { CodeQL } from "../codeql"; import { Config } from "../config-utils"; import { addDiagnostic, makeDiagnostic } from "../diagnostics"; import { Feature } from "../feature-flags"; -import { macOSVersion } from "../platform"; +import { macOSVersion, xcodeVersion } from "../platform"; import { ToolsFeature } from "../tools-features"; -import { ConfigurationError, getErrorMessage } from "../util"; +import { ConfigurationError } 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( - /(?Xcode_(?\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; - } -} - /** * Creates a diagnostic indicating that `version` of `product` is unsupported for traced Swift analysis. * Depending on `skipUnsupportedTracedAnalysis`, this function then either throws a {@link ConfigurationError} diff --git a/src/platform.test.ts b/src/platform.test.ts index 803e28b90..5de468d82 100644 --- a/src/platform.test.ts +++ b/src/platform.test.ts @@ -1,7 +1,23 @@ -import test from "ava"; +import fs from "fs"; -import { BundlePlatform, getBundlePlatform, macOSVersion } from "./platform"; -import { initAllState } from "./testing-utils"; +import test from "ava"; +import * as sinon from "sinon"; + +import { + BundlePlatform, + getBundlePlatform, + macOSVersion, + XCODE_SELECT_LINK_PATH, + xcodeVersion, +} from "./platform"; +import { + checkExpectedLogMessages, + initAllState, + RecordingLogger, + setupTests, +} from "./testing-utils"; + +setupTests(test); for (const [platform, arch, expected] of [ ["linux", "x64", BundlePlatform.Linux64], @@ -54,3 +70,123 @@ test("macOSVersion returns semver if parsing succeeds", (t) => { t.is(version["patch"], 1); } }); + +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]); +}); diff --git a/src/platform.ts b/src/platform.ts index 59785d312..d5c77c9f7 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -1,6 +1,9 @@ +import fs from "fs"; + import * as semver from "semver"; -import type { ActionState } from "./action-common"; +import type { ActionState, Logger } from "./action-common"; +import { getErrorMessage } from "./util"; /** Platform identifiers used in CodeQL bundle asset names. */ export enum BundlePlatform { @@ -53,3 +56,67 @@ export function macOSVersion( return version; } + +/** 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( + /(?Xcode_(?\d+.\d+).app)/, +); + +/** + * 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; + } +}