tor/scripts/maint/lintChanges.py

117 lines
3.3 KiB
Python
Raw Normal View History

#!/usr/bin/python
from __future__ import print_function
from __future__ import with_statement
import sys
import re
import os
KNOWN_GROUPS = set([
"Minor bugfix",
"Minor bugfixes",
"Major bugfix",
"Major bugfixes",
"Minor feature",
"Minor features",
"Major feature",
"Major features",
"New system requirements",
"Testing",
"Documentation",
"Code simplification and refactoring",
"Removed features",
"Deprecated features"])
NEEDS_SUBCATEGORIES = set([
"Minor bugfix",
"Minor bugfixes",
"Major bugfix",
"Major bugfixes",
"Minor feature",
"Minor features",
"Major feature",
"Major features",
])
def lintfile(fname):
have_warned = []
def warn(s):
if not have_warned:
have_warned.append(1)
print("{}:".format(fname))
print("\t{}".format(s))
m = re.search(r'(\d{3,})', os.path.basename(fname))
if m:
bugnum = m.group(1)
else:
bugnum = None
with open(fname) as f:
contents = f.read()
if bugnum and bugnum not in contents:
warn("bug number {} does not appear".format(bugnum))
m = re.match(r'^[ ]{2}o ([^\(:]*)([^:]*):', contents)
if not m:
warn("Header not in format expected. (' o Foo:' or ' o Foo (Bar):')")
elif m.group(1).strip() not in KNOWN_GROUPS:
warn("Unrecognized header: %r" % m.group(1))
elif (m.group(1) in NEEDS_SUBCATEGORIES and '(' not in m.group(2)):
warn("Missing subcategory on %r" % m.group(1))
2016-09-22 14:33:09 +02:00
if m:
isBug = ("bug" in m.group(1).lower() or "fix" in m.group(1).lower())
else:
isBug = False
2014-12-22 16:00:34 +01:00
contents = " ".join(contents.split())
2015-02-19 15:54:09 +01:00
if re.search(r'\#\d{2,}', contents):
warn("Don't use a # before ticket numbers. ('bug 1234' not '#1234')")
2015-02-19 15:54:09 +01:00
if isBug and not re.search(r'(\d+)', contents):
warn("Ticket marked as bugfix, but does not mention a number.")
2014-12-22 15:49:33 +01:00
elif isBug and not re.search(r'Fixes ([a-z ]*)bug (\d+)', contents):
warn("Ticket marked as bugfix, but does not say 'Fixes bug XXX'")
2014-12-22 15:49:33 +01:00
if re.search(r'[bB]ug (\d+)', contents):
if not re.search(r'[Bb]ugfix on ', contents):
warn("Bugfix does not say 'bugfix on X.Y.Z'")
elif not re.search('[fF]ixes ([a-z ]*)bug (\d+); bugfix on ',
contents):
warn("Bugfix does not say 'Fixes bug X; bugfix on Y'")
elif re.search('tor-([0-9]+)', contents):
warn("Do not prefix versions with 'tor-'. ('0.1.2', not 'tor-0.1.2'.)")
return have_warned != []
def files(args):
"""Walk through the arguments: for directories, yield their contents;
for files, just yield the files. Only search one level deep, because
that's how the changes directory is laid out."""
for f in args:
if os.path.isdir(f):
for item in os.listdir(f):
if item.startswith("."): #ignore dotfiles
continue
yield os.path.join(f, item)
else:
yield f
if __name__ == '__main__':
problems = 0
for fname in files(sys.argv[1:]):
if fname.endswith("~"):
continue
if lintfile(fname):
problems += 1
if problems:
sys.exit(1)
else:
sys.exit(0)