From 58a59d31f69ccf6f35d6960c8c7715ec50f0a845 Mon Sep 17 00:00:00 2001 From: "Fotis Koutoulakis (@NlightNFotis)" Date: Mon, 31 Mar 2025 15:35:53 +0100 Subject: [PATCH] feat: add module for checking basic system requirements --- lib/system-reqs.js | 112 +++++++++++++++++++++++++ lib/system-reqs.js.map | 1 + node_modules/.package-lock.json | 14 ++++ package-lock.json | 15 ++++ package.json | 1 + src/system-reqs.ts | 139 ++++++++++++++++++++++++++++++++ 6 files changed, 282 insertions(+) create mode 100644 lib/system-reqs.js create mode 100644 lib/system-reqs.js.map create mode 100644 src/system-reqs.ts diff --git a/lib/system-reqs.js b/lib/system-reqs.js new file mode 100644 index 0000000000..5ec6d3adf4 --- /dev/null +++ b/lib/system-reqs.js @@ -0,0 +1,112 @@ +"use strict"; +// Enforce system requirements for the CodeQL CLI. +// https://codeql.github.com/docs/codeql-overview/system-requirements/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.passesSystemRequirements = passesSystemRequirements; +const macos_version_1 = require("macos-version"); +const util_1 = require("./util"); +const fs_1 = require("fs"); +const os_1 = __importDefault(require("os")); +const platformMap = { + 'darwin': 'macOS', + 'linux': 'Linux', + 'win32': 'Windows', +}; +const platformRequirements = { + 'macOS': { + 'version': ['13.0.1', '14.0.1'], + 'arch': ['arm64', 'x64'], + }, + 'Linux': { + // We only accept Ubuntu 20.04, 21.04, and 22.04 + 'version': ['20.04', '21.04', '22.04'], + 'arch': ['x64'], + }, + 'Windows': { + 'version': ['10', 'Server 2019', '11', 'Server 2022'], + 'arch': ['x64'], + }, +}; +function passesSystemRequirements() { + // CodeQL System requirements are two-level: the CLI must run on a specific platform, + // and that platform must have certain capabilities. + const platform = getPlatform(); + switch (platform) { + case 'macOS': + return checkMacOSPlatform(); + case 'Linux': + return checkLinuxPlatform(); + case 'Windows': + return checkWindowsPlatform(); + default: + (0, util_1.assertNever)(platform); + } +} +// MacOS checks +function checkMacOSPlatform() { + const macOSPlatformRequirements = platformRequirements['macOS']; + const passesSystemRequirements = checkMacOSVersion(macOSPlatformRequirements['version']) && checkMacOSArch(macOSPlatformRequirements['arch']); + return passesSystemRequirements; +} +function checkMacOSVersion(supportedVersions) { + return supportedVersions.some((version) => { + (0, macos_version_1.isMacOSVersionGreaterThanOrEqualTo)(version); + }); +} +function checkMacOSArch(supportedArchs) { + const arch = getArch(); + return supportedArchs.includes(arch); +} +// Linux checks +function checkLinuxPlatform() { + const linuxPlatformRequirements = platformRequirements['Linux']; + return checkLinuxVersion(linuxPlatformRequirements['version']) && checkLinuxArch(linuxPlatformRequirements['arch']); +} +function checkLinuxVersion(supportedVersions) { + const data = (0, fs_1.readFileSync)('/etc/os-release', 'utf8'); + const lines = data.split('\n'); + const releaseDetails = {}; + lines.forEach((line) => { + // Split the line into a key and value delimited by '=' + const words = line.split('='); + if (words.length === 2) { + releaseDetails[words[0].trim().toLowerCase()] = words[1].trim(); + } + }); + return releaseDetails.name == 'Ubuntu' && supportedVersions.includes(releaseDetails.version_id); +} +function checkLinuxArch(supportedArchs) { + const arch = getArch(); + return supportedArchs.includes(arch); +} +// Windows checks +function checkWindowsPlatform() { + const windowsPlatformRequirements = platformRequirements['Windows']; + return checkWindowsVersion(windowsPlatformRequirements['version']) && checkWindowsArch(windowsPlatformRequirements['arch']); +} +function checkWindowsVersion(supportedVersions) { + // os.release() on windows returns a string like "Windows 11 Home" + const windowsVersion = os_1.default.release(); + return supportedVersions.some(version => new RegExp(version, 'i').test(windowsVersion)); +} +function checkWindowsArch(supportedArchs) { + const arch = getArch(); + return supportedArchs.includes(arch); +} +; +// Auxiliary functions +function getPlatform() { + const platform = process.platform; + const mappedPlatform = platformMap[platform]; + if (mappedPlatform === undefined) { + throw new util_1.ConfigurationError(`Unsupported platform: ${platform}`); + } + return mappedPlatform; +} +function getArch() { + return process.arch; +} +//# sourceMappingURL=system-reqs.js.map \ No newline at end of file diff --git a/lib/system-reqs.js.map b/lib/system-reqs.js.map new file mode 100644 index 0000000000..e21730f718 --- /dev/null +++ b/lib/system-reqs.js.map @@ -0,0 +1 @@ +{"version":3,"file":"system-reqs.js","sourceRoot":"","sources":["../src/system-reqs.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 7370d99d2c..42f4aef64e 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -6010,6 +6010,20 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/macos-version": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/macos-version/-/macos-version-6.0.0.tgz", + "integrity": "sha512-O2S8voA+pMfCHhBn/TIYDXzJ1qNHpPDU32oFxglKnVdJABiYYITt45oLkV9yhwA3E2FDwn3tQqUFrTsr1p3sBQ==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/map-age-cleaner": { "version": "0.1.3", "dev": true, diff --git a/package-lock.json b/package-lock.json index 438aa1409a..15d3bef5da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "js-yaml": "^4.1.0", "jsonschema": "1.4.1", "long": "^5.3.1", + "macos-version": "^6.0.0", "node-forge": "^1.3.1", "octokit": "^4.1.2", "path": "^0.12.7", @@ -6078,6 +6079,20 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/macos-version": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/macos-version/-/macos-version-6.0.0.tgz", + "integrity": "sha512-O2S8voA+pMfCHhBn/TIYDXzJ1qNHpPDU32oFxglKnVdJABiYYITt45oLkV9yhwA3E2FDwn3tQqUFrTsr1p3sBQ==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/map-age-cleaner": { "version": "0.1.3", "dev": true, diff --git a/package.json b/package.json index 349a7aa936..0e3e04f0f3 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "js-yaml": "^4.1.0", "jsonschema": "1.4.1", "long": "^5.3.1", + "macos-version": "^6.0.0", "node-forge": "^1.3.1", "octokit": "^4.1.2", "path": "^0.12.7", diff --git a/src/system-reqs.ts b/src/system-reqs.ts new file mode 100644 index 0000000000..9d374178cd --- /dev/null +++ b/src/system-reqs.ts @@ -0,0 +1,139 @@ +// Enforce system requirements for the CodeQL CLI. +// https://codeql.github.com/docs/codeql-overview/system-requirements/ + +import { readFileSync } from "fs"; +import os from "os"; + +import { isMacOSVersionGreaterThanOrEqualTo } from "macos-version"; + +import { assertNever, ConfigurationError } from "./util"; + +const platformMap = { + darwin: "macOS", + linux: "Linux", + win32: "Windows", +}; + +const platformRequirements = { + macOS: { + version: ["13.0.1", "14.0.1"], + arch: ["arm64", "x64"], + }, + Linux: { + // We only accept Ubuntu 20.04, 21.04, and 22.04 + version: ["20.04", "21.04", "22.04"], + arch: ["x64"], + }, + Windows: { + version: ["10", "Server 2019", "11", "Server 2022"], + arch: ["x64"], + }, +}; + +export function passesSystemRequirements(): boolean { + // CodeQL System requirements are two-level: the CLI must run on a specific platform, + // and that platform must have certain capabilities. + + const platform = getPlatform(); + switch (platform) { + case "macOS": + return checkMacOSPlatform(); + case "Linux": + return checkLinuxPlatform(); + case "Windows": + return checkWindowsPlatform(); + default: + assertNever(platform); + } +} + +// MacOS checks + +function checkMacOSPlatform(): boolean { + const macOSPlatformRequirements = platformRequirements["macOS"]; + return ( + checkMacOSVersion(macOSPlatformRequirements["version"]) && + checkMacOSArch(macOSPlatformRequirements["arch"]) + ); +} + +function checkMacOSVersion(supportedVersions: string[]): boolean { + return supportedVersions.some((version) => { + return isMacOSVersionGreaterThanOrEqualTo(version); + }); +} + +function checkMacOSArch(supportedArchs: string[]): boolean { + const arch = getArch(); + return supportedArchs.includes(arch); +} + +// Linux checks + +function checkLinuxPlatform(): boolean { + const linuxPlatformRequirements = platformRequirements["Linux"]; + return ( + checkLinuxVersion(linuxPlatformRequirements["version"]) && + checkLinuxArch(linuxPlatformRequirements["arch"]) + ); +} + +function checkLinuxVersion(supportedVersions: string[]): boolean { + const data = readFileSync("/etc/os-release", "utf8"); + const lines = data.split("\n"); + const releaseDetails: Record = {}; + for (const line of lines) { + const words = line.split("="); + if (words.length === 2) { + releaseDetails[words[0].trim().toLowerCase()] = words[1].trim(); + } + } + + return ( + releaseDetails.name === "Ubuntu" && + supportedVersions.includes(releaseDetails.version_id) + ); +} + +function checkLinuxArch(supportedArchs: string[]): boolean { + const arch = getArch(); + return supportedArchs.includes(arch); +} + +// Windows checks + +function checkWindowsPlatform(): boolean { + const windowsPlatformRequirements = platformRequirements["Windows"]; + return ( + checkWindowsVersion(windowsPlatformRequirements["version"]) && + checkWindowsArch(windowsPlatformRequirements["arch"]) + ); +} + +function checkWindowsVersion(supportedVersions: string[]): boolean { + // os.release() on windows returns a string like "Windows 11 Home" + const windowsVersion = os.release(); + return supportedVersions.some((version) => + new RegExp(version, "i").test(windowsVersion), + ); +} + +function checkWindowsArch(supportedArchs: string[]): boolean { + const arch = getArch(); + return supportedArchs.includes(arch); +} + +// Auxiliary functions + +function getPlatform(): "macOS" | "Linux" | "Windows" { + const platform = process.platform; + const mappedPlatform = platformMap[platform]; + if (mappedPlatform === undefined) { + throw new ConfigurationError(`Unsupported platform: ${platform}`); + } + return mappedPlatform; +} + +function getArch(): string { + return process.arch; +}