overcommit 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/hooks/commit-msg +8 -0
- data/bin/hooks/post-checkout +8 -0
- data/bin/hooks/post-merge +23 -0
- data/bin/hooks/pre-commit +8 -0
- data/bin/hooks/prepare-commit-msg +160 -0
- data/bin/overcommit +11 -0
- data/bin/run-hook +8 -0
- data/bin/scripts/csslint-rhino.js +9080 -0
- data/bin/scripts/gerrit-change-id +174 -0
- data/bin/scripts/index-tags +19 -0
- data/bin/scripts/jshint.js +5921 -0
- data/bin/scripts/jshint_runner.js +48 -0
- data/config/templates.yml +12 -0
- data/lib/overcommit.rb +8 -0
- data/lib/overcommit/cli.rb +95 -0
- data/lib/overcommit/configuration.rb +58 -0
- data/lib/overcommit/console_methods.rb +23 -0
- data/lib/overcommit/git_hook.rb +50 -0
- data/lib/overcommit/hook_specific_check.rb +81 -0
- data/lib/overcommit/hooks/commit_msg.rb +7 -0
- data/lib/overcommit/hooks/pre_commit.rb +9 -0
- data/lib/overcommit/installer.rb +59 -0
- data/lib/overcommit/plugins/commit_msg/change_id.rb +14 -0
- data/lib/overcommit/plugins/commit_msg/release_note.rb +14 -0
- data/lib/overcommit/plugins/commit_msg/russian_novel.rb +16 -0
- data/lib/overcommit/plugins/commit_msg/text_width.rb +20 -0
- data/lib/overcommit/plugins/commit_msg/trailing_period.rb +13 -0
- data/lib/overcommit/plugins/pre_commit/author_name.rb +16 -0
- data/lib/overcommit/plugins/pre_commit/causes_email.rb +15 -0
- data/lib/overcommit/plugins/pre_commit/css_linter.rb +17 -0
- data/lib/overcommit/plugins/pre_commit/erb_syntax.rb +21 -0
- data/lib/overcommit/plugins/pre_commit/haml_syntax.rb +23 -0
- data/lib/overcommit/plugins/pre_commit/js_console_log.rb +16 -0
- data/lib/overcommit/plugins/pre_commit/js_syntax.rb +18 -0
- data/lib/overcommit/plugins/pre_commit/restricted_paths.rb +15 -0
- data/lib/overcommit/plugins/pre_commit/ruby_syntax.rb +19 -0
- data/lib/overcommit/plugins/pre_commit/scss_lint.rb +20 -0
- data/lib/overcommit/plugins/pre_commit/test_history.rb +58 -0
- data/lib/overcommit/plugins/pre_commit/whitespace.rb +19 -0
- data/lib/overcommit/plugins/pre_commit/yaml_syntax.rb +22 -0
- data/lib/overcommit/reporter.rb +80 -0
- data/lib/overcommit/utils.rb +60 -0
- data/lib/overcommit/version.rb +3 -0
- 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
|