From 1bd7b413875cff694b428f40cf28c8a3e43bd824 Mon Sep 17 00:00:00 2001 From: Nick Frostbutter <75431177+nickfrosty@users.noreply.github.com> Date: Thu, 30 Jan 2025 14:21:21 -0500 Subject: [PATCH] install core vs all tools (#47) --- .changeset/dirty-buttons-brake.md | 5 + src/commands/install.ts | 348 ++++++++++++++++-------------- 2 files changed, 191 insertions(+), 162 deletions(-) create mode 100644 .changeset/dirty-buttons-brake.md diff --git a/.changeset/dirty-buttons-brake.md b/.changeset/dirty-buttons-brake.md new file mode 100644 index 0000000..416b8d2 --- /dev/null +++ b/.changeset/dirty-buttons-brake.md @@ -0,0 +1,5 @@ +--- +"mucho": minor +--- + +add option flags to the `install` command for core tools or all tools diff --git a/src/commands/install.ts b/src/commands/install.ts index a6b7333..bc6faf3 100644 --- a/src/commands/install.ts +++ b/src/commands/install.ts @@ -1,7 +1,7 @@ -import { Argument, Command } from "@commander-js/extra-typings"; +import { Argument, Command, Option } from "@commander-js/extra-typings"; import { cliOutputConfig } from "@/lib/cli"; -import { titleMessage, warnMessage } from "@/lib/logs"; -import { detectOperatingSystem } from "@/lib/shell"; +import { titleMessage, warningOutro } from "@/lib/logs"; +import { detectOperatingSystem, getCommandOutput } from "@/lib/shell"; import type { ToolNames } from "@/types"; import { installAnchorVersionManager, @@ -37,178 +37,202 @@ const toolNames: Array = [ * Setup your local machine for Solana development */ export function installCommand() { - return ( - new Command("install") - .configureOutput(cliOutputConfig) - .description("install Solana development tooling") - .addArgument( - new Argument("", "tool to install (default: all)") - .choices(toolNames) - .argOptional(), - ) - .addArgument( - new Argument( - "", - "desired tool version to install (default: stable)", - ).argOptional(), - ) - // .addOption( - // new Option( - // "--dry-run", - // "only check the versions of the installed tools, do not actually install anything", - // ).implies({ - // drink: "small", - // }), - // ) - .action(async (toolName, version, options) => { - titleMessage("Install Solana development tooling"); - - const os = detectOperatingSystem(); - - if (os == "windows") { - warnMessage( - "Windows is not yet natively supported for the rust based tooling.\n" + - "We recommend using WSL inside your Windows terminal.", - ); - } + return new Command("install") + .configureOutput(cliOutputConfig) + .description("install Solana development tooling") + .addArgument( + new Argument("", "tool to install") + .choices(toolNames) + .argOptional(), + ) + .addArgument( + new Argument( + "", + "desired tool version to install (default: stable)", + ).argOptional(), + ) + .addOption( + new Option( + "--core", + "install only the core tools and their dependencies - rust, solana, anchor", + ).default(true), + ) + .addOption( + new Option( + "--all", + "install all the tools, not just the core tooling", + ).default(false), + ) + .action(async (toolName, version, options) => { + titleMessage("Install Solana development tools"); + if (options.all) options.core = true; + + const os = detectOperatingSystem(); + + if (os == "windows") { + return warningOutro( + "Windows is not yet natively supported for the rust based tooling.\n" + + "We recommend using WSL inside your Windows terminal.", + ); + } + + const postInstallMessages: string[] = []; + + // track which commands may require a path/session refresh + const pathsToRefresh: string[] = []; + + const updatesAvailable = await getNpmPackageUpdates("mucho", true); + + if (options.all) { + console.log(""); // spacer + console.log("Core tools:"); + } + + // always check and install `mucho` + await installMucho({ + os, + version, + updateAvailable: updatesAvailable.filter( + (data) => data.name.toLowerCase() == "mucho", + )[0], + }); + + // we require rust to check for available updates on many of the other tools + // so we install it first before performing that version check + if (!toolName || toolName == "rust") { + await installRust({ + os, + version, + }); - // track which commands may require a path/session refresh - const pathsToRefresh: string[] = []; + await checkShellPathSource( + TOOL_CONFIG.rust.version, + TOOL_CONFIG.rust.pathSource, + ).then((status) => + status == PathSourceStatus.MISSING_PATH + ? pathsToRefresh.push(TOOL_CONFIG.rust.pathSource) + : true, + ); + } + + const anchorVersions = await getAvailableAnchorVersions(); + if ( + anchorVersions.current && + anchorVersions.latest && + isVersionNewer(anchorVersions.latest, anchorVersions.current) + ) { + updatesAvailable.push({ + installed: anchorVersions.current, + latest: anchorVersions.latest, + name: "anchor", + needsUpdate: true, + }); + } - const updatesAvailable = await getNpmPackageUpdates("mucho", true); + // this requires cargo to already be installed, so we must do it after the rust check + updatesAvailable.push(...(await getCargoUpdateOutput())); - // always check and install `mucho` - await installMucho({ + if (!toolName || toolName == "solana") { + const res = await installSolana({ os, version, - updateAvailable: updatesAvailable.filter( - (data) => data.name.toLowerCase() == "mucho", - )[0], }); - if (!toolName || toolName == "rust") { - await installRust({ - os, - version, - }); - - await checkShellPathSource( - TOOL_CONFIG.rust.version, - TOOL_CONFIG.rust.pathSource, - ).then((status) => - status == PathSourceStatus.MISSING_PATH - ? pathsToRefresh.push(TOOL_CONFIG.rust.pathSource) - : true, + // string response means this was a fresh install + // so we can perform additional setup steps + if (typeof res == "string") { + await getCommandOutput("solana config set --url localhost"); + postInstallMessages.push( + "Create a Solana wallet for your CLI with the following command: solana-keygen new", ); } - const anchorVersions = await getAvailableAnchorVersions(); - if ( - anchorVersions.current && - anchorVersions.latest && - isVersionNewer(anchorVersions.latest, anchorVersions.current) - ) { - updatesAvailable.push({ - installed: anchorVersions.current, - latest: anchorVersions.latest, - name: "anchor", - needsUpdate: true, - }); - } - - // this requires cargo to already be installed, so we must do it after - updatesAvailable.push(...(await getCargoUpdateOutput())); - - if (!toolName || toolName == "solana") { - const res = await installSolana({ - os, - version, - }); - - // string response means this was a fresh install - if (typeof res == "string") { - } - - await checkShellPathSource( - TOOL_CONFIG.solana.version, - TOOL_CONFIG.solana.pathSource, - ).then((status) => - status == PathSourceStatus.MISSING_PATH - ? pathsToRefresh.push(TOOL_CONFIG.solana.pathSource) - : true, - ); - } - if (!toolName || toolName == "avm") { - // const version = "0.28.0"; //"latest"; // v0.29.0 has the "ahash yanked" issue - await installAnchorVersionManager({ - os, - version, - updateAvailable: updatesAvailable.filter( - (data) => data.name.toLowerCase() == "avm", - )[0], - }); - } - if (!toolName || toolName == "anchor") { - await installAnchorUsingAvm({ - os, - version, - updateAvailable: updatesAvailable.filter( - (data) => data.name.toLowerCase() == "anchor", - )[0], - }); - } - if (!toolName || toolName == "trident") { - await installTrident({ - os, - version, - updateAvailable: updatesAvailable.filter( - (data) => data.name.toLowerCase() == "trident-cli", - )[0], - }); - } - if (!toolName || toolName == "zest") { - await installZest({ - os, - version, - updateAvailable: updatesAvailable.filter( - (data) => data.name.toLowerCase() == "zest", - )[0], - }); - } - if (!toolName || toolName == "verify") { - await installSolanaVerify({ - os, - version, - updateAvailable: updatesAvailable.filter( - (data) => data.name.toLowerCase() == "solana-verify", - )[0], - }); - } - if (!toolName || toolName == "yarn") { - await installYarn({ - os, - version, - }); - } + await checkShellPathSource( + TOOL_CONFIG.solana.version, + TOOL_CONFIG.solana.pathSource, + ).then((status) => + status == PathSourceStatus.MISSING_PATH + ? pathsToRefresh.push(TOOL_CONFIG.solana.pathSource) + : true, + ); + } + + // anchor is installed via avm + if (!toolName || toolName == "avm" || toolName == "anchor") { + await installAnchorVersionManager({ + os, + version, + updateAvailable: updatesAvailable.filter( + (data) => data.name.toLowerCase() == "avm", + )[0], + }); + } + if (!toolName || toolName == "anchor") { + await installAnchorUsingAvm({ + os, + version, + updateAvailable: updatesAvailable.filter( + (data) => data.name.toLowerCase() == "anchor", + )[0], + }); + } - if (pathsToRefresh.length > 0) { - console.log( - "\nClose and reopen your terminal to apply the required", - "PATH changes \nor run the following in your existing shell:", - "\n", - ); - console.log(`export PATH="${pathsToRefresh.join(":")}:$PATH"`, "\n"); - } + if (!toolName || toolName == "verify") { + await installSolanaVerify({ + os, + version, + updateAvailable: updatesAvailable.filter( + (data) => data.name.toLowerCase() == "solana-verify", + )[0], + }); + } - // if (tools.allInstalled) { - // return successOutro("All tools are installed!"); - // } + // yarn is considered a "core" tool because it is currently a dependency of anchor (sadly) + // this is expected to change in anchor 0.31 + if (!toolName || toolName == "yarn") { + await installYarn({ + os, + version, + }); + } - // if (options.drink !== undefined) console.log(`drink: ${options.drink}`); + if (options.all) { + console.log(""); // spacer + console.log("Additional tools:"); + } - // if (tool) - }) - ); + if ((options.all && !toolName) || toolName == "trident") { + await installTrident({ + os, + version, + updateAvailable: updatesAvailable.filter( + (data) => data.name.toLowerCase() == "trident-cli", + )[0], + }); + } - // .addCommand(setupInstallCommand()) + if ((options.all && !toolName) || toolName == "zest") { + await installZest({ + os, + version, + updateAvailable: updatesAvailable.filter( + (data) => data.name.toLowerCase() == "zest", + )[0], + }); + } + + if (pathsToRefresh.length > 0) { + console.log( + "\nClose and reopen your terminal to apply the required", + "PATH changes \nor run the following in your existing shell:", + "\n", + ); + console.log(`export PATH="${pathsToRefresh.join(":")}:$PATH"`, "\n"); + } + + if (postInstallMessages.length > 0) { + console.log(); // spacer line + console.log(postInstallMessages.join("\n")); + } + }); }