Skip to content

Commit

Permalink
[web] Blame view
Browse files Browse the repository at this point in the history
- `CodeChecker store` command will store blame information for every
source files if the project which was analyzed is a git repository.
- The GUI will have an option on the report detail view to show
blame information alongside the source file.
- Hovering the mouse over a blame line, commit details will be shown
in a pop-up window. Clicking on the hash will try to jump-to the remote
url of the repository and show the commit which related to a blame line.
- Add new test cases.
  • Loading branch information
csordasmarton committed Aug 19, 2021
1 parent e3b6396 commit 9087b79
Show file tree
Hide file tree
Showing 31 changed files with 1,137 additions and 76 deletions.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion web/api/js/codechecker-api-node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codechecker-api",
"version": "6.41.0",
"version": "6.42.0",
"description": "Generated node.js compatible API stubs for CodeChecker server.",
"main": "lib",
"homepage": "https://github.com/Ericsson/codechecker",
Expand Down
Binary file modified web/api/py/codechecker_api/dist/codechecker_api.tar.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion web/api/py/codechecker_api/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
with open('README.md', encoding='utf-8', errors="ignore") as f:
long_description = f.read()

api_version = '6.41.0'
api_version = '6.42.0'

setup(
name='codechecker_api',
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion web/api/py/codechecker_api_shared/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
with open('README.md', encoding='utf-8', errors="ignore") as f:
long_description = f.read()

api_version = '6.41.0'
api_version = '6.42.0'

setup(
name='codechecker_api_shared',
Expand Down
35 changes: 34 additions & 1 deletion web/api/report_server.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ enum CommentKind {
struct SourceFileData {
1: i64 fileId,
2: string filePath,
3: optional string fileContent
3: optional string fileContent,
4: optional bool hasBlameInfo,
5: optional string remoteUrl,
6: optional string trackingBranch,
}

struct SortMode {
Expand Down Expand Up @@ -377,6 +380,31 @@ struct AnalysisInfo {
1: string analyzerCommand,
}

typedef string CommitHash

struct BlameData {
1: i64 startLine,
2: i64 endLine,
3: CommitHash commitHash,
}

struct CommitAuthor {
1: string name,
2: string email,
}

struct Commit {
1: CommitAuthor author,
2: string summary,
3: string message,
4: string committedDateTime,
}

struct BlameInfo {
1: map<CommitHash, Commit> commits,
2: list<BlameData> blame,
}

service codeCheckerDBAccess {

// Gives back all analyzed runs.
Expand Down Expand Up @@ -497,6 +525,11 @@ service codeCheckerDBAccess {
3: Encoding encoding)
throws (1: codechecker_api_shared.RequestFailed requestError),

// Get blame information for a given file.
// PERMISSION: PRODUCT_ACCESS
BlameInfo getBlameInfo(1: i64 fileId)
throws (1: codechecker_api_shared.RequestFailed requestError),

// Get line content information for multiple files in different positions.
// The first key of the map is a file id, the second is a line number:
// (e.g.: lineContent = result[fileId][line])
Expand Down
57 changes: 56 additions & 1 deletion web/client/codechecker_client/cmd/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

import argparse
import base64
from git import Repo
from git.exc import InvalidGitRepositoryError
import hashlib
import json
import os
Expand Down Expand Up @@ -579,6 +581,49 @@ def parse_report_files(report_files: Set[str], zip_iter=map):
missing_source_files)


def get_blame_info(repo: Repo, file_path: str):
""" Get blame info for the given file in the given git repo. """
tracking_branch = None
try:
# If a commit is checked out, accessing the active_branch member will
# throw a type error. In this case we will use the current commit hash.
tracking_branch = str(repo.active_branch.tracking_branch())
except Exception:
tracking_branch = repo.head.commit.hexsha

try:
blame = repo.blame_incremental(repo.head.commit.hexsha, file_path)

res = {
'version': 'v1',
'tracking_branch': tracking_branch,
'remote_url': next(repo.remote().urls, None),
'commits': {},
'blame': []}

for b in blame:
commit = b.commit

if commit.hexsha not in res['commits']:
res['commits'][commit.hexsha] = {
'author': {
'name': commit.author.name,
'email': commit.author.email,
},
'summary': commit.summary,
'message': commit.message,
'committed_datetime': str(commit.committed_datetime)}

res['blame'].append({
'from': b.linenos[0],
'to': b.linenos[-1],
'commit': commit.hexsha})
return res
except Exception as ex:
LOG.warning("Failed to get blame information for %s: %s",
file_path, ex)


def assemble_zip(inputs, zip_file, client):
"""Collect and compress report and source files, together with files
contanining analysis related information into a zip file which
Expand Down Expand Up @@ -687,6 +732,16 @@ def assemble_zip(inputs, zip_file, client):
except KeyError:
zipf.write(f, file_path)

try:
repo = Repo(f, search_parent_directories=True)
blame_info = get_blame_info(repo, f) if repo else None
if blame_info:
zipf.writestr(
os.path.join('blame', f.lstrip('/')),
json.dumps(blame_info))
except InvalidGitRepositoryError:
pass

zipf.writestr('content_hashes.json', json.dumps(file_to_hash))

# Compressing .zip file
Expand Down Expand Up @@ -887,7 +942,7 @@ def main(args):
except Exception as ex:
print(ex)
import traceback
traceback.print_stack()
traceback.print_exc()
LOG.error("Failed to assemble zip file.")
sys.exit(1)

Expand Down
2 changes: 1 addition & 1 deletion web/codechecker_web/shared/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# The newest supported minor version (value) for each supported major version
# (key) in this particular build.
SUPPORTED_VERSIONS = {
6: 41
6: 42
}

# Used by the client to automatically identify the latest major and minor
Expand Down
20 changes: 20 additions & 0 deletions web/codechecker_web/shared/webserver_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from collections.abc import Mapping
import os
import re
import sys

from codechecker_common import logger
Expand Down Expand Up @@ -65,6 +66,7 @@ def __init__(self):
load_json_or_empty(self.checkers_severity_map_file, {}))
self.__system_comment_map = \
load_json_or_empty(self.system_comment_map_file, {})
self.__git_commit_urls = self.__get_git_commit_urls()
self.__package_version = None
self.__package_build_date = None
self.__package_git_hash = None
Expand All @@ -75,6 +77,15 @@ def __init__(self):

self.__set_version()

def __get_git_commit_urls(self):
""" Get commit urls from the configuration file. """
git_commit_urls = load_json_or_empty(self.git_commit_urls_file, [])

for git_commit_url in git_commit_urls:
git_commit_url["regex"] = re.compile(git_commit_url["regex"])

return git_commit_urls

def __get_package_layout(self):
""" Get package layout configuration. """
layout_cfg_file = os.path.join(
Expand Down Expand Up @@ -147,6 +158,15 @@ def system_comment_map_file(self):
return os.path.join(self._data_files_dir_path, 'config',
'system_comment_kinds.json')

@property
def git_commit_urls_file(self):
return os.path.join(
self._data_files_dir_path, 'config', 'git_commit_urls.json')

@property
def git_commit_urls(self):
return self.__git_commit_urls

@property
def path_plist_to_html_dist(self):
return os.path.join(self._lib_dir_path, 'plist_to_html', 'static')
Expand Down
14 changes: 14 additions & 0 deletions web/config/git_commit_urls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"regex": "(?P<protocol>(http[s]?)):\/\/(?P<user>\\w+@)?(?P<url>([^\/]*gerrit[^\/]*))/a/(?P<team>\\w+)/(?P<project>\\w+)",
"url": "$protocol://$url/gitweb?p=$team/$project.git;a=commit;h=$commit"
},
{
"regex": "(?P<url>.*bitbucket.*((?=\\.git)|$(?<!\\.git)))",
"url": "$url/commits/$commit"
},
{
"regex": "(?P<url>.*((?=\\.git)|$(?<!\\.git)))",
"url": "$url/commit/$commit"
}
]
1 change: 1 addition & 0 deletions web/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ portalocker==2.2.1
psutil==5.8.0
mypy_extensions==0.4.3
thrift==0.13.0
gitpython==3.1.11

./api/py/codechecker_api/dist/codechecker_api.tar.gz
./api/py/codechecker_api_shared/dist/codechecker_api_shared.tar.gz
1 change: 1 addition & 0 deletions web/requirements_py/dev/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mkdocs==1.1.2
mypy_extensions==0.4.3
coverage==5.5.0
thrift==0.13.0
gitpython==3.1.11

./api/py/codechecker_api/dist/codechecker_api.tar.gz
./api/py/codechecker_api_shared/dist/codechecker_api_shared.tar.gz
Expand Down
Loading

0 comments on commit 9087b79

Please sign in to comment.