diff --git a/npm/lib/download-release.js b/npm/lib/download-release.js deleted file mode 100644 index 255ebf0..0000000 --- a/npm/lib/download-release.js +++ /dev/null @@ -1,54 +0,0 @@ -// @ts-check -"use strict"; - -const { REPO } = require("./constants"); -const get = require("./get"); - -/** - * @param {string} repo - * @param {string} tag - */ -function getApiUrl(repo, tag) { - return `https://api.github.com/repos/${repo}/releases/tags/${tag}`; -} - -/** - * @param {{ token?: string; version: string; }} opts - */ -async function getReleaseFromGitHubApi(opts) { - const downloadOpts = { - headers: { - "user-agent": "rustywind", - }, - }; - - if (opts.token) { - downloadOpts.headers.authorization = `token ${opts.token}`; - } - - console.log(`Finding rustywind ${opts.version} release`); - const release = await get(getApiUrl(REPO, opts.version), downloadOpts); - let jsonRelease; - try { - jsonRelease = JSON.parse(release); - } catch (e) { - throw new Error("Malformed API response: " + e.stack); - } - - if (!jsonRelease.assets) { - throw new Error("Bad API response: " + JSON.stringify(release)); - } - - return jsonRelease; -} - -/** - * @param {{ token?: string; version: string; }} opts - */ -module.exports = async (opts) => { - if (!opts.version) { - return Promise.reject(new Error("Missing version")); - } - - return getReleaseFromGitHubApi(opts); -}; diff --git a/npm/lib/download.js b/npm/lib/download.js index 2b926fc..7947b52 100644 --- a/npm/lib/download.js +++ b/npm/lib/download.js @@ -11,8 +11,6 @@ const URL = url.URL; const child_process = require("child_process"); const proxy_from_env = require("proxy-from-env"); -const get = require("./get"); - const packageVersion = require("../package.json").version; const tmpDir = path.join(os.tmpdir(), `rustywind-cache-${packageVersion}`); @@ -79,10 +77,11 @@ function downloadWin(url, dest, opts) { }); } -function download(_url, dest, opts) { +async function download(_url, dest, opts) { + // Handle proxy setup const proxy = proxy_from_env.getProxyForUrl(url.parse(_url)); - if (proxy !== "") { - var HttpsProxyAgent = require("https-proxy-agent"); + if (proxy) { + const HttpsProxyAgent = require("https-proxy-agent"); opts = { ...opts, agent: new HttpsProxyAgent(proxy), @@ -90,46 +89,56 @@ function download(_url, dest, opts) { }; } + // Windows-specific handling if (isWindows) { - // This alternative strategy shouldn't be necessary but sometimes on Windows the file does not get closed, - // so unzipping it fails, and I don't know why. return downloadWin(_url, dest, opts); } + // Remove auth headers for non-GitHub URLs if (opts.headers.authorization && !isGithubUrl(_url)) { delete opts.headers.authorization; } return new Promise((resolve, reject) => { - console.log(`Download options: ${JSON.stringify(opts)}`); const outFile = fs.createWriteStream(dest); - const mergedOpts = { - ...url.parse(_url), - ...opts, - }; - https - .get(mergedOpts, (response) => { - console.log("statusCode: " + response.statusCode); - if (response.statusCode === 302) { - console.log("Following redirect to: " + response.headers.location); - return download(response.headers.location, dest, opts).then( - resolve, - reject - ); - } else if (response.statusCode !== 200) { - reject(new Error("Download failed with " + response.statusCode)); - return; - } + const mergedOpts = { ...url.parse(_url), ...opts }; - response.pipe(outFile); - outFile.on("finish", () => { - resolve(); - }); - }) - .on("error", async (err) => { - await fsUnlink(dest); - reject(err); - }); + const request = https.get(mergedOpts, (response) => { + // Handle redirects + if (response.statusCode === 302) { + outFile.destroy(); + return download(response.headers.location, dest, opts).then(resolve, reject); + } + + // Handle error status codes + if (response.statusCode !== 200) { + outFile.destroy(); + reject(new Error(`Download failed with ${response.statusCode}`)); + return; + } + + // Pipe the response to file + response.pipe(outFile); + }); + + // Handle write stream errors + outFile.on('error', async (err) => { + outFile.destroy(); + await fsUnlink(dest).catch(() => { }); + reject(err); + }); + + // Handle successful completion + outFile.on('finish', () => { + outFile.close(() => resolve(null)); + }); + + // Handle request errors + request.on('error', async (err) => { + outFile.destroy(); + await fsUnlink(dest).catch(() => { }); + reject(err); + }); }); } @@ -229,6 +238,8 @@ function untar(zipPath, destinationDir) { if (code !== 0) { reject(new Error(`tar xvf exited with ${code}`)); return; + } else { + process.exit(0); } resolve(); @@ -281,7 +292,7 @@ module.exports = async (opts) => { console.log("Deleting invalid download cache"); try { await fsUnlink(assetDownloadPath); - } catch (e) {} + } catch (e) { } throw e; } @@ -300,7 +311,7 @@ module.exports = async (opts) => { try { await fsUnlink(assetDownloadPath); - } catch (e) {} + } catch (e) { } throw e; } diff --git a/npm/lib/install.js b/npm/lib/install.js deleted file mode 100644 index 12cf710..0000000 --- a/npm/lib/install.js +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env node -const os = require("os"); -const request = require("request"); -const rp = require("request-promise"); -const fs = require("fs"); -const rimraf = require("rimraf"); -const decompress = require("decompress"); -const decompressTargz = require("decompress-targz"); - -//////////////////////////////////////////////////////////////////////////////// -const APP_NAME = "rustywind"; -const REPO = "avencera/rustywind"; -const GITHUB_REPO = `https://github.com/${REPO}`; -//////////////////////////////////////////////////////////////////////////////// - -const INSTALL_LOCATION = process.cwd(); - -const randomString = () => { - return Math.random() - .toString(36) - .substring(10); -}; - -const getPlatform = () => { - switch (os.platform()) { - case "darwin": { - return "apple-darwin"; - } - case "linux": { - return "unknown-linux-musl"; - } - case "win32": { - return "pc-windows-gnu"; - } - default: - throw `we don't current support your os (${os.platform()}) please make an issue on github`; - } -}; - -const getArch = () => { - switch (os.arch()) { - case "x64": - return "x86_64"; - default: - throw `we don't current support your cpu arch (${os.arch()}) please make an issue on github`; - } -}; - -async function getTag(repo) { - const url = `https://api.github.com/repos/${REPO}/releases/latest`; - const response = await rp(url, { - json: true, - headers: { "user-agent": "node.js" } - }); - - return response.tag_name; -} - -async function download() { - const tag = await getTag(REPO); - const url = `${GITHUB_REPO}/releases/download/${tag}/${APP_NAME}-${tag}-${getArch()}-${getPlatform()}.tar.gz`; - - const tmpdir = os.tmpdir(); - - const release = `${tmpdir}/${APP_NAME}-${tag}-${getArch()}-${getPlatform()}-${randomString()}.tar.gz`; - - console.log(`Downloading: ${APP_NAME}`); - console.log(`from: ${GITHUB_REPO}`); - console.log(`version: ${tag}`); - console.log(`platform: ${getArch()}-${getPlatform()}\n`); - - /* Create an empty file where we can save data */ - let file = fs.createWriteStream(release); - /* Using Promises so that we can use the ASYNC AWAIT syntax */ - - await new Promise((resolve, reject) => { - let stream = request({ - uri: url - }) - .pipe(file) - .on("finish", () => { - console.log("Download complete"); - resolve(); - }) - .on("error", error => { - reject(error); - }); - }).catch(error => { - console.log(`\n ------------- Something happened: ${error} --------- \n`); - }); - - await rimraf.sync(`${INSTALL_LOCATION}/rustywind`); - - console.log(`Installing to: ${INSTALL_LOCATION}`); - await decompress(release, INSTALL_LOCATION, { - plugins: [decompressTargz()] - }); - - console.log("Installation complete\n"); - - console.log("Cleaning up"); - await rimraf(`${INSTALL_LOCATION}/node_modules`, _ => - console.log(`Clean up complete\n`) - ); -} - -download(); diff --git a/npm/lib/postinstall.js b/npm/lib/postinstall.js index 638db30..a3d8bea 100644 --- a/npm/lib/postinstall.js +++ b/npm/lib/postinstall.js @@ -1,6 +1,7 @@ // @ts-check "use strict"; +// Imports const os = require("os"); const fs = require("fs"); const path = require("path"); @@ -19,14 +20,26 @@ if (forceInstall) { const { VERSION } = require("./constants"); const BIN_PATH = path.join(__dirname, "../bin"); + +//////////////////////////////////////////////////////////////////////////////// +const APP_NAME = "rustywind"; +const REPO = "avencera/rustywind"; +const GITHUB_REPO = `https://github.com/${REPO}`; +//////////////////////////////////////////////////////////////////////////////// + process.on("unhandledRejection", (reason, promise) => { console.log("Unhandled rejection: ", promise, "reason:", reason); }); function getTarget() { - const arch = process.env.npm_config_arch || os.arch(); + const arch = os.arch(); + const platform = os.platform(); - switch (os.platform()) { + console.log(`Downloading: ${APP_NAME}`); + console.log(` from: ${GITHUB_REPO}`); + console.log(` for platform: ${arch}-${platform}\n`); + + switch (platform) { case "darwin": return arch == "x64" ? "x86_64-apple-darwin" : "aarch64-apple-darwin"; case "win32": @@ -35,12 +48,12 @@ function getTarget() { return arch === "x64" ? "x86_64-unknown-linux-musl" : arch === "arm" - ? "arm-unknown-linux-gnueabihf" - : arch === "arm64" - ? "aarch64-unknown-linux-gnu" - : arch === "ppc64" - ? "powerpc64le-unknown-linux-gnu" - : "i686-unknown-linux-musl"; + ? "arm-unknown-linux-gnueabihf" + : arch === "arm64" + ? "aarch64-unknown-linux-gnu" + : arch === "ppc64" + ? "powerpc64le-unknown-linux-gnu" + : "i686-unknown-linux-musl"; default: throw new Error("Unknown platform: " + os.platform()); } @@ -59,6 +72,7 @@ async function main() { destDir: BIN_PATH, force: forceInstall, }; + try { await download(opts); } catch (err) {