From 0b23cdb74b900fba6d246551e0260d88b0601007 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 12 Mar 2026 09:44:23 +0100 Subject: [PATCH 01/10] feat(cli): Add batch splitting for code-mappings upload Split large mapping files into batches of 300 (the backend limit) per request. Each batch is sent sequentially with progress reporting. Results are merged into a single summary table. Batch-level HTTP failures are captured without aborting remaining batches, and the final exit code reflects any errors. Co-Authored-By: Claude Opus 4.6 --- src/api/data_types/code_mappings.rs | 2 +- src/commands/code_mappings/upload.rs | 79 ++++++++++++++++++----- tests/integration/code_mappings/upload.rs | 62 +++++++++++++++++- 3 files changed, 124 insertions(+), 19 deletions(-) diff --git a/src/api/data_types/code_mappings.rs b/src/api/data_types/code_mappings.rs index 0f66a1504b..7865eeb098 100644 --- a/src/api/data_types/code_mappings.rs +++ b/src/api/data_types/code_mappings.rs @@ -11,7 +11,7 @@ pub struct BulkCodeMappingsRequest<'a> { pub mappings: &'a [BulkCodeMapping], } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct BulkCodeMapping { pub stack_root: String, diff --git a/src/commands/code_mappings/upload.rs b/src/commands/code_mappings/upload.rs index 5718ab56d6..f268307d0b 100644 --- a/src/commands/code_mappings/upload.rs +++ b/src/commands/code_mappings/upload.rs @@ -4,11 +4,16 @@ use anyhow::{bail, Context as _, Result}; use clap::{Arg, ArgMatches, Command}; use log::debug; -use crate::api::{Api, BulkCodeMapping, BulkCodeMappingResult, BulkCodeMappingsRequest}; +use crate::api::{ + Api, BulkCodeMapping, BulkCodeMappingResult, BulkCodeMappingsRequest, BulkCodeMappingsResponse, +}; use crate::config::Config; use crate::utils::formatting::Table; use crate::utils::vcs; +/// Maximum number of mappings the backend accepts per request. +const BATCH_SIZE: usize = 300; + pub fn make_command(command: Command) -> Command { command .about("Upload code mappings for a project from a JSON file. Each mapping pairs a stack trace root (e.g. com/example/module) with the corresponding source path in your repository (e.g. modules/module/src/main/java/com/example/module).") @@ -72,31 +77,53 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { )?; let mapping_count = mappings.len(); - let request = BulkCodeMappingsRequest { - project: &project, - repository: &repo_name, - default_branch: &default_branch, - mappings: &mappings, - }; + let batches: Vec<&[BulkCodeMapping]> = mappings.chunks(BATCH_SIZE).collect(); + let total_batches = batches.len(); println!("Uploading {mapping_count} code mapping(s)..."); let api = Api::current(); - let response = api - .authenticated()? - .bulk_upload_code_mappings(&org, &request)?; + let authenticated = api.authenticated()?; + + let mut merged = MergedResponse::default(); + + for (i, batch) in batches.iter().enumerate() { + if total_batches > 1 { + println!("Sending batch {}/{total_batches}...", i + 1); + } + let request = BulkCodeMappingsRequest { + project: &project, + repository: &repo_name, + default_branch: &default_branch, + mappings: batch, + }; + match authenticated.bulk_upload_code_mappings(&org, &request) { + Ok(response) => merged.add(response), + Err(err) => { + merged + .batch_errors + .push(format!("Batch {}/{total_batches} failed: {err}", i + 1)); + } + } + } + + // Display results + if !merged.mappings.is_empty() { + print_results_table(merged.mappings); + } + + for err in &merged.batch_errors { + println!("{err}"); + } - print_results_table(response.mappings); println!( "Created: {}, Updated: {}, Errors: {}", - response.created, response.updated, response.errors + merged.created, merged.updated, merged.errors ); - if response.errors > 0 { - bail!( - "{} mapping(s) failed to upload. See errors above.", - response.errors - ); + if merged.errors > 0 || !merged.batch_errors.is_empty() { + let total_errors = merged.errors + merged.batch_errors.len() as u64; + bail!("{total_errors} error(s) during upload. See details above."); } Ok(()) @@ -220,6 +247,24 @@ fn print_results_table(mappings: Vec) { println!(); } +#[derive(Default)] +struct MergedResponse { + created: u64, + updated: u64, + errors: u64, + mappings: Vec, + batch_errors: Vec, +} + +impl MergedResponse { + fn add(&mut self, response: BulkCodeMappingsResponse) { + self.created += response.created; + self.updated += response.updated; + self.errors += response.errors; + self.mappings.extend(response.mappings); + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/tests/integration/code_mappings/upload.rs b/tests/integration/code_mappings/upload.rs index 861ed199a9..3f7a198eca 100644 --- a/tests/integration/code_mappings/upload.rs +++ b/tests/integration/code_mappings/upload.rs @@ -1,4 +1,6 @@ -use crate::integration::{MockEndpointBuilder, TestManager}; +use std::sync::atomic::{AtomicU16, Ordering}; + +use crate::integration::{AssertCommand, MockEndpointBuilder, TestManager}; #[test] fn command_code_mappings_upload() { @@ -10,3 +12,61 @@ fn command_code_mappings_upload() { .register_trycmd_test("code_mappings/code-mappings-upload.trycmd") .with_default_token(); } + +#[test] +fn command_code_mappings_upload_batches() { + // Generate a fixture with 301 mappings to force 2 batches (300 + 1). + let mut mappings = Vec::with_capacity(301); + for i in 0..301 { + mappings.push(serde_json::json!({ + "stackRoot": format!("com/example/m{i}"), + "sourceRoot": format!("modules/m{i}/src/main/java/com/example/m{i}"), + })); + } + let fixture = tempfile::NamedTempFile::new().expect("failed to create temp file"); + serde_json::to_writer(&fixture, &mappings).expect("failed to write fixture"); + + let call_count = AtomicU16::new(0); + + TestManager::new() + .mock_endpoint( + MockEndpointBuilder::new("POST", "/api/0/organizations/wat-org/code-mappings/bulk/") + .expect(2) + .with_response_fn(move |_request| { + let n = call_count.fetch_add(1, Ordering::Relaxed); + // Return appropriate counts per batch + let (created, mapping_count) = if n == 0 { (300, 300) } else { (1, 1) }; + let mut batch_mappings = Vec::new(); + for i in 0..mapping_count { + let idx = n as usize * 300 + i; + batch_mappings.push(serde_json::json!({ + "stackRoot": format!("com/example/m{idx}"), + "sourceRoot": format!("modules/m{idx}/src/main/java/com/example/m{idx}"), + "status": "created", + })); + } + serde_json::to_vec(&serde_json::json!({ + "created": created, + "updated": 0, + "errors": 0, + "mappings": batch_mappings, + })) + .expect("failed to serialize response") + }), + ) + .assert_cmd([ + "code-mappings", + "upload", + fixture.path().to_str().expect("valid utf-8 path"), + "--org", + "wat-org", + "--project", + "wat-project", + "--repo", + "owner/repo", + "--default-branch", + "main", + ]) + .with_default_token() + .run_and_assert(AssertCommand::Success); +} From 8e00d0a7eea1a6aacf7ad9a04f134468d34acfcc Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Thu, 12 Mar 2026 09:58:31 +0100 Subject: [PATCH 02/10] ref(cli): Only show error rows in code-mappings upload table For large uploads (hundreds of mappings), printing every row floods the terminal. Show only failed mappings in the table since those are actionable; successful ones are already reflected in the summary counts. Co-Authored-By: Claude Opus 4.6 --- src/commands/code_mappings/upload.rs | 25 ++++++++++--------- .../code-mappings-upload-partial-error.trycmd | 17 +++++++++++++ .../code_mappings/code-mappings-upload.trycmd | 7 ------ .../post-bulk-partial-error.json | 9 +++++++ tests/integration/code_mappings/upload.rs | 11 ++++++++ 5 files changed, 50 insertions(+), 19 deletions(-) create mode 100644 tests/integration/_cases/code_mappings/code-mappings-upload-partial-error.trycmd create mode 100644 tests/integration/_responses/code_mappings/post-bulk-partial-error.json diff --git a/src/commands/code_mappings/upload.rs b/src/commands/code_mappings/upload.rs index f268307d0b..a4568a7250 100644 --- a/src/commands/code_mappings/upload.rs +++ b/src/commands/code_mappings/upload.rs @@ -107,10 +107,8 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { } } - // Display results - if !merged.mappings.is_empty() { - print_results_table(merged.mappings); - } + // Display error details (successful mappings are summarized in counts only). + print_error_table(&merged.mappings); for err in &merged.batch_errors { println!("{err}"); @@ -223,24 +221,27 @@ fn infer_default_branch(git_repo: Option<&git2::Repository>, remote_name: Option }) } -fn print_results_table(mappings: Vec) { +fn print_error_table(mappings: &[BulkCodeMappingResult]) { + let error_mappings: Vec<_> = mappings.iter().filter(|r| r.status == "error").collect(); + + if error_mappings.is_empty() { + return; + } + let mut table = Table::new(); table .title_row() .add("Stack Root") .add("Source Root") - .add("Status"); + .add("Detail"); - for result in mappings { - let status = match result.detail { - Some(detail) if result.status == "error" => format!("error: {detail}"), - _ => result.status, - }; + for result in &error_mappings { + let detail = result.detail.as_deref().unwrap_or("unknown error"); table .add_row() .add(&result.stack_root) .add(&result.source_root) - .add(&status); + .add(detail); } table.print(); diff --git a/tests/integration/_cases/code_mappings/code-mappings-upload-partial-error.trycmd b/tests/integration/_cases/code_mappings/code-mappings-upload-partial-error.trycmd new file mode 100644 index 0000000000..252d7a1866 --- /dev/null +++ b/tests/integration/_cases/code_mappings/code-mappings-upload-partial-error.trycmd @@ -0,0 +1,17 @@ +``` +$ sentry-cli code-mappings upload tests/integration/_fixtures/code_mappings/mappings.json --org wat-org --project wat-project --repo owner/repo --default-branch main +? failed +Uploading 2 code mapping(s)... ++------------------+---------------------------------------------+-------------------+ +| Stack Root | Source Root | Detail | ++------------------+---------------------------------------------+-------------------+ +| com/example/maps | modules/maps/src/main/java/com/example/maps | duplicate mapping | ++------------------+---------------------------------------------+-------------------+ + +Created: 1, Updated: 0, Errors: 1 +error: 1 error(s) during upload. See details above. + +Add --log-level=[info|debug] or export SENTRY_LOG_LEVEL=[info|debug] to see more output. +Please attach the full debug log to all bug reports. + +``` diff --git a/tests/integration/_cases/code_mappings/code-mappings-upload.trycmd b/tests/integration/_cases/code_mappings/code-mappings-upload.trycmd index 72c35d9d19..779db9b650 100644 --- a/tests/integration/_cases/code_mappings/code-mappings-upload.trycmd +++ b/tests/integration/_cases/code_mappings/code-mappings-upload.trycmd @@ -2,13 +2,6 @@ $ sentry-cli code-mappings upload tests/integration/_fixtures/code_mappings/mappings.json --org wat-org --project wat-project --repo owner/repo --default-branch main ? success Uploading 2 code mapping(s)... -+------------------+---------------------------------------------+---------+ -| Stack Root | Source Root | Status | -+------------------+---------------------------------------------+---------+ -| com/example/core | modules/core/src/main/java/com/example/core | created | -| com/example/maps | modules/maps/src/main/java/com/example/maps | created | -+------------------+---------------------------------------------+---------+ - Created: 2, Updated: 0, Errors: 0 ``` diff --git a/tests/integration/_responses/code_mappings/post-bulk-partial-error.json b/tests/integration/_responses/code_mappings/post-bulk-partial-error.json new file mode 100644 index 0000000000..f44f466634 --- /dev/null +++ b/tests/integration/_responses/code_mappings/post-bulk-partial-error.json @@ -0,0 +1,9 @@ +{ + "created": 1, + "updated": 0, + "errors": 1, + "mappings": [ + {"stackRoot": "com/example/core", "sourceRoot": "modules/core/src/main/java/com/example/core", "status": "created"}, + {"stackRoot": "com/example/maps", "sourceRoot": "modules/maps/src/main/java/com/example/maps", "status": "error", "detail": "duplicate mapping"} + ] +} diff --git a/tests/integration/code_mappings/upload.rs b/tests/integration/code_mappings/upload.rs index 3f7a198eca..3580c45837 100644 --- a/tests/integration/code_mappings/upload.rs +++ b/tests/integration/code_mappings/upload.rs @@ -13,6 +13,17 @@ fn command_code_mappings_upload() { .with_default_token(); } +#[test] +fn command_code_mappings_upload_partial_error() { + TestManager::new() + .mock_endpoint( + MockEndpointBuilder::new("POST", "/api/0/organizations/wat-org/code-mappings/bulk/") + .with_response_file("code_mappings/post-bulk-partial-error.json"), + ) + .register_trycmd_test("code_mappings/code-mappings-upload-partial-error.trycmd") + .with_default_token(); +} + #[test] fn command_code_mappings_upload_batches() { // Generate a fixture with 301 mappings to force 2 batches (300 + 1). From 334d54f5dbc7666b649746a7027390c823b25069 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 18 Mar 2026 17:44:42 +0100 Subject: [PATCH 03/10] test(code-mappings): Add test for HTTP 207 partial success response Verify the CLI correctly handles HTTP 207 Multi-Status responses from the bulk code-mappings endpoint. The CLI treats 207 as a success at the HTTP level and relies on the JSON body to surface per-mapping errors. Co-Authored-By: Claude Opus 4.6 --- ...ode-mappings-upload-207-partial-error.trycmd | 17 +++++++++++++++++ .../post-bulk-207-partial-error.json | 9 +++++++++ tests/integration/code_mappings/upload.rs | 12 ++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 tests/integration/_cases/code_mappings/code-mappings-upload-207-partial-error.trycmd create mode 100644 tests/integration/_responses/code_mappings/post-bulk-207-partial-error.json diff --git a/tests/integration/_cases/code_mappings/code-mappings-upload-207-partial-error.trycmd b/tests/integration/_cases/code_mappings/code-mappings-upload-207-partial-error.trycmd new file mode 100644 index 0000000000..252d7a1866 --- /dev/null +++ b/tests/integration/_cases/code_mappings/code-mappings-upload-207-partial-error.trycmd @@ -0,0 +1,17 @@ +``` +$ sentry-cli code-mappings upload tests/integration/_fixtures/code_mappings/mappings.json --org wat-org --project wat-project --repo owner/repo --default-branch main +? failed +Uploading 2 code mapping(s)... ++------------------+---------------------------------------------+-------------------+ +| Stack Root | Source Root | Detail | ++------------------+---------------------------------------------+-------------------+ +| com/example/maps | modules/maps/src/main/java/com/example/maps | duplicate mapping | ++------------------+---------------------------------------------+-------------------+ + +Created: 1, Updated: 0, Errors: 1 +error: 1 error(s) during upload. See details above. + +Add --log-level=[info|debug] or export SENTRY_LOG_LEVEL=[info|debug] to see more output. +Please attach the full debug log to all bug reports. + +``` diff --git a/tests/integration/_responses/code_mappings/post-bulk-207-partial-error.json b/tests/integration/_responses/code_mappings/post-bulk-207-partial-error.json new file mode 100644 index 0000000000..f44f466634 --- /dev/null +++ b/tests/integration/_responses/code_mappings/post-bulk-207-partial-error.json @@ -0,0 +1,9 @@ +{ + "created": 1, + "updated": 0, + "errors": 1, + "mappings": [ + {"stackRoot": "com/example/core", "sourceRoot": "modules/core/src/main/java/com/example/core", "status": "created"}, + {"stackRoot": "com/example/maps", "sourceRoot": "modules/maps/src/main/java/com/example/maps", "status": "error", "detail": "duplicate mapping"} + ] +} diff --git a/tests/integration/code_mappings/upload.rs b/tests/integration/code_mappings/upload.rs index 3580c45837..13d3c79320 100644 --- a/tests/integration/code_mappings/upload.rs +++ b/tests/integration/code_mappings/upload.rs @@ -24,6 +24,18 @@ fn command_code_mappings_upload_partial_error() { .with_default_token(); } +#[test] +fn command_code_mappings_upload_207_partial_error() { + TestManager::new() + .mock_endpoint( + MockEndpointBuilder::new("POST", "/api/0/organizations/wat-org/code-mappings/bulk/") + .with_status(207) + .with_response_file("code_mappings/post-bulk-207-partial-error.json"), + ) + .register_trycmd_test("code_mappings/code-mappings-upload-207-partial-error.trycmd") + .with_default_token(); +} + #[test] fn command_code_mappings_upload_batches() { // Generate a fixture with 301 mappings to force 2 batches (300 + 1). From 1549990c50cf9018cba4122e6ab9162e7b5d246e Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 18 Mar 2026 23:06:22 +0100 Subject: [PATCH 04/10] fix(code-mappings): Include batch errors in summary error count The Errors count in the summary line now includes both per-mapping errors and batch-level transport failures, matching the bail message. Co-Authored-By: Claude Opus 4.6 --- src/commands/code_mappings/upload.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands/code_mappings/upload.rs b/src/commands/code_mappings/upload.rs index a4568a7250..6cf32fb5db 100644 --- a/src/commands/code_mappings/upload.rs +++ b/src/commands/code_mappings/upload.rs @@ -114,13 +114,13 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { println!("{err}"); } + let total_errors = merged.errors + merged.batch_errors.len() as u64; println!( "Created: {}, Updated: {}, Errors: {}", - merged.created, merged.updated, merged.errors + merged.created, merged.updated, total_errors ); - if merged.errors > 0 || !merged.batch_errors.is_empty() { - let total_errors = merged.errors + merged.batch_errors.len() as u64; + if total_errors > 0 { bail!("{total_errors} error(s) during upload. See details above."); } From 3649ce3e299a2a2193497226de04e760f3b61cfa Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 18 Mar 2026 23:10:02 +0100 Subject: [PATCH 05/10] style(code-mappings): Inline format arg to fix clippy warning Co-Authored-By: Claude Opus 4.6 --- src/commands/code_mappings/upload.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/code_mappings/upload.rs b/src/commands/code_mappings/upload.rs index 6cf32fb5db..f81d29bbaa 100644 --- a/src/commands/code_mappings/upload.rs +++ b/src/commands/code_mappings/upload.rs @@ -116,8 +116,8 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { let total_errors = merged.errors + merged.batch_errors.len() as u64; println!( - "Created: {}, Updated: {}, Errors: {}", - merged.created, merged.updated, total_errors + "Created: {}, Updated: {}, Errors: {total_errors}", + merged.created, merged.updated ); if total_errors > 0 { From 75ceb8c413906b9d1e066cdb80984f01b2ac97ff Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 24 Mar 2026 14:48:51 +0100 Subject: [PATCH 06/10] ref(code-mappings): Address review feedback on batching - Use div_ceil and lazy chunks iteration instead of collecting into Vec - Implement FromIterator for MergedResponse to separate merging from request logic - Avoid allocating Vec in print_error_table, iterate filter directly - Use AtomicU8 instead of AtomicU16 in test Co-Authored-By: Claude Opus 4.6 (1M context) --- src/commands/code_mappings/upload.rs | 70 ++++++++++++----------- tests/integration/code_mappings/upload.rs | 4 +- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/commands/code_mappings/upload.rs b/src/commands/code_mappings/upload.rs index f81d29bbaa..b27ee8c1e7 100644 --- a/src/commands/code_mappings/upload.rs +++ b/src/commands/code_mappings/upload.rs @@ -77,35 +77,31 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { )?; let mapping_count = mappings.len(); - let batches: Vec<&[BulkCodeMapping]> = mappings.chunks(BATCH_SIZE).collect(); - let total_batches = batches.len(); + let total_batches = mapping_count.div_ceil(BATCH_SIZE); println!("Uploading {mapping_count} code mapping(s)..."); let api = Api::current(); let authenticated = api.authenticated()?; - let mut merged = MergedResponse::default(); - - for (i, batch) in batches.iter().enumerate() { - if total_batches > 1 { - println!("Sending batch {}/{total_batches}...", i + 1); - } - let request = BulkCodeMappingsRequest { - project: &project, - repository: &repo_name, - default_branch: &default_branch, - mappings: batch, - }; - match authenticated.bulk_upload_code_mappings(&org, &request) { - Ok(response) => merged.add(response), - Err(err) => { - merged - .batch_errors - .push(format!("Batch {}/{total_batches} failed: {err}", i + 1)); + let merged: MergedResponse = mappings + .chunks(BATCH_SIZE) + .enumerate() + .map(|(i, batch)| { + if total_batches > 1 { + println!("Sending batch {}/{total_batches}...", i + 1); } - } - } + let request = BulkCodeMappingsRequest { + project: &project, + repository: &repo_name, + default_branch: &default_branch, + mappings: batch, + }; + authenticated + .bulk_upload_code_mappings(&org, &request) + .map_err(|err| format!("Batch {}/{total_batches} failed: {err}", i + 1)) + }) + .collect(); // Display error details (successful mappings are summarized in counts only). print_error_table(&merged.mappings); @@ -222,9 +218,7 @@ fn infer_default_branch(git_repo: Option<&git2::Repository>, remote_name: Option } fn print_error_table(mappings: &[BulkCodeMappingResult]) { - let error_mappings: Vec<_> = mappings.iter().filter(|r| r.status == "error").collect(); - - if error_mappings.is_empty() { + if !mappings.iter().any(|r| r.status == "error") { return; } @@ -235,7 +229,7 @@ fn print_error_table(mappings: &[BulkCodeMappingResult]) { .add("Source Root") .add("Detail"); - for result in &error_mappings { + for result in mappings.iter().filter(|r| r.status == "error") { let detail = result.detail.as_deref().unwrap_or("unknown error"); table .add_row() @@ -257,12 +251,24 @@ struct MergedResponse { batch_errors: Vec, } -impl MergedResponse { - fn add(&mut self, response: BulkCodeMappingsResponse) { - self.created += response.created; - self.updated += response.updated; - self.errors += response.errors; - self.mappings.extend(response.mappings); +impl FromIterator> for MergedResponse { + fn from_iter(iter: I) -> Self + where + I: IntoIterator>, + { + let mut merged = Self::default(); + for result in iter { + match result { + Ok(response) => { + merged.created += response.created; + merged.updated += response.updated; + merged.errors += response.errors; + merged.mappings.extend(response.mappings); + } + Err(err) => merged.batch_errors.push(err), + } + } + merged } } diff --git a/tests/integration/code_mappings/upload.rs b/tests/integration/code_mappings/upload.rs index 13d3c79320..f3c710c0fd 100644 --- a/tests/integration/code_mappings/upload.rs +++ b/tests/integration/code_mappings/upload.rs @@ -1,4 +1,4 @@ -use std::sync::atomic::{AtomicU16, Ordering}; +use std::sync::atomic::{AtomicU8, Ordering}; use crate::integration::{AssertCommand, MockEndpointBuilder, TestManager}; @@ -49,7 +49,7 @@ fn command_code_mappings_upload_batches() { let fixture = tempfile::NamedTempFile::new().expect("failed to create temp file"); serde_json::to_writer(&fixture, &mappings).expect("failed to write fixture"); - let call_count = AtomicU16::new(0); + let call_count = AtomicU8::new(0); TestManager::new() .mock_endpoint( From 0a9c0669fc0023593300f4dee16bf9d811ca8200 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 24 Mar 2026 17:22:09 +0100 Subject: [PATCH 07/10] ref(code-mappings): Drop unused Clone derive from BulkCodeMapping Co-Authored-By: Claude Opus 4.6 (1M context) --- src/api/data_types/code_mappings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/data_types/code_mappings.rs b/src/api/data_types/code_mappings.rs index 7865eeb098..0f66a1504b 100644 --- a/src/api/data_types/code_mappings.rs +++ b/src/api/data_types/code_mappings.rs @@ -11,7 +11,7 @@ pub struct BulkCodeMappingsRequest<'a> { pub mappings: &'a [BulkCodeMapping], } -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct BulkCodeMapping { pub stack_root: String, From 80f1418c28c0d46bae5f6859957f815859f1fc51 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 24 Mar 2026 17:23:04 +0100 Subject: [PATCH 08/10] docs(changelog): Add code-mappings upload entry Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e68542894c..53b6c71550 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ ## 3.3.3 +### New Features ✨ + +- Add `sentry-cli code-mappings upload` command to bulk upload code mappings from a JSON file, with automatic git inference for repository name and default branch ([#3207](https://github.com/getsentry/sentry-cli/pull/3207), [#3208](https://github.com/getsentry/sentry-cli/pull/3208), [#3209](https://github.com/getsentry/sentry-cli/pull/3209), [#3210](https://github.com/getsentry/sentry-cli/pull/3210)). + ### Internal Changes 🔧 - (npm) 🤖 Bump optional dependencies to 3.3.2 in [afdef906](https://github.com/getsentry/sentry-cli/commit/afdef906ba225132f138469afd0d63df859ce0b4) From e30e46245f66e39948e19708937a578c9a9753a1 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 24 Mar 2026 18:38:00 +0100 Subject: [PATCH 09/10] docs(changelog): Expand code-mappings upload entry with details Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53b6c71550..14719a076f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,13 @@ ### New Features ✨ - Add `sentry-cli build download` command to download installable builds (IPA/APK) by build ID ([#3221](https://github.com/getsentry/sentry-cli/pull/3221)). +- Add `sentry-cli code-mappings upload` command to bulk upload code mappings from a JSON file ([#3207](https://github.com/getsentry/sentry-cli/pull/3207), [#3208](https://github.com/getsentry/sentry-cli/pull/3208), [#3209](https://github.com/getsentry/sentry-cli/pull/3209), [#3210](https://github.com/getsentry/sentry-cli/pull/3210)). + - Code mappings link stack trace paths (e.g. `com/example/module`) to source paths in your repository (e.g. `src/main/java/com/example/module`), enabling Sentry to display source context and link directly to your code from error stack traces. + - Repository name and default branch are automatically inferred from your local git remotes, or can be specified explicitly with `--repo` and `--default-branch`. + - Large mapping files are automatically split into batches for upload. ## 3.3.3 -### New Features ✨ - -- Add `sentry-cli code-mappings upload` command to bulk upload code mappings from a JSON file, with automatic git inference for repository name and default branch ([#3207](https://github.com/getsentry/sentry-cli/pull/3207), [#3208](https://github.com/getsentry/sentry-cli/pull/3208), [#3209](https://github.com/getsentry/sentry-cli/pull/3209), [#3210](https://github.com/getsentry/sentry-cli/pull/3210)). - ### Internal Changes 🔧 - (npm) 🤖 Bump optional dependencies to 3.3.2 in [afdef906](https://github.com/getsentry/sentry-cli/commit/afdef906ba225132f138469afd0d63df859ce0b4) From f795997fb4022c9c190f04915be7d02c39b717d3 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 24 Mar 2026 19:54:39 +0100 Subject: [PATCH 10/10] ref(code-mappings): Remove redundant 207 partial error test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 207 test was identical to the 200 partial error test — same response body, same expected CLI output. No special 207 handling exists in the API client, so the test added no coverage. Co-Authored-By: Claude Opus 4.6 (1M context) --- ...ode-mappings-upload-207-partial-error.trycmd | 17 ----------------- .../post-bulk-207-partial-error.json | 9 --------- tests/integration/code_mappings/upload.rs | 12 ------------ 3 files changed, 38 deletions(-) delete mode 100644 tests/integration/_cases/code_mappings/code-mappings-upload-207-partial-error.trycmd delete mode 100644 tests/integration/_responses/code_mappings/post-bulk-207-partial-error.json diff --git a/tests/integration/_cases/code_mappings/code-mappings-upload-207-partial-error.trycmd b/tests/integration/_cases/code_mappings/code-mappings-upload-207-partial-error.trycmd deleted file mode 100644 index 252d7a1866..0000000000 --- a/tests/integration/_cases/code_mappings/code-mappings-upload-207-partial-error.trycmd +++ /dev/null @@ -1,17 +0,0 @@ -``` -$ sentry-cli code-mappings upload tests/integration/_fixtures/code_mappings/mappings.json --org wat-org --project wat-project --repo owner/repo --default-branch main -? failed -Uploading 2 code mapping(s)... -+------------------+---------------------------------------------+-------------------+ -| Stack Root | Source Root | Detail | -+------------------+---------------------------------------------+-------------------+ -| com/example/maps | modules/maps/src/main/java/com/example/maps | duplicate mapping | -+------------------+---------------------------------------------+-------------------+ - -Created: 1, Updated: 0, Errors: 1 -error: 1 error(s) during upload. See details above. - -Add --log-level=[info|debug] or export SENTRY_LOG_LEVEL=[info|debug] to see more output. -Please attach the full debug log to all bug reports. - -``` diff --git a/tests/integration/_responses/code_mappings/post-bulk-207-partial-error.json b/tests/integration/_responses/code_mappings/post-bulk-207-partial-error.json deleted file mode 100644 index f44f466634..0000000000 --- a/tests/integration/_responses/code_mappings/post-bulk-207-partial-error.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "created": 1, - "updated": 0, - "errors": 1, - "mappings": [ - {"stackRoot": "com/example/core", "sourceRoot": "modules/core/src/main/java/com/example/core", "status": "created"}, - {"stackRoot": "com/example/maps", "sourceRoot": "modules/maps/src/main/java/com/example/maps", "status": "error", "detail": "duplicate mapping"} - ] -} diff --git a/tests/integration/code_mappings/upload.rs b/tests/integration/code_mappings/upload.rs index f3c710c0fd..8c01f753a2 100644 --- a/tests/integration/code_mappings/upload.rs +++ b/tests/integration/code_mappings/upload.rs @@ -24,18 +24,6 @@ fn command_code_mappings_upload_partial_error() { .with_default_token(); } -#[test] -fn command_code_mappings_upload_207_partial_error() { - TestManager::new() - .mock_endpoint( - MockEndpointBuilder::new("POST", "/api/0/organizations/wat-org/code-mappings/bulk/") - .with_status(207) - .with_response_file("code_mappings/post-bulk-207-partial-error.json"), - ) - .register_trycmd_test("code_mappings/code-mappings-upload-207-partial-error.trycmd") - .with_default_token(); -} - #[test] fn command_code_mappings_upload_batches() { // Generate a fixture with 301 mappings to force 2 batches (300 + 1).