diff --git a/CHANGELOG.md b/CHANGELOG.md index 18bab66665..65fe9962c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,18 @@ as the changelog is updated for minor releases. Prior to merging the PR stack for 3.0, we should rename this section to "Unreleased" --> +### New Sentry Support Policy + +`sentry-cli` 3.0.0 and above only officially supports Sentry SaaS and Sentry self-hosted versions [25.10.0](https://github.com/getsentry/sentry/releases/tag/25.10.0) and higher. While many Sentry CLI features may, in practice, continue working with some older Sentry versions, continued support for Sentry versions older than 25.10.0 is not guaranteed. Changes which break support for Sentry versions below 25.10.0 may occur in minor or patch releases. + ### Breaking Changes - Removed the `upload-proguard` subcommand's `--app-id`, `--version`, and `--version-code` arguments ([#2876](https://github.com/getsentry/sentry-cli/pull/2876)). Users using these arguments should stop using them, as they are unnecessary. The information passed to these arguments is no longer visible in Sentry. +### Fixes + +- Fixed misleading error message claiming the server doesn't support chunk uploading when the actual error was a non-existent organization ([#2930](https://github.com/getsentry/sentry-cli/pull/2930)). + ## 2.58.2 ### Improvements diff --git a/src/api/errors/api_error.rs b/src/api/errors/api_error.rs index 775f46d509..be6dda24fe 100644 --- a/src/api/errors/api_error.rs +++ b/src/api/errors/api_error.rs @@ -28,8 +28,6 @@ pub(in crate::api) enum ApiErrorKind { ProjectNotFound, #[error("Release not found. Ensure that you configured the correct release, project, and organization.")] ReleaseNotFound, - #[error("chunk upload endpoint not supported by sentry server")] - ChunkUploadNotSupported, #[error("API request failed")] RequestFailed, #[error("could not compress data")] @@ -63,6 +61,9 @@ impl ApiError { } } + // This method is currently only used in the macOS binary, there is no reason + // why not to expose it on other platforms, if we ever need it. + #[cfg(target_os = "macos")] pub(in crate::api) fn kind(&self) -> ApiErrorKind { self.inner } diff --git a/src/api/mod.rs b/src/api/mod.rs index 2778a2a364..f41459f406 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -956,21 +956,10 @@ impl<'a> AuthenticatedApi<'a> { } /// Get the server configuration for chunked file uploads. - pub fn get_chunk_upload_options(&self, org: &str) -> ApiResult> { + pub fn get_chunk_upload_options(&self, org: &str) -> ApiResult { let url = format!("/organizations/{}/chunk-upload/", PathArg(org)); - match self - .get(&url)? - .convert_rnf::(ApiErrorKind::ChunkUploadNotSupported) - { - Ok(options) => Ok(Some(options)), - Err(error) => { - if error.kind() == ApiErrorKind::ChunkUploadNotSupported { - Ok(None) - } else { - Err(error) - } - } - } + self.get(&url)? + .convert_rnf::(ApiErrorKind::OrganizationNotFound) } /// Request DIF assembling and processing from chunks. diff --git a/src/commands/build/upload.rs b/src/commands/build/upload.rs index 9b605a9a86..58cbfe8217 100644 --- a/src/commands/build/upload.rs +++ b/src/commands/build/upload.rs @@ -523,12 +523,7 @@ fn upload_file( build_configuration.unwrap_or("unknown"), ); - let chunk_upload_options = api.get_chunk_upload_options(org)?.ok_or_else(|| { - anyhow!( - "The Sentry server lacks chunked uploading support, which \ - is required for build uploads. {SELF_HOSTED_ERROR_HINT}" - ) - })?; + let chunk_upload_options = api.get_chunk_upload_options(org)?; if !chunk_upload_options.supports(ChunkUploadCapability::PreprodArtifacts) { bail!( diff --git a/src/commands/dart_symbol_map/upload.rs b/src/commands/dart_symbol_map/upload.rs index b224fee696..bd97fbec46 100644 --- a/src/commands/dart_symbol_map/upload.rs +++ b/src/commands/dart_symbol_map/upload.rs @@ -132,10 +132,7 @@ pub(super) fn execute(args: DartSymbolMapUploadArgs) -> Result<()> { ))?; let chunk_upload_options = api .authenticated()? - .get_chunk_upload_options(org)? - .ok_or_else(|| anyhow::anyhow!( - "server does not support chunked uploading. Please update your Sentry server." - ))?; + .get_chunk_upload_options(org)?; if !chunk_upload_options.supports(ChunkUploadCapability::DartSymbolMap) { bail!( diff --git a/src/commands/files/upload.rs b/src/commands/files/upload.rs index 469211e4f4..160de7e1ac 100644 --- a/src/commands/files/upload.rs +++ b/src/commands/files/upload.rs @@ -159,7 +159,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { note: None, wait, max_wait, - chunk_upload_options: chunk_upload_options.as_ref(), + chunk_upload_options: &chunk_upload_options, }; let path = Path::new(matches.get_one::("path").unwrap()); diff --git a/src/commands/react_native/appcenter.rs b/src/commands/react_native/appcenter.rs index 8205fec043..3d2d61564b 100644 --- a/src/commands/react_native/appcenter.rs +++ b/src/commands/react_native/appcenter.rs @@ -202,7 +202,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { note: None, wait, max_wait, - chunk_upload_options: chunk_upload_options.as_ref(), + chunk_upload_options: &chunk_upload_options, })?; } Some(dists) => { @@ -220,7 +220,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { note: None, wait, max_wait, - chunk_upload_options: chunk_upload_options.as_ref(), + chunk_upload_options: &chunk_upload_options, })?; } } diff --git a/src/commands/react_native/gradle.rs b/src/commands/react_native/gradle.rs index f8573c6ab4..e5a661f51a 100644 --- a/src/commands/react_native/gradle.rs +++ b/src/commands/react_native/gradle.rs @@ -129,7 +129,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { note: None, wait, max_wait, - chunk_upload_options: chunk_upload_options.as_ref(), + chunk_upload_options: &chunk_upload_options, })?; } } else { @@ -142,7 +142,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { note: None, wait, max_wait, - chunk_upload_options: chunk_upload_options.as_ref(), + chunk_upload_options: &chunk_upload_options, })?; } diff --git a/src/commands/react_native/xcode.rs b/src/commands/react_native/xcode.rs index a88f23486a..69b9822b1e 100644 --- a/src/commands/react_native/xcode.rs +++ b/src/commands/react_native/xcode.rs @@ -351,7 +351,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { note: None, wait, max_wait, - chunk_upload_options: chunk_upload_options.as_ref(), + chunk_upload_options: &chunk_upload_options, })?; } else { let (dist, release_name) = match (&dist_from_env, &release_from_env) { @@ -386,7 +386,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { note: None, wait, max_wait, - chunk_upload_options: chunk_upload_options.as_ref(), + chunk_upload_options: &chunk_upload_options, })?; } Some(dists) => { @@ -399,7 +399,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { note: None, wait, max_wait, - chunk_upload_options: chunk_upload_options.as_ref(), + chunk_upload_options: &chunk_upload_options, })?; } } diff --git a/src/commands/sourcemaps/upload.rs b/src/commands/sourcemaps/upload.rs index ecc98fa30c..b57b4f0c33 100644 --- a/src/commands/sourcemaps/upload.rs +++ b/src/commands/sourcemaps/upload.rs @@ -437,10 +437,10 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { log::warn!("The --use-artifact-bundle option and the SENTRY_FORCE_ARTIFACT_BUNDLES environment variable \ are both deprecated, and both will be removed in the next major version."); - if let Some(ref mut options) = chunk_upload_options { - if !options.supports(ChunkUploadCapability::ArtifactBundles) { - options.accept.push(ChunkUploadCapability::ArtifactBundles); - } + if !chunk_upload_options.supports(ChunkUploadCapability::ArtifactBundles) { + chunk_upload_options + .accept + .push(ChunkUploadCapability::ArtifactBundles); } } @@ -461,7 +461,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { note: matches.get_one::("note").map(String::as_str), wait, max_wait, - chunk_upload_options: chunk_upload_options.as_ref(), + chunk_upload_options: &chunk_upload_options, }; if matches.get_flag("strict") { diff --git a/src/commands/upload_proguard.rs b/src/commands/upload_proguard.rs index fd1af97484..2fa6ba4470 100644 --- a/src/commands/upload_proguard.rs +++ b/src/commands/upload_proguard.rs @@ -184,17 +184,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { authenticated_api = api.authenticated()?; (org, project) = config.get_org_and_project(matches)?; - let chunk_upload_options = authenticated_api - .get_chunk_upload_options(&org) - .map_err(|e| anyhow::anyhow!(e)) - .and_then(|options| { - options.ok_or_else(|| { - anyhow::anyhow!( - "server does not support chunked uploading. unset \ - {CHUNK_UPLOAD_ENV_VAR} to continue." - ) - }) - })?; + let chunk_upload_options = authenticated_api.get_chunk_upload_options(&org)?; proguard::chunk_upload(&mappings, chunk_upload_options, &org, &project)?; } else { diff --git a/src/utils/dif_upload/mod.rs b/src/utils/dif_upload/mod.rs index e576d3e343..bb4ccf3a32 100644 --- a/src/utils/dif_upload/mod.rs +++ b/src/utils/dif_upload/mod.rs @@ -1625,26 +1625,25 @@ impl<'a> DifUpload<'a> { } let api = Api::current(); - if let Some(chunk_options) = api.authenticated()?.get_chunk_upload_options(self.org)? { - if chunk_options.max_file_size > 0 { - self.max_file_size = chunk_options.max_file_size; - } - if chunk_options.max_wait > 0 { - self.max_wait = self - .max_wait - .min(Duration::from_secs(chunk_options.max_wait)); - } + let chunk_options = api.authenticated()?.get_chunk_upload_options(self.org)?; + if chunk_options.max_file_size > 0 { + self.max_file_size = chunk_options.max_file_size; + } + if chunk_options.max_wait > 0 { + self.max_wait = self + .max_wait + .min(Duration::from_secs(chunk_options.max_wait)); + } - self.pdbs_allowed = chunk_options.supports(ChunkUploadCapability::Pdbs); - self.portablepdbs_allowed = chunk_options.supports(ChunkUploadCapability::PortablePdbs); - self.sources_allowed = chunk_options.supports(ChunkUploadCapability::Sources); - self.bcsymbolmaps_allowed = chunk_options.supports(ChunkUploadCapability::BcSymbolmap); - self.il2cpp_mappings_allowed = chunk_options.supports(ChunkUploadCapability::Il2Cpp); + self.pdbs_allowed = chunk_options.supports(ChunkUploadCapability::Pdbs); + self.portablepdbs_allowed = chunk_options.supports(ChunkUploadCapability::PortablePdbs); + self.sources_allowed = chunk_options.supports(ChunkUploadCapability::Sources); + self.bcsymbolmaps_allowed = chunk_options.supports(ChunkUploadCapability::BcSymbolmap); + self.il2cpp_mappings_allowed = chunk_options.supports(ChunkUploadCapability::Il2Cpp); - if chunk_options.supports(ChunkUploadCapability::DebugFiles) { - self.validate_capabilities(); - return upload_difs_chunked(self, chunk_options); - } + if chunk_options.supports(ChunkUploadCapability::DebugFiles) { + self.validate_capabilities(); + return upload_difs_chunked(self, chunk_options); } self.validate_capabilities(); diff --git a/src/utils/file_upload.rs b/src/utils/file_upload.rs index cb837beb3e..95f03e90af 100644 --- a/src/utils/file_upload.rs +++ b/src/utils/file_upload.rs @@ -29,9 +29,6 @@ use crate::utils::source_bundle; use super::file_search::ReleaseFileMatch; -/// Fallback concurrency for release file uploads. -static DEFAULT_CONCURRENCY: usize = 4; - /// Old versions of Sentry cannot assemble artifact bundles straight away, they require /// that those bundles are associated to a release. /// @@ -42,11 +39,10 @@ pub fn initialize_legacy_release_upload(context: &UploadContext) -> Result<()> { // need to do anything here. Artifact bundles will also only work // 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() - && context.chunk_upload_options.is_some_and(|x| { - x.supports(ChunkUploadCapability::ArtifactBundles) - || x.supports(ChunkUploadCapability::ArtifactBundlesV2) - }) + && (chunk_options.supports(ChunkUploadCapability::ArtifactBundles) + || chunk_options.supports(ChunkUploadCapability::ArtifactBundlesV2)) { return Ok(()); } @@ -75,19 +71,8 @@ pub fn initialize_legacy_release_upload(context: &UploadContext) -> Result<()> { ..Default::default() }, )?; - } else if context.chunk_upload_options.is_some() { - bail!("This version of Sentry does not support artifact bundles. A release slug is required (provide with --release or by setting the SENTRY_RELEASE environment variable)"); } else { - // We got a 404 when trying to get the chunk options from the server. Most likely, the - // organization does not exist, though old self-hosted Sentry servers may also completely - // lack support for chunked uploads. - bail!( - "The provided organization \"{}\" does not exist. If you are using a self-hosted \ - Sentry server, it is also possible that your Sentry server lacks support for \ - uploading artifact bundles, in which case you need to provide a release slug with \ - --release or by setting the SENTRY_RELEASE environment variable.", - context.org - ); + bail!("This version of Sentry does not support artifact bundles. A release slug is required (provide with --release or by setting the SENTRY_RELEASE environment variable)"); } Ok(()) } @@ -101,7 +86,7 @@ pub struct UploadContext<'a> { pub note: Option<&'a str>, pub wait: bool, pub max_wait: Duration, - pub chunk_upload_options: Option<&'a ChunkServerOptions>, + pub chunk_upload_options: &'a ChunkServerOptions, } impl UploadContext<'_> { @@ -372,11 +357,17 @@ impl<'a> FileUpload<'a> { // multiple projects OK initialize_legacy_release_upload(self.context)?; - if let Some(chunk_options) = self.context.chunk_upload_options { - if chunk_options.supports(ChunkUploadCapability::ReleaseFiles) { - // multiple projects OK - return upload_files_chunked(self.context, &self.files, chunk_options); - } + if self + .context + .chunk_upload_options + .supports(ChunkUploadCapability::ReleaseFiles) + { + // multiple projects OK + return upload_files_chunked( + self.context, + &self.files, + self.context.chunk_upload_options, + ); } log::warn!( @@ -398,10 +389,7 @@ impl<'a> FileUpload<'a> { ); } - let concurrency = self - .context - .chunk_upload_options - .map_or(DEFAULT_CONCURRENCY, |o| usize::from(o.concurrency)); + let concurrency = self.context.chunk_upload_options.concurrency as usize; let legacy_context = &self.context.try_into().map_err(|e| { anyhow::anyhow!( @@ -701,15 +689,16 @@ fn print_upload_context_details(context: &UploadContext) { style("> Dist:").dim(), style(context.dist.unwrap_or("None")).yellow() ); - let upload_type = match context.chunk_upload_options { - None => "single file", - Some(opts) - if opts.supports(ChunkUploadCapability::ArtifactBundles) - || opts.supports(ChunkUploadCapability::ArtifactBundlesV2) => - { - "artifact bundle" - } - _ => "release bundle", + let upload_type = if context + .chunk_upload_options + .supports(ChunkUploadCapability::ArtifactBundles) + || context + .chunk_upload_options + .supports(ChunkUploadCapability::ArtifactBundlesV2) + { + "artifact bundle" + } else { + "release bundle" }; println!( "{} {}",