diff --git a/src/cli.rs b/src/cli.rs index 31807fa261..0a3cd0321e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,6 +1,7 @@ /// The CLI specific code lives in the cli module and sub-modules. #[macro_use] pub mod log; +mod ci; pub mod common; mod docs; pub mod errors; diff --git a/src/cli/ci.rs b/src/cli/ci.rs new file mode 100644 index 0000000000..a8cbf8bed5 --- /dev/null +++ b/src/cli/ci.rs @@ -0,0 +1,54 @@ +use std::{fmt, io::Write}; + +use anyhow::Result; +use clap::{ValueEnum, builder::PossibleValue}; + +use crate::{config::Cfg, utils::ExitCode}; + +pub(crate) fn env(cfg: &Cfg<'_>) -> Result { + print_str(include_str!("ci/base.env"), cfg) +} + +pub(crate) fn problem_matcher(flavor: Flavor, cfg: &Cfg<'_>) -> Result { + print_str(flavor.problem_matcher(), cfg) +} + +fn print_str(s: &str, cfg: &Cfg<'_>) -> Result { + let stdout = cfg.process.stdout(); + write!(stdout.lock(), "{s}")?; + Ok(ExitCode::SUCCESS) +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub(crate) enum Flavor { + Github, +} + +impl Flavor { + fn problem_matcher(&self) -> &'static str { + match self { + Self::Github => include_str!("ci/matcher/github.json"), + } + } +} + +impl ValueEnum for Flavor { + fn value_variants<'a>() -> &'a [Self] { + &[Self::Github] + } + + fn to_possible_value<'a>(&self) -> Option { + Some(match self { + Self::Github => PossibleValue::new("github"), + }) + } +} + +impl fmt::Display for Flavor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.to_possible_value() { + Some(v) => write!(f, "{}", v.get_name()), + None => unreachable!(), + } + } +} diff --git a/src/cli/ci/base.env b/src/cli/ci/base.env new file mode 100644 index 0000000000..adae3d42eb --- /dev/null +++ b/src/cli/ci/base.env @@ -0,0 +1,6 @@ +CARGO_INCREMENTAL=0 +CARGO_PROFILE_DEV_DEBUG=0 +CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse +CARGO_TERM_COLOR=always +CARGO_UNSTABLE_SPARSE_REGISTRY=true +RUST_BACKTRACE=short diff --git a/src/cli/ci/matcher/github.json b/src/cli/ci/matcher/github.json new file mode 100644 index 0000000000..358464ca5e --- /dev/null +++ b/src/cli/ci/matcher/github.json @@ -0,0 +1,44 @@ +{ + "problemMatcher": [ + { + "owner": "rust-compiler", + "pattern": [ + { + "regexp": "^(?:\\x1B\\[[0-9;]*[a-zA-Z])*(warning|warn|error)(\\[(\\S*)\\])?(?:\\x1B\\[[0-9;]*[a-zA-Z])*: (.*?)(?:\\x1B\\[[0-9;]*[a-zA-Z])*$", + "severity": 1, + "message": 4, + "code": 3 + }, + { + "regexp": "^(?:\\x1B\\[[0-9;]*[a-zA-Z])*\\s+(?:\\x1B\\[[0-9;]*[a-zA-Z])*-->\\s(?:\\x1B\\[[0-9;]*[a-zA-Z])*(\\S+):(\\d+):(\\d+)(?:\\x1B\\[[0-9;]*[a-zA-Z])*$", + "file": 1, + "line": 2, + "column": 3 + } + ] + }, + { + "owner": "rust-formatter", + "pattern": [ + { + "regexp": "^(Diff in (\\S+)) at line (\\d+):", + "message": 1, + "file": 2, + "line": 3 + } + ] + }, + { + "owner": "rust-panic", + "pattern": [ + { + "regexp": "^.*panicked\\s+at\\s+'(.*)',\\s+(.*):(\\d+):(\\d+)$", + "message": 1, + "file": 2, + "line": 3, + "column": 4 + } + ] + } + ] +} diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index cd127c5788..1a0de10fcb 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -28,6 +28,7 @@ use tracing_subscriber::{EnvFilter, Registry, reload::Handle}; use crate::{ cli::{ + ci, common::{self, PackageUpdate, update_console_filter}, docs, errors::CliError, @@ -296,6 +297,13 @@ enum RustupSubcmd { #[arg(default_value = "rustup")] command: CompletionCommand, }, + + /// Generate CI-specific configurations + #[command(hide = true)] + Ci { + #[command(subcommand)] + subcmd: CiSubcmd, + }, } fn update_toolchain_value_parser(s: &str) -> Result { @@ -318,6 +326,7 @@ impl RustupSubcmd { // These subcommands don't require the active toolchain, so auto-installing it should be // disabled to avoid surprises. Self::Check { .. } + | Self::Ci { .. } | Self::Completions { .. } | Self::Component { .. } | Self::Default { .. } @@ -344,6 +353,7 @@ impl RustupSubcmd { // For all other subcommands, the hint may be useful if rustup is still unusable after // the command has completed. Self::Check { .. } + | Self::Ci { .. } | Self::Component { .. } | Self::Default { .. } | Self::Doc { .. } @@ -666,6 +676,20 @@ enum SetSubcmd { }, } +#[derive(Debug, Subcommand)] +#[command(arg_required_else_help = true, subcommand_required = true)] +enum CiSubcmd { + /// Show the recommended environment variables in `.env` format + Env, + + /// Show the example problem matcher for the given CI flavor + #[command(alias = "matcher")] + ProblemMatcher { + #[arg(value_enum)] + flavor: ci::Flavor, + }, +} + #[tracing::instrument(level = "trace", fields(args = format!("{:?}", process.args_os().collect::>())), skip(process, console_filter))] pub async fn main( current_dir: PathBuf, @@ -857,6 +881,10 @@ pub async fn main( RustupSubcmd::Completions { shell, command } => { output_completion_script(shell, command, process) } + RustupSubcmd::Ci { subcmd } => match subcmd { + CiSubcmd::Env => ci::env(cfg), + CiSubcmd::ProblemMatcher { flavor } => ci::problem_matcher(flavor, cfg), + }, }?; if should_warn && cfg.list_toolchains()?.is_empty() && cfg.get_default()?.is_none() { diff --git a/tests/suite/cli_rustup_ui.rs b/tests/suite/cli_rustup_ui.rs index 289b8c8713..ebe111d8e5 100644 --- a/tests/suite/cli_rustup_ui.rs +++ b/tests/suite/cli_rustup_ui.rs @@ -535,6 +535,11 @@ fn rustup_upgrade_cmd_help_flag() { test_help("rustup_upgrade_cmd_help_flag", &["upgrade", "--help"]); } +#[test] +fn rustup_ci_cmd_help_flag() { + test_help("rustup_ci_cmd_help_flag", &["ci", "--help"]); +} + #[test] fn rustup_which_cmd_help_flag() { test_help("rustup_which_cmd_help_flag", &["which", "--help"]); diff --git a/tests/suite/cli_rustup_ui/rustup_ci_cmd_help_flag.stdout.term.svg b/tests/suite/cli_rustup_ui/rustup_ci_cmd_help_flag.stdout.term.svg new file mode 100644 index 0000000000..7de9eca59a --- /dev/null +++ b/tests/suite/cli_rustup_ui/rustup_ci_cmd_help_flag.stdout.term.svg @@ -0,0 +1,49 @@ + + + + + + + Generate CI-specific configurations + + + + Usage: rustup[EXE] ci <COMMAND> + + + + Commands: + + env Show the recommended environment variables in `.env` format + + problem-matcher Show the example problem matcher for the given CI flavor + + help Print this message or the help of the given subcommand(s) + + + + Options: + + -h, --help Print help + + + + + +