@go-task/cli
Version:
A task runner / simpler Make alternative written in Go
221 lines (198 loc) • 5.3 kB
JavaScript
// This file was generated by GoReleaser. DO NOT EDIT.
const { archives } = require("./package.json");
const fs = require("fs");
const crypto = require("crypto");
const path = require("path");
const JSZip = require("jszip");
const tar = require("tar");
const axios = require("axios");
const { ProxyAgent } = require("proxy-agent");
const { spawnSync } = require("child_process");
const agent = new ProxyAgent();
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 tar.x({
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);
return;
case "zip":
return extractZip(
archivePath,
archive.bins,
binDir,
archive.wrappedIn,
);
case "tar":
case "tar.gz":
case "tgz":
return extractTar(
archivePath,
archive.bins,
binDir,
archive.wrappedIn,
);
case "tar.zst":
case "tzst":
case "tar.xz":
case "txz":
default:
throw new Error(`unsupported format: ${archive.format}`);
}
} 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) => {
try {
console.log(`Downloading ${url} to ${filename}...`);
const dir = path.dirname(filename);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
const response = await axios({
method: "GET",
url: url,
responseType: "stream",
timeout: 300000, // 5min
httpAgent: agent,
httpsAgent: agent,
});
const writer = fs.createWriteStream(filename);
response.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on("finish", () => {
console.log(`Download complete: ${filename}`);
resolve(dir);
});
writer.on("error", (err) => {
console.error(`Error writing file: ${err.message}`);
reject(err);
});
});
} 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)));
}
module.exports = {
install,
run,
getArchive,
};