diff --git a/guides/getting-started-aws-lambda.md b/guides/getting-started-aws-lambda.md index 8cb00d9..88548ac 100644 --- a/guides/getting-started-aws-lambda.md +++ b/guides/getting-started-aws-lambda.md @@ -1,5 +1,11 @@ # Getting started: Roll out a Lambda function with MultiTool +> [!WARNING] +> **Deprecated.** The `multi login` and `multi run` canary-rollout workflow +> described below is a legacy feature and is no longer supported. It still works +> for now but will be removed in a future release. This repository now hosts +> **MultiTool Checks** — see [Validate requirements with `multi check`](./checks.md). + This tutorial walks through rolling out a simple AWS Lambda function behind an API Gateway. You’ll simulate user traffic to the API, and the MultiTool agent will automatically decide whether to promote or roll out based on the observed error rate. You will: diff --git a/guides/getting-started-cloudflare-workers.md b/guides/getting-started-cloudflare-workers.md index 5270894..f05194a 100644 --- a/guides/getting-started-cloudflare-workers.md +++ b/guides/getting-started-cloudflare-workers.md @@ -1,5 +1,11 @@ # Getting started: Roll out a Cloudflare Worker with MultiTool +> [!WARNING] +> **Deprecated.** The `multi login` and `multi run` canary-rollout workflow +> described below is a legacy feature and is no longer supported. It still works +> for now but will be removed in a future release. This repository now hosts +> **MultiTool Checks** — see [Validate requirements with `multi check`](./checks.md). + This tutorial walks through rolling out a simple Cloudflare Worker. You’ll simulate user traffic to the API, and the MultiTool agent will automatically decide whether to promote or roll out based on the observed error rate. You will: diff --git a/guides/permissions.md b/guides/permissions.md index 4044a20..1c70ba5 100644 --- a/guides/permissions.md +++ b/guides/permissions.md @@ -1,5 +1,11 @@ # Required permissions for MultiTool +> [!WARNING] +> **Deprecated.** These cloud permissions apply to the legacy `multi run` +> canary-rollout workflow, which is no longer supported and will be removed in a +> future release. This repository now hosts **MultiTool Checks** — see +> [Validate requirements with `multi check`](./checks.md). + MultiTool is designed to minimize security risk. MultiTool never stores your cloud credentials and relies solely on local credentials to interact with your cloud provider. ## Cloudflare permissions diff --git a/guides/quickstart.md b/guides/quickstart.md index 66cb3b3..932b4f4 100644 --- a/guides/quickstart.md +++ b/guides/quickstart.md @@ -1,5 +1,11 @@ # Quickstart: Deploy with MultiTool +> [!WARNING] +> **Deprecated.** The `multi login` and `multi run` canary-rollout workflow +> described below is a legacy feature and is no longer supported. It still works +> for now but will be removed in a future release. This repository now hosts +> **MultiTool Checks** — see [Validate requirements with `multi check`](./checks.md). + This guide shows you how to deploy your own AWS Lambda code using MultiTool. You'll connect the CLI to your MultiTool dashboard and run a deployment using Lambda code packaged as a zip file. ## ✅ Prerequisites diff --git a/src/config/command.rs b/src/config/command.rs index baadd0f..e60c8f9 100644 --- a/src/config/command.rs +++ b/src/config/command.rs @@ -13,18 +13,29 @@ use super::ProxySubcommand; /// A `MultiCommand` is one of the top-level commands accepted by /// the multi CLI. +/// +/// The repository now hosts MultiTool Checks, whose only relevant subcommand is +/// `check`. The remaining subcommands are leftovers from the tool's previous +/// life and are soft-deprecated (MULTI-1365): they still function but are hidden +/// from help and emit a deprecation notice when invoked. They will be removed in +/// a future release (MULTI-1366). Standard utility commands (`check`, `version`, +/// and clap's built-in `help`) are preserved and never deprecated. #[derive(Subcommand, Clone)] pub enum MultiCommand { /// Log in to the hosted SaaS. + #[command(hide = true)] Login(LoginSubcommand), + #[command(hide = true)] Logout, /// Initialize a new application or prepare the configuration of an existing one #[command(hide = true)] Init(InitSubcommand), #[cfg(feature = "proxy")] + #[command(hide = true)] Proxy(ProxySubcommand), /// Run will execute `multi` in "runner mode", where it will /// immediately deploy the provided artifact and start canarying. + #[command(hide = true)] Run(RunSubcommand), /// Validate the requirements declared in `CHECKS.md` files using AI-agent checks. Check(CheckSubcommand), @@ -33,8 +44,29 @@ pub enum MultiCommand { } impl MultiCommand { + /// The user-facing name of this subcommand if it is a soft-deprecated legacy + /// command, or `None` for supported commands (`check`, `version`). + /// + /// Used to emit a deprecation notice on dispatch. Kept exhaustive (no `_` + /// arm) so adding a new subcommand forces a deliberate keep-vs-deprecate + /// decision here. + fn deprecated_name(&self) -> Option<&'static str> { + match self { + Self::Login(_) => Some("login"), + Self::Logout => Some("logout"), + Self::Init(_) => Some("init"), + #[cfg(feature = "proxy")] + Self::Proxy(_) => Some("proxy"), + Self::Run(_) => Some("run"), + Self::Check(_) | Self::Version => None, + } + } + /// dispatch the user-provided arguments to the command handler. pub fn dispatch(self, console: Terminal) -> Result<()> { + if let Some(name) = self.deprecated_name() { + console.deprecation_notice(name)?; + } match self { Self::Login(flags) => Login::new(console, flags)?.dispatch(), Self::Logout => Logout::new(console).dispatch(), @@ -47,3 +79,66 @@ impl MultiCommand { } } } + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use clap::{CommandFactory, Parser}; + + use super::MultiCommand; + use crate::Cli; + + /// Parse a full CLI invocation and return the selected subcommand. + fn command_of(args: &[&str]) -> MultiCommand { + Cli::parse_from(args) + .cmd() + .clone() + .expect("a subcommand was provided") + } + + #[test] + fn legacy_commands_report_their_deprecated_name() { + assert_eq!( + command_of(&["multi", "login"]).deprecated_name(), + Some("login") + ); + assert_eq!( + command_of(&["multi", "logout"]).deprecated_name(), + Some("logout") + ); + assert_eq!( + command_of(&["multi", "init"]).deprecated_name(), + Some("init") + ); + assert_eq!(command_of(&["multi", "run"]).deprecated_name(), Some("run")); + } + + #[test] + fn supported_commands_are_not_deprecated() { + assert_eq!(command_of(&["multi", "check"]).deprecated_name(), None); + assert_eq!(command_of(&["multi", "version"]).deprecated_name(), None); + } + + #[test] + fn legacy_commands_are_hidden_and_supported_commands_are_visible() { + let hidden: HashMap = Cli::command() + .get_subcommands() + .map(|c| (c.get_name().to_string(), c.is_hide_set())) + .collect(); + + for name in ["login", "logout", "init", "run"] { + assert!( + hidden[name], + "legacy `{name}` should be hidden from help output" + ); + } + // `help` is clap's built-in utility; `check`/`version` are supported. + for name in ["check", "version"] { + assert!( + !hidden[name], + "`{name}` should remain advertised in help output" + ); + } + } +} diff --git a/src/terminal/mod.rs b/src/terminal/mod.rs index a34956c..7168a60 100644 --- a/src/terminal/mod.rs +++ b/src/terminal/mod.rs @@ -73,6 +73,25 @@ impl Terminal { self.stdout.allow_color() } + /// Emit a deprecation notice for a legacy subcommand to stderr. + /// + /// MultiTool now centers on `multi check`; the remaining legacy subcommands + /// are soft-deprecated (MULTI-1365) and still function, but invoking one + /// surfaces this warning. Routed through stderr so it never pollutes a + /// command's stdout, and honors the global `--enable-colors` setting. + pub fn deprecation_notice(&self, command: &str) -> Result<()> { + let body = format!( + "warning: `multi {command}` is deprecated and will be removed in a future release. \ + MultiTool now centers on `multi check`." + ); + let line = if self.stderr.allow_color() { + console::style(body).yellow().to_string() + } else { + body + }; + self.stderr.term().write_line(&line).into_diagnostic() + } + pub fn print_version(&self, version: &'static str) -> Result<()> { let msg = format!("v{version}"); self.stdout