@@ -3,18 +3,12 @@ use std::fs;
33use anyhow:: { bail, Context as _, Result } ;
44use clap:: { Arg , ArgMatches , Command } ;
55use log:: debug;
6- use serde:: { Deserialize , Serialize } ;
76
7+ use crate :: api:: { Api , BulkCodeMapping , BulkCodeMappingsRequest } ;
88use crate :: config:: Config ;
9+ use crate :: utils:: formatting:: Table ;
910use crate :: utils:: vcs;
1011
11- #[ derive( Debug , Deserialize , Serialize ) ]
12- #[ serde( rename_all = "camelCase" ) ]
13- struct CodeMapping {
14- stack_root : String ,
15- source_root : String ,
16- }
17-
1812pub fn make_command ( command : Command ) -> Command {
1913 command
2014 . 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)." )
@@ -39,12 +33,16 @@ pub fn make_command(command: Command) -> Command {
3933}
4034
4135pub fn execute ( matches : & ArgMatches ) -> Result < ( ) > {
36+ let config = Config :: current ( ) ;
37+ let org = config. get_org ( matches) ?;
38+ let project = config. get_project ( matches) ?;
39+
4240 let path = matches
4341 . get_one :: < String > ( "path" )
4442 . expect ( "path is a required argument" ) ;
4543 let data = fs:: read ( path) . with_context ( || format ! ( "Failed to read mappings file '{path}'" ) ) ?;
4644
47- let mappings: Vec < CodeMapping > =
45+ let mappings: Vec < BulkCodeMapping > =
4846 serde_json:: from_slice ( & data) . context ( "Failed to parse mappings JSON" ) ?;
4947
5048 if mappings. is_empty ( ) {
@@ -73,7 +71,6 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
7371 // Prefer explicit config (SENTRY_VCS_REMOTE / ini), then inspect
7472 // the repo for the best remote (upstream > origin > first).
7573 let remote_name = git_repo. as_ref ( ) . ok ( ) . and_then ( |repo| {
76- let config = Config :: current ( ) ;
7774 let configured_remote = config. get_cached_vcs_remote ( ) ;
7875 if vcs:: git_repo_remote_url ( repo, & configured_remote) . is_ok ( ) {
7976 debug ! ( "Using configured VCS remote: {configured_remote}" ) ;
@@ -146,9 +143,57 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
146143 }
147144 } ;
148145
149- println ! ( "Found {} code mapping(s) in {path}" , mappings. len( ) ) ;
150- println ! ( "Repository: {repo_name}" ) ;
151- println ! ( "Default branch: {default_branch}" ) ;
146+ let mapping_count = mappings. len ( ) ;
147+ let request = BulkCodeMappingsRequest {
148+ project,
149+ repository : repo_name,
150+ default_branch,
151+ mappings,
152+ } ;
153+
154+ println ! ( "Uploading {mapping_count} code mapping(s)..." ) ;
155+
156+ let api = Api :: current ( ) ;
157+ let response = api
158+ . authenticated ( ) ?
159+ . bulk_upload_code_mappings ( & org, & request) ?;
160+
161+ // Display results
162+ let mut table = Table :: new ( ) ;
163+ table
164+ . title_row ( )
165+ . add ( "Stack Root" )
166+ . add ( "Source Root" )
167+ . add ( "Status" ) ;
168+
169+ for result in & response. mappings {
170+ let status = match result. status . as_str ( ) {
171+ "error" => match & result. detail {
172+ Some ( detail) => format ! ( "error: {detail}" ) ,
173+ None => "error" . to_owned ( ) ,
174+ } ,
175+ s => s. to_owned ( ) ,
176+ } ;
177+ table
178+ . add_row ( )
179+ . add ( & result. stack_root )
180+ . add ( & result. source_root )
181+ . add ( & status) ;
182+ }
183+
184+ table. print ( ) ;
185+ println ! ( ) ;
186+ println ! (
187+ "Created: {}, Updated: {}, Errors: {}" ,
188+ response. created, response. updated, response. errors
189+ ) ;
190+
191+ if response. errors > 0 {
192+ bail ! (
193+ "{} mapping(s) failed to upload. See errors above." ,
194+ response. errors
195+ ) ;
196+ }
152197
153198 Ok ( ( ) )
154199}
0 commit comments