overcommit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,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
+
@@ -0,0 +1,12 @@
1
+ ---
2
+ default:
3
+ excludes:
4
+ pre_commit:
5
+ - causes_email
6
+ - test_history
7
+ - restricted_paths
8
+ commit_msg:
9
+ - change_id
10
+ - release_note
11
+ all:
12
+ excludes:
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,7 @@
1
+ module Overcommit
2
+ class CommitMessageHook < GitHook::BaseHook
3
+ # No special behavior
4
+ end
5
+
6
+ Utils.register_hook(CommitMessageHook)
7
+ end
@@ -0,0 +1,9 @@
1
+ module Overcommit
2
+ class PreCommitHook < GitHook::BaseHook
3
+ def requires_modified_files?
4
+ true
5
+ end
6
+ end
7
+
8
+ Utils.register_hook(PreCommitHook)
9
+ 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