├── .gitignore ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── Requirements.txt ├── gerritcheck ├── __init__.py ├── check.py └── test.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.pyc -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | gerrit-check 2 | Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gerrit-check -- static code analysis to Gerrit comments 2 | 3 | **TLDR: Turn cpplint / cppcheck / flake8 messages to Gerrit comments** 4 | 5 | Many projects use Gerrit for code-reviews and in addition use static code 6 | analysis tools to report about the quality of the code in review. For example, 7 | Jenkins comes with a nice integration of Gerrit, there is no suitable way to 8 | generate line-by-line annotations of the output of static code analysis tools 9 | for Gerrit. 10 | 11 | This project allows you to transform the output of the tools: cppcheck, 12 | cpplint and flake8 into line by line comments in Gerrit. 13 | 14 | ## Installation 15 | 16 | This is as easy as: 17 | 18 | sudo pip install gerrit-check 19 | 20 | ## Usage 21 | 22 | The typical usage for this project is to use in conjunction with a continuous 23 | integration server that runs for example pre-commit checks on a particular 24 | Gerrit change set. 25 | 26 | gerrit-check -g gerrit.some.company.com \ 27 | -t cpplint \ 28 | --user jenkins \ 29 | --commit ${GIT_HASH} 30 | 31 | This will run cpplint on the modified files and lines identified by the 32 | *GIT_HASH* and submit a review on this patch to the Gerrit instance identified 33 | in the arguments. 34 | 35 | If access to the Gerrit server is not directly possible, the `-l` option will 36 | force the output to be written to stdout so that it can be saved and 37 | post-processed. 38 | 39 | In a similar way to the above described simplication the script could be used 40 | as follows: 41 | 42 | gerrit-check -t cpplint --commit ${GIT_HASH} \ 43 | | ssh jenkins@gerrit.some.company gerrit review ${GIT_HASH} --json 44 | 45 | ## License 46 | 47 | Licensed under the Apache License, Version 2.0 (the "License"). You may not 48 | use this file except in compliance with the License. A copy of the License 49 | is located at: http://aws.amazon.com/apache2.0/ 50 | 51 | or in the "license" file accompanying this file. This file is distributed 52 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 53 | express or implied. See the License for the specific language governing 54 | permissions and limitations under the License. -------------------------------------------------------------------------------- /Requirements.txt: -------------------------------------------------------------------------------- 1 | plumbum 2 | cpplint 3 | flake8 4 | -------------------------------------------------------------------------------- /gerritcheck/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grundprinzip/gerrit-check/28c2f17ba69e45ea4198db6f1d0ffaa35a75d522/gerritcheck/__init__.py -------------------------------------------------------------------------------- /gerritcheck/check.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Amazon.com, Inc. or its 2 | # affiliates. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"). You may not 5 | # use this file except in compliance with the License. A copy of the License 6 | # is located at 7 | # 8 | # http://aws.amazon.com/apache2.0/ 9 | # 10 | # or in the "license" file accompanying this file. This file is distributed 11 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | # express or implied. See the License for the specific language governing 13 | # permissions and limitations under the License. 14 | from __future__ import absolute_import, print_function 15 | 16 | from collections import defaultdict 17 | from plumbum import local, SshMachine 18 | from flake8.api.legacy import get_style_guide 19 | 20 | # Subprocess is used to address https://github.com/tomerfiliba/plumbum/issues/295 21 | from subprocess import Popen, PIPE 22 | import argparse 23 | try: 24 | from exceptions import RuntimeError 25 | except: 26 | # In Python3 all exceptions are built-in 27 | pass 28 | import json 29 | import multiprocessing 30 | import os 31 | import sys 32 | 33 | 34 | 35 | CPP_HEADER_FILES = (".h", ".hpp") 36 | CPP_SOURCE_FILES = (".c", ".cc", ".cpp") 37 | CPP_FILES = CPP_HEADER_FILES + CPP_SOURCE_FILES 38 | 39 | # Default cpplint options that are passed on during the execution 40 | DEFAULT_CPPLINT_FILTER_OPTIONS=("-legal/copyright", "-build/include_order") 41 | 42 | # Prepare global git cmd 43 | git = local["git"] 44 | 45 | class GerritCheckExecption(RuntimeError): 46 | pass 47 | 48 | def extract_files_for_commit(rev): 49 | """ 50 | :return: A list of files that where modified in revision 'rev' 51 | """ 52 | diff = Popen(["git", "diff-tree", "--no-commit-id", "--name-only", "-r", str(rev)], 53 | stdout=PIPE) 54 | 55 | out, err = diff.communicate() 56 | 57 | if err: 58 | raise GerritCheckExecption("Could not run diff on current revision. " 59 | "Make sure that the current revision has a " 60 | "parent:" + err) 61 | return [f.strip() for f in out.splitlines() if len(f)] 62 | 63 | 64 | def filter_files(files, suffix=CPP_FILES): 65 | result = [] 66 | for f in files: 67 | if f.endswith(suffix) and os.path.exists(f): 68 | result.append(f) 69 | return result 70 | 71 | 72 | def line_part_of_commit(file, line, commit): 73 | """Helper function that returns true if a particular `line` in a 74 | particular `file` was last changed in `commit`.""" 75 | if line == '0': return False 76 | 77 | line_val = git("blame", "-l", "-L{0},{0}".format(line), file) 78 | return line_val.split(" ", 1)[0] == commit 79 | 80 | 81 | def flake8_on_files(files, commit): 82 | """ Runs flake8 on the files to report style guide violations. 83 | """ 84 | style = get_style_guide(config_file=None, quiet=False) 85 | 86 | # We need to redirect stdout while generating the JSON to avoid spilling 87 | # messages to the user. 88 | old_stdout = sys.stdout 89 | sys.stdout = open("/dev/null", "w") 90 | review = {} 91 | for file in filter_files(files, (".py", )): 92 | report = style.check_files((file, )) 93 | if report.total_errors: 94 | if not "comments" in review: 95 | review["comments"] = defaultdict(list) 96 | for line_number, offset, code, text, doc in report._deferred_print: 97 | if not line_part_of_commit(file, line_number, commit): continue 98 | review["comments"][file].append({ 99 | "path": file, 100 | "line": line_number, 101 | "message": "[{0}] {1}".format(code, text) 102 | }) 103 | if "comments" in review and len(review["comments"]): 104 | review["message"] = "[FLAKE8] Some issues found." 105 | else: 106 | review["message"] = "[FLAKE8] No issues found. OK" 107 | sys.stdout = old_stdout 108 | return json.dumps(review) 109 | 110 | 111 | def cppcheck_on_files(files, commit): 112 | """ Runs cppcheck on a list of input files changed in `commit` and 113 | returns a JSON structure in the format required for Gerrit 114 | to submit a review. 115 | """ 116 | cppcheck_cmd = local["cppcheck"][ 117 | "--quiet", 118 | "-j %d" % (multiprocessing.cpu_count() * 2), 119 | "--template={file}###{line}###{severity}###{message}"] 120 | 121 | # Each line in the output is an issue 122 | review = {} 123 | rc, out, err = cppcheck_cmd.run(filter_files(files, CPP_SOURCE_FILES), 124 | retcode=None) 125 | if len(err) > 0: 126 | review["message"] = "[CPPCHECK] Some issues need to be fixed." 127 | 128 | review["comments"] = defaultdict(list) 129 | for c in err.split("\n"): 130 | if len(c.strip()) == 0: continue 131 | 132 | parts = c.split("###") 133 | 134 | # Only add a comment if code was changed in the modified region 135 | if not line_part_of_commit(parts[0], parts[1], commit): continue 136 | 137 | review["comments"][parts[0]].append({ 138 | "path": parts[0], 139 | "line": parts[1], 140 | "message": "[{0}] {1}".format(parts[2], parts[3]) 141 | }) 142 | 143 | if len(review["comments"]): 144 | review["labels"] = {"Code-Review": -1} 145 | return json.dumps(review) 146 | 147 | # Check the return code only just now as cppcheck might still have returned 148 | # some valid comments. 149 | if rc != 0: 150 | review["message"] = "[CPPCHECK] Did not complete successfully: " + out 151 | return json.dumps(review) 152 | 153 | # Add a review comment that no issues have been found 154 | review["message"] = "[CPPCHECK] No issues found. OK" 155 | return json.dumps(review) 156 | 157 | 158 | def cpplint_on_files(files, commit, filters=DEFAULT_CPPLINT_FILTER_OPTIONS): 159 | """ Runs cpplint on a list of input files changed in `commit` and 160 | returns a JSON structure in the format required for Gerrit 161 | to submit a review. 162 | """ 163 | cpplint_cmd = local["cpplint"]["--filter={0}".format(",".join(filters))] 164 | 165 | # Each line in the output is an issue 166 | review = {} 167 | rc, out, err = cpplint_cmd.run(filter(os.path.exists, files), retcode=None) 168 | if len(err) > 0 and len(files): 169 | review["message"] = "[CPPLINT] Some issues need to be fixed." 170 | review["comments"] = defaultdict(list) 171 | for c in err.split("\n"): 172 | if len(c.strip()) == 0 or c.strip().startswith("Done") or \ 173 | c.strip().startswith("Total") or \ 174 | c.strip().startswith("Ignoring"): continue 175 | 176 | # cpplint cannot be configured to output a custom format so we 177 | # rely on knowing that the individual components are 178 | # two-space separated. 179 | location, rest = c.split(" ", 1) 180 | message, category = rest.rsplit(" ", 1) 181 | file, line, _ = location.split(":", 2) 182 | 183 | # Only add a comment if code was changed in the modified region 184 | if not line_part_of_commit(file, line, commit): continue 185 | review["comments"][file].append({ 186 | "path": file, 187 | "line": line, 188 | "message": "[{0}] {1}".format(category, message) 189 | }) 190 | if len(review["comments"]): 191 | review["labels"] = {"Code-Review": -1} 192 | return json.dumps(review) 193 | 194 | # Check the return code only just now as cpplint might still have returned 195 | # some valid comments. 196 | if rc != 0: 197 | review["message"] = "[CPPLINT] Did not complete successfully: " + out 198 | return json.dumps(review) 199 | 200 | # Add a review comment that no issues have been found 201 | review["message"] = "[CPPLINT] No issues found. OK" 202 | return json.dumps(review) 203 | 204 | 205 | def submit_review(change, user, host, data, port=22): 206 | """Uses the data as input to submit a new review.""" 207 | remote = local["ssh"]["-p {0}".format(port),"{0}@{1}".format(user, host)] 208 | (local["cat"] << data | remote["gerrit", "review", change, "--json"])() 209 | 210 | 211 | 212 | # Mapping a particular checking function to a tool name 213 | CHECKER_MAPPING = { 214 | "cppcheck": cppcheck_on_files, 215 | "cpplint": cpplint_on_files, 216 | "flake8": flake8_on_files 217 | } 218 | 219 | def main(): 220 | parser = argparse.ArgumentParser( 221 | description=("Execute code analysis and report results locally " 222 | "or to gerrit"), 223 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 224 | 225 | parser.add_argument("-g", "--gerrit-host", help="Gerrit host") 226 | parser.add_argument("-u", "--user", help="Username", default="jenkins") 227 | parser.add_argument("-p", "--port", help="SSH port Gerrit listens on", 228 | default=22) 229 | parser.add_argument("-c", "--commit", 230 | help="Git Hash of the commit to check", 231 | default="HEAD") 232 | parser.add_argument("-t", "--tool", help="Which validation to run", 233 | choices=CHECKER_MAPPING.keys(), action="append", 234 | required=True) 235 | parser.add_argument("-l", "--local", action="store_true", default=False, 236 | help=("Display output locally instead " 237 | "of submitting it to Gerrit")) 238 | 239 | args = parser.parse_args() 240 | 241 | # If commit is set to HEAD, no need to backup the previous revision 242 | if args.commit != "HEAD": 243 | hash_before = local["git"]("rev-parse", "HEAD").strip() 244 | local["git"]("checkout", args.commit) 245 | 246 | modified_files = extract_files_for_commit(args.commit) 247 | 248 | current_hash = local["git"]("rev-parse", args.commit).strip() 249 | for t in args.tool: 250 | result = CHECKER_MAPPING[t](modified_files, current_hash) 251 | if args.local: 252 | print (json.dumps(json.loads(result))) 253 | else: 254 | submit_review(args.commit, args.user, 255 | args.gerrit_host, result, args.port) 256 | 257 | # Only need to revert to previous change if the commit is 258 | # different from HEAD 259 | if args.commit != "HEAD": 260 | git("checkout", hash_before) 261 | 262 | if __name__ == "__main__": 263 | main() 264 | -------------------------------------------------------------------------------- /gerritcheck/test.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grundprinzip/gerrit-check/28c2f17ba69e45ea4198db6f1d0ffaa35a75d522/gerritcheck/test.py -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2016 Amazon.com, Inc. or its 3 | # affiliates. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"). You may not 6 | # use this file except in compliance with the License. A copy of the License 7 | # is located at 8 | # 9 | # http://aws.amazon.com/apache2.0/ 10 | # 11 | # or in the "license" file accompanying this file. This file is distributed 12 | # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 13 | # express or implied. See the License for the specific language governing 14 | # permissions and limitations under the License. 15 | 16 | from setuptools import setup, find_packages 17 | 18 | setup(name='gerrit-check', 19 | version='0.1.1', 20 | description='Line annotations for syntax checkers for Gerrit', 21 | author='Martin Grund', 22 | author_email='grundprinzip@gmail.com', 23 | url="https://github.com/grundprinzip/gerrit-check", 24 | platforms=("Linux", ), 25 | long_description=""" 26 | Many projects use Gerrit for code-reviews and in addition use static code 27 | analysis tools to report about the quality of the code in review. For example, 28 | Jenkins comes with a nice integration of Gerrit, there is no suitable way to 29 | generate line-by-line annotations of the output of static code analysis tools 30 | for Gerrit. 31 | 32 | This project allows you to transform the output of the tools: cppcheck, 33 | cpplint and flake8 into line by line comments in Gerrit. 34 | """, 35 | license="Apache 2.0", 36 | packages=find_packages(), 37 | install_requires=['plumbum', 'flake8', 'cpplint'], 38 | entry_points={ 39 | 'console_scripts': [ 40 | 'gerrit-check=gerritcheck.check:main' 41 | ] 42 | } 43 | ) 44 | --------------------------------------------------------------------------------