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,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
|