New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add sourcemaps inject command #1469
Merged
Merged
Changes from 14 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
3291fc4
feat: Add sourcemaps inject command
loewenheim 2b58001
improve path logic
loewenheim 536a72f
generate debug_id in fixup_sourcemap
loewenheim 37f3ef4
typo
loewenheim fbd35e1
change sourcemap debug id key
loewenheim 5a351ae
make backup copies before modifying files
loewenheim 4ccd3ab
Merge branch 'master' into feat/sourcemap-inject
loewenheim 85138b4
fix test
loewenheim ae418b2
fix test again
loewenheim 77a262c
Update debug id comment prefix
loewenheim 359d90f
_actually_ update it
loewenheim 43c673f
change debug_id key
loewenheim 8ce5f11
Merge branch 'master' into feat/sourcemap-inject
loewenheim 0337991
use new symbolic functions
loewenheim fecb8e9
remove unnecessary loop label
loewenheim e568123
don't backup files
loewenheim 00ee6e8
Merge branch 'master' into feat/sourcemap-inject
loewenheim af7e66b
remove comment
loewenheim 9390055
fix windows test
loewenheim d955896
hide command
loewenheim File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
use std::fs::{self, File}; | ||
use std::io::{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 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"; | ||
|
||
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<PathBuf> = 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 JavaScript 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(); | ||
|
||
debug!("Processing js file {}", js_path.display()); | ||
|
||
let file = | ||
fs::read_to_string(js_path).context(format!("Failed to open {}", js_path.display()))?; | ||
|
||
if js::discover_debug_id(&file).is_some() { | ||
debug!("File {} was previously processed", js_path.display()); | ||
continue 'paths; | ||
loewenheim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
let Some(sourcemap_url) = js::discover_sourcemaps_location(&file) 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; | ||
} | ||
|
||
let debug_id = fixup_sourcemap(&sourcemap_path) | ||
.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: | ||
/// ``` | ||
/// | ||
/// <CODE_SNIPPET>[<debug_id>] | ||
/// //# sentryDebugId=<debug_id> | ||
///``` | ||
/// where `<CODE_SNIPPET>[<debug_id>]` | ||
/// 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()); | ||
writeln!(js_file)?; | ||
writeln!(js_file, "{to_inject}")?; | ||
write!(js_file, "{DEBUGID_COMMENT_PREFIX}={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, 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) -> Result<Uuid> { | ||
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(SOURCEMAP_DEBUGID_KEY) { | ||
Some(id) => { | ||
let debug_id = serde_json::from_value(id.clone())?; | ||
debug!("Sourcemap already has a debug id"); | ||
Ok(debug_id) | ||
} | ||
|
||
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); | ||
|
||
serde_json::to_writer(&mut sourcemap_file, &sourcemap)?; | ||
Ok(debug_id) | ||
} | ||
} | ||
} | ||
|
||
// 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(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
tests/integration/_cases/sourcemaps/sourcemaps-inject-help.trycmd
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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[EXE] sourcemaps inject [OPTIONS] <PATH> | ||
|
||
ARGS: | ||
<PATH> | ||
The path or glob to the javascript files. | ||
|
||
OPTIONS: | ||
--auth-token <AUTH_TOKEN> | ||
Use the given Sentry auth token. | ||
|
||
-h, --help | ||
Print help information | ||
|
||
--header <KEY:VALUE> | ||
Custom headers that should be attached to all requests | ||
in key:value format. | ||
|
||
--log-level <LOG_LEVEL> | ||
Set the log output verbosity. | ||
|
||
[possible values: trace, debug, info, warn, error] | ||
|
||
-o, --org <ORG> | ||
The organization slug | ||
|
||
-p, --project <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 <RELEASE> | ||
The release slug. | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
use crate::integration::register_test; | ||
|
||
#[test] | ||
fn command_sourcemaps_inject_help() { | ||
register_test("sourcemaps/sourcemaps-inject-help.trycmd"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
use crate::integration::register_test; | ||
|
||
mod explain; | ||
mod inject; | ||
mod resolve; | ||
mod upload; | ||
|
||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They suppose to be terminal line-break specific to clap, but for some reason you say they stopped working? 🤔
https://github.com/clap-rs/clap/blob/ad5d67623a89415c7f5f7e1f7a47bf5abdfd0b6c/src/output/help_template.rs#L1018
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The placeholders have apparently been in the
send_envelope-help
test case since the beginning.https://github.com/getsentry/sentry-cli/blame/master/tests/integration/_cases/send_envelope/send_envelope-help.trycmd