From be26c243fff42511027fd12f93e4024e401c34a5 Mon Sep 17 00:00:00 2001 From: Daniel Szoke Date: Fri, 14 Nov 2025 13:56:00 +0100 Subject: [PATCH] ref(sourcemaps): `UploadContext` always has `projects` (#2959) ### Description Following #2956, the `UploadContext` struct's `projects` field is always `Some`, indicating we have at least one project. Here, we change the type from `Option` to `NonEmptySlice` to indicate that we always have a project set. --- src/commands/react_native/appcenter.rs | 4 +- src/commands/react_native/gradle.rs | 4 +- src/commands/react_native/xcode.rs | 6 +-- src/commands/sourcemaps/upload.rs | 2 +- src/utils/file_upload.rs | 53 ++++++-------------------- src/utils/source_bundle.rs | 2 +- 6 files changed, 21 insertions(+), 50 deletions(-) diff --git a/src/commands/react_native/appcenter.rs b/src/commands/react_native/appcenter.rs index 3d2d61564b..b5e271f008 100644 --- a/src/commands/react_native/appcenter.rs +++ b/src/commands/react_native/appcenter.rs @@ -196,7 +196,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { processor.upload(&UploadContext { org: &org, - projects: Some(projects.as_non_empty_slice()), + projects: projects.as_non_empty_slice(), release: Some(&release), dist: None, note: None, @@ -214,7 +214,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { processor.upload(&UploadContext { org: &org, - projects: Some(projects.as_non_empty_slice()), + projects: projects.as_non_empty_slice(), release: Some(&release), dist: Some(dist), note: None, diff --git a/src/commands/react_native/gradle.rs b/src/commands/react_native/gradle.rs index e5a661f51a..ff706d601f 100644 --- a/src/commands/react_native/gradle.rs +++ b/src/commands/react_native/gradle.rs @@ -123,7 +123,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { processor.upload(&UploadContext { org: &org, - projects: Some(projects.as_non_empty_slice()), + projects: projects.as_non_empty_slice(), release: Some(version), dist: Some(dist), note: None, @@ -136,7 +136,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { // Debug Id Upload processor.upload(&UploadContext { org: &org, - projects: Some(projects.as_non_empty_slice()), + projects: projects.as_non_empty_slice(), release: None, dist: None, note: None, diff --git a/src/commands/react_native/xcode.rs b/src/commands/react_native/xcode.rs index 69b9822b1e..4ff365f34e 100644 --- a/src/commands/react_native/xcode.rs +++ b/src/commands/react_native/xcode.rs @@ -345,7 +345,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { if dist_from_env.is_err() && release_from_env.is_err() && matches.get_flag("no_auto_release") { processor.upload(&UploadContext { org: &org, - projects: Some(projects.as_non_empty_slice()), + projects: projects.as_non_empty_slice(), release: None, dist: None, note: None, @@ -380,7 +380,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { None => { processor.upload(&UploadContext { org: &org, - projects: Some(projects.as_non_empty_slice()), + projects: projects.as_non_empty_slice(), release: release_name.as_deref(), dist: dist.as_deref(), note: None, @@ -393,7 +393,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { for dist in dists { processor.upload(&UploadContext { org: &org, - projects: Some(projects.as_non_empty_slice()), + projects: projects.as_non_empty_slice(), release: release_name.as_deref(), dist: Some(dist), note: None, diff --git a/src/commands/sourcemaps/upload.rs b/src/commands/sourcemaps/upload.rs index b57b4f0c33..a08f13a9b1 100644 --- a/src/commands/sourcemaps/upload.rs +++ b/src/commands/sourcemaps/upload.rs @@ -455,7 +455,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { let max_wait = wait_for_secs.map_or(DEFAULT_MAX_WAIT, Duration::from_secs); let upload_context = UploadContext { org: &org, - projects: Some(projects.as_non_empty_slice()), + projects: projects.as_non_empty_slice(), release: version.as_deref(), dist: matches.get_one::("dist").map(String::as_str), note: matches.get_one::("note").map(String::as_str), diff --git a/src/utils/file_upload.rs b/src/utils/file_upload.rs index 95f03e90af..7990d11203 100644 --- a/src/utils/file_upload.rs +++ b/src/utils/file_upload.rs @@ -40,34 +40,19 @@ pub fn initialize_legacy_release_upload(context: &UploadContext) -> Result<()> { // if a project is provided which is technically unnecessary for the // legacy upload though it will unlikely to be what users want. let chunk_options = context.chunk_upload_options; - if context.projects.is_some() - && (chunk_options.supports(ChunkUploadCapability::ArtifactBundles) - || chunk_options.supports(ChunkUploadCapability::ArtifactBundlesV2)) + if chunk_options.supports(ChunkUploadCapability::ArtifactBundles) + || chunk_options.supports(ChunkUploadCapability::ArtifactBundlesV2) { return Ok(()); } - // TODO: make this into an error later down the road - if context.projects.is_none() { - eprintln!( - "{}", - style( - "warning: no project specified. \ - While this upload will succeed it will be unlikely that \ - this is what you wanted. Future versions of sentry will \ - require a project to be set." - ) - .red() - ); - } - if let Some(version) = context.release { let api = Api::current(); api.authenticated()?.new_release( context.org, &NewRelease { version: version.to_owned(), - projects: context.projects.map(Vec::from).unwrap_or_default(), + projects: context.projects.into(), ..Default::default() }, )?; @@ -80,7 +65,7 @@ pub fn initialize_legacy_release_upload(context: &UploadContext) -> Result<()> { #[derive(Debug, Clone)] pub struct UploadContext<'a> { pub org: &'a str, - pub projects: Option>, + pub projects: NonEmptySlice<'a, String>, pub release: Option<&'a str>, pub dist: Option<&'a str>, pub note: Option<&'a str>, @@ -185,13 +170,11 @@ impl<'a> TryFrom<&'a UploadContext<'_>> for LegacyUploadContext<'a> { .. } = value; - let project = projects - .map(|projects| match <&[_]>::from(projects) { - [] => unreachable!("NonEmptySlice cannot be empty"), - [project] => Ok(project.as_str()), - [_, _, ..] => Err(LegacyUploadContextError::ProjectMultiple), - }) - .transpose()?; + let project = Some(match <&[_]>::from(projects) { + [] => unreachable!("NonEmptySlice cannot be empty"), + [project] => Ok(project.as_str()), + [_, _, ..] => Err(LegacyUploadContextError::ProjectMultiple), + }?); let release = release.ok_or(LegacyUploadContextError::ReleaseMissing)?; @@ -536,23 +519,12 @@ fn poll_assemble( ); } - // We fall back to legacy release upload if server lacks artifact bundle support, or if - // no projects are specified. context.projects has Some(projects) in all cases, besides - // the following: - // - For `files upload`, we can have None projects. We don't need a separate warning, - // because `files upload` is already deprecated. - // - For `debug-files bundle-jvm`, but although that codepath uses the `UploadContext`, - // it does not actually use it to perform an upload, so we never hit this codepath. - let artifact_bundle_projects = server_supports_artifact_bundles - .then_some(context.projects) - .flatten(); - let response = loop { // prefer standalone artifact bundle upload over legacy release based upload - let response = if let Some(projects) = artifact_bundle_projects { + let response = if server_supports_artifact_bundles { authenticated_api.assemble_artifact_bundle( context.org, - projects, + context.projects, checksum, chunks, context.release, @@ -642,7 +614,6 @@ fn upload_files_chunked( if let Some(projects) = options .supports(ChunkUploadCapability::ArtifactBundlesV2) .then_some(context.projects) - .flatten() { let api = Api::current(); let response = api.authenticated()?.assemble_artifact_bundle( @@ -677,7 +648,7 @@ fn print_upload_context_details(context: &UploadContext) { println!( "{} {}", style("> Projects:").dim(), - style(context.projects.as_deref().unwrap_or_default().join(", ")).yellow() + style(context.projects.join(", ")).yellow() ); println!( "{} {}", diff --git a/src/utils/source_bundle.rs b/src/utils/source_bundle.rs index 461ad5097a..5378df0ef0 100644 --- a/src/utils/source_bundle.rs +++ b/src/utils/source_bundle.rs @@ -43,7 +43,7 @@ impl<'a> From<&'a UploadContext<'a>> for BundleContext<'a> { fn from(context: &'a UploadContext<'a>) -> Self { Self { org: context.org, - projects: context.projects, + projects: Some(context.projects), note: context.note, release: context.release, dist: context.dist,