diff --git a/src/commands/code_mappings/mod.rs b/src/commands/code_mappings/mod.rs new file mode 100644 index 0000000000..4c2583bd61 --- /dev/null +++ b/src/commands/code_mappings/mod.rs @@ -0,0 +1,45 @@ +use anyhow::Result; +use clap::{ArgMatches, Command}; + +use crate::utils::args::ArgExt as _; + +pub mod upload; + +macro_rules! each_subcommand { + ($mac:ident) => { + $mac!(upload); + }; +} + +pub fn make_command(mut command: Command) -> Command { + macro_rules! add_subcommand { + ($name:ident) => {{ + command = command.subcommand(crate::commands::code_mappings::$name::make_command( + Command::new(stringify!($name).replace('_', "-")), + )); + }}; + } + + command = command + .about("Manage code mappings for Sentry.") + .subcommand_required(true) + .arg_required_else_help(true) + .org_arg() + .project_arg(false); + each_subcommand!(add_subcommand); + command +} + +pub fn execute(matches: &ArgMatches) -> Result<()> { + macro_rules! execute_subcommand { + ($name:ident) => {{ + if let Some(sub_matches) = + matches.subcommand_matches(&stringify!($name).replace('_', "-")) + { + return crate::commands::code_mappings::$name::execute(&sub_matches); + } + }}; + } + each_subcommand!(execute_subcommand); + unreachable!(); +} diff --git a/src/commands/code_mappings/upload.rs b/src/commands/code_mappings/upload.rs new file mode 100644 index 0000000000..f0141c7d42 --- /dev/null +++ b/src/commands/code_mappings/upload.rs @@ -0,0 +1,62 @@ +use std::fs; + +use anyhow::{bail, Context as _, Result}; +use clap::{Arg, ArgMatches, Command}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct CodeMapping { + stack_root: String, + source_root: String, +} + +pub fn make_command(command: Command) -> Command { + command + .about("Upload code mappings for a project from a JSON file.") + .arg( + Arg::new("path") + .value_name("PATH") + .required(true) + .help("Path to a JSON file containing code mappings."), + ) + .arg( + Arg::new("repo") + .long("repo") + .value_name("REPO") + .help("The repository name (e.g. owner/repo). Defaults to the git remote."), + ) + .arg( + Arg::new("default_branch") + .long("default-branch") + .value_name("BRANCH") + .default_value("main") + .help("The default branch name."), + ) +} + +pub fn execute(matches: &ArgMatches) -> Result<()> { + #[expect(clippy::unwrap_used, reason = "path is a required argument")] + let path = matches.get_one::("path").unwrap(); + let data = fs::read(path).with_context(|| format!("Failed to read mappings file '{path}'"))?; + + let mappings: Vec = + serde_json::from_slice(&data).context("Failed to parse mappings JSON")?; + + if mappings.is_empty() { + bail!("Mappings file contains an empty array. Nothing to upload."); + } + + for (i, mapping) in mappings.iter().enumerate() { + if mapping.stack_root.is_empty() { + bail!("Mapping at index {i} has an empty stackRoot."); + } + if mapping.source_root.is_empty() { + bail!("Mapping at index {i} has an empty sourceRoot."); + } + } + + println!("Found {} code mapping(s) in {path}", mappings.len()); + + Ok(()) +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index bf74f76d42..65ca7573b4 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -21,6 +21,7 @@ use crate::utils::value_parsers::auth_token_parser; mod bash_hook; mod build; +mod code_mappings; mod dart_symbol_map; mod debug_files; mod deploys; @@ -52,6 +53,7 @@ macro_rules! each_subcommand { ($mac:ident) => { $mac!(bash_hook); $mac!(build); + $mac!(code_mappings); $mac!(debug_files); $mac!(deploys); $mac!(events); diff --git a/tests/integration/_cases/code_mappings/code-mappings-help.trycmd b/tests/integration/_cases/code_mappings/code-mappings-help.trycmd new file mode 100644 index 0000000000..0d29b3dd63 --- /dev/null +++ b/tests/integration/_cases/code_mappings/code-mappings-help.trycmd @@ -0,0 +1,25 @@ +``` +$ sentry-cli code-mappings --help +? success +Manage code mappings for Sentry. + +Usage: sentry-cli[EXE] code-mappings [OPTIONS] + +Commands: + upload Upload code mappings for a project from a JSON file. + help Print this message or the help of the given subcommand(s) + +Options: + -o, --org The organization ID or slug. + --header Custom headers that should be attached to all requests + in key:value format. + -p, --project The project ID or slug. + --auth-token Use the given Sentry auth token. + --log-level Set the log output verbosity. [possible values: trace, debug, info, + warn, error] + --quiet Do not print any output while preserving correct exit code. This + flag is currently implemented only for selected subcommands. + [aliases: --silent] + -h, --help Print help + +``` diff --git a/tests/integration/_cases/code_mappings/code-mappings-no-subcommand.trycmd b/tests/integration/_cases/code_mappings/code-mappings-no-subcommand.trycmd new file mode 100644 index 0000000000..36df45fa6f --- /dev/null +++ b/tests/integration/_cases/code_mappings/code-mappings-no-subcommand.trycmd @@ -0,0 +1,25 @@ +``` +$ sentry-cli code-mappings +? failed +Manage code mappings for Sentry. + +Usage: sentry-cli[EXE] code-mappings [OPTIONS] + +Commands: + upload Upload code mappings for a project from a JSON file. + help Print this message or the help of the given subcommand(s) + +Options: + -o, --org The organization ID or slug. + --header Custom headers that should be attached to all requests + in key:value format. + -p, --project The project ID or slug. + --auth-token Use the given Sentry auth token. + --log-level Set the log output verbosity. [possible values: trace, debug, info, + warn, error] + --quiet Do not print any output while preserving correct exit code. This + flag is currently implemented only for selected subcommands. + [aliases: --silent] + -h, --help Print help + +``` diff --git a/tests/integration/_cases/code_mappings/code-mappings-upload-help.trycmd b/tests/integration/_cases/code_mappings/code-mappings-upload-help.trycmd new file mode 100644 index 0000000000..cf46abe2c2 --- /dev/null +++ b/tests/integration/_cases/code_mappings/code-mappings-upload-help.trycmd @@ -0,0 +1,26 @@ +``` +$ sentry-cli code-mappings upload --help +? success +Upload code mappings for a project from a JSON file. + +Usage: sentry-cli[EXE] code-mappings upload [OPTIONS] + +Arguments: + Path to a JSON file containing code mappings. + +Options: + -o, --org The organization ID or slug. + --repo The repository name (e.g. owner/repo). Defaults to the git remote. + --default-branch The default branch name. [default: main] + --header Custom headers that should be attached to all requests + in key:value format. + -p, --project The project ID or slug. + --auth-token Use the given Sentry auth token. + --log-level Set the log output verbosity. [possible values: trace, debug, info, + warn, error] + --quiet Do not print any output while preserving correct exit code. This + flag is currently implemented only for selected subcommands. + [aliases: --silent] + -h, --help Print help + +``` diff --git a/tests/integration/_cases/help/help-windows.trycmd b/tests/integration/_cases/help/help-windows.trycmd index e634561ea7..34213e9438 100644 --- a/tests/integration/_cases/help/help-windows.trycmd +++ b/tests/integration/_cases/help/help-windows.trycmd @@ -12,6 +12,7 @@ Usage: sentry-cli[EXE] [OPTIONS] Commands: completions Generate completions for the specified shell. build Manage builds. + code-mappings Manage code mappings for Sentry. debug-files Locate, analyze or upload debug information files. [aliases: dif] deploys Manage deployments for Sentry releases. events Manage events on Sentry. diff --git a/tests/integration/_cases/help/help.trycmd b/tests/integration/_cases/help/help.trycmd index aa51cd0222..fcc5302ea7 100644 --- a/tests/integration/_cases/help/help.trycmd +++ b/tests/integration/_cases/help/help.trycmd @@ -12,6 +12,7 @@ Usage: sentry-cli[EXE] [OPTIONS] Commands: completions Generate completions for the specified shell. build Manage builds. + code-mappings Manage code mappings for Sentry. debug-files Locate, analyze or upload debug information files. [aliases: dif] deploys Manage deployments for Sentry releases. events Manage events on Sentry. diff --git a/tests/integration/code_mappings/mod.rs b/tests/integration/code_mappings/mod.rs new file mode 100644 index 0000000000..bcfc6ec6a5 --- /dev/null +++ b/tests/integration/code_mappings/mod.rs @@ -0,0 +1,16 @@ +use crate::integration::TestManager; + +#[test] +fn command_code_mappings_help() { + TestManager::new().register_trycmd_test("code_mappings/code-mappings-help.trycmd"); +} + +#[test] +fn command_code_mappings_no_subcommand() { + TestManager::new().register_trycmd_test("code_mappings/code-mappings-no-subcommand.trycmd"); +} + +#[test] +fn command_code_mappings_upload_help() { + TestManager::new().register_trycmd_test("code_mappings/code-mappings-upload-help.trycmd"); +} diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs index fde0647603..ba5828049d 100644 --- a/tests/integration/mod.rs +++ b/tests/integration/mod.rs @@ -1,5 +1,6 @@ mod bash_hook; mod build; +mod code_mappings; mod debug_files; mod deploys; mod events;