Index: utils/check_patch.py |
diff --git a/utils/check_patch.py b/utils/check_patch.py |
index 576b97d1a074a5e0b1b41bceefef44fed6941ef8..78af6b9ae266dc1f03a0405c135fe9ceccc54f97 100755 |
--- a/utils/check_patch.py |
+++ b/utils/check_patch.py |
@@ -20,7 +20,7 @@ Usage: check_patch.py -p [/path/to/patch] |
@author: Lucas Meneghel Rodrigues <lmr@redhat.com> |
""" |
-import os, stat, logging, sys, optparse |
+import os, stat, logging, sys, optparse, time |
import common |
from autotest_lib.client.common_lib import utils, error, logging_config |
from autotest_lib.client.common_lib import logging_manager |
@@ -32,6 +32,20 @@ class CheckPatchLoggingConfig(logging_config.LoggingConfig): |
verbose=verbose) |
+def ask(question, auto=False): |
+ """ |
+ Raw input with a prompt that emulates logging. |
+ |
+ @param question: Question to be asked |
+ @param auto: Whether to return "y" instead of asking the question |
+ """ |
+ if auto: |
+ logging.info("%s (y/n) y" % question) |
+ return "y" |
+ return raw_input("%s INFO | %s (y/n) " % |
+ (time.strftime("%H:%M:%S", time.localtime()), question)) |
+ |
+ |
class VCS(object): |
""" |
Abstraction layer to the version control system. |
@@ -104,6 +118,7 @@ class SubVersionBackend(object): |
""" |
def __init__(self): |
logging.debug("Subversion VCS backend initialized.") |
+ self.ignored_extension_list = ['.orig', '.bak'] |
def get_unknown_files(self): |
@@ -112,7 +127,9 @@ class SubVersionBackend(object): |
for line in status.split("\n"): |
status_flag = line[0] |
if line and status_flag == "?": |
- unknown_files.append(line[1:].strip()) |
+ for extension in self.ignored_extension_list: |
+ if not line.endswith(extension): |
+ unknown_files.append(line[1:].strip()) |
return unknown_files |
@@ -181,13 +198,16 @@ class FileChecker(object): |
Picks up a given file and performs various checks, looking after problems |
and eventually suggesting solutions. |
""" |
- def __init__(self, path): |
+ def __init__(self, path, confirm=False): |
""" |
Class constructor, sets the path attribute. |
@param path: Path to the file that will be checked. |
+ @param confirm: Whether to answer yes to all questions asked without |
+ prompting the user. |
""" |
self.path = path |
+ self.confirm = confirm |
self.basename = os.path.basename(self.path) |
if self.basename.endswith('.py'): |
self.is_python = True |
@@ -204,7 +224,7 @@ class FileChecker(object): |
self.first_line = checked_file.readline() |
checked_file.close() |
self.corrective_actions = [] |
- self.indentation_exceptions = ['cli/job_unittest.py'] |
+ self.indentation_exceptions = ['job_unittest.py'] |
def _check_indent(self): |
@@ -226,8 +246,6 @@ class FileChecker(object): |
reindent_results = reindent_raw.split(" ")[-1].strip(".") |
if reindent_results == "changed": |
if self.basename not in self.indentation_exceptions: |
- logging.error("Possible indentation and spacing issues on " |
- "file %s" % self.path) |
self.corrective_actions.append("reindent.py -v %s" % self.path) |
@@ -242,8 +260,7 @@ class FileChecker(object): |
c_cmd = 'run_pylint.py %s' % self.path |
rc = utils.system(c_cmd, ignore_status=True) |
if rc != 0: |
- logging.error("Possible syntax problems on file %s", self.path) |
- logging.error("You might want to rerun '%s'", c_cmd) |
+ logging.error("Syntax issues found during '%s'", c_cmd) |
def _check_unittest(self): |
@@ -260,9 +277,8 @@ class FileChecker(object): |
unittest_cmd = 'python %s' % unittest_path |
rc = utils.system(unittest_cmd, ignore_status=True) |
if rc != 0: |
- logging.error("Problems during unit test execution " |
- "for file %s", self.path) |
- logging.error("You might want to rerun '%s'", unittest_cmd) |
+ logging.error("Unittest issues found during '%s'", |
+ unittest_cmd) |
def _check_permissions(self): |
@@ -273,14 +289,10 @@ class FileChecker(object): |
""" |
if self.first_line.startswith("#!"): |
if not self.is_executable: |
- logging.info("File %s seems to require execution " |
- "permissions. ", self.path) |
- self.corrective_actions.append("chmod +x %s" % self.path) |
+ self.corrective_actions.append("svn propset svn:executable ON %s" % self.path) |
else: |
if self.is_executable: |
- logging.info("File %s does not seem to require execution " |
- "permissions. ", self.path) |
- self.corrective_actions.append("chmod -x %s" % self.path) |
+ self.corrective_actions.append("svn propdel svn:executable %s" % self.path) |
def report(self): |
@@ -294,10 +306,9 @@ class FileChecker(object): |
self._check_code() |
self._check_unittest() |
if self.corrective_actions: |
- logging.info("The following corrective actions are suggested:") |
for action in self.corrective_actions: |
- logging.info(action) |
- answer = raw_input("Would you like to apply it? (y/n) ") |
+ answer = ask("Would you like to execute %s?" % action, |
+ auto=self.confirm) |
if answer == "y": |
rc = utils.system(action, ignore_status=True) |
if rc != 0: |
@@ -305,7 +316,8 @@ class FileChecker(object): |
class PatchChecker(object): |
- def __init__(self, patch=None, patchwork_id=None): |
+ def __init__(self, patch=None, patchwork_id=None, confirm=False): |
+ self.confirm = confirm |
self.base_dir = os.getcwd() |
if patch: |
self.patch = os.path.abspath(patch) |
@@ -322,7 +334,7 @@ class PatchChecker(object): |
if changed_files_before: |
logging.error("Repository has changed files prior to patch " |
"application. ") |
- answer = raw_input("Would you like to revert them? (y/n) ") |
+ answer = ask("Would you like to revert them?", auto=self.confirm) |
if answer == "n": |
logging.error("Not safe to proceed without reverting files.") |
sys.exit(1) |
@@ -370,20 +382,20 @@ class PatchChecker(object): |
for untracked_file in add_to_vcs: |
logging.info(untracked_file) |
logging.info("Might need to be added to VCS") |
- logging.info("Would you like to add them to VCS ? (y/n/abort) ") |
- answer = raw_input() |
+ answer = ask("Would you like to add them to VCS ?") |
if answer == "y": |
for untracked_file in add_to_vcs: |
self.vcs.add_untracked_file(untracked_file) |
modified_files_after.append(untracked_file) |
elif answer == "n": |
pass |
- elif answer == "abort": |
- sys.exit(1) |
for modified_file in modified_files_after: |
- file_checker = FileChecker(modified_file) |
- file_checker.report() |
+ # Additional safety check, new commits might introduce |
+ # new directories |
+ if os.path.isfile(modified_file): |
+ file_checker = FileChecker(modified_file) |
+ file_checker.report() |
def check(self): |
@@ -399,20 +411,37 @@ if __name__ == "__main__": |
help='id of a given patchwork patch') |
parser.add_option('--verbose', dest="debug", action='store_true', |
help='include debug messages in console output') |
+ parser.add_option('-f', '--full-check', dest="full_check", |
+ action='store_true', |
+ help='check the full tree for corrective actions') |
+ parser.add_option('-y', '--yes', dest="confirm", |
+ action='store_true', |
+ help='Answer yes to all questions') |
options, args = parser.parse_args() |
local_patch = options.local_patch |
id = options.id |
debug = options.debug |
+ full_check = options.full_check |
+ confirm = options.confirm |
logging_manager.configure_logging(CheckPatchLoggingConfig(), verbose=debug) |
- if local_patch: |
- patch_checker = PatchChecker(patch=local_patch) |
- elif id: |
- patch_checker = PatchChecker(patchwork_id=id) |
+ ignore_file_list = ['common.py'] |
+ if full_check: |
+ for root, dirs, files in os.walk('.'): |
+ if not '.svn' in root: |
+ for file in files: |
+ if file not in ignore_file_list: |
+ path = os.path.join(root, file) |
+ file_checker = FileChecker(path, confirm=confirm) |
+ file_checker.report() |
else: |
- logging.error('No patch or patchwork id specified. Aborting.') |
- sys.exit(1) |
- |
- patch_checker.check() |
+ if local_patch: |
+ patch_checker = PatchChecker(patch=local_patch, confirm=confirm) |
+ elif id: |
+ patch_checker = PatchChecker(patchwork_id=id, confirm=confirm) |
+ else: |
+ logging.error('No patch or patchwork id specified. Aborting.') |
+ sys.exit(1) |
+ patch_checker.check() |