Skip to content

Commit

Permalink
Merge pull request #3425 from csordasmarton/handle_no_git_usecase
Browse files Browse the repository at this point in the history
[cli] Handle use case when git is not available
  • Loading branch information
csordasmarton authored Sep 7, 2021
2 parents 0eee1df + f9fc688 commit 6892118
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 83 deletions.
111 changes: 111 additions & 0 deletions web/client/codechecker_client/blame_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import json
import os
import sys
import zipfile

from concurrent.futures import ProcessPoolExecutor
from git import Repo
from git.exc import InvalidGitRepositoryError
from typing import Dict, List, Optional

from codechecker_common.logger import get_logger

LOG = get_logger('system')


FileBlameInfo = Dict[str, Optional[Dict]]


def __get_blame_info(file_path: str):
""" Get blame info for the given file. """
try:
repo = Repo(file_path, search_parent_directories=True)
except InvalidGitRepositoryError:
return

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 TypeError:
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})

LOG.debug("Collected blame info for %s", file_path)

return res
except Exception as ex:
LOG.debug("Failed to get blame information for %s: %s", file_path, ex)


def __collect_blame_info_for_files(
file_paths: List[str],
zip_iter=map
) -> FileBlameInfo:
""" Collect blame information for the given file paths. """
file_blame_info = {}
for file_path, blame_info in zip(file_paths,
zip_iter(__get_blame_info, file_paths)):
file_blame_info[file_path] = blame_info

return file_blame_info


def assemble_blame_info(
zip_file: zipfile.ZipFile,
file_paths: List[str]
) -> bool:
"""
Collect and write blame information for the given files to the zip file.
Returns true if at least one blame information is collected.
"""
# Currently ProcessPoolExecutor fails completely in windows.
# Reason is most likely combination of venv and fork() not
# being present in windows, so stuff like setting up
# PYTHONPATH in parent CodeChecker before store is executed
# are lost.
if sys.platform == "win32":
file_blame_info = __collect_blame_info_for_files(file_paths)
else:
with ProcessPoolExecutor() as executor:
file_blame_info = __collect_blame_info_for_files(
file_paths, executor.map)

# Add blame information to the zip for the files which will be sent
# to the server if exist.
for f, blame_info in file_blame_info.items():
if blame_info:
zip_file.writestr(
os.path.join('blame', f.lstrip('/')),
json.dumps(blame_info))

return any(v for v in file_blame_info.values())
100 changes: 17 additions & 83 deletions web/client/codechecker_client/cmd/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@

import argparse
import base64
from git import Repo
from git.exc import InvalidGitRepositoryError
import hashlib
import json
import os
Expand All @@ -32,7 +30,6 @@
from codechecker_api_shared.ttypes import RequestFailed, ErrorCode

from codechecker_client import client as libclient

from codechecker_common import arg, logger, plist_parser, util, cmd_config
from codechecker_common.report import Report
from codechecker_common.output import twodim
Expand All @@ -43,6 +40,11 @@
from codechecker_web.shared import webserver_context, host_check
from codechecker_web.shared.env import get_default_workspace

try:
from codechecker_client.blame_info import assemble_blame_info
except ImportError:
pass

LOG = logger.get_logger('system')

MAX_UPLOAD_SIZE = 1 * 1024 * 1024 * 1024 # 1GiB
Expand Down Expand Up @@ -581,66 +583,6 @@ def parse_report_files(report_files: Set[str], zip_iter=map):
missing_source_files)


def get_blame_info(file_path: str):
""" Get blame info for the given file. """
try:
repo = Repo(file_path, search_parent_directories=True)
except InvalidGitRepositoryError:
return

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 TypeError:
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})

LOG.debug("Collected blame info for %s", file_path)

return res
except Exception as ex:
LOG.debug("Failed to get blame information for %s: %s", file_path, ex)


def collect_blame_info_for_files(file_paths: List[str], zip_iter=map):
""" Collect blame information for the given file paths. """
file_blame_info = {}
for file_path, blame_info in zip(file_paths,
zip_iter(get_blame_info, file_paths)):
file_blame_info[file_path] = blame_info

return file_blame_info


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 @@ -751,26 +693,18 @@ def assemble_zip(inputs, zip_file, client):
except KeyError:
zipf.write(f, file_path)

# Currently ProcessPoolExecutor fails completely in windows.
# Reason is most likely combination of venv and fork() not
# being present in windows, so stuff like setting up
# PYTHONPATH in parent CodeChecker before store is executed
# are lost.
if sys.platform == "win32":
file_blame_info = collect_blame_info_for_files(
collected_file_paths)
else:
with ProcessPoolExecutor() as executor:
file_blame_info = collect_blame_info_for_files(
collected_file_paths, executor.map)

# Add blame information to the zip for the files which will be sent to
# the server if exist.
for f, blame_info in file_blame_info.items():
if blame_info:
zipf.writestr(
os.path.join('blame', f.lstrip('/')),
json.dumps(blame_info))
if collected_file_paths:
LOG.info("Collecting blame information for source files...")
try:
if assemble_blame_info(zipf, collected_file_paths):
LOG.info("Collecting blame information done.")
else:
LOG.info("No blame information found for source files.")
except NameError:
LOG.warning(
"Collecting blame information has been failed. Make sure "
"'git' is available on your system to hide this warning "
"message.")

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

Expand Down

0 comments on commit 6892118

Please sign in to comment.