@@ -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." )
@@ -39,11 +33,15 @@ 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 #[ expect( clippy:: unwrap_used, reason = "path is a required argument" ) ]
4341 let path = matches. get_one :: < String > ( "path" ) . unwrap ( ) ;
4442 let data = fs:: read ( path) . with_context ( || format ! ( "Failed to read mappings file '{path}'" ) ) ?;
4543
46- let mappings: Vec < CodeMapping > =
44+ let mappings: Vec < BulkCodeMapping > =
4745 serde_json:: from_slice ( & data) . context ( "Failed to parse mappings JSON" ) ?;
4846
4947 if mappings. is_empty ( ) {
@@ -74,7 +72,6 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
7472 } ) ?;
7573 // Prefer explicit config (SENTRY_VCS_REMOTE / ini), then inspect
7674 // the repo for the best remote (upstream > origin > first).
77- let config = Config :: current ( ) ;
7875 let configured_remote = config. get_cached_vcs_remote ( ) ;
7976 let remote_name = if vcs:: git_repo_remote_url ( & git_repo, & configured_remote) . is_ok ( ) {
8077 debug ! ( "Using configured VCS remote: {configured_remote}" ) ;
@@ -120,9 +117,57 @@ pub fn execute(matches: &ArgMatches) -> Result<()> {
120117 }
121118 } ;
122119
123- println ! ( "Found {} code mapping(s) in {path}" , mappings. len( ) ) ;
124- println ! ( "Repository: {repo_name}" ) ;
125- println ! ( "Default branch: {default_branch}" ) ;
120+ let mapping_count = mappings. len ( ) ;
121+ let request = BulkCodeMappingsRequest {
122+ project,
123+ repository : repo_name,
124+ default_branch,
125+ mappings,
126+ } ;
127+
128+ println ! ( "Uploading {mapping_count} code mapping(s)..." ) ;
129+
130+ let api = Api :: current ( ) ;
131+ let response = api
132+ . authenticated ( ) ?
133+ . bulk_upload_code_mappings ( & org, & request) ?;
134+
135+ // Display results
136+ let mut table = Table :: new ( ) ;
137+ table
138+ . title_row ( )
139+ . add ( "Stack Root" )
140+ . add ( "Source Root" )
141+ . add ( "Status" ) ;
142+
143+ for result in & response. mappings {
144+ let status = match result. status . as_str ( ) {
145+ "error" => match & result. detail {
146+ Some ( detail) => format ! ( "error: {detail}" ) ,
147+ None => "error" . to_owned ( ) ,
148+ } ,
149+ s => s. to_owned ( ) ,
150+ } ;
151+ table
152+ . add_row ( )
153+ . add ( & result. stack_root )
154+ . add ( & result. source_root )
155+ . add ( & status) ;
156+ }
157+
158+ table. print ( ) ;
159+ println ! ( ) ;
160+ println ! (
161+ "Created: {}, Updated: {}, Errors: {}" ,
162+ response. created, response. updated, response. errors
163+ ) ;
164+
165+ if response. errors > 0 {
166+ bail ! (
167+ "{} mapping(s) failed to upload. See errors above." ,
168+ response. errors
169+ ) ;
170+ }
126171
127172 Ok ( ( ) )
128173}
0 commit comments