From a0f582e3034cfa274ae5b461fee6311c9eafd568 Mon Sep 17 00:00:00 2001 From: Daniel Szoke Date: Wed, 12 Nov 2025 13:02:44 +0100 Subject: [PATCH] chore(sourcemaps): Remove `sourcemaps explain` (#2947) ### Description Removed the `sentry-cli sourcemaps explain` command, which has been deprecated for some time. ### Issues - Resolves #2865 - Resolves [CLI-2865](https://linear.app/getsentry/issue/CLI-195/remove-sentry-cli-sourcemaps-explain-command) --- CHANGELOG.md | 1 + src/api/mod.rs | 109 +--- src/commands/sourcemaps/explain.rs | 521 ------------------ src/commands/sourcemaps/mod.rs | 2 - .../sourcemaps-explain-already-mapped.trycmd | 22 - ...maps-explain-artifact-dist-mismatch.trycmd | 14 - ...sourcemaps-explain-artifact-no-dist.trycmd | 14 - ...ps-explain-detect-from-file-content.trycmd | 17 - ...xplain-detect-from-sourcemap-header.trycmd | 16 - ...plain-detect-from-xsourcemap-header.trycmd | 16 - .../sourcemaps-explain-event-no-dist.trycmd | 14 - ...aps-explain-frame-malformed-abspath.trycmd | 11 - ...sourcemaps-explain-frame-no-abspath.trycmd | 11 - ...urcemaps-explain-frame-no-extension.trycmd | 11 - .../sourcemaps-explain-frame-no-inapp.trycmd | 11 - .../sourcemaps/sourcemaps-explain-help.trycmd | 29 - .../sourcemaps-explain-missing-event.trycmd | 8 - ...ourcemaps-explain-missing-exception.trycmd | 9 - .../sourcemaps-explain-missing-release.trycmd | 11 - ...urcemaps-explain-missing-stacktrace.trycmd | 10 - .../sourcemaps-explain-no-artifacts.trycmd | 12 - ...cemaps-explain-no-matching-artifact.trycmd | 11 - ...s-explain-partial-matching-artifact.trycmd | 12 - .../sourcemaps-explain-print-sourcemap.trycmd | 27 - ...s-explain-select-frame-out-of-range.trycmd | 11 - .../sourcemaps-explain-select-frame.trycmd | 11 - .../sourcemaps/sourcemaps-explain.trycmd | 11 - tests/integration/sourcemaps/explain.rs | 417 -------------- tests/integration/sourcemaps/mod.rs | 1 - 29 files changed, 5 insertions(+), 1365 deletions(-) delete mode 100644 src/commands/sourcemaps/explain.rs delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-already-mapped.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-artifact-dist-mismatch.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-artifact-no-dist.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-detect-from-file-content.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-detect-from-sourcemap-header.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-detect-from-xsourcemap-header.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-event-no-dist.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-frame-malformed-abspath.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-frame-no-abspath.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-frame-no-extension.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-frame-no-inapp.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-help.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-missing-event.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-missing-exception.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-missing-release.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-missing-stacktrace.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-no-artifacts.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-no-matching-artifact.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-partial-matching-artifact.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-print-sourcemap.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-select-frame-out-of-range.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain-select-frame.trycmd delete mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-explain.trycmd delete mode 100644 tests/integration/sourcemaps/explain.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index b6b1270b63..99a0b5b6ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ we should rename this section to "Unreleased" --> ### Breaking Changes +- Removed the `sentry-cli sourcemaps explain` command ([#2947](https://github.com/getsentry/sentry-cli/pull/2947)). The command had been deprecated for some time, since Sentry now has a better in-product debugging flow for source map problems via the "Unminify Code" button, which is displayed on any JavaScript issues which could not be unminified. - Removed support for the legacy API key authentication method ([#2935](https://github.com/getsentry/sentry-cli/pull/2935)). Sentry CLI now only supports authenticating with Auth Tokens. If you are using API key authentication via any of the following methods, you need to generate and use an [Auth Token](https://docs.sentry.io/account/auth-tokens/), instead: - `--api-key` CLI flag - `SENTRY_API_KEY` environment variable diff --git a/src/api/mod.rs b/src/api/mod.rs index f69ac2d658..a400e6981f 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -37,7 +37,6 @@ use log::{debug, info, warn}; use parking_lot::Mutex; use regex::{Captures, Regex}; use secrecy::ExposeSecret as _; -use sentry::protocol::{Exception, Values}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use sha1_smol::Digest; @@ -286,6 +285,10 @@ impl Api { } /// Convenience method that downloads a file into the given file object. + /// + /// Currently only used on macOS, but we could make it available on other platforms + /// if needed. + #[cfg(target_os = "macos")] pub fn download(&self, url: &str, dst: &mut File) -> ApiResult { self.request(Method::Get, url, None)? .follow_location(true)? @@ -551,73 +554,6 @@ impl<'a> AuthenticatedApi<'a> { self.list_release_files_by_checksum(org, project, release, &[]) } - /// Get a single release file and store it inside provided descriptor. - pub fn get_release_file( - &self, - org: &str, - project: Option<&str>, - version: &str, - file_id: &str, - file_desc: &mut File, - ) -> Result<(), ApiError> { - let path = if let Some(project) = project { - format!( - "/projects/{}/{}/releases/{}/files/{}/?download=1", - PathArg(org), - PathArg(project), - PathArg(version), - PathArg(file_id) - ) - } else { - format!( - "/organizations/{}/releases/{}/files/{}/?download=1", - PathArg(org), - PathArg(version), - PathArg(file_id) - ) - }; - - let resp = self.api.download(&path, file_desc)?; - if resp.status() == 404 { - resp.convert_rnf(ApiErrorKind::ResourceNotFound) - } else { - Ok(()) - } - } - - /// Get a single release file metadata. - pub fn get_release_file_metadata( - &self, - org: &str, - project: Option<&str>, - version: &str, - file_id: &str, - ) -> ApiResult> { - let path = if let Some(project) = project { - format!( - "/projects/{}/{}/releases/{}/files/{}/", - PathArg(org), - PathArg(project), - PathArg(version), - PathArg(file_id) - ) - } else { - format!( - "/organizations/{}/releases/{}/files/{}/", - PathArg(org), - PathArg(version), - PathArg(file_id) - ) - }; - - let resp = self.get(&path)?; - if resp.status() == 404 { - Ok(None) - } else { - resp.convert() - } - } - /// Deletes a single release file. Returns `true` if the file was /// deleted or `false` otherwise. pub fn delete_release_file( @@ -1302,37 +1238,6 @@ impl<'a> AuthenticatedApi<'a> { Ok(rv) } - /// Looks up an event, which was already processed by Sentry and returns it. - /// If it does not exist `None` will be returned. - pub fn get_event( - &self, - org: &str, - project: Option<&str>, - event_id: &str, - ) -> ApiResult> { - let path = if let Some(project) = project { - format!( - "/projects/{}/{}/events/{}/json/", - PathArg(org), - PathArg(project), - PathArg(event_id) - ) - } else { - format!( - "/organizations/{}/events/{}/json/", - PathArg(org), - PathArg(event_id) - ) - }; - - let resp = self.get(&path)?; - if resp.status() == 404 { - Ok(None) - } else { - resp.convert() - } - } - fn get_region_url(&self, org: &str) -> ApiResult { self.get(&format!("/organizations/{org}/region/")) .and_then(|resp| resp.convert::()) @@ -2383,12 +2288,6 @@ pub struct ProcessedEvent { #[expect(dead_code)] pub project: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub release: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub dist: Option, - #[serde(default, skip_serializing_if = "Values::is_empty")] - pub exception: Values, - #[serde(default, skip_serializing_if = "Option::is_none")] pub user: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub tags: Option>, diff --git a/src/commands/sourcemaps/explain.rs b/src/commands/sourcemaps/explain.rs deleted file mode 100644 index 2e04a0ee3b..0000000000 --- a/src/commands/sourcemaps/explain.rs +++ /dev/null @@ -1,521 +0,0 @@ -#![expect(clippy::unwrap_used, reason = "deprecated command")] - -use std::io::Read as _; -use std::path::Path; - -use anyhow::{bail, format_err, Result}; -use clap::{Arg, ArgAction, ArgMatches, Command}; -use console::style; -use sentry::protocol::{Frame, Stacktrace}; -use url::Url; - -use crate::api::{Api, Artifact, ProcessedEvent}; -use crate::config::Config; -use crate::utils::fs::TempFile; -use crate::utils::system::QuietExit; - -use super::resolve::print_source; - -pub fn make_command(command: Command) -> Command { - command - .about("[DEPRECATED] Explain why sourcemaps are not working for a given event.") - .hide(true) - .alias("why") - .arg( - Arg::new("event") - .value_name("EVENT_ID") - .required(true) - .help("ID of an event to be explained."), - ) - .arg( - Arg::new("frame") - .long("frame") - .default_value("0") - .value_parser(clap::value_parser!(usize)) - .help("Position of the frame that should be used for source map resolution."), - ) - .arg( - Arg::new("force") - .long("force") - .short('f') - .action(ArgAction::SetTrue) - .help("Force full validation flow, even when event is already source mapped."), - ) -} - -fn tip(msg: S) -where - S: std::fmt::Display, -{ - println!("{}", style(format!("ℹ {msg}")).blue()); -} - -fn success(msg: S) -where - S: std::fmt::Display, -{ - println!("{}", style(format!("✔ {msg}")).green()); -} - -fn warning(msg: S) -where - S: std::fmt::Display, -{ - println!("{}", style(format!("⚠ {msg}")).yellow()); -} - -fn error(msg: S) -where - S: std::fmt::Display, -{ - println!("{}", style(format!("✖ {msg}")).red()); -} - -fn fetch_event(org: &str, project: &str, event_id: &str) -> Result { - match Api::current() - .authenticated()? - .get_event(org, Some(project), event_id)? - { - Some(event) => { - success(format!("Fetched data for event: {event_id}")); - Ok(event) - } - None => { - error(format!("Could not retrieve event {event_id}")); - tip("Make sure that event ID you used is valid."); - Err(QuietExit(1).into()) - } - } -} - -fn extract_in_app_frames(stacktrace: &Stacktrace) -> Vec<&Frame> { - stacktrace - .frames - .iter() - .filter(|frame| frame.in_app.unwrap_or(false)) - .collect() -} - -fn extract_nth_frame(stacktrace: &Stacktrace, position: usize) -> Result<&Frame> { - let mut in_app_frames = extract_in_app_frames(stacktrace); - - if in_app_frames.is_empty() { - bail!("Event exception stacktrace has no in_app frames"); - } - - // Frames are in bottom-up order. - in_app_frames.reverse(); - - let frame = in_app_frames - .get(position) - .ok_or_else(|| format_err!("Selected frame ({position}) is missing."))?; - - let abs_path = frame - .abs_path - .as_ref() - .ok_or_else(|| format_err!("Selected frame ({position}) is missing an abs_path"))?; - - if let Ok(abs_path) = Url::parse(abs_path) { - if Path::new(abs_path.path()).extension().is_none() { - bail!("Selected frame ({position}) of event exception originates from the