UNPKG

@moonwall/cli

Version:

Testing framework for the Moon family of projects

166 lines 6.61 kB
import fs from "node:fs"; import { execSync } from "node:child_process"; import chalk from "chalk"; import os from "node:os"; import path from "node:path"; import { select } from "@inquirer/prompts"; export async function checkExists(path) { const binPath = path.split(" ")[0]; const fsResult = fs.existsSync(binPath); if (!fsResult) { throw new Error(`No binary file found at location: ${binPath} \n Are you sure your ${chalk.bgWhiteBright.blackBright("moonwall.config.json")} file has the correct "binPath" in launchSpec?`); } const binArch = await getBinaryArchitecture(binPath); const currentArch = os.arch(); if (binArch !== currentArch && binArch !== "unknown") { throw new Error(`The binary architecture ${chalk.bgWhiteBright.blackBright(binArch)} does not match this system's architecture ${chalk.bgWhiteBright.blackBright(currentArch)}\nDownload or compile a new binary executable for ${chalk.bgWhiteBright.blackBright(currentArch)} `); } return true; } export async function downloadBinsIfMissing(binPath) { const binName = path.basename(binPath); const binDir = path.dirname(binPath); const binPathExists = fs.existsSync(binPath); if (!binPathExists && process.arch === "x64") { const download = await select({ message: `The binary ${chalk.bgBlack.greenBright(binName)} is missing from ${chalk.bgBlack.greenBright(path.join(process.cwd(), binDir))}.\nWould you like to download it now?`, default: 0, choices: [ { name: `Yes, download ${binName}`, value: true }, { name: "No, quit program", value: false }, ], }); if (!download) { process.exit(0); } else { execSync(`mkdir -p ${binDir}`); execSync(`pnpm moonwall download ${binName} latest ${binDir}`, { stdio: "inherit", }); } } else if (!binPathExists) { console.log(`The binary: ${chalk.bgBlack.greenBright(binName)} is missing from: ${chalk.bgBlack.greenBright(path.join(process.cwd(), binDir))}`); console.log(`Given you are running ${chalk.bgBlack.yellowBright(process.arch)} architecture, you will need to build it manually from source 🛠️`); throw new Error("Executable binary not available"); } } export function checkListeningPorts(processId) { try { const stdOut = execSync(`lsof -p ${processId} | grep LISTEN`, { encoding: "utf-8", }); const binName = stdOut.split("\n")[0].split(" ")[0]; const ports = stdOut .split("\n") .filter(Boolean) .map((line) => { const port = line.split(":")[1]; return port.split(" ")[0]; }); const filtered = new Set(ports); return { binName, processId, ports: [...filtered].sort() }; } catch (e) { const binName = execSync(`ps -p ${processId} -o comm=`).toString().trim(); console.log(`Process ${processId} is running which for binary ${binName}, however it is unresponsive.`); console.log("Running Moonwall with this in the background may cause unexpected behaviour. Please manually kill the process and try running Moonwall again."); console.log(`N.B. You can kill it with: sudo kill -9 ${processId}`); throw new Error(e); } } export function checkAlreadyRunning(binaryName) { try { console.log(`Checking if ${chalk.bgWhiteBright.blackBright(binaryName)} is already running...`); // pgrep only supports 15 characters const stdout = execSync(`pgrep ${[binaryName.slice(0, 14)]}`, { encoding: "utf8", timeout: 2000, }); const pIdStrings = stdout.split("\n").filter(Boolean); return pIdStrings.map((pId) => Number.parseInt(pId, 10)); } catch (error) { if (error.status === 1) { return []; } throw error; } } export async function promptAlreadyRunning(pids) { const alreadyRunning = await select({ message: `The following processes are already running: \n${pids .map((pid) => { const { binName, ports } = checkListeningPorts(pid); return `${binName} - pid: ${pid}, listenPorts: [${ports.join(", ")}]`; }) .join("\n")}`, default: 1, choices: [ { name: "🪓 Kill processes and continue", value: "kill" }, { name: "➡️ Continue (and let processes live)", value: "continue" }, { name: "🛑 Abort (and let processes live)", value: "abort" }, ], }); switch (alreadyRunning) { case "kill": for (const pid of pids) { execSync(`kill ${pid}`); } // pids.forEach((pid) => { // execSync(`kill ${pid}`); // }); break; case "continue": break; case "abort": throw new Error("Abort Signal Picked"); } } export function checkAccess(path) { const binPath = path.split(" ")[0]; try { fs.accessSync(binPath, fs.constants.X_OK); } catch (err) { console.error(`The file ${binPath} is not executable`); throw new Error(`The file at ${binPath} , lacks execute permissions.`); } } async function getBinaryArchitecture(filePath) { return new Promise((resolve, reject) => { const architectureMap = { 0: "unknown", 3: "x86", 62: "x64", 183: "arm64", }; fs.open(filePath, "r", (err, fd) => { if (err) { reject(err); return; } const buffer = Buffer.alloc(20); fs.read(fd, buffer, 0, 20, 0, (err, bytesRead, buffer) => { if (err) { reject(err); return; } // if ( // buffer.readUInt8(0) !== 0x7f || // buffer.readUInt8(1) !== 0x45 || // buffer.readUInt8(2) !== 0x4c || // buffer.readUInt8(3) !== 0x46 // ) { // // reject(new Error("Not an ELF file")); // return; // } const e_machine = buffer.readUInt16LE(18); const architecture = architectureMap[e_machine] || "unknown"; resolve(architecture); }); }); }); } //# sourceMappingURL=fileCheckers.js.map