@@ -42,7 +42,7 @@ pub fn initialize_legacy_release_upload(context: &UploadContext) -> Result<()> {
42
42
// need to do anything here. Artifact bundles will also only work
43
43
// if a project is provided which is technically unnecessary for the
44
44
// legacy upload though it will unlikely to be what users want.
45
- if context. project . is_some ( )
45
+ if ! context. projects . is_empty ( )
46
46
&& context. chunk_upload_options . is_some_and ( |x| {
47
47
x. supports ( ChunkUploadCapability :: ArtifactBundles )
48
48
|| x. supports ( ChunkUploadCapability :: ArtifactBundlesV2 )
@@ -52,7 +52,7 @@ pub fn initialize_legacy_release_upload(context: &UploadContext) -> Result<()> {
52
52
}
53
53
54
54
// TODO: make this into an error later down the road
55
- if context. project . is_none ( ) {
55
+ if context. projects . is_empty ( ) {
56
56
eprintln ! (
57
57
"{}" ,
58
58
style(
@@ -71,7 +71,7 @@ pub fn initialize_legacy_release_upload(context: &UploadContext) -> Result<()> {
71
71
context. org ,
72
72
& NewRelease {
73
73
version : version. to_string ( ) ,
74
- projects : context. project . map ( |x| x . to_string ( ) ) . into_iter ( ) . collect ( ) ,
74
+ projects : context. projects . to_vec ( ) ,
75
75
..Default :: default ( )
76
76
} ,
77
77
) ?;
@@ -84,7 +84,7 @@ pub fn initialize_legacy_release_upload(context: &UploadContext) -> Result<()> {
84
84
#[ derive( Debug , Clone ) ]
85
85
pub struct UploadContext < ' a > {
86
86
pub org : & ' a str ,
87
- pub project : Option < & ' a str > ,
87
+ pub projects : & ' a [ String ] ,
88
88
pub release : Option < & ' a str > ,
89
89
pub dist : Option < & ' a str > ,
90
90
pub note : Option < & ' a str > ,
@@ -105,6 +105,8 @@ impl UploadContext<'_> {
105
105
pub enum LegacyUploadContextError {
106
106
#[ error( "a release is required for this upload" ) ]
107
107
ReleaseMissing ,
108
+ #[ error( "only a single project is supported for this upload" ) ]
109
+ ProjectMultiple ,
108
110
}
109
111
110
112
/// Represents the context for legacy release uploads.
@@ -182,12 +184,18 @@ impl<'a> TryFrom<&'a UploadContext<'_>> for LegacyUploadContext<'a> {
182
184
fn try_from ( value : & ' a UploadContext ) -> Result < Self , Self :: Error > {
183
185
let & UploadContext {
184
186
org,
185
- project ,
187
+ projects ,
186
188
release,
187
189
dist,
188
190
..
189
191
} = value;
190
192
193
+ let project = match projects {
194
+ [ ] => None ,
195
+ [ project] => Some ( project. as_str ( ) ) ,
196
+ [ _, _, ..] => Err ( LegacyUploadContextError :: ProjectMultiple ) ?,
197
+ } ;
198
+
191
199
let release = release. ok_or ( LegacyUploadContextError :: ReleaseMissing ) ?;
192
200
193
201
Ok ( Self {
@@ -292,14 +300,23 @@ impl<'a> FileUpload<'a> {
292
300
}
293
301
294
302
pub fn upload ( & self ) -> Result < ( ) > {
303
+ // multiple projects OK
295
304
initialize_legacy_release_upload ( self . context ) ?;
296
305
297
306
if let Some ( chunk_options) = self . context . chunk_upload_options {
298
307
if chunk_options. supports ( ChunkUploadCapability :: ReleaseFiles ) {
308
+ // multiple projects OK
299
309
return upload_files_chunked ( self . context , & self . files , chunk_options) ;
300
310
}
301
311
}
302
312
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
+
303
320
// Do not permit uploads of more than 20k files if the server does not
304
321
// support artifact bundles. This is a temporary downside protection to
305
322
// protect users from uploading more sources than we support.
@@ -318,10 +335,12 @@ impl<'a> FileUpload<'a> {
318
335
let legacy_context = & self . context . try_into ( ) . map_err ( |e| {
319
336
anyhow:: anyhow!(
320
337
"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.",
323
340
match e {
324
341
LegacyUploadContextError :: ReleaseMissing => "without specifying a release" ,
342
+ LegacyUploadContextError :: ProjectMultiple =>
343
+ "to multiple projects simultaneously" ,
325
344
}
326
345
)
327
346
} ) ?;
@@ -448,13 +467,13 @@ fn poll_assemble(
448
467
let authenticated_api = api. authenticated ( ) ?;
449
468
let use_artifact_bundle = ( options. supports ( ChunkUploadCapability :: ArtifactBundles )
450
469
|| options. supports ( ChunkUploadCapability :: ArtifactBundlesV2 ) )
451
- && context. project . is_some ( ) ;
470
+ && ! context. projects . is_empty ( ) ;
452
471
let response = loop {
453
472
// prefer standalone artifact bundle upload over legacy release based upload
454
473
let response = if use_artifact_bundle {
455
474
authenticated_api. assemble_artifact_bundle (
456
475
context. org ,
457
- & [ context. project . unwrap ( ) . to_string ( ) ] ,
476
+ context. projects ,
458
477
checksum,
459
478
chunks,
460
479
context. release ,
@@ -540,11 +559,11 @@ fn upload_files_chunked(
540
559
541
560
// Filter out chunks that are already on the server. This only matters if the server supports
542
561
// `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 ( ) {
544
563
let api = Api :: current ( ) ;
545
564
let response = api. authenticated ( ) ?. assemble_artifact_bundle (
546
565
context. org ,
547
- & [ context. project . unwrap ( ) . to_string ( ) ] ,
566
+ context. projects ,
548
567
checksum,
549
568
& checksums,
550
569
context. release ,
@@ -611,8 +630,9 @@ fn build_artifact_bundle(
611
630
}
612
631
613
632
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) ;
616
636
}
617
637
if let Some ( release) = context. release {
618
638
bundle. set_attribute ( "release" . to_owned ( ) , release. to_owned ( ) ) ;
@@ -703,8 +723,8 @@ fn print_upload_context_details(context: &UploadContext) {
703
723
) ;
704
724
println ! (
705
725
"{} {}" ,
706
- style( "> Project :" ) . dim( ) ,
707
- style( context. project . unwrap_or ( "None ") ) . yellow( )
726
+ style( "> Projects :" ) . dim( ) ,
727
+ style( context. projects . join ( ", ") ) . yellow( )
708
728
) ;
709
729
println ! (
710
730
"{} {}" ,
@@ -768,7 +788,7 @@ mod tests {
768
788
fn build_artifact_bundle_deterministic ( ) {
769
789
let context = UploadContext {
770
790
org : "wat-org" ,
771
- project : Some ( "wat-project" ) ,
791
+ projects : & [ "wat-project" . into ( ) ] ,
772
792
release : None ,
773
793
dist : None ,
774
794
note : None ,
0 commit comments