diff --git a/packages/safe-chain/src/installation/downloadAgent.js b/packages/safe-chain/src/installation/downloadAgent.js index 0ee994e..2f2baac 100644 --- a/packages/safe-chain/src/installation/downloadAgent.js +++ b/packages/safe-chain/src/installation/downloadAgent.js @@ -3,31 +3,31 @@ import { createHash } from "crypto"; import { pipeline } from "stream/promises"; import fetch from "make-fetch-happen"; -const ULTIMATE_VERSION = "v0.2.0"; +const ULTIMATE_VERSION = "v0.2.1"; const DOWNLOAD_URLS = { win32: { x64: { - url: "https://github.com/AikidoSec/safechain-internals/releases/download/v0.0.2-macos-release-artifact/SafeChainAgent-windows-amd64.msi", + url: `https://github.com/AikidoSec/safechain-internals/releases/download/${ULTIMATE_VERSION}/SafeChainAgent-windows-amd64.msi`, checksum: - "sha256:bba5deb250ebc6008f1cb33fa4209d2455a2f47fa99f0a40e3babef64939ac77", + "sha256:8d86a44d314746099ba50cfae0cc1eae6232522deb348b226da92aae12754eec", }, arm64: { - url: "https://github.com/AikidoSec/safechain-internals/releases/download/v0.0.2-macos-release-artifact/SafeChainAgent-windows-arm64.msi", + url: `https://github.com/AikidoSec/safechain-internals/releases/download/${ULTIMATE_VERSION}/SafeChainAgent-windows-arm64.msi`, checksum: - "sha256:9553ed15d5efed4185b990a1b86af0b11c23f11d96f8ce04e16b6b98aaf0506e", + "sha256:ab5b8335cc257d53424f73d6681920875083cd9b3f53e52d944bf867a415e027", }, }, darwin: { x64: { - url: "https://github.com/AikidoSec/safechain-internals/releases/download/v0.0.2-macos-release-artifact/SafeChainAgent-darwin-amd64.pkg", + url: `https://github.com/AikidoSec/safechain-internals/releases/download/${ULTIMATE_VERSION}/SafeChainAgent-darwin-amd64.pkg`, checksum: - "sha256:cbccf32e987a45bc8cc20b620f7b597ff7f9c2f966c2bc21132349612ddb619f", + "sha256:73f83d9352c4fd25f7693d9e53bbbb2b7ac70d16217d745495c9efb50dc4a3a6", }, arm64: { - url: "https://github.com/AikidoSec/safechain-internals/releases/download/v0.0.2-macos-release-artifact/SafeChainAgent-darwin-arm64.pkg", + url: `https://github.com/AikidoSec/safechain-internals/releases/download/${ULTIMATE_VERSION}/SafeChainAgent-darwin-arm64.pkg`, checksum: - "sha256:4d53a43a47bf7e8133eb61d306a1fb16348b9ec89c1c825e5f746f4fe847796e", + "sha256:bd419e9c82488539b629b04c97aa1d2dc90e54ff045bd7277a6b40d26f8ebc73", }, }, }; diff --git a/packages/safe-chain/src/installation/installOnMacOS.js b/packages/safe-chain/src/installation/installOnMacOS.js index b2a953a..0d7081c 100644 --- a/packages/safe-chain/src/installation/installOnMacOS.js +++ b/packages/safe-chain/src/installation/installOnMacOS.js @@ -2,7 +2,7 @@ import { tmpdir } from "os"; import { unlinkSync } from "fs"; import { join } from "path"; import { ui } from "../environment/userInteraction.js"; -import { safeSpawn } from "../utils/safeSpawn.js"; +import { printVerboseAndSafeSpawn, safeSpawn } from "../utils/safeSpawn.js"; import { downloadAgentToFile, getAgentVersion } from "./downloadAgent.js"; export async function installOnMacOS() { @@ -49,9 +49,13 @@ function isRunningAsRoot() { * @param {string} pkgPath */ async function runPkgInstaller(pkgPath) { - ui.writeVerbose(`Running: installer -pkg "${pkgPath}" -target /`); + // Uses installer to install the package (https://ss64.com/mac/installer.html) + // Options: + // -pkg (required): The package to be installed. + // -target (required): The target volume is specified with the -target parameter. + // --> "-target /" installs to the current boot volume. - const result = await safeSpawn( + const result = await printVerboseAndSafeSpawn( "installer", ["-pkg", pkgPath, "-target", "/"], { diff --git a/packages/safe-chain/src/installation/installOnWindows.js b/packages/safe-chain/src/installation/installOnWindows.js index 2380a7f..c6bc744 100644 --- a/packages/safe-chain/src/installation/installOnWindows.js +++ b/packages/safe-chain/src/installation/installOnWindows.js @@ -3,7 +3,7 @@ import { unlinkSync } from "fs"; import { join } from "path"; import { execSync } from "child_process"; import { ui } from "../environment/userInteraction.js"; -import { safeSpawn } from "../utils/safeSpawn.js"; +import { printVerboseAndSafeSpawn, safeSpawn } from "../utils/safeSpawn.js"; import { downloadAgentToFile, getAgentVersion } from "./downloadAgent.js"; const WINDOWS_SERVICE_NAME = "SafeChainAgent"; @@ -87,7 +87,12 @@ async function uninstallIfInstalled() { ui.writeInformation("đŸ—‘ī¸ Removing previous installation..."); ui.writeVerbose(`Found product code: ${productCode}`); - const uninstallResult = await safeSpawn( + // Use msiexec to run the msi installer quitely (https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/msiexec) + // Options: + // - /x: Uninstalls the package. + // - /qn: Specifies there's no UI during the installation process. + // - /norestart: Stops the device from restarting after the installation completes. + const uninstallResult = await printVerboseAndSafeSpawn( "msiexec", ["/x", productCode, "/qn", "/norestart"], { stdio: "inherit" }, @@ -102,11 +107,18 @@ async function uninstallIfInstalled() { * @param {string} msiPath */ async function runMsiInstaller(msiPath) { - ui.writeVerbose(`Running: msiexec /i "${msiPath}" /qn`); + // Use msiexec to run the msi installer quitely (https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/msiexec) + // Options: + // - /i: Specifies normal installation + // - /qn: Specifies there's no UI during the installation process. - const result = await safeSpawn("msiexec", ["/i", msiPath, "/qn"], { - stdio: "inherit", - }); + const result = await printVerboseAndSafeSpawn( + "msiexec", + ["/i", msiPath, "/qn"], + { + stdio: "inherit", + }, + ); if (result.status !== 0) { throw new Error(`MSI installer failed (exit code: ${result.status})`); @@ -116,9 +128,13 @@ async function runMsiInstaller(msiPath) { async function stopServiceIfRunning() { ui.writeInformation("âšī¸ Stopping running service..."); - const result = await safeSpawn("net", ["stop", WINDOWS_SERVICE_NAME], { - stdio: "pipe", - }); + const result = await printVerboseAndSafeSpawn( + "net", + ["stop", WINDOWS_SERVICE_NAME], + { + stdio: "pipe", + }, + ); if (result.status !== 0) { ui.writeVerbose("Service not running (will start after installation)."); diff --git a/packages/safe-chain/src/utils/safeSpawn.js b/packages/safe-chain/src/utils/safeSpawn.js index e17bdb5..69c827a 100644 --- a/packages/safe-chain/src/utils/safeSpawn.js +++ b/packages/safe-chain/src/utils/safeSpawn.js @@ -1,5 +1,6 @@ import { spawn, execSync } from "child_process"; import os from "os"; +import { ui } from "../environment/userInteraction.js"; /** * @param {string} arg @@ -135,3 +136,18 @@ export async function safeSpawn(command, args, options = {}) { }); }); } + +/** + * @param {string} command + * @param {string[]} args + * @param {import("child_process").SpawnOptions} options + * + * @returns {Promise<{status: number, stdout: string, stderr: string}>} + */ +export async function printVerboseAndSafeSpawn(command, args, options = {}) { + ui.writeVerbose(`Running: ${command} ${args.join(" ")}`); + + const result = await safeSpawn(command, args, options); + + return result; +}