Files
codeql-action/node_modules/@protobuf-ts/protoc/util.js
Angela P Wen a196a714b8 Bump artifact dependencies if CODEQL_ACTION_ARTIFACT_V2_UPGRADE enabled (#2482)
Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
Co-authored-by: Henry Mercer <henrymercer@github.com>
2024-10-01 09:59:05 -07:00

427 lines
13 KiB
JavaScript

const path = require("path");
const fs = require("fs");
const os = require("os");
const assert = require("assert");
const standardInstallDirectory = path.join(__dirname, "installed");
module.exports.standardInstallDirectory = standardInstallDirectory;
/**
* Make directory, creating missing parent directories as well.
* Equivalent to fs.mkdirSync(p, {recursive: true});
* @param {string} dirname
*/
module.exports.mkDirRecursive = function mkDirRecursive(dirname) {
if (!path.isAbsolute(dirname)) {
dirname = path.join(process.cwd(), dirname);
}
dirname = path.normalize(dirname);
let parts = dirname.split(path.sep);
for (let i = 2; i <= parts.length; i++) {
let p = parts.slice(0, i).join(path.sep);
if (fs.existsSync(p)) {
let i = fs.lstatSync(p);
if (!i.isDirectory()) {
throw new Error("cannot mkdir '" + dirname + "'. '" + p + "' is not a directory.");
}
} else {
fs.mkdirSync(p);
}
}
};
/**
* @typedef {Object} DistEntry
* @property {string} name
* @property {string} version
* @property {string} protocPath
* @property {string} includePath
*/
/**
* @param {string} installDir
* @return {DistEntry[]}
*/
module.exports.listInstalled = function listInstalled(installDir = standardInstallDirectory) {
let entries = [];
for (let name of fs.readdirSync(installDir)) {
let abs = path.join(installDir, name);
if (!fs.lstatSync(abs).isDirectory()) {
continue;
}
// looking for directory names "protoc-3.13.0-win32"
if (!name.startsWith("protoc-")) {
continue;
}
let version = name.split("-")[1];
let protocPath = path.join(abs, "bin/protoc.exe");
if (!fs.existsSync(protocPath)) {
protocPath = path.join(abs, "bin/protoc");
}
let includePath = path.join(abs, "include/")
entries.push({name, version, protocPath, includePath});
}
return entries;
};
/**
* Download url into path. Returns path.
* @param {string} url
* @returns {Promise<Buffer>}
*/
module.exports.httpDownload = function download(url) {
assert(typeof url === "string" && url.length > 0);
assert(url.startsWith("https://") || url.startsWith("http://"));
const chunks = [];
return new Promise((resolve, reject) => {
httpGet(url, []).then(
response => {
response.setEncoding("binary");
response.on("data", chunk => {
chunks.push(Buffer.from(chunk, "binary"))
});
response.on("end", () => {
resolve(Buffer.concat(chunks));
})
},
reason => reject(reason)
);
});
};
/**
* @param {string} url
* @return {Promise<string>}
*/
module.exports.httpGetRedirect = function httpGetRedirect(url) {
assert(typeof url === "string" && url.length > 0);
assert(url.startsWith("https://") || url.startsWith("http://"));
const client = url.startsWith("https") ? require("https") : require("http");
return new Promise((resolve, reject) => {
const request = client.get(url, (response) => {
if (response.statusCode >= 300 && response.statusCode < 400) {
let location = response.headers.location;
assert(location && location.length > 0);
resolve(location);
} else if (response.statusCode !== 200) {
reject(new Error(`HTTP ${response.statusCode} for ${url}`));
} else {
reject(new Error(`Did not get expected redirect for ${url}`));
}
});
request.on("error", reject);
});
};
/**
* HTTP GET, follows up to 3 redirects
* @param {string} url
* @param {string[]} redirects
* @returns {Promise<IncomingMessage>}
*/
function httpGet(url, redirects) {
assert(typeof url === "string" && url.length > 0);
assert(url.startsWith("https://") || url.startsWith("http://"));
assert(Array.isArray(redirects));
assert(redirects.length <= 3);
const client = url.startsWith("https") ? require("https") : require("http");
return new Promise((resolve, reject) => {
const request = client.get(url, (response) => {
if (response.statusCode >= 300 && response.statusCode < 400) {
let location = response.headers.location;
assert(location && location.length > 0);
let follow = httpGet(location, redirects.concat(location));
resolve(follow);
} else if (response.statusCode !== 200) {
reject(new Error(`HTTP ${response.statusCode} for ${url}`));
} else {
resolve(response);
}
});
request.on("error", reject);
});
}
/**
* @typedef {Object} ReleaseParameters
* @property {NodeJS.Platform} platform
* @property {CPUArchitecture} arch
* @property {string} version - without leading "v"
*/
/**
* @typedef {("arm" | "arm64" | "ia32" | "mips" | "mipsel" | "ppc" | "ppc64" | "s390" | "s390x" | "x32" | "x64")} CPUArchitecture
*/
/**
* protoc-22.3-linux-aarch_64.zip
* protoc-22.3-linux-ppcle_64.zip
* protoc-22.3-linux-s390_64.zip
* protoc-22.3-linux-x86_32.zip
* protoc-22.3-linux-x86_64.zip
* protoc-22.3-osx-aarch_64.zip
* protoc-22.3-osx-universal_binary.zip
* protoc-22.3-osx-x86_64.zip
* protoc-22.3-win32.zip
+ protoc-22.3-win64.zip
*
* @param {ReleaseParameters} params
* @return {string}
*/
module.exports.makeReleaseName = function makeReleaseName(params) {
let build = `${params.platform}-${params.arch}`;
switch (params.platform) {
case "darwin":
if (params.arch === "arm64") {
build = 'osx-aarch_64'
} else if (params.arch === "x64") {
build = 'osx-x86_64'
} else {
build = 'osx-universal_binary'
}
break;
case "linux":
if (params.arch === "x64") {
build = 'linux-x86_64'
} else if (params.arch === "x32") {
build = 'linux-x86_32'
} else if (params.arch === "arm64") {
build = 'linux-aarch_64'
}
break;
case "win32":
if (params.arch === "x64") {
build = 'win64'
} else if (params.arch === "x32" || params.arch === "ia32") {
build = 'win32'
}
break;
}
return `protoc-${params.version}-${build}`;
}
/**
* Reads the package json from the given path if it exists and
* looks for config.protocVersion.
*
* If the package.json does not exist or does not specify a
* config.protocVersion value, walk the file system up until
* a package.json with a config.protocVersion is found.
*
* If nothing was found, return undefined.
*
* @param {string} cwd
* @returns {string | undefined}
*/
module.exports.findProtocVersionConfig = function findProtocVersionConfig(cwd) {
let version = undefined;
let dirname = cwd;
while (true) {
version = tryReadProtocVersion(path.join(dirname, "package.json"));
if (version !== undefined) {
break;
}
let parent = path.dirname(dirname);
if (parent === dirname) {
break;
}
dirname = parent;
}
return version;
};
function tryReadProtocVersion(pkgPath) {
if (!fs.existsSync(pkgPath)) {
return undefined;
}
let json = fs.readFileSync(pkgPath, "utf8");
let pkg;
try {
pkg = JSON.parse(json);
} catch (e) {
return undefined;
}
if (typeof pkg === "object" && typeof pkg.config === "object" && pkg.config !== null) {
if (pkg.config.hasOwnProperty("protocVersion") && typeof pkg.config.protocVersion == "string") {
let version = pkg.config.protocVersion;
if (typeof version === "string") {
return version;
}
}
}
return undefined;
}
/**
* @param {string} cwd
* @returns {string|undefined}
*/
module.exports.findProtobufTs = function (cwd) {
let plugin = path.join(cwd, "node_modules", "@protobuf-ts", "plugin");
return fs.existsSync(plugin) ? plugin : undefined;
}
/**
* @param {string} cwd
* @returns {string[]}
*/
module.exports.findProtocPlugins = function (cwd) {
let plugins = [];
let binDir = path.join(cwd, "node_modules", ".bin");
if (!fs.existsSync(binDir)) {
return plugins;
}
if (!fs.lstatSync(binDir).isDirectory()) {
return plugins;
}
for (let name of fs.readdirSync(binDir)) {
if (!name.startsWith("protoc-gen-")) {
continue;
}
let plugin = path.join("node_modules", ".bin", name);
plugins.push(plugin);
}
return plugins;
};
/**
* @param {string|undefined} envPath from process.env.PATH
* @returns {string|undefined}
*/
module.exports.findProtocInPath = function (envPath) {
if (typeof envPath !== "string") {
return undefined;
}
const candidates = envPath.split(path.delimiter)
.filter(p => !p.endsWith(`node_modules${path.sep}.bin`)) // make sure to exlude ...
.filter(p => !p.endsWith(`.npm-global${path.sep}bin`)) // ...
.map(p => path.join(p, os.platform() === "win32" ? "protoc.exe" : "protoc")) // we are looking for "protoc"
.map(p => p[0] === "~" ? path.join(os.homedir(), p.slice(1)) : p) // try expand "~"
;
for (let c of candidates) {
if (fs.existsSync(c)) {
return c;
}
}
return undefined;
};
/**
* @callback fileCallback
* @param {Buffer} data
* @param {LocalHeader} header
*/
/**
* @param {Buffer} buffer
* @param {fileCallback} onFile
*/
module.exports.unzip = function unzip(buffer, onFile) {
const
zlib = require("zlib"),
localHeaderSig = 0x04034b50, // Local file header signature
centralHeaderSig = 0x02014b50, // Central directory file header signature
eocdRecordSig = 0x06054b50, // End of central directory record (EOCD)
optDataDescSig = 0x08074b50, // Optional data descriptor signature
methodStored = 0,
methodDeflated = 8;
let pos = 0;
let cenFound = false;
while (pos < buffer.byteLength && !cenFound) {
if (buffer.byteLength - pos < 2) {
throw new Error("Signature too short at " + pos);
}
let sig = buffer.readUInt32LE(pos);
switch (sig) {
case localHeaderSig:
let header = readLocalHeader();
let compressedData = buffer.subarray(pos, pos + header.compressedSize);
let uncompressedData;
switch (header.compressionMethod) {
case methodDeflated:
uncompressedData = zlib.inflateRawSync(compressedData);
break;
case methodStored:
uncompressedData = compressedData;
break;
default:
throw new Error("Unsupported compression method " + header.compressionMethod);
}
if (header.filename[header.filename.length - 1] !== "/") {
onFile(uncompressedData, header);
}
pos += header.compressedSize;
break;
case centralHeaderSig:
cenFound = true;
break;
default:
throw new Error("Unexpected signature " + sig);
}
}
/**
* @typedef {Object} LocalHeader
* @property {number} version
* @property {number} generalPurposeFlags
* @property {number} compressionMethod
* @property {number} lastModificationTime
* @property {number} lastModificationDate
* @property {number} crc32
* @property {number} compressedSize
* @property {number} uncompressedSize
* @property {string} filename
* @property {Buffer} extraField
*
* @returns {undefined|LocalHeader}
*/
function readLocalHeader() {
let sig = buffer.readUInt32LE(pos);
if (sig !== localHeaderSig) {
throw new Error("Unexpected local header signature " + sig.toString(16) + " at " + pos);
}
if (buffer.byteLength - pos < 30) {
throw new Error("Local header too short at " + pos);
}
let version = buffer.readUInt32LE(pos + 4);
let generalPurposeFlags = buffer.readUInt16LE(pos + 6);
let compressionMethod = buffer.readUInt16LE(pos + 8);
let lastModificationTime = buffer.readUInt16LE(pos + 10);
let lastModificationDate = buffer.readUInt16LE(pos + 12);
let crc32 = buffer.readUInt32LE(pos + 14);
let compressedSize = buffer.readUInt32LE(pos + 18);
let uncompressedSize = buffer.readUInt32LE(pos + 22);
let filenameLength = buffer.readUInt16LE(pos + 26);
let extraFieldLength = buffer.readUInt16LE(pos + 28);
let filename = buffer.subarray(pos + 30, pos + 30 + filenameLength).toString();
let extraField = buffer.subarray(pos + 30 + filenameLength, pos + 30 + filenameLength + extraFieldLength);
pos += 30 + filenameLength + extraFieldLength;
return {
version,
generalPurposeFlags,
compressionMethod,
lastModificationTime,
lastModificationDate,
crc32,
compressedSize,
uncompressedSize,
filename,
extraField
};
}
}