rugby
Version:
A Node.js CLI to collect npm package tarballs and assets (including those fetched by pre/postinstall scripts) for offline Artifactory use.
69 lines (57 loc) • 2.02 kB
JavaScript
;
// This module is injected via NODE_OPTIONS=--require to intercept http/https requests
// performed by lifecycle scripts and tee responses into files for offline capture.
const fs = require("fs");
const path = require("path");
const crypto = require("crypto");
const urlLib = require("url");
const assetsDir = process.env.RUGBY_ASSETS_DIR || path.join(process.cwd(), ".rugby-assets");
const logPath = process.env.RUGBY_LOG || path.join(assetsDir, "network.log");
function log(line) {
try {
fs.mkdirSync(path.dirname(logPath), { recursive: true });
fs.appendFileSync(logPath, line + "\n");
} catch {}
}
function teeResponse(url, res) {
try {
const safe = crypto.createHash("sha256").update(String(url)).digest("hex");
const dest = path.join(assetsDir, `${safe}.bin`);
fs.mkdirSync(assetsDir, { recursive: true });
const out = fs.createWriteStream(dest);
res.pipe(out);
out.on("finish", () => out.close());
log(`[http-hook] captured ${url} -> ${dest} (${res.statusCode})`);
} catch (e) {
log(`[http-hook] error capturing ${url}: ${e.message}`);
}
}
function wrapModule(modName) {
const mod = require(modName);
const origRequest = mod.request;
const origGet = mod.get;
mod.request = function wrappedRequest(url, options, callback) {
let reqUrl = url;
let cb = callback;
if (typeof url === "string") {
// string url
} else if (url && typeof url === "object") {
reqUrl = urlLib.format(url);
}
if (typeof options === "function") {
cb = options;
options = undefined; // eslint-disable-line no-param-reassign
}
return origRequest.call(this, reqUrl, options, function onResponse(res) {
try { teeResponse(reqUrl, res); } catch {}
if (cb) cb(res);
});
};
mod.get = function wrappedGet(url, options, callback) {
const req = mod.request(url, options, callback);
req.end();
return req;
};
}
try { wrapModule("http"); } catch {}
try { wrapModule("https"); } catch {}