UNPKG

@speedscale/proxymock

Version:

A free desktop CLI that automatically creates mocks and tests from watching your app run.

182 lines (154 loc) 4.74 kB
import { get } from "https"; import { createWriteStream, createReadStream, readFileSync, copyFileSync, unlinkSync, mkdirSync, chmodSync, } from "fs"; import { join } from "path"; import { tmpdir, platform, arch } from "os"; import { createHash } from "crypto"; import logger from "./log.js"; import { getPackageVersion } from "./version.js"; import { INSTALLROOT } from "./constants.js"; async function downloadFile(url, destPath) { return new Promise((resolve, reject) => { const file = createWriteStream(destPath); get(url, (response) => { if (response.statusCode !== 200) { reject(new Error(`Failed to download: ${response.statusCode}`)); return; } response.pipe(file); file.on("finish", () => { file.close(resolve); }); }).on("error", reject); }); } function getPlatformBinary() { const os = platform(); const architecture = arch(); let osName, archName; // Map Node.js platform names to our binary names switch (os) { case "darwin": osName = "darwin"; break; case "linux": osName = "linux"; break; case "win32": return "proxymock.exe"; // Windows binary doesn't have arch suffix default: throw new Error(`Unsupported platform: ${os}`); } // Map Node.js arch names to our binary names switch (architecture) { case "x64": archName = "amd64"; break; case "arm64": archName = "arm64"; break; default: throw new Error(`Unsupported architecture: ${architecture}`); } return `proxymock-${osName}-${archName}`; } async function validateChecksum(filePath, expectedChecksum) { return new Promise((resolve, reject) => { const hash = createHash("sha256"); const stream = createReadStream(filePath); stream.on("data", (data) => { hash.update(data); }); stream.on("end", () => { const actualChecksum = hash.digest("hex"); resolve(actualChecksum === expectedChecksum); }); stream.on("error", reject); }); } export default async function install() { try { // Read version from package.json const npmVersion = getPackageVersion(); // Convert npm version to proxymock version format // npm: "2.3.660" -> proxymock: "v2.3.660" const proxymockVersion = `v${npmVersion}`; const srcfile = getPlatformBinary(); // Build versioned URL const baseUrl = `https://downloads.speedscale.com/proxymock/${proxymockVersion}`; const srcUrl = `${baseUrl}/${srcfile}`; const shaUrl = `${baseUrl}/${srcfile}.sha256`; // Ensure install directory exists try { mkdirSync(INSTALLROOT, { recursive: true }); } catch (error) { throw new Error( `Failed to create install directory ${INSTALLROOT}: ${error.message}`, ); } // Download checksum first const tempShaPath = join(tmpdir(), `${srcfile}.sha256`); await downloadFile(shaUrl, tempShaPath); const expectedChecksum = readFileSync(tempShaPath, "utf8") .trim() .split(" ")[0]; // Download binary const tempBinPath = join(tmpdir(), srcfile); await downloadFile(srcUrl, tempBinPath); // Validate checksum const isValid = await validateChecksum(tempBinPath, expectedChecksum); if (!isValid) { throw new Error("Checksum validation failed"); } // Install - always as "proxymock" since npm version provides isolation const dstPath = join(INSTALLROOT, "proxymock"); try { copyFileSync(tempBinPath, dstPath); } catch (error) { throw new Error( `Failed to install binary to ${dstPath}: ${error.message}`, ); } // Make executable on Unix-like systems if (platform() !== "win32") { try { chmodSync(dstPath, "755"); } catch (error) { throw new Error(`Failed to make binary executable: ${error.message}`); } } // Cleanup temp files (non-fatal if it fails) try { unlinkSync(tempShaPath); } catch (error) { logger.error( `Failed to cleanup temp file ${tempShaPath}: ${error.message}`, ); } try { unlinkSync(tempBinPath); } catch (error) { logger.error( `Failed to cleanup temp file ${tempBinPath}: ${error.message}`, ); } logger.info(`installed proxymock ${proxymockVersion}`); } catch (error) { logger.error(`Installation failed: ${error.message}`); throw error; // Let the caller handle the error } } // Run installation if this script is executed directly if (import.meta.url === `file://${process.argv[1]}`) { install().catch((error) => { logger.error(`Installation failed: ${error.message}`); process.exit(1); }); }