Skip to content

Commit fb49d43

Browse files
committed
feat(cli): Add code-mappings upload command scaffold with file parsing
Add a new `code-mappings` subcommand group with an `upload` subcommand that reads and validates a JSON file of code mappings. This is the first step toward CLI support for bulk code mapping uploads to Sentry.
1 parent a0e91bf commit fb49d43

8 files changed

Lines changed: 202 additions & 0 deletions

File tree

src/commands/code_mappings/mod.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use anyhow::Result;
2+
use clap::{ArgMatches, Command};
3+
4+
use crate::utils::args::ArgExt as _;
5+
6+
pub mod upload;
7+
8+
macro_rules! each_subcommand {
9+
($mac:ident) => {
10+
$mac!(upload);
11+
};
12+
}
13+
14+
pub fn make_command(mut command: Command) -> Command {
15+
macro_rules! add_subcommand {
16+
($name:ident) => {{
17+
command = command.subcommand(crate::commands::code_mappings::$name::make_command(
18+
Command::new(stringify!($name).replace('_', "-")),
19+
));
20+
}};
21+
}
22+
23+
command = command
24+
.about("Manage code mappings for Sentry.")
25+
.subcommand_required(true)
26+
.arg_required_else_help(true)
27+
.org_arg()
28+
.project_arg(false);
29+
each_subcommand!(add_subcommand);
30+
command
31+
}
32+
33+
pub fn execute(matches: &ArgMatches) -> Result<()> {
34+
macro_rules! execute_subcommand {
35+
($name:ident) => {{
36+
if let Some(sub_matches) =
37+
matches.subcommand_matches(&stringify!($name).replace('_', "-"))
38+
{
39+
return crate::commands::code_mappings::$name::execute(&sub_matches);
40+
}
41+
}};
42+
}
43+
each_subcommand!(execute_subcommand);
44+
unreachable!();
45+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use std::fs;
2+
3+
use anyhow::{bail, Context as _, Result};
4+
use clap::{Arg, ArgMatches, Command};
5+
use serde::{Deserialize, Serialize};
6+
7+
#[derive(Debug, Deserialize, Serialize)]
8+
#[serde(rename_all = "camelCase")]
9+
struct CodeMapping {
10+
stack_root: String,
11+
source_root: String,
12+
}
13+
14+
pub fn make_command(command: Command) -> Command {
15+
command
16+
.about("Upload code mappings for a project from a JSON file.")
17+
.arg(
18+
Arg::new("path")
19+
.value_name("PATH")
20+
.required(true)
21+
.help("Path to a JSON file containing code mappings."),
22+
)
23+
.arg(
24+
Arg::new("repo")
25+
.long("repo")
26+
.value_name("REPO")
27+
.help("The repository name (e.g. owner/repo). Defaults to the git remote."),
28+
)
29+
.arg(
30+
Arg::new("default_branch")
31+
.long("default-branch")
32+
.value_name("BRANCH")
33+
.default_value("main")
34+
.help("The default branch name."),
35+
)
36+
}
37+
38+
pub fn execute(matches: &ArgMatches) -> Result<()> {
39+
#[expect(clippy::unwrap_used, reason = "path is a required argument")]
40+
let path = matches.get_one::<String>("path").unwrap();
41+
let data = fs::read(path).with_context(|| format!("Failed to read mappings file '{path}'"))?;
42+
43+
let mappings: Vec<CodeMapping> =
44+
serde_json::from_slice(&data).context("Failed to parse mappings JSON")?;
45+
46+
if mappings.is_empty() {
47+
bail!("Mappings file contains an empty array. Nothing to upload.");
48+
}
49+
50+
for (i, mapping) in mappings.iter().enumerate() {
51+
if mapping.stack_root.is_empty() {
52+
bail!("Mapping at index {i} has an empty stackRoot.");
53+
}
54+
if mapping.source_root.is_empty() {
55+
bail!("Mapping at index {i} has an empty sourceRoot.");
56+
}
57+
}
58+
59+
println!("Found {} code mapping(s) in {path}", mappings.len());
60+
61+
Ok(())
62+
}

src/commands/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::utils::value_parsers::auth_token_parser;
2121

2222
mod bash_hook;
2323
mod build;
24+
mod code_mappings;
2425
mod dart_symbol_map;
2526
mod debug_files;
2627
mod deploys;
@@ -52,6 +53,7 @@ macro_rules! each_subcommand {
5253
($mac:ident) => {
5354
$mac!(bash_hook);
5455
$mac!(build);
56+
$mac!(code_mappings);
5557
$mac!(debug_files);
5658
$mac!(deploys);
5759
$mac!(events);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
```
2+
$ sentry-cli code-mappings --help
3+
? success
4+
Manage code mappings for Sentry.
5+
6+
Usage: sentry-cli[EXE] code-mappings [OPTIONS] <COMMAND>
7+
8+
Commands:
9+
upload Upload code mappings for a project from a JSON file.
10+
help Print this message or the help of the given subcommand(s)
11+
12+
Options:
13+
-o, --org <ORG> The organization ID or slug.
14+
--header <KEY:VALUE> Custom headers that should be attached to all requests
15+
in key:value format.
16+
-p, --project <PROJECT> The project ID or slug.
17+
--auth-token <AUTH_TOKEN> Use the given Sentry auth token.
18+
--log-level <LOG_LEVEL> Set the log output verbosity. [possible values: trace, debug, info,
19+
warn, error]
20+
--quiet Do not print any output while preserving correct exit code. This
21+
flag is currently implemented only for selected subcommands.
22+
[aliases: --silent]
23+
-h, --help Print help
24+
25+
```
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
```
2+
$ sentry-cli code-mappings
3+
? failed
4+
Manage code mappings for Sentry.
5+
6+
Usage: sentry-cli[EXE] code-mappings [OPTIONS] <COMMAND>
7+
8+
Commands:
9+
upload Upload code mappings for a project from a JSON file.
10+
help Print this message or the help of the given subcommand(s)
11+
12+
Options:
13+
-o, --org <ORG> The organization ID or slug.
14+
--header <KEY:VALUE> Custom headers that should be attached to all requests
15+
in key:value format.
16+
-p, --project <PROJECT> The project ID or slug.
17+
--auth-token <AUTH_TOKEN> Use the given Sentry auth token.
18+
--log-level <LOG_LEVEL> Set the log output verbosity. [possible values: trace, debug, info,
19+
warn, error]
20+
--quiet Do not print any output while preserving correct exit code. This
21+
flag is currently implemented only for selected subcommands.
22+
[aliases: --silent]
23+
-h, --help Print help
24+
25+
```
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
```
2+
$ sentry-cli code-mappings upload --help
3+
? success
4+
Upload code mappings for a project from a JSON file.
5+
6+
Usage: sentry-cli[EXE] code-mappings upload [OPTIONS] <PATH>
7+
8+
Arguments:
9+
<PATH> Path to a JSON file containing code mappings.
10+
11+
Options:
12+
-o, --org <ORG> The organization ID or slug.
13+
--repo <REPO> The repository name (e.g. owner/repo). Defaults to the git remote.
14+
--default-branch <BRANCH> The default branch name. [default: main]
15+
--header <KEY:VALUE> Custom headers that should be attached to all requests
16+
in key:value format.
17+
-p, --project <PROJECT> The project ID or slug.
18+
--auth-token <AUTH_TOKEN> Use the given Sentry auth token.
19+
--log-level <LOG_LEVEL> Set the log output verbosity. [possible values: trace, debug, info,
20+
warn, error]
21+
--quiet Do not print any output while preserving correct exit code. This
22+
flag is currently implemented only for selected subcommands.
23+
[aliases: --silent]
24+
-h, --help Print help
25+
26+
```
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use crate::integration::TestManager;
2+
3+
#[test]
4+
fn command_code_mappings_help() {
5+
TestManager::new().register_trycmd_test("code_mappings/code-mappings-help.trycmd");
6+
}
7+
8+
#[test]
9+
fn command_code_mappings_no_subcommand() {
10+
TestManager::new().register_trycmd_test("code_mappings/code-mappings-no-subcommand.trycmd");
11+
}
12+
13+
#[test]
14+
fn command_code_mappings_upload_help() {
15+
TestManager::new().register_trycmd_test("code_mappings/code-mappings-upload-help.trycmd");
16+
}

tests/integration/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod bash_hook;
22
mod build;
3+
mod code_mappings;
34
mod debug_files;
45
mod deploys;
56
mod events;

0 commit comments

Comments
 (0)