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