From 3291fc41162fe136ab0d13070aed703eba5ee505 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Fri, 10 Feb 2023 15:54:26 +0100 Subject: [PATCH 01/17] feat: Add sourcemaps inject command --- src/commands/sourcemaps/inject.rs | 157 ++++++++++++++++++ src/commands/sourcemaps/mod.rs | 2 + .../_cases/sourcemaps/sourcemaps-help.trycmd | 1 + .../sourcemaps/sourcemaps-inject-help.trycmd | 47 ++++++ .../sourcemaps-no-subcommand.trycmd | 1 + tests/integration/sourcemaps/inject.rs | 6 + tests/integration/sourcemaps/mod.rs | 1 + 7 files changed, 215 insertions(+) create mode 100644 src/commands/sourcemaps/inject.rs create mode 100644 tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd create mode 100644 tests/integration/sourcemaps/inject.rs diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs new file mode 100644 index 0000000000..7b892ee748 --- /dev/null +++ b/src/commands/sourcemaps/inject.rs @@ -0,0 +1,157 @@ +use std::fs::File; +use std::io::{BufRead, BufReader, Seek, Write}; +use std::path::{Path, PathBuf}; + +use anyhow::{bail, Context, Result}; +use clap::{Arg, ArgMatches, Command}; +use glob::glob; +use log::{debug, warn}; +use serde_json::Value; +use uuid::Uuid; + +const CODE_SNIPPET_TEMPLATE: &str = r#"!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="__SENTRY_DEBUG_ID__")}catch(e){}}()"#; +const DEBUGID_PLACEHOLDER: &str = "__SENTRY_DEBUG_ID__"; + +pub fn make_command(command: Command) -> Command { + command + .about("Fixes up JavaScript source files and sourcemaps with debug ids.") + // TODO: What are these {n}{n}s? They show up verbatim in the help output. + .long_about( + "Fixes up JavaScript source files and sourcemaps with debug ids.{n}{n}\ + For every JS source file that references a sourcemap, a debug id is generated and \ + inserted into both files. If the referenced sourcemap already contains a debug id, \ + that id is used instead.", + ) + .arg( + Arg::new("path") + .value_name("PATH") + .required(true) + .help("The path or glob to the javascript files."), + ) +} + +pub fn execute(matches: &ArgMatches) -> Result<()> { + let path = matches.value_of("path").unwrap(); + + let collected_paths: Vec = glob(path).unwrap().flatten().collect(); + + if collected_paths.is_empty() { + warn!("Did not match any files for pattern: {}", path); + return Ok(()); + } + + fixup_files(&collected_paths) +} + +fn fixup_files(paths: &[PathBuf]) -> Result<()> { + 'paths: for path in paths { + let js_path = path.as_path(); + + let Some(ext) = js_path.extension() else { + continue; + }; + + if ext != "js" { + continue; + } + + debug!("Processing js file {}", js_path.display()); + + let f = File::open(js_path).context(format!("Failed to open {}", js_path.display()))?; + let f = BufReader::new(f); + + let mut sourcemap_url = None; + for line in f.lines() { + let line = line.context(format!("Failed to process {}", js_path.display()))?; + + if line.starts_with("//# sentryDebugId") { + debug!("File {} was previously processed", js_path.display()); + continue 'paths; + } + + if let Some(url) = line.strip_prefix("//# sourceMappingURL=") { + sourcemap_url = Some(PathBuf::from(url)); + } + } + + let Some(sourcemap_url) = sourcemap_url else { + debug!("File {} does not contain a sourcemap url", js_path.display()); + continue; + }; + + let sourcemap_path = js_path.with_file_name(sourcemap_url); + + if !sourcemap_path.exists() { + warn!("Sourcemap file {} not found", sourcemap_path.display()); + continue; + } + + // Generate a fresh debug id. This might not end up being used + // if the sourcemap already contains a debug id. + let fresh_debug_id = Uuid::new_v4(); + + let debug_id = fixup_sourcemap(&sourcemap_path, fresh_debug_id) + .context(format!("Failed to process {}", sourcemap_path.display()))?; + + fixup_js_file(js_path, debug_id) + .context(format!("Failed to process {}", js_path.display()))?; + } + + Ok(()) +} + +/// Appends the following text to a file: +/// ``` +/// +/// [] +/// //# sentryDebugId= +///```` +/// where `[]` +/// is `CODE_SNIPPET_TEMPLATE` with `debug_id` substituted for the `__SENTRY_DEBUG_ID__` +/// placeholder. +fn fixup_js_file(js_path: &Path, debug_id: Uuid) -> Result<()> { + let mut js_file = File::options().append(true).open(js_path)?; + let to_inject = + CODE_SNIPPET_TEMPLATE.replace(DEBUGID_PLACEHOLDER, &debug_id.hyphenated().to_string()); + writeln!(js_file)?; + writeln!(js_file, "{to_inject}")?; + write!(js_file, "//# sentryDebugId={debug_id}")?; + + Ok(()) +} + +/// Fixes up a sourcemap file with a debug id. +/// +/// If the file already contains a debug id under the `debugId` key, it is left unmodified. +/// Otherwise, the provided debug id is inserted under that key. +/// +/// In either case, the value of the `debugId` key is returned. +fn fixup_sourcemap(sourcemap_path: &Path, debug_id: Uuid) -> Result { + let mut sourcemap_file = File::options() + .read(true) + .write(true) + .open(sourcemap_path)?; + let mut sourcemap: Value = serde_json::from_reader(&sourcemap_file)?; + + sourcemap_file.rewind()?; + + let Some(map) = sourcemap.as_object_mut() else { + bail!("Invalid sourcemap"); + }; + + match map.get("debugID") { + Some(id) => { + let debug_id = serde_json::from_value(id.clone())?; + debug!("Sourcemap already has a debug id"); + Ok(debug_id) + } + + None => { + let id = serde_json::to_value(debug_id)?; + map.insert("debugID".to_string(), id); + + serde_json::to_writer(&mut sourcemap_file, &sourcemap)?; + Ok(debug_id) + } + } +} diff --git a/src/commands/sourcemaps/mod.rs b/src/commands/sourcemaps/mod.rs index 2407763e89..b37f3b3601 100644 --- a/src/commands/sourcemaps/mod.rs +++ b/src/commands/sourcemaps/mod.rs @@ -4,12 +4,14 @@ use clap::{ArgMatches, Command}; use crate::utils::args::ArgExt; pub mod explain; +pub mod inject; pub mod resolve; pub mod upload; macro_rules! each_subcommand { ($mac:ident) => { $mac!(explain); + $mac!(inject); $mac!(resolve); $mac!(upload); }; diff --git a/tests/integration/_cases/sourcemaps/sourcemaps-help.trycmd b/tests/integration/_cases/sourcemaps/sourcemaps-help.trycmd index 110adfeaef..11e357542f 100644 --- a/tests/integration/_cases/sourcemaps/sourcemaps-help.trycmd +++ b/tests/integration/_cases/sourcemaps/sourcemaps-help.trycmd @@ -24,6 +24,7 @@ OPTIONS: SUBCOMMANDS: explain Explain why sourcemaps are not working for a given event. help Print this message or the help of the given subcommand(s) + inject Fixes up JavaScript source files and sourcemaps with debug ids. resolve Resolve sourcemap for a given line/column position. upload Upload sourcemaps for a release. diff --git a/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd b/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd new file mode 100644 index 0000000000..5b97ee3925 --- /dev/null +++ b/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd @@ -0,0 +1,47 @@ +``` +$ sentry-cli sourcemaps inject --help +? success +sentry-cli[EXE]-sourcemaps-inject +Fixes up JavaScript source files and sourcemaps with debug ids.{n}{n}For every JS source file that +references a sourcemap, a debug id is generated and inserted into both files. If the referenced +sourcemap already contains a debug id, that id is used instead. + +USAGE: + sentry-cli sourcemaps inject [OPTIONS] + +ARGS: + + The path or glob to the javascript files. + +OPTIONS: + --auth-token + Use the given Sentry auth token. + + -h, --help + Print help information + + --header + Custom headers that should be attached to all requests + in key:value format. + + --log-level + Set the log output verbosity. + + [possible values: trace, debug, info, warn, error] + + -o, --org + The organization slug + + -p, --project + The project slug. + + --quiet + Do not print any output while preserving correct exit code. This flag is currently + implemented only for selected subcommands. + + [aliases: silent] + + -r, --release + The release slug. + +``` \ No newline at end of file diff --git a/tests/integration/_cases/sourcemaps/sourcemaps-no-subcommand.trycmd b/tests/integration/_cases/sourcemaps/sourcemaps-no-subcommand.trycmd index fc242a9f47..866081ccfd 100644 --- a/tests/integration/_cases/sourcemaps/sourcemaps-no-subcommand.trycmd +++ b/tests/integration/_cases/sourcemaps/sourcemaps-no-subcommand.trycmd @@ -24,6 +24,7 @@ OPTIONS: SUBCOMMANDS: explain Explain why sourcemaps are not working for a given event. help Print this message or the help of the given subcommand(s) + inject Fixes up JavaScript source files and sourcemaps with debug ids. resolve Resolve sourcemap for a given line/column position. upload Upload sourcemaps for a release. diff --git a/tests/integration/sourcemaps/inject.rs b/tests/integration/sourcemaps/inject.rs new file mode 100644 index 0000000000..a4eafc2abb --- /dev/null +++ b/tests/integration/sourcemaps/inject.rs @@ -0,0 +1,6 @@ +use crate::integration::register_test; + +#[test] +fn command_sourcemaps_inject_help() { + register_test("sourcemaps/sourcemaps-inject-help.trycmd"); +} diff --git a/tests/integration/sourcemaps/mod.rs b/tests/integration/sourcemaps/mod.rs index c2c374da5c..5e1a263042 100644 --- a/tests/integration/sourcemaps/mod.rs +++ b/tests/integration/sourcemaps/mod.rs @@ -1,6 +1,7 @@ use crate::integration::register_test; mod explain; +mod inject; mod resolve; mod upload; From 2b58001a1c11cf9a813a4d3c99934da6a44b335d Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Fri, 10 Feb 2023 16:16:24 +0100 Subject: [PATCH 02/17] improve path logic --- src/commands/sourcemaps/inject.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index 7b892ee748..30f999896f 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -33,10 +33,14 @@ pub fn make_command(command: Command) -> Command { pub fn execute(matches: &ArgMatches) -> Result<()> { let path = matches.value_of("path").unwrap(); - let collected_paths: Vec = glob(path).unwrap().flatten().collect(); + let collected_paths: Vec = glob(path) + .unwrap() + .flatten() + .filter(|path| path.extension().map_or(false, |ext| ext == "js")) + .collect(); if collected_paths.is_empty() { - warn!("Did not match any files for pattern: {}", path); + warn!("Did not match any JavaScript files for pattern: {}", path); return Ok(()); } @@ -47,14 +51,6 @@ fn fixup_files(paths: &[PathBuf]) -> Result<()> { 'paths: for path in paths { let js_path = path.as_path(); - let Some(ext) = js_path.extension() else { - continue; - }; - - if ext != "js" { - continue; - } - debug!("Processing js file {}", js_path.display()); let f = File::open(js_path).context(format!("Failed to open {}", js_path.display()))?; From 536a72f4610e565f8e38b8aeefcebd4c59ce1282 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Fri, 10 Feb 2023 16:16:40 +0100 Subject: [PATCH 03/17] generate debug_id in fixup_sourcemap --- src/commands/sourcemaps/inject.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index 30f999896f..ae8b2bcb02 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -82,11 +82,7 @@ fn fixup_files(paths: &[PathBuf]) -> Result<()> { continue; } - // Generate a fresh debug id. This might not end up being used - // if the sourcemap already contains a debug id. - let fresh_debug_id = Uuid::new_v4(); - - let debug_id = fixup_sourcemap(&sourcemap_path, fresh_debug_id) + let debug_id = fixup_sourcemap(&sourcemap_path) .context(format!("Failed to process {}", sourcemap_path.display()))?; fixup_js_file(js_path, debug_id) @@ -118,11 +114,11 @@ fn fixup_js_file(js_path: &Path, debug_id: Uuid) -> Result<()> { /// Fixes up a sourcemap file with a debug id. /// -/// If the file already contains a debug id under the `debugId` key, it is left unmodified. -/// Otherwise, the provided debug id is inserted under that key. +/// If the file already contains a debug id under the `debugID` key, it is left unmodified. +/// Otherwise, a fresh debug id is inserted under that key. /// -/// In either case, the value of the `debugId` key is returned. -fn fixup_sourcemap(sourcemap_path: &Path, debug_id: Uuid) -> Result { +/// In either case, the value of the `debugID` key is returned. +fn fixup_sourcemap(sourcemap_path: &Path) -> Result { let mut sourcemap_file = File::options() .read(true) .write(true) @@ -143,6 +139,7 @@ fn fixup_sourcemap(sourcemap_path: &Path, debug_id: Uuid) -> Result { } None => { + let debug_id = Uuid::new_v4(); let id = serde_json::to_value(debug_id)?; map.insert("debugID".to_string(), id); From 37f3ef40b8b36f7b92bb9bd9b57ba628e4190b4b Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Fri, 10 Feb 2023 16:24:12 +0100 Subject: [PATCH 04/17] typo --- src/commands/sourcemaps/inject.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index ae8b2bcb02..67abd20221 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -97,7 +97,7 @@ fn fixup_files(paths: &[PathBuf]) -> Result<()> { /// /// [] /// //# sentryDebugId= -///```` +///``` /// where `[]` /// is `CODE_SNIPPET_TEMPLATE` with `debug_id` substituted for the `__SENTRY_DEBUG_ID__` /// placeholder. From fbd35e12d56b19afd5c52efba084cfa27e3b6609 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Fri, 10 Feb 2023 18:37:24 +0100 Subject: [PATCH 05/17] change sourcemap debug id key --- src/commands/sourcemaps/inject.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index 67abd20221..388947d1a9 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -11,6 +11,7 @@ use uuid::Uuid; const CODE_SNIPPET_TEMPLATE: &str = r#"!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="__SENTRY_DEBUG_ID__")}catch(e){}}()"#; const DEBUGID_PLACEHOLDER: &str = "__SENTRY_DEBUG_ID__"; +const SOURCEMAP_DEBUG_ID_KEY: &str = "x_sentry_debug_id"; pub fn make_command(command: Command) -> Command { command @@ -131,7 +132,7 @@ fn fixup_sourcemap(sourcemap_path: &Path) -> Result { bail!("Invalid sourcemap"); }; - match map.get("debugID") { + match map.get(SOURCEMAP_DEBUG_ID_KEY) { Some(id) => { let debug_id = serde_json::from_value(id.clone())?; debug!("Sourcemap already has a debug id"); @@ -141,7 +142,7 @@ fn fixup_sourcemap(sourcemap_path: &Path) -> Result { None => { let debug_id = Uuid::new_v4(); let id = serde_json::to_value(debug_id)?; - map.insert("debugID".to_string(), id); + map.insert(SOURCEMAP_DEBUG_ID_KEY.to_string(), id); serde_json::to_writer(&mut sourcemap_file, &sourcemap)?; Ok(debug_id) From 5a351aec09b27168fb94887759cbcd1dd39e4caa Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Tue, 21 Feb 2023 12:26:09 +0100 Subject: [PATCH 06/17] make backup copies before modifying files --- src/commands/sourcemaps/inject.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index 388947d1a9..831c70a24b 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -103,6 +103,7 @@ fn fixup_files(paths: &[PathBuf]) -> Result<()> { /// is `CODE_SNIPPET_TEMPLATE` with `debug_id` substituted for the `__SENTRY_DEBUG_ID__` /// placeholder. fn fixup_js_file(js_path: &Path, debug_id: Uuid) -> Result<()> { + make_backup_copy(js_path)?; let mut js_file = File::options().append(true).open(js_path)?; let to_inject = CODE_SNIPPET_TEMPLATE.replace(DEBUGID_PLACEHOLDER, &debug_id.hyphenated().to_string()); @@ -140,6 +141,7 @@ fn fixup_sourcemap(sourcemap_path: &Path) -> Result { } None => { + make_backup_copy(sourcemap_path)?; let debug_id = Uuid::new_v4(); let id = serde_json::to_value(debug_id)?; map.insert(SOURCEMAP_DEBUG_ID_KEY.to_string(), id); @@ -149,3 +151,13 @@ fn fixup_sourcemap(sourcemap_path: &Path) -> Result { } } } + +// Makes a backup copy of a file that has `.bak` appended to the path. +fn make_backup_copy(path: &Path) -> Result<()> { + let mut file_name = path.file_name().unwrap_or_default().to_os_string(); + file_name.push(".bak"); + let backup_path = path.with_file_name(file_name); + std::fs::copy(path, backup_path).context("Failed to back up {path}")?; + + Ok(()) +} From 85138b47d6397a110f72e03a7f3ba694634c67bc Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Tue, 21 Feb 2023 13:32:32 +0100 Subject: [PATCH 07/17] fix test --- .../_cases/sourcemaps/sourcemaps-inject-help.trycmd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd b/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd index 5b97ee3925..a4358ec876 100644 --- a/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd +++ b/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd @@ -1,7 +1,7 @@ ``` $ sentry-cli sourcemaps inject --help ? success -sentry-cli[EXE]-sourcemaps-inject +sentry-cli[EXE]-sourcemaps-inject Fixes up JavaScript source files and sourcemaps with debug ids.{n}{n}For every JS source file that references a sourcemap, a debug id is generated and inserted into both files. If the referenced sourcemap already contains a debug id, that id is used instead. @@ -26,7 +26,7 @@ OPTIONS: --log-level Set the log output verbosity. - + [possible values: trace, debug, info, warn, error] -o, --org @@ -38,7 +38,7 @@ OPTIONS: --quiet Do not print any output while preserving correct exit code. This flag is currently implemented only for selected subcommands. - + [aliases: silent] -r, --release From ae418b2633152098f730ba5148500f4735a562f2 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Tue, 21 Feb 2023 14:08:55 +0100 Subject: [PATCH 08/17] fix test again --- .../integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd b/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd index a4358ec876..ff530c907c 100644 --- a/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd +++ b/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd @@ -7,7 +7,7 @@ references a sourcemap, a debug id is generated and inserted into both files. If sourcemap already contains a debug id, that id is used instead. USAGE: - sentry-cli sourcemaps inject [OPTIONS] + sentry-cli[EXE] sourcemaps inject [OPTIONS] ARGS: From 77a262c64dd0fc44444ab10c0eb2c7819af17c07 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Tue, 21 Feb 2023 15:37:23 +0100 Subject: [PATCH 09/17] Update debug id comment prefix --- src/commands/sourcemaps/inject.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index 831c70a24b..a5b0e3af58 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -11,7 +11,9 @@ use uuid::Uuid; const CODE_SNIPPET_TEMPLATE: &str = r#"!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="__SENTRY_DEBUG_ID__")}catch(e){}}()"#; const DEBUGID_PLACEHOLDER: &str = "__SENTRY_DEBUG_ID__"; -const SOURCEMAP_DEBUG_ID_KEY: &str = "x_sentry_debug_id"; +const SOURCEMAP_DEBUGID_KEY: &str = "x_sentry_debug_id"; +const DEBUGID_COMMENT_PREFIX: &str = "//# sentryDebugId"; +const SOURCEMAP_URL_COMMENT_PREFIX: &str = "//# sourceMappingURL="; pub fn make_command(command: Command) -> Command { command @@ -61,12 +63,12 @@ fn fixup_files(paths: &[PathBuf]) -> Result<()> { for line in f.lines() { let line = line.context(format!("Failed to process {}", js_path.display()))?; - if line.starts_with("//# sentryDebugId") { + if line.starts_with(DEBUGID_COMMENT_PREFIX) { debug!("File {} was previously processed", js_path.display()); continue 'paths; } - if let Some(url) = line.strip_prefix("//# sourceMappingURL=") { + if let Some(url) = line.strip_prefix(SOURCEMAP_URL_COMMENT_PREFIX) { sourcemap_url = Some(PathBuf::from(url)); } } @@ -109,7 +111,7 @@ fn fixup_js_file(js_path: &Path, debug_id: Uuid) -> Result<()> { CODE_SNIPPET_TEMPLATE.replace(DEBUGID_PLACEHOLDER, &debug_id.hyphenated().to_string()); writeln!(js_file)?; writeln!(js_file, "{to_inject}")?; - write!(js_file, "//# sentryDebugId={debug_id}")?; + write!(js_file, "{DEBUGID_COMMENT_PREFIX}={debug_id}")?; Ok(()) } @@ -133,7 +135,7 @@ fn fixup_sourcemap(sourcemap_path: &Path) -> Result { bail!("Invalid sourcemap"); }; - match map.get(SOURCEMAP_DEBUG_ID_KEY) { + match map.get(SOURCEMAP_DEBUGID_KEY) { Some(id) => { let debug_id = serde_json::from_value(id.clone())?; debug!("Sourcemap already has a debug id"); @@ -144,7 +146,7 @@ fn fixup_sourcemap(sourcemap_path: &Path) -> Result { make_backup_copy(sourcemap_path)?; let debug_id = Uuid::new_v4(); let id = serde_json::to_value(debug_id)?; - map.insert(SOURCEMAP_DEBUG_ID_KEY.to_string(), id); + map.insert(SOURCEMAP_DEBUGID_KEY.to_string(), id); serde_json::to_writer(&mut sourcemap_file, &sourcemap)?; Ok(debug_id) From 359d90ff76be236e1c556d96c4bc158e7dd06c6d Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Tue, 21 Feb 2023 15:39:13 +0100 Subject: [PATCH 10/17] _actually_ update it --- src/commands/sourcemaps/inject.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index a5b0e3af58..efb44d490a 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -12,7 +12,7 @@ use uuid::Uuid; const CODE_SNIPPET_TEMPLATE: &str = r#"!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="__SENTRY_DEBUG_ID__")}catch(e){}}()"#; const DEBUGID_PLACEHOLDER: &str = "__SENTRY_DEBUG_ID__"; const SOURCEMAP_DEBUGID_KEY: &str = "x_sentry_debug_id"; -const DEBUGID_COMMENT_PREFIX: &str = "//# sentryDebugId"; +const DEBUGID_COMMENT_PREFIX: &str = "//# debugId"; const SOURCEMAP_URL_COMMENT_PREFIX: &str = "//# sourceMappingURL="; pub fn make_command(command: Command) -> Command { From 43c673f07c2a69f2b391d623ceb04331d85a7ac3 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Tue, 21 Feb 2023 15:41:52 +0100 Subject: [PATCH 11/17] change debug_id key --- src/commands/sourcemaps/inject.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index efb44d490a..543865668b 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -11,7 +11,7 @@ use uuid::Uuid; const CODE_SNIPPET_TEMPLATE: &str = r#"!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="__SENTRY_DEBUG_ID__")}catch(e){}}()"#; const DEBUGID_PLACEHOLDER: &str = "__SENTRY_DEBUG_ID__"; -const SOURCEMAP_DEBUGID_KEY: &str = "x_sentry_debug_id"; +const SOURCEMAP_DEBUGID_KEY: &str = "debug_id"; const DEBUGID_COMMENT_PREFIX: &str = "//# debugId"; const SOURCEMAP_URL_COMMENT_PREFIX: &str = "//# sourceMappingURL="; From 0337991253143444e3a186eb2131a6ec0f64c84c Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Wed, 22 Feb 2023 12:52:10 +0100 Subject: [PATCH 12/17] use new symbolic functions --- src/commands/sourcemaps/inject.rs | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index 543865668b..cb3b443328 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -1,5 +1,5 @@ -use std::fs::File; -use std::io::{BufRead, BufReader, Seek, Write}; +use std::fs::{self, File}; +use std::io::{Seek, Write}; use std::path::{Path, PathBuf}; use anyhow::{bail, Context, Result}; @@ -7,13 +7,13 @@ use clap::{Arg, ArgMatches, Command}; use glob::glob; use log::{debug, warn}; use serde_json::Value; +use symbolic::debuginfo::js; use uuid::Uuid; const CODE_SNIPPET_TEMPLATE: &str = r#"!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="__SENTRY_DEBUG_ID__")}catch(e){}}()"#; const DEBUGID_PLACEHOLDER: &str = "__SENTRY_DEBUG_ID__"; const SOURCEMAP_DEBUGID_KEY: &str = "debug_id"; const DEBUGID_COMMENT_PREFIX: &str = "//# debugId"; -const SOURCEMAP_URL_COMMENT_PREFIX: &str = "//# sourceMappingURL="; pub fn make_command(command: Command) -> Command { command @@ -56,24 +56,15 @@ fn fixup_files(paths: &[PathBuf]) -> Result<()> { debug!("Processing js file {}", js_path.display()); - let f = File::open(js_path).context(format!("Failed to open {}", js_path.display()))?; - let f = BufReader::new(f); + let file = + fs::read_to_string(js_path).context(format!("Failed to open {}", js_path.display()))?; - let mut sourcemap_url = None; - for line in f.lines() { - let line = line.context(format!("Failed to process {}", js_path.display()))?; - - if line.starts_with(DEBUGID_COMMENT_PREFIX) { - debug!("File {} was previously processed", js_path.display()); - continue 'paths; - } - - if let Some(url) = line.strip_prefix(SOURCEMAP_URL_COMMENT_PREFIX) { - sourcemap_url = Some(PathBuf::from(url)); - } + if js::discover_debug_id(&file).is_some() { + debug!("File {} was previously processed", js_path.display()); + continue 'paths; } - let Some(sourcemap_url) = sourcemap_url else { + let Some(sourcemap_url) = js::discover_sourcemaps_location(&file) else { debug!("File {} does not contain a sourcemap url", js_path.display()); continue; }; From fecb8e93a14218f93d48b918cb07f1bfb6d978eb Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Wed, 22 Feb 2023 13:00:33 +0100 Subject: [PATCH 13/17] remove unnecessary loop label --- src/commands/sourcemaps/inject.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index cb3b443328..f54513788a 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -51,7 +51,7 @@ pub fn execute(matches: &ArgMatches) -> Result<()> { } fn fixup_files(paths: &[PathBuf]) -> Result<()> { - 'paths: for path in paths { + for path in paths { let js_path = path.as_path(); debug!("Processing js file {}", js_path.display()); @@ -61,7 +61,7 @@ fn fixup_files(paths: &[PathBuf]) -> Result<()> { if js::discover_debug_id(&file).is_some() { debug!("File {} was previously processed", js_path.display()); - continue 'paths; + continue; } let Some(sourcemap_url) = js::discover_sourcemaps_location(&file) else { From e5681239f2cb917570971ff49012cf40d10c39f1 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Wed, 22 Feb 2023 15:21:49 +0100 Subject: [PATCH 14/17] don't backup files --- src/commands/sourcemaps/inject.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index f54513788a..0ed10f1692 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -96,7 +96,6 @@ fn fixup_files(paths: &[PathBuf]) -> Result<()> { /// is `CODE_SNIPPET_TEMPLATE` with `debug_id` substituted for the `__SENTRY_DEBUG_ID__` /// placeholder. fn fixup_js_file(js_path: &Path, debug_id: Uuid) -> Result<()> { - make_backup_copy(js_path)?; let mut js_file = File::options().append(true).open(js_path)?; let to_inject = CODE_SNIPPET_TEMPLATE.replace(DEBUGID_PLACEHOLDER, &debug_id.hyphenated().to_string()); @@ -134,7 +133,6 @@ fn fixup_sourcemap(sourcemap_path: &Path) -> Result { } None => { - make_backup_copy(sourcemap_path)?; let debug_id = Uuid::new_v4(); let id = serde_json::to_value(debug_id)?; map.insert(SOURCEMAP_DEBUGID_KEY.to_string(), id); @@ -144,13 +142,3 @@ fn fixup_sourcemap(sourcemap_path: &Path) -> Result { } } } - -// Makes a backup copy of a file that has `.bak` appended to the path. -fn make_backup_copy(path: &Path) -> Result<()> { - let mut file_name = path.file_name().unwrap_or_default().to_os_string(); - file_name.push(".bak"); - let backup_path = path.with_file_name(file_name); - std::fs::copy(path, backup_path).context("Failed to back up {path}")?; - - Ok(()) -} From af7e66bde1ff24ff7e105562c7a5e8d8e4bb800c Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Thu, 23 Feb 2023 12:01:32 +0100 Subject: [PATCH 15/17] remove comment --- src/commands/sourcemaps/inject.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index acf5b88a02..25ac68379f 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -18,7 +18,6 @@ const DEBUGID_COMMENT_PREFIX: &str = "//# debugId"; pub fn make_command(command: Command) -> Command { command .about("Fixes up JavaScript source files and sourcemaps with debug ids.") - // TODO: What are these {n}{n}s? They show up verbatim in the help output. .long_about( "Fixes up JavaScript source files and sourcemaps with debug ids.{n}{n}\ For every JS source file that references a sourcemap, a debug id is generated and \ From 9390055759b8af9fb035906b7ccd4a45b136dd11 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Thu, 23 Feb 2023 14:11:46 +0100 Subject: [PATCH 16/17] fix windows test --- .../_cases/sourcemaps/sourcemaps-inject-help.trycmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd b/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd index a95a00b4bd..867762db1b 100644 --- a/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd +++ b/tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd @@ -1,12 +1,12 @@ ``` -$ sentry-cli sourcemaps inject --help +$ sentry-cli[EXE] sourcemaps inject --help ? success Fixes up JavaScript source files and sourcemaps with debug ids. For every JS source file that references a sourcemap, a debug id is generated and inserted into both files. If the referenced sourcemap already contains a debug id, that id is used instead. -Usage: sentry-cli sourcemaps inject [OPTIONS] +Usage: sentry-cli[EXE] sourcemaps inject [OPTIONS] Arguments: From d95589624d605374a75af9352d2c4be8e0cdf730 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Thu, 23 Feb 2023 14:23:00 +0100 Subject: [PATCH 17/17] hide command --- src/commands/sourcemaps/inject.rs | 1 + tests/integration/_cases/sourcemaps/sourcemaps-help.trycmd | 1 - .../_cases/sourcemaps/sourcemaps-no-subcommand.trycmd | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/commands/sourcemaps/inject.rs b/src/commands/sourcemaps/inject.rs index 25ac68379f..d126d2b446 100644 --- a/src/commands/sourcemaps/inject.rs +++ b/src/commands/sourcemaps/inject.rs @@ -30,6 +30,7 @@ pub fn make_command(command: Command) -> Command { .required(true) .help("The path or glob to the javascript files."), ) + .hide(true) } pub fn execute(matches: &ArgMatches) -> Result<()> { diff --git a/tests/integration/_cases/sourcemaps/sourcemaps-help.trycmd b/tests/integration/_cases/sourcemaps/sourcemaps-help.trycmd index e7f253b362..4c416f5a94 100644 --- a/tests/integration/_cases/sourcemaps/sourcemaps-help.trycmd +++ b/tests/integration/_cases/sourcemaps/sourcemaps-help.trycmd @@ -7,7 +7,6 @@ Usage: sentry-cli[EXE] sourcemaps [OPTIONS] Commands: explain Explain why sourcemaps are not working for a given event. - inject Fixes up JavaScript source files and sourcemaps with debug ids. resolve Resolve sourcemap for a given line/column position. upload Upload sourcemaps for a release. help Print this message or the help of the given subcommand(s) diff --git a/tests/integration/_cases/sourcemaps/sourcemaps-no-subcommand.trycmd b/tests/integration/_cases/sourcemaps/sourcemaps-no-subcommand.trycmd index 5564393c3c..6812c7bfb0 100644 --- a/tests/integration/_cases/sourcemaps/sourcemaps-no-subcommand.trycmd +++ b/tests/integration/_cases/sourcemaps/sourcemaps-no-subcommand.trycmd @@ -7,7 +7,6 @@ Usage: sentry-cli[EXE] sourcemaps [OPTIONS] Commands: explain Explain why sourcemaps are not working for a given event. - inject Fixes up JavaScript source files and sourcemaps with debug ids. resolve Resolve sourcemap for a given line/column position. upload Upload sourcemaps for a release. help Print this message or the help of the given subcommand(s)