22use std:: collections:: { BTreeMap , HashMap } ;
33use std:: ffi:: OsStr ;
44use std:: fmt:: { self , Display } ;
5- use std:: io:: BufWriter ;
65use std:: path:: PathBuf ;
76use std:: str;
87use std:: sync:: Arc ;
98use std:: time:: { Duration , Instant } ;
109
1110use anyhow:: { anyhow, bail, Result } ;
1211use console:: style;
13- use log:: info;
1412use parking_lot:: RwLock ;
1513use rayon:: prelude:: * ;
1614use rayon:: ThreadPoolBuilder ;
1715use sentry:: types:: DebugId ;
1816use sha1_smol:: Digest ;
1917use symbolic:: common:: ByteView ;
2018use symbolic:: debuginfo:: js;
21- use symbolic:: debuginfo:: sourcebundle:: {
22- SourceBundleErrorKind , SourceBundleWriter , SourceFileInfo , SourceFileType ,
23- } ;
19+ use symbolic:: debuginfo:: sourcebundle:: SourceFileType ;
2420use thiserror:: Error ;
25- use url:: Url ;
2621
2722use crate :: api:: NewRelease ;
2823use crate :: api:: { Api , ChunkServerOptions , ChunkUploadCapability } ;
@@ -31,6 +26,7 @@ use crate::utils::chunks::{upload_chunks, Chunk, ASSEMBLE_POLL_INTERVAL};
3126use crate :: utils:: fs:: { get_sha1_checksums, TempFile } ;
3227use crate :: utils:: non_empty:: NonEmptySlice ;
3328use crate :: utils:: progress:: { ProgressBar , ProgressBarMode , ProgressStyle } ;
29+ use crate :: utils:: source_bundle;
3430
3531use super :: file_search:: ReleaseFileMatch ;
3632
@@ -415,7 +411,7 @@ impl<'a> FileUpload<'a> {
415411 }
416412
417413 pub fn build_jvm_bundle ( & self , debug_id : Option < DebugId > ) -> Result < TempFile > {
418- build_artifact_bundle ( self . context , & self . files , debug_id)
414+ source_bundle :: build ( self . context , & self . files , debug_id)
419415 }
420416}
421417
@@ -622,7 +618,7 @@ fn upload_files_chunked(
622618 files : & SourceFiles ,
623619 options : & ChunkServerOptions ,
624620) -> Result < ( ) > {
625- let archive = build_artifact_bundle ( context, files, None ) ?;
621+ let archive = source_bundle :: build ( context, files, None ) ?;
626622
627623 let progress_style =
628624 ProgressStyle :: default_spinner ( ) . template ( "{spinner} Optimizing bundle for upload..." ) ;
@@ -678,137 +674,6 @@ fn upload_files_chunked(
678674 poll_assemble ( checksum, & checksums, context, options)
679675}
680676
681- /// Creates a debug id from a map of source files by hashing each file's
682- /// URL, contents, type, and headers.
683- fn build_debug_id ( files : & SourceFiles ) -> DebugId {
684- let mut hash = sha1_smol:: Sha1 :: new ( ) ;
685- for source_file in files. values ( ) {
686- hash. update ( source_file. url . as_bytes ( ) ) ;
687- hash. update ( & source_file. contents ) ;
688- hash. update ( format ! ( "{:?}" , source_file. ty) . as_bytes ( ) ) ;
689-
690- for ( key, value) in & source_file. headers {
691- hash. update ( key. as_bytes ( ) ) ;
692- hash. update ( value. as_bytes ( ) ) ;
693- }
694- }
695-
696- let mut sha1_bytes = [ 0u8 ; 16 ] ;
697- sha1_bytes. copy_from_slice ( & hash. digest ( ) . bytes ( ) [ ..16 ] ) ;
698- DebugId :: from_uuid ( uuid:: Builder :: from_sha1_bytes ( sha1_bytes) . into_uuid ( ) )
699- }
700-
701- fn build_artifact_bundle (
702- context : & UploadContext ,
703- files : & SourceFiles ,
704- debug_id : Option < DebugId > ,
705- ) -> Result < TempFile > {
706- let progress_style = ProgressStyle :: default_bar ( ) . template (
707- "{prefix:.dim} Bundling files for upload... {msg:.dim}\
708- \n {wide_bar} {pos}/{len}",
709- ) ;
710-
711- let pb = ProgressBar :: new ( files. len ( ) ) ;
712- pb. set_style ( progress_style) ;
713- pb. set_prefix ( ">" ) ;
714-
715- let archive = TempFile :: create ( ) ?;
716- let mut bundle = SourceBundleWriter :: start ( BufWriter :: new ( archive. open ( ) ?) ) ?;
717-
718- // artifact bundles get a random UUID as debug id
719- let debug_id = debug_id. unwrap_or_else ( || build_debug_id ( files) ) ;
720- bundle. set_attribute ( "debug_id" , debug_id. to_string ( ) ) ;
721-
722- if let Some ( note) = context. note {
723- bundle. set_attribute ( "note" , note. to_owned ( ) ) ;
724- }
725-
726- bundle. set_attribute ( "org" . to_owned ( ) , context. org . to_owned ( ) ) ;
727- if let Some ( [ project] ) = context. projects . as_deref ( ) {
728- // Only set project if there is exactly one project
729- bundle. set_attribute ( "project" . to_owned ( ) , project) ;
730- }
731- if let Some ( release) = context. release {
732- bundle. set_attribute ( "release" . to_owned ( ) , release. to_owned ( ) ) ;
733- }
734- if let Some ( dist) = context. dist {
735- bundle. set_attribute ( "dist" . to_owned ( ) , dist. to_owned ( ) ) ;
736- }
737-
738- let mut bundle_file_count = 0 ;
739-
740- for file in files. values ( ) {
741- pb. inc ( 1 ) ;
742- pb. set_message ( & file. url ) ;
743-
744- let mut info = SourceFileInfo :: new ( ) ;
745- info. set_ty ( file. ty ) ;
746- info. set_url ( file. url . clone ( ) ) ;
747- for ( k, v) in & file. headers {
748- info. add_header ( k. clone ( ) , v. clone ( ) ) ;
749- }
750-
751- let bundle_path = url_to_bundle_path ( & file. url ) ?;
752- if let Err ( e) = bundle. add_file ( bundle_path, file. contents . as_slice ( ) , info) {
753- if e. kind ( ) == SourceBundleErrorKind :: ReadFailed {
754- info ! (
755- "Skipping {} because it is not valid UTF-8." ,
756- file. path. display( )
757- ) ;
758- continue ;
759- } else {
760- return Err ( e. into ( ) ) ;
761- }
762- }
763- bundle_file_count += 1 ;
764- }
765-
766- bundle. finish ( ) ?;
767-
768- pb. finish_with_duration ( "Bundling" ) ;
769-
770- println ! (
771- "{} Bundled {} {} for upload" ,
772- style( ">" ) . dim( ) ,
773- style( bundle_file_count) . yellow( ) ,
774- match bundle_file_count {
775- 1 => "file" ,
776- _ => "files" ,
777- }
778- ) ;
779-
780- println ! (
781- "{} Bundle ID: {}" ,
782- style( ">" ) . dim( ) ,
783- style( debug_id) . yellow( ) ,
784- ) ;
785-
786- Ok ( archive)
787- }
788-
789- fn url_to_bundle_path ( url : & str ) -> Result < String > {
790- let base = Url :: parse ( "http://~" ) . expect ( "this url is valid" ) ;
791- let url = if let Some ( rest) = url. strip_prefix ( "~/" ) {
792- base. join ( rest) ?
793- } else {
794- base. join ( url) ?
795- } ;
796-
797- let mut path = url. path ( ) . to_owned ( ) ;
798- if let Some ( fragment) = url. fragment ( ) {
799- path = format ! ( "{path}#{fragment}" ) ;
800- }
801- if path. starts_with ( '/' ) {
802- path. remove ( 0 ) ;
803- }
804-
805- Ok ( match url. host_str ( ) {
806- Some ( "~" ) => format ! ( "_/_/{path}" ) ,
807- Some ( host) => format ! ( "{}/{host}/{path}" , url. scheme( ) ) ,
808- None => format ! ( "{}/_/{path}" , url. scheme( ) ) ,
809- } )
810- }
811-
812677fn print_upload_context_details ( context : & UploadContext ) {
813678 println ! (
814679 "{} {}" ,
@@ -853,77 +718,3 @@ fn is_hermes_bytecode(slice: &[u8]) -> bool {
853718 const HERMES_MAGIC : [ u8 ; 8 ] = [ 0xC6 , 0x1F , 0xBC , 0x03 , 0xC1 , 0x03 , 0x19 , 0x1F ] ;
854719 slice. starts_with ( & HERMES_MAGIC )
855720}
856-
857- #[ cfg( test) ]
858- mod tests {
859- use sha1_smol:: Sha1 ;
860-
861- use super :: * ;
862-
863- #[ test]
864- fn test_url_to_bundle_path ( ) {
865- assert_eq ! ( url_to_bundle_path( "~/bar" ) . unwrap( ) , "_/_/bar" ) ;
866- assert_eq ! ( url_to_bundle_path( "~/foo/bar" ) . unwrap( ) , "_/_/foo/bar" ) ;
867- assert_eq ! (
868- url_to_bundle_path( "~/dist/js/bundle.js.map" ) . unwrap( ) ,
869- "_/_/dist/js/bundle.js.map"
870- ) ;
871- assert_eq ! (
872- url_to_bundle_path( "~/babel.config.js" ) . unwrap( ) ,
873- "_/_/babel.config.js"
874- ) ;
875-
876- assert_eq ! ( url_to_bundle_path( "~/#/bar" ) . unwrap( ) , "_/_/#/bar" ) ;
877- assert_eq ! ( url_to_bundle_path( "~/foo/#/bar" ) . unwrap( ) , "_/_/foo/#/bar" ) ;
878- assert_eq ! (
879- url_to_bundle_path( "~/dist/#js/bundle.js.map" ) . unwrap( ) ,
880- "_/_/dist/#js/bundle.js.map"
881- ) ;
882- assert_eq ! (
883- url_to_bundle_path( "~/#foo/babel.config.js" ) . unwrap( ) ,
884- "_/_/#foo/babel.config.js"
885- ) ;
886- }
887-
888- #[ test]
889- fn build_artifact_bundle_deterministic ( ) {
890- let projects_slice = & [ "wat-project" . into ( ) ] ;
891- let context = UploadContext {
892- org : "wat-org" ,
893- projects : Some ( projects_slice. into ( ) ) ,
894- release : None ,
895- dist : None ,
896- note : None ,
897- wait : false ,
898- max_wait : DEFAULT_MAX_WAIT ,
899- chunk_upload_options : None ,
900- } ;
901-
902- let source_files = [ "bundle.min.js.map" , "vendor.min.js.map" ]
903- . into_iter ( )
904- . map ( |name| {
905- let file = SourceFile {
906- url : format ! ( "~/{name}" ) ,
907- path : format ! ( "tests/integration/_fixtures/{name}" ) . into ( ) ,
908- contents : std:: fs:: read ( format ! ( "tests/integration/_fixtures/{name}" ) )
909- . unwrap ( )
910- . into ( ) ,
911- ty : SourceFileType :: SourceMap ,
912- headers : Default :: default ( ) ,
913- messages : Default :: default ( ) ,
914- already_uploaded : false ,
915- } ;
916- ( format ! ( "~/{name}" ) , file)
917- } )
918- . collect ( ) ;
919-
920- let file = build_artifact_bundle ( & context, & source_files, None ) . unwrap ( ) ;
921-
922- let buf = std:: fs:: read ( file. path ( ) ) . unwrap ( ) ;
923- let hash = Sha1 :: from ( buf) ;
924- assert_eq ! (
925- hash. digest( ) . to_string( ) ,
926- "f0e25ae149b711c510148e022ebc883ad62c7c4c"
927- ) ;
928- }
929- }
0 commit comments