overcommit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/bin/hooks/commit-msg +8 -0
  3. data/bin/hooks/post-checkout +8 -0
  4. data/bin/hooks/post-merge +23 -0
  5. data/bin/hooks/pre-commit +8 -0
  6. data/bin/hooks/prepare-commit-msg +160 -0
  7. data/bin/overcommit +11 -0
  8. data/bin/run-hook +8 -0
  9. data/bin/scripts/csslint-rhino.js +9080 -0
  10. data/bin/scripts/gerrit-change-id +174 -0
  11. data/bin/scripts/index-tags +19 -0
  12. data/bin/scripts/jshint.js +5921 -0
  13. data/bin/scripts/jshint_runner.js +48 -0
  14. data/config/templates.yml +12 -0
  15. data/lib/overcommit.rb +8 -0
  16. data/lib/overcommit/cli.rb +95 -0
  17. data/lib/overcommit/configuration.rb +58 -0
  18. data/lib/overcommit/console_methods.rb +23 -0
  19. data/lib/overcommit/git_hook.rb +50 -0
  20. data/lib/overcommit/hook_specific_check.rb +81 -0
  21. data/lib/overcommit/hooks/commit_msg.rb +7 -0
  22. data/lib/overcommit/hooks/pre_commit.rb +9 -0
  23. data/lib/overcommit/installer.rb +59 -0
  24. data/lib/overcommit/plugins/commit_msg/change_id.rb +14 -0
  25. data/lib/overcommit/plugins/commit_msg/release_note.rb +14 -0
  26. data/lib/overcommit/plugins/commit_msg/russian_novel.rb +16 -0
  27. data/lib/overcommit/plugins/commit_msg/text_width.rb +20 -0
  28. data/lib/overcommit/plugins/commit_msg/trailing_period.rb +13 -0
  29. data/lib/overcommit/plugins/pre_commit/author_name.rb +16 -0
  30. data/lib/overcommit/plugins/pre_commit/causes_email.rb +15 -0
  31. data/lib/overcommit/plugins/pre_commit/css_linter.rb +17 -0
  32. data/lib/overcommit/plugins/pre_commit/erb_syntax.rb +21 -0
  33. data/lib/overcommit/plugins/pre_commit/haml_syntax.rb +23 -0
  34. data/lib/overcommit/plugins/pre_commit/js_console_log.rb +16 -0
  35. data/lib/overcommit/plugins/pre_commit/js_syntax.rb +18 -0
  36. data/lib/overcommit/plugins/pre_commit/restricted_paths.rb +15 -0
  37. data/lib/overcommit/plugins/pre_commit/ruby_syntax.rb +19 -0
  38. data/lib/overcommit/plugins/pre_commit/scss_lint.rb +20 -0
  39. data/lib/overcommit/plugins/pre_commit/test_history.rb +58 -0
  40. data/lib/overcommit/plugins/pre_commit/whitespace.rb +19 -0
  41. data/lib/overcommit/plugins/pre_commit/yaml_syntax.rb +22 -0
  42. data/lib/overcommit/reporter.rb +80 -0
  43. data/lib/overcommit/utils.rb +60 -0
  44. data/lib/overcommit/version.rb +3 -0
  45. metadata +88 -0
