#!/usr/bin/python import os import sys import re import hashlib import argparse import operator from subprocess import Popen from subprocess import PIPE def parse_show(commit, search_file): p = Popen("git show %s:%s" % (commit, search_file), shell=True, stdout=PIPE, stderr=PIPE) (out, err) = p.communicate() if err != "": return "" return out def parse_log(search_file, string, omit_directory, top): p = Popen("git log --stat --stat-width=10000 --pretty=oneline --format='\x11%H\x12'", shell=True, stdout=PIPE) (log, _) = p.communicate() files = {} commits = [] version = {} changed = 0 commit = "" current_version = "" for i in log.split('\n'): if re.match('\x11.*\x12', i): if changed == 0: version[commit] = current_version changed = 0 commit = re.sub('[\x11\x12]', '', i) commits.append(commit) elif '|' in i: file = i.split()[0] if file == search_file: try: current_version = '-'.join(re.search(string, parse_show(commit, search_file)).groups()) + "-commitid-" + commit version[commit] = current_version changed = 1 except: pass else: if files.has_key(file): files[file][commit] = [] else: files[file] = {} files[file][commit] = [] tmp = {} for file in files: if not re.match("^.*\.(php|asp|xml|sql|ini)$", file) and re.search(omit_directory, file) == None: tmp[file] = files[file] files = tmp if top != 0: files = dict(list(reversed(sorted(files.iteritems(), key=operator.itemgetter(1))))[:top]) for file in files: for commit in files[file]: p = Popen("git show %s:%s" % (commit, file), shell=True, stdout=PIPE, stderr=PIPE) (out, err) = p.communicate() if err != "": continue files[file][commit] = hashlib.md5(out).hexdigest() return files, commits, version def clone(url): path = url.split('/')[-1].split(".")[0] p = Popen("git clone %s" % url, shell=True, stdout=PIPE, stderr=PIPE) (out, err) = p.communicate() os.chdir(path) return path if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("-c", "--clone", help="Clone the repo first.", action="store_true") parser.add_argument("-u", "-p", "--url", "--path", help="Path or URL to the repository.", required=True) parser.add_argument("-f", "--file", help="File to search", required=True) parser.add_argument("-m", "--match", help="Regex to match line with version number (ie: '^\\\\\\$wp_version = \\x27([^']+)\\x27;$')", required=True) parser.add_argument("--omit-directory", help="Comma separated list of directories to omit.", default="") parser.add_argument("-t", "--top", help="Top 'n' files to use. (0 for unlimited)", default=10, type=int) args=parser.parse_args() if args.clone: print "Cloning: %s" % args.url path = clone(args.url) else: os.chdir(args.url) path = args.url (files, commits, version) = parse_log(args.file, args.match, "(" + '|'.join(args.omit_directory.split(",")) + ")", args.top) os.chdir("..") try: os.stat("sigs") except: os.mkdir("sigs") for file in files: f = open("sigs/%s-%s" % (path, file.rstrip("/").split("/")[-1]), "w") f.write("---\n") f.write("config:\n") f.write(" app_name: " + path + "\n") f.write(" check_file: " + file + "\n") f.write("sigs:\n") for revision in files[file]: try: f.write(" " + version[revision] + ": " + files[file][revision] + "\n") except: pass f.close()