overcommit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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