@@ -0,0 +1,14 @@
1
+ module Overcommit::GitHook
2
+ class ChangeID < HookSpecificCheck
3
+ include HookRegistry
4
+
5
+ stealth! # Not really a 'check', but we need it to run
6
+
7
+ SCRIPT_LOCATION = Overcommit::Utils.script_path 'gerrit-change-id'
8
+
9
+ def run_check
10
+ system `#{SCRIPT_LOCATION} #{@arguments.join(' ')}`
11
+ :good
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Overcommit::GitHook
2
+ class ReleaseNote < HookSpecificCheck
3
+ include HookRegistry
4
+
5
+ EMPTY_RELEASE_NOTE = /^release notes?\s*[:.]?\n{2,}/im
6
+ def run_check
7
+ if user_commit_message.join =~ EMPTY_RELEASE_NOTE
8
+ return :warn, 'Empty release note found, either add one or remove it'
9
+ end
10
+
11
+ :good
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ module Overcommit::GitHook
2
+ class RussianNovel < HookSpecificCheck
3
+ include HookRegistry
4
+
5
+ stealth!
6
+
7
+ RUSSIAN_NOVEL_LENGTH = 30
8
+ def run_check
9
+ if user_commit_message.length > RUSSIAN_NOVEL_LENGTH
10
+ return :warn, 'You seem to have authored a Russian novel; congratulations!'
11
+ end
12
+
13
+ :good
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ module Overcommit::GitHook
2
+ class TextWidth < HookSpecificCheck
3
+ include HookRegistry
4
+
5
+ def run_check
6
+ if user_commit_message.first.size > 60
7
+ return :warn, 'Please keep the subject < ~60 characters'
8
+ end
9
+
10
+ user_commit_message.each do |line|
11
+ chomped = line.chomp
12
+ if chomped.size > 72
13
+ return :warn, "> 72 characters, please hard wrap: '#{chomped}'"
14
+ end
15
+ end
16
+
17
+ :good
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ module Overcommit::GitHook
2
+ class TrailingPeriod < HookSpecificCheck
3
+ include HookRegistry
4
+
5
+ def run_check
6
+ if commit_message[0].rstrip.end_with?('.')
7
+ return :warn, 'Please omit trailing period from commit message subject.'
8
+ end
9
+
10
+ :good
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module Overcommit::GitHook
2
+ class AuthorName < HookSpecificCheck
3
+ include HookRegistry
4
+
5
+ def run_check
6
+ name = `git config --get user.name`.chomp
7
+ unless name.split(' ').count >= 2
8
+ return :bad, "Author must have at least first and last name; " <<
9
+ "was: '#{name}'.\n Set your name with " <<
10
+ "`git config --global user.name 'Your Name'`"
11
+ end
12
+
13
+ :good
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ module Overcommit::GitHook
2
+ class CausesEmail < HookSpecificCheck
3
+ include HookRegistry
4
+
5
+ def run_check
6
+ email = `git config --get user.email`.chomp
7
+ unless email =~ /@causes\.com$/
8
+ return :bad, "Author must use a causes.com address; was '#{email}'.\n" <<
9
+ "Set user with `git config --global user.email YOUR_EMAIL@causes.com`"
10
+ end
11
+
12
+ :good
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module Overcommit::GitHook
2
+ class CssLinter < HookSpecificCheck
3
+ include HookRegistry
4
+ file_type :css
5
+
6
+ CSS_LINTER_PATH = Overcommit::Utils.script_path 'csslint-rhino.js'
7
+
8
+ def run_check
9
+ return :warn, "Rhino is not installed" unless in_path? 'rhino'
10
+
11
+ paths = staged.join(' ')
12
+ output = `rhino #{CSS_LINTER_PATH} --quiet --format=compact #{paths} | grep 'Error - '`
13
+
14
+ return (output !~ /Error - (?!Unknown @ rule)/ ? :good : :bad), output
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ require 'erb'
2
+
3
+ module Overcommit::GitHook
4
+ class ErbSyntax < HookSpecificCheck
5
+ include HookRegistry
6
+ file_type :erb
7
+ ERB_CHECKER = 'bin/check-rails-erb'
8
+
9
+ def skip?
10
+ return 'Bundler is not installed' unless in_path? 'bundle'
11
+ unless File.executable? ERB_CHECKER
12
+ return "Can't find/execute #{ERB_CHECKER}"
13
+ end
14
+ end
15
+
16
+ def run_check
17
+ output = `bundle exec #{ERB_CHECKER} #{staged.join(' ')}`
18
+ return (output !~ /: compile error$/ ? :good : :bad), output
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module Overcommit::GitHook
2
+ class HamlSyntax < HookSpecificCheck
3
+ include HookRegistry
4
+ file_type :haml
5
+
6
+ def run_check
7
+ begin
8
+ require 'haml'
9
+ rescue LoadError
10
+ return :warn, "'haml' gem not installed -- run `gem install haml`"
11
+ end
12
+
13
+ staged.each do |path|
14
+ begin
15
+ Haml::Engine.new(File.read(path), :check_syntax => true)
16
+ rescue Haml::Error => e
17
+ return :bad, e.message
18
+ end
19
+ end
20
+ return :good, nil
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module Overcommit::GitHook
2
+ class JSConsoleLog < HookSpecificCheck
3
+ include HookRegistry
4
+ file_type :js
5
+
6
+ # https://www.pivotaltracker.com/story/show/18119495
7
+ def run_check
8
+ paths = staged.join(' ')
9
+ output = `grep -n -e 'console\\.log' #{paths}`.split("\n").reject do |line|
10
+ /^\d+:\s*\/\// =~ line || # Skip comments
11
+ /ALLOW_CONSOLE_LOG/ =~ line # and lines with ALLOW_CONSOLE_LOG
12
+ end.join("\n")
13
+ return (output.empty? ? :good : :bad), output
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ module Overcommit::GitHook
2
+ class JSSyntax < HookSpecificCheck
3
+ include HookRegistry
4
+ file_type :js
5
+
6
+ JS_HINT_PATH = Overcommit::Utils.script_path 'jshint.js'
7
+ JS_HINT_RUNNER_PATH = Overcommit::Utils.script_path 'jshint_runner.js'
8
+
9
+ def run_check
10
+ return :warn, "Rhino is not installed" unless in_path? 'rhino'
11
+
12
+ paths = staged.join(' ')
13
+
14
+ output = `rhino -strict -f #{JS_HINT_PATH} #{JS_HINT_RUNNER_PATH} #{paths} 2>&1 | grep -v warning | grep -v -e '^js: '`
15
+ return (output !~ /^ERROR/ ? :good : :bad), output
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ module Overcommit::GitHook
2
+ class RestrictedPaths < HookSpecificCheck
3
+ include HookRegistry
4
+ RESTRICTED_PATHS = %w[vendor]
5
+
6
+ def run_check
7
+ RESTRICTED_PATHS.each do |path|
8
+ if !system("git diff --cached --quiet -- #{path}")
9
+ return :stop, "changes staged under #{path}"
10
+ end
11
+ end
12
+ return :good
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ module Overcommit::GitHook
2
+ class RubySyntax < HookSpecificCheck
3
+ include HookRegistry
4
+ file_type :rb
5
+
6
+ def run_check
7
+ clean = true
8
+ output = []
9
+ staged.each do |staged|
10
+ syntax = `ruby -c #{staged} 2>&1`
11
+ unless $?.success?
12
+ output += syntax.lines.to_a
13
+ clean = false
14
+ end
15
+ end
16
+ return (clean ? :good : :bad), output
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module Overcommit::GitHook
2
+ class ScssLint < HookSpecificCheck
3
+ include HookRegistry
4
+ file_type :scss
5
+
6
+ def run_check
7
+ begin
8
+ require 'scss_lint'
9
+ rescue LoadError
10
+ return :warn, 'scss-lint not installed -- run `gem install scss-lint`'
11
+ end
12
+
13
+ paths = staged.join(' ')
14
+
15
+ output = `scss-lint #{paths} 2>&1`
16
+
17
+ return (output.empty? ? :good : :bad), output
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,58 @@
1
+ module Overcommit::GitHook
2
+ class TestHistory < HookSpecificCheck
3
+ include HookRegistry
4
+
5
+ def relevant_tests
6
+ @relevant_test ||=
7
+ `relevant-tests 2> /dev/null -- #{modified_files.join(' ')}`.
8
+ split("\n").map { |r| File.expand_path r }
9
+ end
10
+
11
+ def skip?
12
+ !FileTest.exist?('spec/support/record_results_formatter.rb')
13
+ end
14
+
15
+ TEST_RESULTS_FILE = '.spec-results'
16
+ def run_check
17
+ output = []
18
+ unless relevant_tests.any?
19
+ return :warn, 'No relevant tests for this change...write some?'
20
+ end
21
+
22
+ begin
23
+ good_tests = File.open(TEST_RESULTS_FILE, 'r').readlines.map do |spec_file|
24
+ File.expand_path spec_file.strip
25
+ end
26
+ rescue Errno::ENOENT
27
+ good_tests = []
28
+ end
29
+
30
+ unless good_tests.any?
31
+ return :bad,
32
+ 'The relevant tests for this change have not yet been run using `specr`'
33
+ end
34
+
35
+ missed_tests = (relevant_tests - good_tests)
36
+ unless missed_tests.empty?
37
+ output << 'The following relevant tests have not been run recently:'
38
+ output << missed_tests.sort
39
+ return :bad, output
40
+ end
41
+
42
+ # Find files modified after the tests were run
43
+ test_time = File.mtime(TEST_RESULTS_FILE)
44
+ untested_files = modified_files.reject do |file|
45
+ File.mtime(file) < test_time
46
+ end
47
+
48
+ unless untested_files.empty?
49
+ output << 'The following files were modified after `specr` was run.'
50
+ output << '(their associated tests may be broken):'
51
+ output << untested_files.sort
52
+ return :bad, output
53
+ end
54
+
55
+ return :good
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,19 @@
1
+ module Overcommit::GitHook
2
+ class Whitespace < HookSpecificCheck
3
+ include HookRegistry
4
+
5
+ def run_check
6
+ # Catches hard tabs
7
+ output = `grep -Inl "\t" #{staged.join(' ')}`
8
+ unless output.empty?
9
+ return :stop, "Don't use hard tabs:\n#{output}"
10
+ end
11
+
12
+ # Catches trailing whitespace, conflict markers etc
13
+ output = `git diff --check --cached`
14
+ return :stop, output unless $?.exitstatus.zero?
15
+
16
+ :good
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ require 'yaml'
2
+
3
+ module Overcommit::GitHook
4
+ class YamlSyntax < HookSpecificCheck
5
+ include HookRegistry
6
+ file_type :yml
7
+
8
+ def run_check
9
+ clean = true
10
+ output = []
11
+ staged.each do |path|
12
+ begin
13
+ YAML.load_file(path)
14
+ rescue ArgumentError => e
15
+ output << "#{e.message} parsing #{path}"
16
+ clean = false
17
+ end
18
+ end
19
+ return (clean ? :good : :bad), output
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,80 @@
1
+ module Overcommit
2
+ class Reporter
3
+ include ConsoleMethods
4
+
5
+ def initialize(name, checks)
6
+ @name = name
7
+ @checks = checks
8
+ @width = 70 - (@checks.map { |s| s.name.length }.max || 0)
9
+ @results = []
10
+ end
11
+
12
+ def with_status(check, &block)
13
+ title = " Checking #{check.name}..."
14
+ print title unless check.stealth?
15
+
16
+ status, output = yield
17
+
18
+ print_incremental_result(title, status, output, check.stealth?)
19
+ @results << status
20
+ end
21
+
22
+ def print_header
23
+ puts "Running #{@name} checks"
24
+ end
25
+
26
+ def print_result
27
+ puts
28
+ case final_result
29
+ when :good
30
+ success "+++ All #{@name} checks passed"
31
+ exit 0
32
+ when :bad
33
+ error "!!! One or more #{@name} checks failed"
34
+ exit 1
35
+ when :stop
36
+ warning "*** One or more #{@name} checks needs attention"
37
+ warning "*** If you really want to commit, use SKIP_CHECKS"
38
+ warning "*** (takes a space-separated list of checks to skip, or 'all')"
39
+ exit 1
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def print_incremental_result(title, status, output, stealth = false)
46
+ if stealth
47
+ return if status == :good
48
+ print title
49
+ end
50
+
51
+ print '.' * (@width - title.length)
52
+ case status
53
+ when :good
54
+ success 'OK'
55
+ when :bad
56
+ error 'FAILED'
57
+ print_report output
58
+ when :warn
59
+ warning output
60
+ when :stop
61
+ warning 'UH OH'
62
+ print_report output
63
+ else
64
+ error '???'
65
+ print_report "Check didn't return a status"
66
+ exit 1
67
+ end
68
+ end
69
+
70
+ def final_result
71
+ return :bad if @results.include?(:bad)
72
+ return :stop if @results.include?(:stop)
73
+ return :good
74
+ end
75
+
76
+ def print_report(*report)
77
+ puts report.flatten.map { |line| " #{line}" }.join("\n")
78
+ end
79
+ end
80
+ end