-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
WIP: Implement reporter for code issues in a PR #5929
Open
p12tic
wants to merge
4
commits into
buildbot:master
Choose a base branch
from
p12tic:reporter-code-issues
base: master
Could not load branches
Branch not found: {{ refName }}
Could not load tags
Nothing to show
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
659a423
common: Disable chained-comparison warning
p12tic 617cb6a
reporters: Add a reporter to submit Github code comments
p12tic 444af27
reporters: Support message context key in MessageFormatterFunction
p12tic 4553e6c
reporters: Implement a report generator for code comments
p12tic 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
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,154 @@ | ||
# This file is part of Buildbot. Buildbot is free software: you can | ||
# redistribute it and/or modify it under the terms of the GNU General Public | ||
# License as published by the Free Software Foundation, version 2. | ||
# | ||
# This program is distributed in the hope that it will be useful, but WITHOUT | ||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
# details. | ||
# | ||
# You should have received a copy of the GNU General Public License along with | ||
# this program; if not, write to the Free Software Foundation, Inc., 51 | ||
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
# | ||
# Copyright Buildbot Team Members | ||
|
||
import bisect | ||
import json | ||
|
||
from twisted.internet import defer | ||
from zope.interface import implementer | ||
|
||
from buildbot import interfaces | ||
from buildbot.reporters import utils | ||
from buildbot.reporters.message import MessageFormatterFunction | ||
|
||
from .utils import BuildStatusGeneratorMixin | ||
|
||
|
||
@implementer(interfaces.IReportGenerator) | ||
class BuildCodeIssueCommentsGenerator(BuildStatusGeneratorMixin): | ||
|
||
wanted_event_keys = [ | ||
('builds', None, 'finished'), | ||
] | ||
|
||
compare_attrs = ['formatter'] | ||
|
||
def __init__(self, diffinfo_data_name=None, tags=None, builders=None, schedulers=None, | ||
branches=None, message_formatter=None): | ||
super().__init__('all', tags, builders, schedulers, branches, subject='', add_logs=False, | ||
add_patch=False) | ||
self.diffinfo_data_name = diffinfo_data_name | ||
self.formatter = message_formatter | ||
if self.formatter is None: | ||
self.formatter = MessageFormatterFunction(lambda ctx: ctx['message'], 'plain') | ||
|
||
@defer.inlineCallbacks | ||
def generate(self, master, reporter, key, build): | ||
_, _, event = key | ||
if event != 'finished': | ||
return None | ||
|
||
yield utils.getDetailsForBuild(master, build, | ||
wantProperties=self.formatter.wantProperties, | ||
wantSteps=self.formatter.wantSteps, | ||
wantPreviousBuild=False, | ||
wantLogs=self.formatter.wantLogs) | ||
|
||
if not self.is_message_needed_by_props(build): | ||
return None | ||
if not self.is_message_needed_by_results(build): | ||
return None | ||
|
||
report = yield self._build_report(self.formatter, master, reporter, build) | ||
return report | ||
|
||
def _get_target_changes_by_path(self, diff_info): | ||
hunks_by_path = {} | ||
for file_info in diff_info: | ||
hunks = [(hunk['ts'], hunk['tl']) for hunk in file_info['hunks']] | ||
if not hunks: | ||
continue | ||
hunks.sort() | ||
|
||
hunks_by_path[file_info['target_file']] = hunks | ||
return hunks_by_path | ||
|
||
def _is_result_usable(self, result): | ||
return result.get('test_code_path') is not None and result.get('line') is not None | ||
|
||
def _is_result_in_target_diff(self, target_changes_by_path, result): | ||
changes_in_path = target_changes_by_path.get(result['test_code_path']) | ||
if not changes_in_path: | ||
return False | ||
|
||
result_line = result['line'] | ||
|
||
preceding_change_i = bisect.bisect(changes_in_path, (result_line + 1, -1)) | ||
if preceding_change_i == 0: | ||
return False # there's no change with start position earlier than the test result line | ||
preceding_change_start, preceding_change_length = changes_in_path[preceding_change_i - 1] | ||
|
||
return result_line >= preceding_change_start and \ | ||
result_line < preceding_change_start + preceding_change_length | ||
|
||
@defer.inlineCallbacks | ||
def _get_usable_results_in_changed_lines(self, master, target_changes_by_path, result_sets): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. an error could be in an untouched line (removal of used import for example). gitlab allows you to comment on untouched lines. So for this generic code, I believe we shouldn't filterout maybe we could split two sets of results something like:
|
||
results_in_changed_lines = [] | ||
|
||
for result_set in result_sets: | ||
if result_set['category'] != 'code_issue': | ||
continue | ||
if result_set['value_unit'] != 'message': | ||
continue | ||
|
||
results = yield master.data.get(('test_result_sets', result_set['test_result_setid'], | ||
'results')) | ||
|
||
results = [tr for tr in results | ||
if self._is_result_usable(tr) and | ||
self._is_result_in_target_diff(target_changes_by_path, tr)] | ||
|
||
results_in_changed_lines.extend(results) | ||
return results_in_changed_lines | ||
|
||
@defer.inlineCallbacks | ||
def _build_report(self, formatter, master, reporter, build): | ||
users = yield reporter.getResponsibleUsersForBuild(master, build['buildid']) | ||
|
||
build_data = yield master.data.get(('builds', build['buildid'], 'data', | ||
self.diffinfo_data_name, 'value')) | ||
|
||
target_changes_by_path = self._get_target_changes_by_path(json.loads(build_data['raw'])) | ||
|
||
result_sets = yield master.data.get(('builds', build['buildid'], 'test_result_sets')) | ||
|
||
results_in_changed_lines = \ | ||
yield self._get_usable_results_in_changed_lines(master, target_changes_by_path, | ||
result_sets) | ||
|
||
result_body = [] | ||
for result in results_in_changed_lines: | ||
|
||
buildmsg = yield formatter.format_message_for_build(master, build, mode=self.mode, | ||
users=users, | ||
message=result['value']) | ||
|
||
result_body.append({ | ||
'codebase': '', | ||
'path': result['test_code_path'], | ||
'line': result['line'], | ||
'body': buildmsg['body'] | ||
}) | ||
|
||
return { | ||
'body': result_body, | ||
'subject': None, | ||
'type': 'code_comments', | ||
'results': build['results'], | ||
'builds': [build], | ||
'users': list(users), | ||
'patches': None, | ||
'logs': None | ||
} |
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
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
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.
Not sure if bisect is very well known module of python.
I wasn't aware of it.
Also, the fact that we are comparing tuple like this looks suspicious.
I think we need a proper helper function to compare code line intervals (with unit tests)