@@ -42,7 +42,7 @@ pub fn initialize_legacy_release_upload(context: &UploadContext) -> Result<()> {
4242 // need to do anything here. Artifact bundles will also only work
4343 // if a project is provided which is technically unnecessary for the
4444 // legacy upload though it will unlikely to be what users want.
45- if context. project . is_some ( )
45+ if ! context. projects . is_empty ( )
4646 && context. chunk_upload_options . is_some_and ( |x| {
4747 x. supports ( ChunkUploadCapability :: ArtifactBundles )
4848 || x. supports ( ChunkUploadCapability :: ArtifactBundlesV2 )
@@ -52,7 +52,7 @@ pub fn initialize_legacy_release_upload(context: &UploadContext) -> Result<()> {
5252 }
5353
5454 // TODO: make this into an error later down the road
55- if context. project . is_none ( ) {
55+ if context. projects . is_empty ( ) {
5656 eprintln ! (
5757 "{}" ,
5858 style(
@@ -71,7 +71,7 @@ pub fn initialize_legacy_release_upload(context: &UploadContext) -> Result<()> {
7171 context. org ,
7272 & NewRelease {
7373 version : version. to_string ( ) ,
74- projects : context. project . map ( |x| x . to_string ( ) ) . into_iter ( ) . collect ( ) ,
74+ projects : context. projects . to_vec ( ) ,
7575 ..Default :: default ( )
7676 } ,
7777 ) ?;
@@ -84,7 +84,7 @@ pub fn initialize_legacy_release_upload(context: &UploadContext) -> Result<()> {
8484#[ derive( Debug , Clone ) ]
8585pub struct UploadContext < ' a > {
8686 pub org : & ' a str ,
87- pub project : Option < & ' a str > ,
87+ pub projects : & ' a [ String ] ,
8888 pub release : Option < & ' a str > ,
8989 pub dist : Option < & ' a str > ,
9090 pub note : Option < & ' a str > ,
@@ -105,6 +105,8 @@ impl UploadContext<'_> {
105105pub enum LegacyUploadContextError {
106106 #[ error( "a release is required for this upload" ) ]
107107 ReleaseMissing ,
108+ #[ error( "only a single project is supported for this upload" ) ]
109+ ProjectMultiple ,
108110}
109111
110112/// Represents the context for legacy release uploads.
@@ -182,12 +184,18 @@ impl<'a> TryFrom<&'a UploadContext<'_>> for LegacyUploadContext<'a> {
182184 fn try_from ( value : & ' a UploadContext ) -> Result < Self , Self :: Error > {
183185 let & UploadContext {
184186 org,
185- project ,
187+ projects ,
186188 release,
187189 dist,
188190 ..
189191 } = value;
190192
193+ let project = match projects {
194+ [ ] => None ,
195+ [ project] => Some ( project. as_str ( ) ) ,
196+ [ _, _, ..] => Err ( LegacyUploadContextError :: ProjectMultiple ) ?,
197+ } ;
198+
191199 let release = release. ok_or ( LegacyUploadContextError :: ReleaseMissing ) ?;
192200
193201 Ok ( Self {
@@ -292,14 +300,23 @@ impl<'a> FileUpload<'a> {
292300 }
293301
294302 pub fn upload ( & self ) -> Result < ( ) > {
303+ // multiple projects OK
295304 initialize_legacy_release_upload ( self . context ) ?;
296305
297306 if let Some ( chunk_options) = self . context . chunk_upload_options {
298307 if chunk_options. supports ( ChunkUploadCapability :: ReleaseFiles ) {
308+ // multiple projects OK
299309 return upload_files_chunked ( self . context , & self . files , chunk_options) ;
300310 }
301311 }
302312
313+ log:: warn!(
314+ "Your Sentry server does not support chunked uploads. \
315+ We are falling back to a legacy upload method, which \
316+ has fewer features and is less reliable. Please consider \
317+ upgrading your Sentry server or switching to our SaaS offering."
318+ ) ;
319+
303320 // Do not permit uploads of more than 20k files if the server does not
304321 // support artifact bundles. This is a temporary downside protection to
305322 // protect users from uploading more sources than we support.
@@ -318,10 +335,12 @@ impl<'a> FileUpload<'a> {
318335 let legacy_context = & self . context . try_into ( ) . map_err ( |e| {
319336 anyhow:: anyhow!(
320337 "Error while performing legacy upload: {e}. \
321- If you would like to upload files {}, you need to upgrade your Sentry server \
322- or switch to our SaaS offering.",
338+ If you would like to upload files {}, you need to upgrade your Sentry server \
339+ or switch to our SaaS offering.",
323340 match e {
324341 LegacyUploadContextError :: ReleaseMissing => "without specifying a release" ,
342+ LegacyUploadContextError :: ProjectMultiple =>
343+ "to multiple projects simultaneously" ,
325344 }
326345 )
327346 } ) ?;
@@ -448,13 +467,13 @@ fn poll_assemble(
448467 let authenticated_api = api. authenticated ( ) ?;
449468 let use_artifact_bundle = ( options. supports ( ChunkUploadCapability :: ArtifactBundles )
450469 || options. supports ( ChunkUploadCapability :: ArtifactBundlesV2 ) )
451- && context. project . is_some ( ) ;
470+ && ! context. projects . is_empty ( ) ;
452471 let response = loop {
453472 // prefer standalone artifact bundle upload over legacy release based upload
454473 let response = if use_artifact_bundle {
455474 authenticated_api. assemble_artifact_bundle (
456475 context. org ,
457- & [ context. project . unwrap ( ) . to_string ( ) ] ,
476+ context. projects ,
458477 checksum,
459478 chunks,
460479 context. release ,
@@ -540,11 +559,11 @@ fn upload_files_chunked(
540559
541560 // Filter out chunks that are already on the server. This only matters if the server supports
542561 // `ArtifactBundlesV2`, otherwise the `missing_chunks` field is meaningless.
543- if options. supports ( ChunkUploadCapability :: ArtifactBundlesV2 ) && context. project . is_some ( ) {
562+ if options. supports ( ChunkUploadCapability :: ArtifactBundlesV2 ) && ! context. projects . is_empty ( ) {
544563 let api = Api :: current ( ) ;
545564 let response = api. authenticated ( ) ?. assemble_artifact_bundle (
546565 context. org ,
547- & [ context. project . unwrap ( ) . to_string ( ) ] ,
566+ context. projects ,
548567 checksum,
549568 & checksums,
550569 context. release ,
@@ -611,8 +630,9 @@ fn build_artifact_bundle(
611630 }
612631
613632 bundle. set_attribute ( "org" . to_owned ( ) , context. org . to_owned ( ) ) ;
614- if let Some ( project) = context. project {
615- bundle. set_attribute ( "project" . to_owned ( ) , project. to_owned ( ) ) ;
633+ if let [ project] = context. projects {
634+ // Only set project if there is exactly one project
635+ bundle. set_attribute ( "project" . to_owned ( ) , project) ;
616636 }
617637 if let Some ( release) = context. release {
618638 bundle. set_attribute ( "release" . to_owned ( ) , release. to_owned ( ) ) ;
@@ -703,8 +723,8 @@ fn print_upload_context_details(context: &UploadContext) {
703723 ) ;
704724 println ! (
705725 "{} {}" ,
706- style( "> Project :" ) . dim( ) ,
707- style( context. project . unwrap_or ( "None ") ) . yellow( )
726+ style( "> Projects :" ) . dim( ) ,
727+ style( context. projects . join ( ", ") ) . yellow( )
708728 ) ;
709729 println ! (
710730 "{} {}" ,
@@ -768,7 +788,7 @@ mod tests {
768788 fn build_artifact_bundle_deterministic ( ) {
769789 let context = UploadContext {
770790 org : "wat-org" ,
771- project : Some ( "wat-project" ) ,
791+ projects : & [ "wat-project" . into ( ) ] ,
772792 release : None ,
773793 dist : None ,
774794 note : None ,
0 commit comments