diff --git a/npm/package-lock.json b/npm/package-lock.json index 30ea704bd..0c3dea6ef 100644 --- a/npm/package-lock.json +++ b/npm/package-lock.json @@ -9,12 +9,22 @@ "version": "0.0.0", "hasInstallScript": true, "dependencies": { - "tar": "^6.2.0" + "tar": "^6.2.0", + "yauzl": "^3.2.0" }, "bin": { "infisical": "bin/infisical" } }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -87,6 +97,12 @@ "node": ">=10" } }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT" + }, "node_modules/tar": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", @@ -107,6 +123,19 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yauzl": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", + "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } } } } diff --git a/npm/package.json b/npm/package.json index c7b2ee7d3..0b9d0bbaf 100644 --- a/npm/package.json +++ b/npm/package.json @@ -8,7 +8,7 @@ "command-line" ], "bin": { - "infisical": "bin/infisical" + "infisical": "./bin/infisical" }, "repository": { "type": "git", @@ -16,9 +16,10 @@ }, "author": "Infisical Inc, ", "scripts": { - "postinstall": "node src/index.cjs" + "preinstall": "node src/index.cjs" }, "dependencies": { - "tar": "^6.2.0" + "tar": "^6.2.0", + "yauzl": "^3.2.0" } } diff --git a/npm/src/index.cjs b/npm/src/index.cjs index 28fe0979b..398b3501a 100644 --- a/npm/src/index.cjs +++ b/npm/src/index.cjs @@ -4,13 +4,20 @@ const stream = require("node:stream"); const tar = require("tar"); const path = require("path"); const zlib = require("zlib"); +const yauzl = require("yauzl"); + const packageJSON = require("../package.json"); -const supportedPlatforms = ["linux", "darwin", "win32", "freebsd"]; +const supportedPlatforms = ["linux", "darwin", "win32", "freebsd", "windows"]; const outputDir = "bin"; const getPlatform = () => { - const platform = process.platform; + let platform = process.platform; + + if (platform === "win32") { + platform = "windows"; + } + if (!supportedPlatforms.includes(platform)) { console.error("Your platform doesn't seem to be of type darwin, linux or windows"); process.exit(1); @@ -53,12 +60,47 @@ const getArchitecture = () => { return arch; }; +async function extractZip(buffer, targetPath) { + return new Promise((resolve, reject) => { + yauzl.fromBuffer(buffer, { lazyEntries: true }, (err, zipfile) => { + if (err) return reject(err); + + zipfile.readEntry(); + zipfile.on("entry", entry => { + const isExecutable = entry.fileName === "infisical" || entry.fileName === "infisical.exe"; + + if (/\/$/.test(entry.fileName) || !isExecutable) { + // Directory entry + zipfile.readEntry(); + } else { + // File entry + zipfile.openReadStream(entry, (err, readStream) => { + if (err) return reject(err); + + const outputPath = path.join(targetPath, entry.fileName.includes("infisical") ? "infisical" : entry.fileName); + const writeStream = fs.createWriteStream(outputPath); + + readStream.pipe(writeStream); + writeStream.on("close", () => { + zipfile.readEntry(); + }); + }); + } + }); + + zipfile.on("end", resolve); + zipfile.on("error", reject); + }); + }); +} + async function main() { const PLATFORM = getPlatform(); const ARCH = getArchitecture(); const NUMERIC_RELEASE_VERSION = packageJSON.version; const LATEST_RELEASE_VERSION = `v${NUMERIC_RELEASE_VERSION}`; - const downloadLink = `https://github.com/Infisical/infisical/releases/download/infisical-cli/${LATEST_RELEASE_VERSION}/infisical_${NUMERIC_RELEASE_VERSION}_${PLATFORM}_${ARCH}.tar.gz`; + const EXTENSION = PLATFORM === "windows" ? "zip" : "tar.gz"; + const downloadLink = `https://github.com/Infisical/infisical/releases/download/infisical-cli/${LATEST_RELEASE_VERSION}/infisical_${NUMERIC_RELEASE_VERSION}_${PLATFORM}_${ARCH}.${EXTENSION}`; // Ensure the output directory exists if (!fs.existsSync(outputDir)) { @@ -77,19 +119,26 @@ async function main() { throw new Error(`Failed to fetch: ${response.status} - ${response.statusText}`); } - await new Promise((resolve, reject) => { - const outStream = stream.Readable.fromWeb(response.body) - .pipe(zlib.createGunzip()) - .pipe( - tar.x({ - C: path.join(outputDir), - filter: path => path === "infisical" - }) - ); + if (EXTENSION === "zip") { + // For ZIP files, we need to buffer the whole thing first + const buffer = await response.arrayBuffer(); + await extractZip(Buffer.from(buffer), outputDir); + } else { + // For tar.gz files, we stream + await new Promise((resolve, reject) => { + const outStream = stream.Readable.fromWeb(response.body) + .pipe(zlib.createGunzip()) + .pipe( + tar.x({ + C: path.join(outputDir), + filter: path => path === "infisical" + }) + ); - outStream.on("error", reject); - outStream.on("close", resolve); - }); + outStream.on("error", reject); + outStream.on("close", resolve); + }); + } // Give the binary execute permissions if we're not on Windows if (PLATFORM !== "win32") {