Skip to content

Commit

Permalink
Support backslashes in code mappings. (dev-env broken so have to use …
Browse files Browse the repository at this point in the history
…CI for testing)
  • Loading branch information
MichaelSun48 committed Apr 12, 2024
1 parent 9c99a10 commit f40e620
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 4 deletions.
23 changes: 19 additions & 4 deletions src/sentry/integrations/utils/code_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,18 @@ class UnsupportedFrameFilename(Exception):
class FrameFilename:
def __init__(self, frame_file_path: str) -> None:
self.raw_path = frame_file_path
if frame_file_path[0] == "/":

if "\\" in frame_file_path:
frame_file_path = frame_file_path.replace("\\", "/")

if frame_file_path[0] == "/" or frame_file_path[0] == "\\":
frame_file_path = frame_file_path[1:]

# Using regexes would be better but this is easier to understand
if (
not frame_file_path
or frame_file_path[0] in ["[", "<"]
or frame_file_path.find(" ") > -1
or frame_file_path.find("\\") > -1 # Windows support
or frame_file_path.find("/") == -1
):
raise UnsupportedFrameFilename("This path is not supported.")
Expand All @@ -79,8 +82,16 @@ def __init__(self, frame_file_path: str) -> None:
if not self.extension:
raise UnsupportedFrameFilename("It needs an extension.")

# Remove drive letter if it exists
if self.is_windows_path and frame_file_path[1] == ":":
frame_file_path = frame_file_path[2:]

start_at_index = get_straight_path_prefix_end_index(frame_file_path)
self.straight_path_prefix = frame_file_path[:start_at_index]

# We normalize the path to be as close to what the path would
# look like in the source code repository, hence why we remove
# the straight path prefix and drive letter
self.normalized_path = frame_file_path[start_at_index:]
if start_at_index == 0:
self.root = frame_file_path.split("/")[0]
Expand Down Expand Up @@ -370,6 +381,10 @@ def convert_stacktrace_frame_path_to_source_path(
If the code mapping does not apply to the frame, returns None.
"""

stack_root = code_mapping.stack_root
if "\\" in code_mapping.stack_root:
stack_root = code_mapping.stack_root.replace("\\", "/")

# In most cases, code mappings get applied to frame.filename, but some platforms such as Java
# contain folder info in other parts of the frame (e.g. frame.module="com.example.app.MainActivity"
# gets transformed to "com/example/app/MainActivity.java"), so in those cases we use the
Expand All @@ -379,13 +394,13 @@ def convert_stacktrace_frame_path_to_source_path(
)

if stacktrace_path and stacktrace_path.startswith(code_mapping.stack_root):
return stacktrace_path.replace(code_mapping.stack_root, code_mapping.source_root, 1)
return stacktrace_path.replace(stack_root, code_mapping.source_root, 1)

# Some platforms only provide the file's name without folder paths, so we
# need to use the absolute path instead. If the code mapping has a non-empty
# stack_root value and it matches the absolute path, we do the mapping on it.
if frame.abs_path and frame.abs_path.startswith(code_mapping.stack_root):
return frame.abs_path.replace(code_mapping.stack_root, code_mapping.source_root, 1)
return frame.abs_path.replace(stack_root, code_mapping.source_root, 1)

return None

Expand Down
74 changes: 74 additions & 0 deletions tests/sentry/tasks/test_derive_code_mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,80 @@ def test_raises_generic_errors(self, mock_logger):
)


class TestBackSlashDeriveCodeMappings(BaseDeriveCodeMappings):
def setUp(self):
super().setUp()
self.platform = "python"
self.event_data = self.generate_data(
[
{"in_app": True, "filename": "\\sentry\\mouse.py"},
{"in_app": True, "filename": "\\sentry\\dog\\cat\\parrot.py"},
{"in_app": True, "filename": "C:\\sentry\\tasks.py"},
{"in_app": True, "filename": "D:\\Users\\code\\sentry\\models\\release.py"},
]
)

@responses.activate
def test_backslash_filename_simple(self):
repo_name = "foo/bar"
with patch(
"sentry.integrations.github.client.GitHubClientMixin.get_trees_for_org"
) as mock_get_trees_for_org:
mock_get_trees_for_org.return_value = {
repo_name: RepoTree(Repo(repo_name, "master"), ["sentry/mouse.py"])
}
derive_code_mappings(self.project.id, self.event_data)
code_mapping = RepositoryProjectPathConfig.objects.all()[0]
assert code_mapping.stack_root == "\\"
assert code_mapping.source_root == ""
assert code_mapping.repository.name == repo_name

@responses.activate
def test_backslash_drive_letter_filename_simple(self):
repo_name = "foo/bar"
with patch(
"sentry.integrations.github.client.GitHubClientMixin.get_trees_for_org"
) as mock_get_trees_for_org:
mock_get_trees_for_org.return_value = {
repo_name: RepoTree(Repo(repo_name, "master"), ["sentry/tasks.py"])
}
derive_code_mappings(self.project.id, self.event_data)
code_mapping = RepositoryProjectPathConfig.objects.all()[0]
assert code_mapping.stack_root == "C:\\"
assert code_mapping.source_root == ""
assert code_mapping.repository.name == repo_name

@responses.activate
def test_backslash_drive_letter_filename_monorepo(self):
repo_name = "foo/bar"
with patch(
"sentry.integrations.github.client.GitHubClientMixin.get_trees_for_org"
) as mock_get_trees_for_org:
mock_get_trees_for_org.return_value = {
repo_name: RepoTree(Repo(repo_name, "master"), ["src/sentry/tasks.py"])
}
derive_code_mappings(self.project.id, self.event_data)
code_mapping = RepositoryProjectPathConfig.objects.all()[0]
assert code_mapping.stack_root == "C:\\sentry\\"
assert code_mapping.source_root == "src/"
assert code_mapping.repository.name == repo_name

@responses.activate
def test_backslash_drive_letter_filename_abs_path(self):
repo_name = "foo/bar"
with patch(
"sentry.integrations.github.client.GitHubClientMixin.get_trees_for_org"
) as mock_get_trees_for_org:
mock_get_trees_for_org.return_value = {
repo_name: RepoTree(Repo(repo_name, "master"), ["sentry/models/release.py"])
}
derive_code_mappings(self.project.id, self.event_data)
code_mapping = RepositoryProjectPathConfig.objects.all()[0]
assert code_mapping.stack_root == "D:\\Users\\code\\"
assert code_mapping.source_root == ""
assert code_mapping.repository.name == repo_name


class TestJavascriptDeriveCodeMappings(BaseDeriveCodeMappings):
def setUp(self):
super().setUp()
Expand Down

0 comments on commit f40e620

Please sign in to comment.