diff --git a/bashate/bashate.py b/bashate/bashate.py index 85283cc..43821df 100644 --- a/bashate/bashate.py +++ b/bashate/bashate.py @@ -93,11 +93,19 @@ def check_arithmetic(line, report): report.print_error(MESSAGES['E041'].msg, line) +def check_hashbang(line, filename, report): + # this check only runs on the first line + # maybe this should check for shell? + if not line.startswith("#!") and not filename.endswith(".sh"): + report.print_error(MESSAGES['E005'].msg, line) + + class BashateRun(object): def __init__(self): # TODO(mrodden): rename these to match convention self.ERRORS = 0 + self.ERRORS_LIST = None self.IGNORE_LIST = None self.WARNINGS = 0 self.WARNINGS_LIST = None @@ -110,10 +118,19 @@ class BashateRun(object): if warnings: self.WARNINGS_LIST = '^(' + '|'.join(warnings.split(',')) + ')' + def register_errors(self, errors): + if errors: + self.ERRORS_LIST = '^(' + '|'.join(errors.split(',')) + ')' + def should_ignore(self, error): return self.IGNORE_LIST and re.search(self.IGNORE_LIST, error) def should_warn(self, error): + # if in the errors list, overrides warning level + if self.ERRORS_LIST and re.search(self.ERRORS_LIST, error): + return False + if messages.is_default_warning(error): + return True return self.WARNINGS_LIST and re.search(self.WARNINGS_LIST, error) def print_error(self, error, line, @@ -179,6 +196,8 @@ class BashateRun(object): prev_file = fileinput.filename() + check_hashbang(line, fileinput.filename(), report) + if verbose: print("Running bashate on %s" % fileinput.filename()) @@ -238,7 +257,9 @@ def main(): help='files to scan for errors') parser.add_argument('-i', '--ignore', help='Rules to ignore') parser.add_argument('-w', '--warn', - help='Rules to warn (rather than error)') + help='Rules to always warn (rather than error)') + parser.add_argument('-e', '--error', + help='Rules to always error (rather than warn)') parser.add_argument('-v', '--verbose', action='store_true', default=False) parser.add_argument('-s', '--show', action='store_true', default=False) opts = parser.parse_args() @@ -255,6 +276,7 @@ def main(): run = BashateRun() run.register_ignores(opts.ignore) run.register_warnings(opts.warn) + run.register_errors(opts.error) try: run.check_files(files, opts.verbose) diff --git a/bashate/messages.py b/bashate/messages.py index 69db474..9715966 100644 --- a/bashate/messages.py +++ b/bashate/messages.py @@ -10,6 +10,9 @@ # License for the specific language governing permissions and limitations # under the License. +import re +import textwrap + class _Message: """An individual bashate message. @@ -26,10 +29,16 @@ class _Message: :param long_msg: A longer more involved message, designed for documentation """ - def __init__(self, msg_id, msg_str, long_msg): + def __init__(self, msg_id, msg_str, long_msg, default): self.msg_id = msg_id self.msg_str = msg_str - self.long_msg = long_msg + # clean-up from """ to a plain string + if long_msg: + self.long_msg = textwrap.dedent(long_msg) + self.long_msg = self.long_msg.strip() + else: + self.long_msg = None + self.default = default @property def msg(self): @@ -37,55 +46,141 @@ class _Message: # that up as .msg property for quick access. return "%s: %s" % (self.msg_id, self.msg_str) + _messages = { 'E001': { 'msg': 'Trailing Whitespace', - 'long_msg': None + 'long_msg': None, + 'default': 'E' }, 'E002': { 'msg': 'Tab indents', - 'long_msg': None + 'long_msg': + """ + Spaces are preferred to tabs in source files. + """, + 'default': 'E' }, 'E003': { 'msg': 'Indent not multiple of 4', - 'long_msg': None + 'long_msg': + """ + Four spaces should be used to offset logical blocks. + """, + 'default': 'E' }, 'E004': { 'msg': 'File did not end with a newline', - 'long_msg': None + 'long_msg': + """ + It is conventional to have a single newline ending files. + """, + 'default': 'E' + }, + 'E005': { + 'msg': 'File does not begin with #! or have .sh prefix', + 'long_msg': + """ + This can be useful for tools that use either the interpreter + directive or the file-exension to select highlighting mode, + syntax mode or determine MIME-type, such as file, gerrit and + editors. + """, + 'default': 'W' }, 'E010': { 'msg': 'Do not on same line as %s', - 'long_msg': None + 'long_msg': + """ + Ensure consistency of "do" directive being on the same line as + it's command. For example: + + for i in $(seq 1 100); + do + echo "hi" + done + + will trigger this error + """, + 'default': 'E' }, 'E011': { 'msg': 'Then keyword is not on same line as if or elif keyword', - 'long_msg': None + 'long_msg': + """ + Similar to E010, this ensures consistency of if/elif statements + """, + 'default': 'E' }, 'E012': { 'msg': 'heredoc did not end before EOF', - 'long_msg': None + 'long_msg': + """ + This check ensures the closure of heredocs (<