overcommit 0.5.0 → 0.6.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.
- checksums.yaml +4 -4
- data/bin/overcommit +3 -4
- data/config/default.yml +139 -0
- data/lib/overcommit.rb +7 -5
- data/lib/overcommit/cli.rb +59 -64
- data/lib/overcommit/configuration.rb +108 -34
- data/lib/overcommit/configuration_loader.rb +47 -0
- data/lib/overcommit/constants.rb +7 -0
- data/lib/overcommit/exceptions.rb +16 -0
- data/lib/overcommit/hook/base.rb +91 -0
- data/lib/overcommit/hook/commit_msg/base.rb +11 -0
- data/lib/overcommit/hook/commit_msg/gerrit_change_id.rb +18 -0
- data/lib/overcommit/{plugins → hook}/commit_msg/hard_tabs.rb +5 -6
- data/lib/overcommit/hook/commit_msg/russian_novel.rb +14 -0
- data/lib/overcommit/hook/commit_msg/single_line_subject.rb +12 -0
- data/lib/overcommit/hook/commit_msg/text_width.rb +20 -0
- data/lib/overcommit/hook/commit_msg/trailing_period.rb +12 -0
- data/lib/overcommit/hook/post_checkout/base.rb +11 -0
- data/lib/overcommit/hook/post_checkout/bundle_check.rb +29 -0
- data/lib/overcommit/hook/post_checkout/index_tags.rb +24 -0
- data/lib/overcommit/hook/pre_commit/author_email.rb +17 -0
- data/lib/overcommit/hook/pre_commit/author_name.rb +17 -0
- data/lib/overcommit/hook/pre_commit/base.rb +10 -0
- data/lib/overcommit/hook/pre_commit/bundle_check.rb +30 -0
- data/lib/overcommit/hook/pre_commit/coffee_lint.rb +14 -0
- data/lib/overcommit/hook/pre_commit/css_lint.rb +16 -0
- data/lib/overcommit/hook/pre_commit/haml_lint.rb +26 -0
- data/lib/overcommit/hook/pre_commit/hard_tabs.rb +16 -0
- data/lib/overcommit/hook/pre_commit/image_optim.rb +41 -0
- data/lib/overcommit/hook/pre_commit/js_hint.rb +15 -0
- data/lib/overcommit/hook/pre_commit/jscs.rb +31 -0
- data/lib/overcommit/hook/pre_commit/python_flake8.rb +14 -0
- data/lib/overcommit/hook/pre_commit/rubocop.rb +26 -0
- data/lib/overcommit/hook/pre_commit/scss_lint.rb +26 -0
- data/lib/overcommit/hook/pre_commit/trailing_whitespace.rb +15 -0
- data/lib/overcommit/hook/pre_commit/yaml_syntax.rb +22 -0
- data/lib/overcommit/hook_context.rb +16 -0
- data/lib/overcommit/hook_context/base.rb +68 -0
- data/lib/overcommit/hook_context/commit_msg.rb +32 -0
- data/lib/overcommit/hook_context/post_checkout.rb +24 -0
- data/lib/overcommit/hook_context/pre_commit.rb +96 -0
- data/lib/overcommit/hook_runner.rb +150 -0
- data/lib/overcommit/installer.rb +61 -68
- data/lib/overcommit/logger.rb +16 -13
- data/lib/overcommit/utils.rb +63 -38
- data/lib/overcommit/version.rb +1 -1
- data/{bin/scripts → libexec}/gerrit-change-id +0 -0
- data/{bin/scripts → libexec}/index-tags +1 -3
- data/template-dir/hooks/commit-msg +83 -0
- data/template-dir/hooks/overcommit-hook +83 -0
- data/template-dir/hooks/post-checkout +83 -0
- data/template-dir/hooks/pre-commit +83 -0
- metadata +76 -57
- data/bin/hooks/commit-msg +0 -8
- data/bin/hooks/post-checkout +0 -9
- data/bin/hooks/post-merge +0 -23
- data/bin/hooks/pre-commit +0 -8
- data/bin/hooks/prepare-commit-msg +0 -159
- data/bin/run-hook +0 -8
- data/bin/scripts/check-gemfile +0 -9
- data/bin/scripts/csslint-rhino.js +0 -9080
- data/bin/scripts/jshint.js +0 -5921
- data/bin/scripts/jshint_runner.js +0 -42
- data/lib/overcommit/errors.rb +0 -3
- data/lib/overcommit/git_hook.rb +0 -89
- data/lib/overcommit/hook_specific_check.rb +0 -110
- data/lib/overcommit/hooks/commit_msg.rb +0 -7
- data/lib/overcommit/hooks/pre_commit.rb +0 -9
- data/lib/overcommit/plugins/commit_msg/change_id.rb +0 -15
- data/lib/overcommit/plugins/commit_msg/release_note.rb +0 -25
- data/lib/overcommit/plugins/commit_msg/russian_novel.rb +0 -16
- data/lib/overcommit/plugins/commit_msg/single_line_subject.rb +0 -13
- data/lib/overcommit/plugins/commit_msg/text_width.rb +0 -20
- data/lib/overcommit/plugins/commit_msg/trailing_period.rb +0 -13
- data/lib/overcommit/plugins/pre_commit/author_name.rb +0 -16
- data/lib/overcommit/plugins/pre_commit/causes_email.rb +0 -15
- data/lib/overcommit/plugins/pre_commit/coffee_lint.rb +0 -16
- data/lib/overcommit/plugins/pre_commit/css_linter.rb +0 -17
- data/lib/overcommit/plugins/pre_commit/haml_style.rb +0 -34
- data/lib/overcommit/plugins/pre_commit/haml_syntax.rb +0 -24
- data/lib/overcommit/plugins/pre_commit/image_optimization.rb +0 -50
- data/lib/overcommit/plugins/pre_commit/js_console_log.rb +0 -16
- data/lib/overcommit/plugins/pre_commit/js_syntax.rb +0 -30
- data/lib/overcommit/plugins/pre_commit/python_flake8.rb +0 -15
- data/lib/overcommit/plugins/pre_commit/ruby_style.rb +0 -67
- data/lib/overcommit/plugins/pre_commit/ruby_syntax.rb +0 -19
- data/lib/overcommit/plugins/pre_commit/scss_lint.rb +0 -66
- data/lib/overcommit/plugins/pre_commit/test_history.rb +0 -58
- data/lib/overcommit/plugins/pre_commit/whitespace.rb +0 -21
- data/lib/overcommit/plugins/pre_commit/yaml_syntax.rb +0 -22
- data/lib/overcommit/reporter.rb +0 -90
- data/lib/overcommit/staged_file.rb +0 -86
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Overcommit
|
4
|
+
# Manages configuration file loading.
|
5
|
+
class ConfigurationLoader
|
6
|
+
DEFAULT_CONFIG_PATH = File.join(OVERCOMMIT_HOME, 'config', 'default.yml')
|
7
|
+
FILE_NAME = '.overcommit.yml'
|
8
|
+
|
9
|
+
def self.load_repo_config
|
10
|
+
overcommit_yml = File.join(Overcommit::Utils.repo_root, FILE_NAME)
|
11
|
+
|
12
|
+
if File.exists?(overcommit_yml)
|
13
|
+
load_file(overcommit_yml)
|
14
|
+
else
|
15
|
+
default_configuration
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.default_configuration
|
20
|
+
@default_config ||= load_from_file(DEFAULT_CONFIG_PATH)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Loads a configuration, ensuring it extends the default configuration.
|
26
|
+
def self.load_file(file)
|
27
|
+
config = load_from_file(file)
|
28
|
+
|
29
|
+
default_configuration.merge(config)
|
30
|
+
rescue => error
|
31
|
+
raise Overcommit::Exceptions::ConfigurationError,
|
32
|
+
"Unable to load configuration from '#{file}': #{error}",
|
33
|
+
error.backtrace
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.load_from_file(file)
|
37
|
+
hash =
|
38
|
+
if yaml = YAML.load_file(file)
|
39
|
+
yaml.to_hash
|
40
|
+
else
|
41
|
+
{}
|
42
|
+
end
|
43
|
+
|
44
|
+
Overcommit::Configuration.new(hash)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Overcommit::Exceptions
|
2
|
+
# Raised when a {Configuration} could not be loaded from a file.
|
3
|
+
class ConfigurationError < StandardError; end
|
4
|
+
|
5
|
+
# Raised when a hook could not be loaded by a {HookRunner}.
|
6
|
+
class HookLoadError < StandardError; end
|
7
|
+
|
8
|
+
# Raised when a {HookRunner} could not be loaded.
|
9
|
+
class HookContextLoadError < StandardError; end
|
10
|
+
|
11
|
+
# Raised when an installation target is not a valid git repository.
|
12
|
+
class InvalidGitRepo < StandardError; end
|
13
|
+
|
14
|
+
# Raised when an installation target already contains non-Overcommit hooks.
|
15
|
+
class PreExistingHooks < StandardError; end
|
16
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Overcommit::Hook
|
4
|
+
# Functionality common to all hooks.
|
5
|
+
class Base
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@context, :modified_files
|
9
|
+
|
10
|
+
def initialize(config, context)
|
11
|
+
@config = config.for_hook(self)
|
12
|
+
@context = context
|
13
|
+
end
|
14
|
+
|
15
|
+
# Runs the hook.
|
16
|
+
def run
|
17
|
+
raise NotImplementedError, 'Hook must define `run`'
|
18
|
+
end
|
19
|
+
|
20
|
+
def name
|
21
|
+
self.class.name.split('::').last
|
22
|
+
end
|
23
|
+
|
24
|
+
def description
|
25
|
+
@config['description'] || "Running #{name}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def required?
|
29
|
+
@config['required']
|
30
|
+
end
|
31
|
+
|
32
|
+
def quiet?
|
33
|
+
@config['quiet']
|
34
|
+
end
|
35
|
+
|
36
|
+
def enabled?
|
37
|
+
@config['enabled'] != false
|
38
|
+
end
|
39
|
+
|
40
|
+
def skip?
|
41
|
+
@config['skip']
|
42
|
+
end
|
43
|
+
|
44
|
+
def run?
|
45
|
+
enabled? &&
|
46
|
+
(!skip? || required?) &&
|
47
|
+
!(requires_modified_files? && applicable_files.empty?)
|
48
|
+
end
|
49
|
+
|
50
|
+
def in_path?(cmd)
|
51
|
+
Overcommit::Utils.in_path?(cmd)
|
52
|
+
end
|
53
|
+
|
54
|
+
def command(cmd)
|
55
|
+
Overcommit::Utils.command(cmd)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Gets a list of staged files that apply to this hook based on its
|
59
|
+
# configured `include` and `exclude` lists.
|
60
|
+
def applicable_files
|
61
|
+
@applicable_files ||= modified_files.select { |file| applicable_file?(file) }
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def requires_modified_files?
|
67
|
+
@config['requires_files']
|
68
|
+
end
|
69
|
+
|
70
|
+
def applicable_file?(file)
|
71
|
+
includes = Array(@config['include']).map { |glob| convert_glob_to_absolute(glob) }
|
72
|
+
included = includes.empty? ||
|
73
|
+
includes.any? { |glob| File.fnmatch(glob, file) }
|
74
|
+
|
75
|
+
excludes = Array(@config['exclude']).map { |glob| convert_glob_to_absolute(glob) }
|
76
|
+
excluded = excludes.any? { |glob| File.fnmatch(glob, file) }
|
77
|
+
|
78
|
+
included && !excluded
|
79
|
+
end
|
80
|
+
|
81
|
+
def convert_glob_to_absolute(glob)
|
82
|
+
repo_root = Overcommit::Utils.repo_root
|
83
|
+
|
84
|
+
if glob.start_with?('**')
|
85
|
+
repo_root + glob # Want ** to match items in the repo root as well
|
86
|
+
else
|
87
|
+
File.join(repo_root, glob)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Overcommit::Hook::CommitMsg
|
4
|
+
# Functionality common to all commit-msg hooks.
|
5
|
+
class Base < Overcommit::Hook::Base
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@context, :commit_message, :update_commit_message,
|
9
|
+
:commit_message_lines, :commit_message_file
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Overcommit::Hook::CommitMsg
|
2
|
+
# Ensures a Gerrit Change-Id line is included in the commit message.
|
3
|
+
#
|
4
|
+
# It may seem odd to do this here instead of in a prepare-commit-msg hook, but
|
5
|
+
# the reality is that if you want to _ensure_ the Change-Id is included then
|
6
|
+
# you need to do it in a commit-msg hook. This is because the user could still
|
7
|
+
# edit the message after a prepare-commit-msg hook was run.
|
8
|
+
class GerritChangeId < Base
|
9
|
+
def run
|
10
|
+
result = command("#{SCRIPT_LOCATION} #{commit_message_file}")
|
11
|
+
return (result.success? ? :good : :bad), result.stdout
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
SCRIPT_LOCATION = Overcommit::Utils.script_path('gerrit-change-id')
|
17
|
+
end
|
18
|
+
end
|
@@ -1,10 +1,9 @@
|
|
1
|
-
module Overcommit::
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
def run_check
|
1
|
+
module Overcommit::Hook::CommitMsg
|
2
|
+
# Checks for hard tabs in commit messages.
|
3
|
+
class HardTabs < Base
|
4
|
+
def run
|
6
5
|
# Catches hard tabs entered by the user (not auto-generated)
|
7
|
-
if commit_message.
|
6
|
+
if commit_message.index(/\t/)
|
8
7
|
return :warn, "Don't use hard tabs in commit messages"
|
9
8
|
end
|
10
9
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Overcommit::Hook::CommitMsg
|
2
|
+
# Checks for long commit messages (not good or bad--just fun to point out)
|
3
|
+
class RussianNovel < Base
|
4
|
+
RUSSIAN_NOVEL_LENGTH = 30
|
5
|
+
|
6
|
+
def run
|
7
|
+
if commit_message_lines.length >= RUSSIAN_NOVEL_LENGTH
|
8
|
+
return :warn, 'You seem to have authored a Russian novel; congratulations!'
|
9
|
+
end
|
10
|
+
|
11
|
+
:good
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Overcommit::Hook::CommitMsg
|
2
|
+
# Ensures commit message subject lines are followed by a blank line.
|
3
|
+
class SingleLineSubject < Base
|
4
|
+
def run
|
5
|
+
unless commit_message_lines[1].to_s.strip.empty?
|
6
|
+
return :warn, 'Subject should be one line and followed by a blank line'
|
7
|
+
end
|
8
|
+
|
9
|
+
:good
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Overcommit::Hook::CommitMsg
|
2
|
+
# Ensures the number of columns the subject and commit message lines occupy is
|
3
|
+
# under the preferred limits.
|
4
|
+
class TextWidth < Base
|
5
|
+
def run
|
6
|
+
if commit_message_lines.first.size > 60
|
7
|
+
return :warn, 'Please keep the subject < ~60 characters'
|
8
|
+
end
|
9
|
+
|
10
|
+
commit_message_lines.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,12 @@
|
|
1
|
+
module Overcommit::Hook::CommitMsg
|
2
|
+
# Ensures commit message subject lines do not have a trailing period
|
3
|
+
class TrailingPeriod < Base
|
4
|
+
def run
|
5
|
+
if commit_message_lines.first.rstrip.end_with?('.')
|
6
|
+
return :warn, 'Please omit trailing period from commit message subject'
|
7
|
+
end
|
8
|
+
|
9
|
+
:good
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Overcommit::Hook::PostCheckout
|
4
|
+
# Functionality common to all post-checkout hooks.
|
5
|
+
class Base < Overcommit::Hook::Base
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@context,
|
9
|
+
:previous_head, :new_head, :branch_checkout?, :file_checkout?
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Overcommit::Hook::PostCheckout
|
2
|
+
# If Gemfile dependencies were modified since HEAD was changed, check if
|
3
|
+
# currently installed gems satisfy the dependencies.
|
4
|
+
class BundleCheck < Base
|
5
|
+
def run
|
6
|
+
unless in_path?('bundle')
|
7
|
+
return :warn, 'bundler not installed -- run `gem install bundler`'
|
8
|
+
end
|
9
|
+
|
10
|
+
return :bad if dependencies_changed? && !dependencies_satisfied?
|
11
|
+
|
12
|
+
:good
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def dependencies_changed?
|
18
|
+
result = command("git diff --exit-code #{new_head} #{previous_head} --name-only")
|
19
|
+
|
20
|
+
result.stdout.split("\n").any? do |file|
|
21
|
+
Array(@config['include']).any? { |glob| File.fnmatch(glob, file) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def dependencies_satisfied?
|
26
|
+
command('bundle check').success?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Overcommit::Hook::PostCheckout
|
2
|
+
# Scans source code each time HEAD changes to generate an up-to-date index of
|
3
|
+
# all function/variable definitions, etc.
|
4
|
+
class IndexTags < Base
|
5
|
+
def run
|
6
|
+
unless in_path?('ctags')
|
7
|
+
return :good # Silently ignore
|
8
|
+
end
|
9
|
+
|
10
|
+
index_tags_in_background
|
11
|
+
|
12
|
+
:good
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
SCRIPT_LOCATION = Overcommit::Utils.script_path('index-tags')
|
18
|
+
|
19
|
+
def index_tags_in_background
|
20
|
+
# TODO: come up with Ruby 1.8-friendly way to do this
|
21
|
+
Process.detach(Process.spawn(SCRIPT_LOCATION))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Overcommit::Hook::PreCommit
|
2
|
+
# Checks the format of an author's email address.
|
3
|
+
class AuthorEmail < Base
|
4
|
+
def run
|
5
|
+
result = command('git config --get user.email')
|
6
|
+
email = result.stdout.chomp
|
7
|
+
|
8
|
+
unless email =~ /#{@config['pattern']}/
|
9
|
+
return :bad, "Author has an invalid email address: '#{email}'\n" <<
|
10
|
+
'Set your email with ' <<
|
11
|
+
'`git config --global user.email your_email@example.com`'
|
12
|
+
end
|
13
|
+
|
14
|
+
:good
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Overcommit::Hook::PreCommit
|
2
|
+
# Ensures that a commit author has a name with at least first and last names.
|
3
|
+
class AuthorName < Base
|
4
|
+
def run
|
5
|
+
result = command('git config --get user.name')
|
6
|
+
name = result.stdout.chomp
|
7
|
+
|
8
|
+
unless name.split(' ').count >= 2
|
9
|
+
return :bad, 'Author must have at least first and last name, but ' <<
|
10
|
+
"was: '#{name}'.\nSet your name with " <<
|
11
|
+
"`git config --global user.name 'Your Name'`"
|
12
|
+
end
|
13
|
+
|
14
|
+
:good
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Overcommit::Hook::PreCommit
|
2
|
+
# Check if local Gemfile.lock matches Gemfile when either changes, unless
|
3
|
+
# Gemfile.lock is ignored by git.
|
4
|
+
class BundleCheck < Base
|
5
|
+
def run
|
6
|
+
unless in_path?('bundle')
|
7
|
+
return :warn, 'bundler not installed -- run `gem install bundler`'
|
8
|
+
end
|
9
|
+
|
10
|
+
# Ignore if Gemfile.lock is not tracked by git
|
11
|
+
return :good if command("git check-ignore #{LOCK_FILE}").success?
|
12
|
+
|
13
|
+
result = command('bundle check')
|
14
|
+
unless result.success?
|
15
|
+
return :bad, result.stdout
|
16
|
+
end
|
17
|
+
|
18
|
+
result = command("git diff --quiet -- #{LOCK_FILE}")
|
19
|
+
unless result.success?
|
20
|
+
return :bad, "#{LOCK_FILE} is not up-to-date -- run `bundle check`"
|
21
|
+
end
|
22
|
+
|
23
|
+
:good
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
LOCK_FILE = 'Gemfile.lock'
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Overcommit::Hook::PreCommit
|
2
|
+
# Runs `coffeelint` against any modified CoffeeScript files.
|
3
|
+
class CoffeeLint < Base
|
4
|
+
def run
|
5
|
+
unless in_path?('coffeelint')
|
6
|
+
return :warn, 'Run `npm install -g coffeelint`'
|
7
|
+
end
|
8
|
+
|
9
|
+
result = command("coffeelint --quiet #{applicable_files.join(' ')}")
|
10
|
+
return :good if result.success?
|
11
|
+
return :bad, result.stdout
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|