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.
- 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,48 @@
|
|
1
|
+
/**
|
2
|
+
* @author kamil@causes.com
|
3
|
+
*
|
4
|
+
* This is a script to run jshint against files passed in via
|
5
|
+
* command line arguments
|
6
|
+
**/
|
7
|
+
|
8
|
+
if (typeof JSHINT === "undefined") {
|
9
|
+
print("jshint not available. Make sure it was included properly.");
|
10
|
+
quit();
|
11
|
+
}
|
12
|
+
|
13
|
+
// Options:
|
14
|
+
// https://gist.github.com/1489652
|
15
|
+
|
16
|
+
var i, j, file, source, result, error,
|
17
|
+
options = {
|
18
|
+
boss: true,
|
19
|
+
curly: true,
|
20
|
+
eqeqeq: true,
|
21
|
+
forin: true,
|
22
|
+
newcap: true
|
23
|
+
};
|
24
|
+
|
25
|
+
for (i = 0; i < arguments.length; i++) {
|
26
|
+
|
27
|
+
file = arguments[i];
|
28
|
+
source = readFile(file);
|
29
|
+
result = JSHINT(source, options);
|
30
|
+
|
31
|
+
if (result === true) {
|
32
|
+
print(arguments[i] + " -- OK");
|
33
|
+
}
|
34
|
+
else {
|
35
|
+
for (j = 0; j < JSHINT.errors.length; j++) {
|
36
|
+
error = JSHINT.errors[j];
|
37
|
+
|
38
|
+
print("ERROR in " + file + " at " + error.line + ":" + error.character);
|
39
|
+
print("");
|
40
|
+
print("\t" + error.reason);
|
41
|
+
print("");
|
42
|
+
print("\t" + error.evidence);
|
43
|
+
print("");
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
|
data/lib/overcommit.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'overcommit/configuration'
|
2
|
+
require 'overcommit/console_methods'
|
3
|
+
require 'overcommit/git_hook'
|
4
|
+
require 'overcommit/hook_specific_check'
|
5
|
+
require 'overcommit/installer'
|
6
|
+
require 'overcommit/reporter'
|
7
|
+
require 'overcommit/utils'
|
8
|
+
require 'overcommit/version'
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Overcommit
|
4
|
+
class CLI
|
5
|
+
def initialize(arguments = [])
|
6
|
+
@arguments = arguments
|
7
|
+
@options = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse_arguments
|
11
|
+
parser = OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: #{opts.program_name} [options] target"
|
13
|
+
|
14
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
15
|
+
print_help opts.help
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on_tail('-v', '--version', 'Show version') do
|
19
|
+
puts VERSION
|
20
|
+
exit 0
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on('-a', '--all', 'Include all git hooks') do
|
24
|
+
@options[:template] = 'all'
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('-t', '--template template',
|
28
|
+
'Specify a template of hooks') do |template|
|
29
|
+
@options[:template] = template
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('-e', '--exclude hook_name,...', Array,
|
33
|
+
'Exclude hooks from installation') do |excludes|
|
34
|
+
# Transform from:
|
35
|
+
#
|
36
|
+
# pre_commit/test_history,commit_msg/change_id
|
37
|
+
#
|
38
|
+
# Into:
|
39
|
+
#
|
40
|
+
# {
|
41
|
+
# 'commit_msg' => ['change_id'],
|
42
|
+
# 'pre_commit' => ['test_history']
|
43
|
+
# }
|
44
|
+
@options[:excludes] = excludes.inject({}) do |memo, exclude|
|
45
|
+
parts = exclude.split(%r{[:/.]})
|
46
|
+
next memo unless parts.size == 2
|
47
|
+
|
48
|
+
memo[parts.first] ||= []
|
49
|
+
memo[parts.first] << parts.last
|
50
|
+
|
51
|
+
memo
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
begin
|
57
|
+
parser.parse!(@arguments)
|
58
|
+
|
59
|
+
# Unconsumed arguments are our targets
|
60
|
+
@options[:targets] = @arguments
|
61
|
+
rescue OptionParser::InvalidOption => ex
|
62
|
+
print_help parser.help, ex
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def run
|
67
|
+
if @options[:targets].nil? || @options[:targets].empty?
|
68
|
+
puts 'You must supply at least one directory to install into.'
|
69
|
+
puts 'For example:', ''
|
70
|
+
puts " #{File.basename($0)} <target directory>"
|
71
|
+
exit 2
|
72
|
+
end
|
73
|
+
|
74
|
+
installer = Installer.new(@options)
|
75
|
+
|
76
|
+
@options[:targets].each do |target|
|
77
|
+
installer.install(target)
|
78
|
+
end
|
79
|
+
|
80
|
+
puts 'Installation complete.'
|
81
|
+
|
82
|
+
rescue ArgumentError => ex
|
83
|
+
puts "Installation failed: #{ex}"
|
84
|
+
exit 3
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def print_help(message, ex = nil)
|
90
|
+
puts ex, '' if ex
|
91
|
+
puts message
|
92
|
+
exit 0
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Overcommit
|
5
|
+
class Configuration
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
attr_reader :templates
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@templates = YAML.load_file(Utils.absolute_path('config/templates.yml'))
|
12
|
+
end
|
13
|
+
|
14
|
+
# Read the repo-specific 'overcommit.yml' file to determine what behavior
|
15
|
+
# the user wants.
|
16
|
+
def repo_settings
|
17
|
+
config_file = Utils.repo_path('.git/hooks/overcommit.yml')
|
18
|
+
|
19
|
+
File.exist?(config_file) ? YAML.load_file(config_file) : {}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Given the current configuration, return a set of paths which should be
|
23
|
+
# loaded as plugins (`require`d)
|
24
|
+
def desired_plugins
|
25
|
+
excludes = repo_settings['excludes']
|
26
|
+
skip_checks = ENV.fetch('SKIP_CHECKS', '').split(/[:, ]/)
|
27
|
+
|
28
|
+
return [] if skip_checks.include? 'all'
|
29
|
+
|
30
|
+
plugin_directories.map do |dir|
|
31
|
+
Dir[File.join(dir, Utils.hook_name, '*.rb')].map do |plugin|
|
32
|
+
basename = File.basename(plugin, '.rb')
|
33
|
+
if !skip_checks.include?(basename) &&
|
34
|
+
!(excludes[Utils.hook_name] || []).include?(basename)
|
35
|
+
plugin
|
36
|
+
end
|
37
|
+
end.compact
|
38
|
+
end.flatten
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def plugin_directories
|
44
|
+
# Start with the base plugins provided by the gem
|
45
|
+
plugin_dirs = [File.expand_path('../plugins', __FILE__)]
|
46
|
+
repo_specific = Utils.repo_path('.githooks')
|
47
|
+
|
48
|
+
# Add on any repo-specific checks
|
49
|
+
plugin_dirs << repo_specific if File.directory?(repo_specific)
|
50
|
+
|
51
|
+
plugin_dirs
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.config
|
56
|
+
Configuration.instance
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Overcommit
|
2
|
+
module ConsoleMethods
|
3
|
+
def bold(str)
|
4
|
+
puts "\033[1;37m#{str}\033[0m"
|
5
|
+
end
|
6
|
+
|
7
|
+
def error(str)
|
8
|
+
puts "\033[31m#{str}\033[0m"
|
9
|
+
end
|
10
|
+
|
11
|
+
def success(str)
|
12
|
+
puts "\033[32m#{str}\033[0m"
|
13
|
+
end
|
14
|
+
|
15
|
+
def warning(str)
|
16
|
+
puts "\033[33m#{str}\033[0m"
|
17
|
+
end
|
18
|
+
|
19
|
+
def notice(str)
|
20
|
+
puts "\033[1;33m#{str}\033[0m"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Overcommit
|
2
|
+
module GitHook
|
3
|
+
class BaseHook
|
4
|
+
include ConsoleMethods
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
Overcommit.config.desired_plugins.each do |plugin|
|
8
|
+
require plugin
|
9
|
+
end
|
10
|
+
rescue NameError => ex
|
11
|
+
error "Couldn't load plugin: #{ex}"
|
12
|
+
exit 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def run(*args)
|
16
|
+
# Support 'bare' installation where we don't have any hooks yet.
|
17
|
+
# Silently pass.
|
18
|
+
exit unless (checks = HookRegistry.checks) && checks.any?
|
19
|
+
|
20
|
+
exit if requires_modified_files? && Utils.modified_files.empty?
|
21
|
+
|
22
|
+
reporter = Reporter.new(Overcommit::Utils.hook_name, checks)
|
23
|
+
|
24
|
+
reporter.print_header
|
25
|
+
|
26
|
+
checks.each do |check_class|
|
27
|
+
check = check_class.new(*args)
|
28
|
+
next if check.skip?
|
29
|
+
|
30
|
+
# Ignore a check if it only applies to a specific file type and there
|
31
|
+
# are no staged files of that type in the tree
|
32
|
+
next if check_class.filetype && check.staged.empty?
|
33
|
+
|
34
|
+
reporter.with_status(check) do
|
35
|
+
check.run_check
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
reporter.print_result
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# If true, only run this check when there are modified files.
|
45
|
+
def requires_modified_files?
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Overcommit
|
2
|
+
module GitHook
|
3
|
+
module HookRegistry
|
4
|
+
@checks = []
|
5
|
+
class << self
|
6
|
+
attr_reader :checks
|
7
|
+
def included(base)
|
8
|
+
@checks << base
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class HookSpecificCheck
|
14
|
+
class << self
|
15
|
+
attr_accessor :filetype
|
16
|
+
attr_accessor :stealth
|
17
|
+
|
18
|
+
def stealth!
|
19
|
+
self.stealth = true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(*args)
|
24
|
+
@arguments = args
|
25
|
+
end
|
26
|
+
|
27
|
+
def name
|
28
|
+
Overcommit::Utils.underscorize self.class.name.to_s.split('::').last
|
29
|
+
end
|
30
|
+
|
31
|
+
def skip?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def stealth?
|
36
|
+
self.class.stealth
|
37
|
+
end
|
38
|
+
|
39
|
+
def staged
|
40
|
+
@staged ||= Utils.modified_files.select do |filename|
|
41
|
+
filename.end_with?(".#{self.class.filetype}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def run_check
|
46
|
+
[:bad, 'No checks defined!']
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def modified_files
|
52
|
+
Overcommit::Utils.modified_files
|
53
|
+
end
|
54
|
+
|
55
|
+
def in_path?(cmd)
|
56
|
+
system("which #{cmd} &> /dev/null")
|
57
|
+
end
|
58
|
+
|
59
|
+
def commit_message
|
60
|
+
@commit_message ||= begin
|
61
|
+
unless @arguments[0] && ::File.exist?(@arguments[0])
|
62
|
+
fail 'Not running in the context of a commit message'
|
63
|
+
end
|
64
|
+
|
65
|
+
File.readlines(@arguments[0])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Strip comments and diff (from git-commit --verbose)
|
70
|
+
def user_commit_message
|
71
|
+
@user_commit_message ||= commit_message.
|
72
|
+
reject { |line| line =~ /^#/ }.
|
73
|
+
take_while { |line| !line.start_with?('diff --git') }
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.file_type(type)
|
77
|
+
self.filetype = type
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Overcommit
|
5
|
+
class Installer
|
6
|
+
def initialize(options = {})
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def install(target)
|
11
|
+
absolute_target = File.expand_path(target)
|
12
|
+
unless File.directory?(File.join(absolute_target, '.git'))
|
13
|
+
|
14
|
+
raise ArgumentError, "#{target} does not appear to be a git repository"
|
15
|
+
end
|
16
|
+
|
17
|
+
puts "Installing hooks into #{target}"
|
18
|
+
hook_path = File.join(absolute_target, '.git/hooks')
|
19
|
+
|
20
|
+
install_scripts(hook_path)
|
21
|
+
install_hooks(hook_path)
|
22
|
+
write_configuration(hook_path)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Make helper scripts available locally inside the repo
|
28
|
+
def install_scripts(target)
|
29
|
+
FileUtils.cp_r Utils.absolute_path('bin/scripts'), target
|
30
|
+
end
|
31
|
+
|
32
|
+
# Install all available git hooks into the repo
|
33
|
+
def install_hooks(target)
|
34
|
+
Dir[Utils.absolute_path('bin/hooks/*')].each do |hook|
|
35
|
+
FileUtils.cp hook, File.join(target, File.basename(hook))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Dump a YAML document containing requested configuration
|
40
|
+
def write_configuration(target)
|
41
|
+
template = @options.fetch(:template, 'default')
|
42
|
+
base_config = Overcommit.config.templates[template]
|
43
|
+
if base_config.nil?
|
44
|
+
raise ArgumentError, "No such template '#{template}'"
|
45
|
+
end
|
46
|
+
|
47
|
+
base_config = base_config.dup
|
48
|
+
(base_config['excludes'] ||= {}).
|
49
|
+
merge!(@options[:excludes] || {}) do |_, a, b|
|
50
|
+
# Concat the arrays together
|
51
|
+
a + b
|
52
|
+
end
|
53
|
+
|
54
|
+
File.open(File.join(target, 'overcommit.yml'), 'w') do |config|
|
55
|
+
YAML.dump(base_config, config)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|