├── README.md └── pre-commit /README.md: -------------------------------------------------------------------------------- 1 | Git Commit Hook 2 | ==================== 3 | Pre-commit hook for Git checking Python and Javascript code quality 4 | 5 | Usage 6 | ------ 7 | The commit hook will automatically be called when you are running `git commit`. 8 | 9 | 10 | Screenshots 11 | ------ 12 | 13 | ![alt text](https://cloud.githubusercontent.com/assets/1684999/3529919/f39c73d4-079e-11e4-85d8-f1a379cc2cc8.png) 14 | ![alt text](https://cloud.githubusercontent.com/assets/1684999/3529923/00b296d4-079f-11e4-9d43-d463511792ca.png) 15 | 16 | 17 | Checklist 18 | ------ 19 | 1. pep8 20 | 2. pyflakes 21 | 3. ipdb 22 | 4. print 23 | 5. console.log 24 | 6. debugger 25 | 7. JShint 26 | 27 | 28 | Requirements 29 | ------------- 30 | 1. pep8 (`pip install pep8`) 31 | 2. pyflakes (`pip install pyflakes`) 32 | 3. jshint (`npm install -g jshint`) 33 | 34 | 35 | Installation: 36 | ------------- 37 | 1. Save pre-commit as your_project/.git/hooks/pre-commit 38 | 2. Mark pre-commit executable: ```$ chmod +x your_project/.git/hooks/pre-commit``` 39 | 40 | 41 | Override 42 | ------ 43 | The hook can be overridden: ```$ git commit --no-verify``` 44 | 45 | 46 | This code was forked from [https://gist.github.com/spulec/1364640](https://gist.github.com/spulec/1364640). 47 | -------------------------------------------------------------------------------- /pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Forked From https://gist.github.com/spulec/1364640#file-pre-commit 5 | """ 6 | 7 | import os 8 | import re 9 | import subprocess 10 | import sys 11 | 12 | modified = re.compile('^[MA]\s+(?P.*)$') 13 | 14 | CHECKS = [ 15 | { 16 | 'output': 'Checking for pdb and ipdbs...', 17 | 'command': 'grep -n "import [i]*pdb" %s', 18 | 'ignore_files': ['.*pre-commit'], 19 | 'print_filename': True, 20 | 'exists': False, 21 | 'package': '' 22 | }, 23 | { 24 | 'output': 'Checking for print statements...', 25 | 'command': 'grep -n "^\s*\bprint" %s', 26 | 'match_files': ['.*\.py$'], 27 | 'ignore_files': ['.*migrations.*', '.*management/commands.*', 28 | '.*manage.py', '.*/scripts/.*'], 29 | 'print_filename': True, 30 | 'exists': False, 31 | 'package': '' 32 | }, 33 | { 34 | 'output': 'Checking for console.log()...', 35 | 'command': 'grep -n console.log %s', 36 | 'match_files': ['.*\.js$'], 37 | 'print_filename': True, 38 | 'exists': False, 39 | 'package': '' 40 | }, 41 | { 42 | 'output': 'Checking for debugger...', 43 | 'command': 'grep -n debugger %s', 44 | 'match_files': ['.*\.js$'], 45 | 'print_filename': True, 46 | 'exists': False, 47 | 'package': '' 48 | }, 49 | { 50 | 'output': 'Running Jshint...', 51 | 'command': 'jshint %s | grep -v "Lint Free!"', 52 | 'match_files': ['.*\.js$'], 53 | 'print_filename': False, 54 | 'exists': True, 55 | 'package': 'jshint' 56 | }, 57 | { 58 | 'output': 'Running Pyflakes...', 59 | 'command': 'pyflakes %s', 60 | 'match_files': ['.*\.py$'], 61 | 'ignore_files': ['.*settings/.*', '.*manage.py', 62 | '.*migrations.*', '.*/terrain/.*'], 63 | 'print_filename': False, 64 | 'exists': True, 65 | 'package': 'pyflakes' 66 | }, 67 | { 68 | 'output': 'Running pep8...', 69 | 'command': 'pep8 -r --ignore=E501,W293 %s', 70 | 'match_files': ['.*\.py$'], 71 | 'ignore_files': ['.*migrations.*'], 72 | 'print_filename': False, 73 | 'exists': True, 74 | 'package': 'pep8' 75 | } 76 | ] 77 | 78 | 79 | def highlight(text, status): 80 | attrs = [] 81 | colors = { 82 | 'green': '32', 'red': '31', 'yellow': '33' 83 | } 84 | if not sys.stdout.isatty(): 85 | return text 86 | attrs.append(colors.get(status, 'red')) 87 | attrs.append('1') 88 | return '\x1b[%sm%s\x1b[0m' % (';'.join(attrs), text) 89 | 90 | 91 | def exists(cmd, error=True): 92 | devnull = open(os.devnull, 'w') 93 | params = {'stdout': devnull, 'stderr': devnull, } 94 | query = 'which %s' % cmd 95 | code = subprocess.call(query.split(), **params) 96 | if code != 0 and error: 97 | print(highlight('not installed %(command)s' % {'command': cmd}, 'yellow')) 98 | sys.exit(1) 99 | 100 | 101 | def matches_file(file_name, match_files): 102 | return any( 103 | re.compile(match_file).match(file_name) for match_file in match_files) 104 | 105 | 106 | def system(*args, **kwargs): 107 | kwargs.setdefault('stdout', subprocess.PIPE) 108 | proc = subprocess.Popen(args, **kwargs) 109 | out, err = proc.communicate() 110 | return out, err 111 | 112 | 113 | def check_files(files, check): 114 | result = 0 115 | print(highlight(check['output'], 'yellow')) 116 | 117 | if check['exists'] and check['package']: 118 | exists(check['package']) 119 | 120 | for file_name in files: 121 | if not 'match_files' in check or matches_file(file_name, 122 | check['match_files']): 123 | if not 'ignore_files' in check or not matches_file( 124 | file_name, check['ignore_files']): 125 | out, err = system(check['command'] % file_name, 126 | stderr=subprocess.PIPE, shell=True) 127 | if out or err: 128 | if check['print_filename']: 129 | prefix = '\t%s:' % file_name 130 | else: 131 | prefix = '\t' 132 | output_lines = ['%s%s' % (prefix, line) for line in 133 | out.splitlines()] 134 | print(highlight('\n'.join(output_lines), 'red')) 135 | if err: 136 | print(highlight(err, 'red')) 137 | result = 1 138 | return result 139 | 140 | 141 | def main(): 142 | # Stash any changes to the working tree that are not going to be committed 143 | subprocess.call(['git', 'stash', '-u', '--keep-index'], 144 | stdout=subprocess.PIPE) 145 | 146 | files = [] 147 | out, err = system('git', 'status', '--porcelain') 148 | for line in out.splitlines(): 149 | match = modified.match(str(line)) 150 | if match: 151 | files.append(match.group('name')) 152 | 153 | result = 0 154 | 155 | print(highlight('Running Django Code Validator...', 'yellow')) 156 | if exists('$VIRTUAL_ENV/bin/python', error=False): 157 | return_code = subprocess.call('$VIRTUAL_ENV/bin/python manage.py ' 158 | 'validate', shell=True) 159 | result = return_code or result 160 | 161 | for check in CHECKS: 162 | result = check_files(files, check) or result 163 | 164 | # Unstash changes to the working tree that we had stashed 165 | subprocess.call(['git', 'reset', '--hard'], stdout=subprocess.PIPE, 166 | stderr=subprocess.PIPE) 167 | subprocess.call(['git', 'stash', 'pop', '--quiet', '--index'], 168 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) 169 | sys.exit(result) 170 | 171 | 172 | if __name__ == '__main__': 173 | main() 174 | --------------------------------------------------------------------------------