@goreleaser/goreleaser-pro
Version:
Release engineering, simplified.
260 lines (232 loc) • 6.32 kB
JavaScript
// This file was generated by GoReleaser. DO NOT EDIT.
import fs from "fs";
import crypto from "crypto";
import http from "http";
import https from "https";
import path from "path";
import JSZip from "jszip";
import { x as tarExtract } from "tar";
import { ProxyAgent } from "proxy-agent";
import { spawnSync } from "child_process";
import { fileURLToPath } from "url";
const { archives, name, version } = JSON.parse(
fs.readFileSync(new URL("./package.json", import.meta.url), "utf8"),
);
const agent = new ProxyAgent();
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const getArchive = () => {
let target = `${process.platform}-${process.arch}`;
const archive = archives[target];
if (!archive) {
throw new Error(`No archive available for ${target}`);
}
return archive;
};
const binDir = path.join(__dirname, "bin");
async function extractTar(tarPath, binaries, dir, wrappedIn) {
try {
const filesToExtract = wrappedIn
? binaries.map((bin) =>
path.join(wrappedIn, bin).replace(/\\/g, "/"),
)
: binaries;
await tarExtract({
file: tarPath,
cwd: dir,
filter: (path) => filesToExtract.includes(path),
});
// If wrapped, move files from wrapped directory to bin directory
if (wrappedIn) {
const wrappedDir = path.join(dir, wrappedIn);
for (const binary of binaries) {
const srcPath = path.join(wrappedDir, binary);
const destPath = path.join(dir, binary);
if (fs.existsSync(srcPath)) {
fs.renameSync(srcPath, destPath);
}
}
// Clean up empty wrapped directory
try {
fs.rmSync(wrappedDir, { recursive: true, force: true });
} catch (err) {
// Ignore cleanup errors
}
}
console.log(`Successfully extracted ${binaries} to "${dir}"`);
} catch (err) {
throw new Error(`Extraction failed: ${err.message}`);
}
}
async function extractZip(zipPath, binaries, dir, wrappedIn) {
try {
const zipData = fs.readFileSync(zipPath);
const zip = await JSZip.loadAsync(zipData);
for (const binary of binaries) {
const binaryPath = wrappedIn
? path.join(wrappedIn, binary).replace(/\\/g, "/")
: binary;
if (!zip.files[binaryPath]) {
throw new Error(
`Error: ${binaryPath} does not exist in ${zipPath}`,
);
}
const content = await zip.files[binaryPath].async("nodebuffer");
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
const file = path.join(dir, binary);
fs.writeFileSync(file, content);
fs.chmodSync(file, "755");
console.log(`Successfully extracted "${binary}" to "${dir}"`);
}
} catch (err) {
throw new Error(`Extraction failed: ${err.message}`);
}
}
const run = async (bin) => {
await install();
if (process.platform === "win32") {
bin += ".exe";
}
const [, , ...args] = process.argv;
let result = spawnSync(path.join(binDir, bin), args, {
cwd: process.cwd(),
stdio: "inherit",
});
if (result.error) {
console.error(result.error);
}
return result.status;
};
const install = async () => {
try {
let archive = getArchive();
if (await exists(archive)) {
return;
}
let tmp = fs.mkdtempSync("archive-");
let archivePath = path.join(tmp, archive.name);
await download(archive.url, archivePath);
verify(archivePath, archive.checksum);
if (!fs.existsSync(binDir)) {
fs.mkdirSync(binDir);
}
switch (archive.format) {
case "binary":
const bin = path.join(binDir, archive.bins[0]);
fs.copyFileSync(archivePath, bin);
fs.chmodSync(bin, 0o755);
break;
case "zip":
await extractZip(
archivePath,
archive.bins,
binDir,
archive.wrappedIn,
);
break;
case "tar":
case "tar.gz":
case "tgz":
await extractTar(
archivePath,
archive.bins,
binDir,
archive.wrappedIn,
);
break;
case "tar.zst":
case "tzst":
case "tar.xz":
case "txz":
default:
throw new Error(`unsupported format: ${archive.format}`);
}
console.log(`Installed ${name} ${version} to ${binDir}`);
} catch (err) {
throw new Error(`Installation failed: ${err.message}`);
}
};
const verify = (filename, checksum) => {
if (checksum.algorithm == "" || checksum.digest == "") {
console.warn("Warning: No checksum provided for verification");
return;
}
let digest = crypto
.createHash(checksum.algorithm)
.update(fs.readFileSync(filename))
.digest("hex");
if (digest != checksum.digest) {
throw new Error(
`${filename}: ${checksum.algorithm} does not match, expected ${checksum.digest}, got ${digest}`,
);
}
};
const download = async (url, filename, maxRedirects = 10) => {
try {
console.log(`Downloading ${url} to ${filename}...`);
const dir = path.dirname(filename);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
return new Promise((resolve, reject) => {
const parsedUrl = new URL(url);
const mod = parsedUrl.protocol === "https:" ? https : http;
const request = mod.get(url, { agent }, (response) => {
if (
response.statusCode >= 300 &&
response.statusCode < 400 &&
response.headers.location
) {
if (maxRedirects <= 0) {
reject(new Error("Too many redirects"));
return;
}
download(response.headers.location, filename, maxRedirects - 1)
.then(resolve)
.catch(reject);
return;
}
if (response.statusCode !== 200) {
reject(
new Error(
`HTTP ${response.statusCode}: ${response.statusMessage}`,
),
);
return;
}
const writer = fs.createWriteStream(filename);
response.pipe(writer);
writer.on("finish", () => {
console.log(`Download complete: ${filename}`);
resolve(dir);
});
writer.on("error", (err) => {
console.error(`Error writing file: ${err.message}`);
reject(err);
});
});
request.on("error", (err) => {
reject(new Error(`Request failed: ${err.message}`));
});
request.setTimeout(300000, () => {
request.destroy();
reject(new Error("Request timed out"));
});
});
} catch (err) {
throw new Error(`Download failed: ${err.message}`);
}
};
function exists(archive) {
if (!fs.existsSync(binDir)) {
return false;
}
return archive.bins.every((bin) => fs.existsSync(path.join(binDir, bin)));
}
export {
install,
run,
getArchive,
download,
};