@goreleaser/example
Version:
Example NPM publish pipeline
175 lines (158 loc) • 4.23 kB
JavaScript
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 { spawnSync } = require("child_process");
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) {
try {
await tar.x({
file: tarPath,
cwd: dir,
filter: (path) => binaries.includes(path),
});
console.log(`Successfully extracted binaries to "${dir}"`);
} catch (err) {
throw new Error(`Extraction failed: ${err.message}`);
}
}
async function extractZip(zipPath, binaries, dir) {
try {
const zipData = fs.readFileSync(zipPath);
const zip = await JSZip.loadAsync(zipData);
for (const binary of binaries) {
if (!zip.files[binary]) {
throw new Error(
`Error: ${binary} does not exist in ${zipPath}`,
);
}
const content = await zip.files[binary].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);
case "tar":
case "tar.gz":
case "tgz":
return extractTar(archivePath, archive.bins, binDir);
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
});
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,
};