rabbitt-githooks 1.2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +34 -0
  3. data/.rubocop.yml +73 -0
  4. data/Gemfile +21 -0
  5. data/Gemfile.lock +45 -0
  6. data/LICENSE.txt +339 -0
  7. data/README.md +4 -0
  8. data/Rakefile +19 -0
  9. data/bin/githooks +15 -0
  10. data/bin/githooks-runner +17 -0
  11. data/hooks/commit-messages.rb +29 -0
  12. data/hooks/formatting.rb +53 -0
  13. data/lib/githooks.rb +89 -0
  14. data/lib/githooks/action.rb +150 -0
  15. data/lib/githooks/cli.rb +93 -0
  16. data/lib/githooks/commands/config.rb +107 -0
  17. data/lib/githooks/core_ext.rb +23 -0
  18. data/lib/githooks/core_ext/array.rb +3 -0
  19. data/lib/githooks/core_ext/array/extract_options.rb +5 -0
  20. data/lib/githooks/core_ext/array/min_max.rb +37 -0
  21. data/lib/githooks/core_ext/array/select_with_index.rb +13 -0
  22. data/lib/githooks/core_ext/numbers.rb +1 -0
  23. data/lib/githooks/core_ext/numbers/infinity.rb +19 -0
  24. data/lib/githooks/core_ext/pathname.rb +27 -0
  25. data/lib/githooks/core_ext/process.rb +7 -0
  26. data/lib/githooks/core_ext/string.rb +3 -0
  27. data/lib/githooks/core_ext/string/git_option_path_split.rb +6 -0
  28. data/lib/githooks/core_ext/string/inflections.rb +67 -0
  29. data/lib/githooks/core_ext/string/strip_empty_lines.rb +9 -0
  30. data/lib/githooks/error.rb +10 -0
  31. data/lib/githooks/hook.rb +159 -0
  32. data/lib/githooks/repository.rb +152 -0
  33. data/lib/githooks/repository/config.rb +170 -0
  34. data/lib/githooks/repository/diff_index_entry.rb +80 -0
  35. data/lib/githooks/repository/file.rb +125 -0
  36. data/lib/githooks/repository/limiter.rb +55 -0
  37. data/lib/githooks/runner.rb +317 -0
  38. data/lib/githooks/section.rb +98 -0
  39. data/lib/githooks/system_utils.rb +109 -0
  40. data/lib/githooks/terminal_colors.rb +63 -0
  41. data/lib/githooks/version.rb +22 -0
  42. data/rabbitt-githooks.gemspec +49 -0
  43. data/thoughts.txt +56 -0
  44. metadata +175 -0
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ githooks
2
+ ========
3
+
4
+ GitHooks Gem
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ =begin
2
+ Copyright (C) 2013 Carl P. Corliss
3
+
4
+ This program is free software; you can redistribute it and/or modify
5
+ it under the terms of the GNU General Public License as published by
6
+ the Free Software Foundation; either version 2 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License along
15
+ with this program; if not, write to the Free Software Foundation, Inc.,
16
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
+ =end
18
+
19
+ require 'bundler/gem_tasks'
data/bin/githooks ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ENV['GITHOOKS_DEV']
4
+ $:.unshift(File.expand_path(File.join(File.expand_path(__FILE__), '/../../lib')))
5
+ end
6
+ require 'githooks'
7
+
8
+ begin
9
+ GitHooks::CLI::Base.start(ARGV)
10
+ rescue GitHooks::Error => e
11
+ puts e.message
12
+ exit 1
13
+ end
14
+
15
+ exit 0
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ENV['GITHOOKS_DEV']
4
+ $:.unshift(File.expand_path(File.join(File.expand_path(__FILE__), '/../../lib')))
5
+ end
6
+ require 'githooks'
7
+
8
+ begin
9
+ GitHooks::Runner.run(
10
+ 'args' => ARGV,
11
+ 'hook' => ENV['GITHOOKS_HOOK']
12
+ )
13
+ rescue GitHooks::Error => e
14
+ puts e.message
15
+ exit 1
16
+ end
17
+ exit 0
@@ -0,0 +1,29 @@
1
+ require 'githooks'
2
+
3
+ SIMPLE_MESSAGES = /^\s*(blah|\.+|foo|bar|baz|nits?|)\s*$/
4
+
5
+ GitHooks::Hook.register 'commit-msg' do
6
+ section 'Commit Message' do
7
+ action 'Message Length > 5 characters' do
8
+ on_argv do |args|
9
+ if args.empty?
10
+ $stderr.puts 'No commit message file passed in - are we executing in the commit-msg phase??'
11
+ return false
12
+ end
13
+
14
+ IO.read(args.first).size > 5 unless args.empty?
15
+ end
16
+ end
17
+
18
+ action 'Verify no simple commit messages' do
19
+ on_argv do |args|
20
+ if args.empty?
21
+ $stderr.puts 'No commit message file passed in - are we executing in the commit-msg phase??'
22
+ return false
23
+ end
24
+ # make sure there is at least one line that isn't a simple message
25
+ IO.read(args.first).split(/\n/).any? { |line| line !~ SIMPLE_MESSAGES }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,53 @@
1
+ require 'githooks'
2
+
3
+ RUBY_FILE_REGEXP = %r{^((app|lib)/.+\.rb|bin/.+)$}.freeze
4
+
5
+ GitHooks::Hook.register 'pre-commit' do
6
+ commands 'scss-lint', :ruby, :rubocop
7
+
8
+ section 'Standards' do
9
+ action 'Validate Ruby Syntax' do
10
+ limit(:type).to :modified, :added, :untracked
11
+ limit(:path).to RUBY_FILE_REGEXP
12
+
13
+ on_each_file do |file|
14
+ ruby '-c', file.path, prefix_output: file.path
15
+ end
16
+ end
17
+
18
+ action 'Validate Ruby Standards' do
19
+ limit(:type).to :modified, :added, :untracked
20
+ limit(:path).to RUBY_FILE_REGEXP
21
+
22
+ on_all_files do |files|
23
+ rubocop '-D', '--format', 'clang', files, strip_empty_lines: true
24
+ end
25
+ end
26
+
27
+ action 'No Leading Tabs in Ruby files' do
28
+ limit(:type).to :modified, :added, :untracked
29
+ limit(:path).to RUBY_FILE_REGEXP
30
+
31
+ on_each_file do |file|
32
+ file.grep(/^[ ]*(\t+)/).tap do |matches|
33
+ matches.each do |line_number, line_text|
34
+ line_text.gsub!(/^[ ]*(\t+)/) do
35
+ underscores = '_' * $1.size
36
+ color_bright_red(underscores)
37
+ end
38
+ $stderr.printf "%s:%#{matches.last.first.to_s.size}d: %s\n", file.path, line_number, line_text
39
+ end
40
+ end.empty?
41
+ end
42
+ end
43
+
44
+ action 'Validate CSS Syntax' do
45
+ limit(:type).to :modified, :added, :untracked
46
+ limit(:path).to %r{^(app|lib)/.+css$}
47
+
48
+ on_all_files do |files|
49
+ scss_lint files.collect(&:path)
50
+ end
51
+ end
52
+ end
53
+ end
data/lib/githooks.rb ADDED
@@ -0,0 +1,89 @@
1
+ # encoding: utf-8
2
+ =begin
3
+ Copyright (C) 2013 Carl P. Corliss
4
+
5
+ This program is free software; you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation; either version 2 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License along
16
+ with this program; if not, write to the Free Software Foundation, Inc.,
17
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
+ =end
19
+
20
+ require 'pathname'
21
+ require 'githooks/error'
22
+ require 'githooks/core_ext'
23
+ require 'githooks/version'
24
+
25
+ module GitHooks
26
+ AUTHOR = 'Carl P. Corliss <rabbitt@gmail.com>'
27
+
28
+ autoload :Config, 'githooks/config'
29
+ autoload :CommandRunner, 'githooks/command'
30
+ autoload :Command, 'githooks/command'
31
+ autoload :CLI, 'githooks/cli'
32
+ autoload :Hook, 'githooks/hook'
33
+ autoload :Section, 'githooks/section'
34
+ autoload :Action, 'githooks/action'
35
+ autoload :Repository, 'githooks/repository'
36
+ autoload :Runner, 'githooks/runner'
37
+ autoload :SystemUtils, 'githooks/system_utils'
38
+ autoload :TerminalColors, 'githooks/terminal_colors'
39
+
40
+ class << self
41
+ attr_reader :debug, :verbose, :ignore_script
42
+
43
+ def debug?
44
+ return true if ENV['GITHOOKS_DEBUG']
45
+ return true if ARGV.include?('--debug')
46
+ return true if ARGV.include?('-d')
47
+ debug
48
+ end
49
+
50
+ def debug=(value)
51
+ @debug = !!value
52
+ end
53
+
54
+ def verbose?
55
+ return true if ENV['GITHOOKS_VERBOSE']
56
+ return true if ARGV.include?('--verbose')
57
+ return true if ARGV.include?('-v')
58
+ verbose
59
+ end
60
+
61
+ def verbose=(value)
62
+ @verbose = !!value
63
+ end
64
+
65
+ def ignore_script=(value)
66
+ @ignore_script = !!value
67
+ end
68
+
69
+ def hook_name
70
+ case GitHooks::HOOK_NAME.to_s
71
+ when 'githooks', 'irb', '', nil then 'pre-commit'
72
+ else GitHooks::HOOK_NAME
73
+ end
74
+ end
75
+ end
76
+
77
+ LIB_PATH = Pathname.new(__FILE__).dirname.realpath
78
+ GEM_PATH = LIB_PATH.parent
79
+ BIN_PATH = GEM_PATH + 'bin'
80
+
81
+ SCRIPT_PATH = Pathname.new($0)
82
+ SCRIPT_NAME = SCRIPT_PATH.basename.to_s
83
+ HOOK_NAME = SCRIPT_NAME.to_s
84
+
85
+ if ARGV.include? '--ignore-script'
86
+ ARGV.delete('--ignore-script')
87
+ self.ignore_script = true
88
+ end
89
+ end
@@ -0,0 +1,150 @@
1
+ # encoding: utf-8
2
+ =begin
3
+ Copyright (C) 2013 Carl P. Corliss
4
+
5
+ This program is free software; you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation; either version 2 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License along
16
+ with this program; if not, write to the Free Software Foundation, Inc.,
17
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
+ =end
19
+
20
+ require 'stringio'
21
+ require 'open3'
22
+
23
+ module GitHooks
24
+ class Action
25
+ include TerminalColors
26
+
27
+ attr_reader :title, :section, :on, :limiters, :success, :errors, :warnings
28
+ private :section, :on
29
+ alias_method :success?, :success
30
+
31
+ def initialize(title, section, &block)
32
+ fail ArgumentError, 'Missing required block' unless block_given?
33
+
34
+ @title = title
35
+ @section = section
36
+ @on = nil
37
+ @limiters = []
38
+ @success = true
39
+ @errors = []
40
+ @warnings = []
41
+
42
+ instance_eval(&block)
43
+
44
+ waiting!
45
+ end
46
+
47
+ def manifest
48
+ @manifest ||= section.hook.manifest.filter(@limiters)
49
+ end
50
+
51
+ def colored_title
52
+ status_colorize title
53
+ end
54
+
55
+ def state_symbol
56
+ return status_colorize('?') unless finished?
57
+ status_colorize success? ? '✓' : 'X'
58
+ end
59
+
60
+ %w(finished running waiting).each do |method|
61
+ define_method(:"#{method}?") { @status == method.to_sym }
62
+ define_method(:"#{method}!") { @status = method.to_sym }
63
+ end
64
+
65
+ def status_colorize(text)
66
+ return color_dark_cyan(text) unless finished?
67
+ success? ? color_bright_green(text) : color_bright_red(text)
68
+ end
69
+
70
+ def run # rubocop:disable MethodLength
71
+ warnings, errors = StringIO.new, StringIO.new
72
+
73
+ begin
74
+ running!
75
+ $stdout, $stderr = warnings, errors
76
+ @success &= @on.call
77
+ rescue => error
78
+ errors.puts "Exception thrown during action call: #{error.class.name}: #{error.message}"
79
+
80
+ if !GitHooks.debug?
81
+ hooks_files = error.backtrace.select! { |line| line =~ %r{/hooks/} }
82
+ hooks_files.collect! { |line| line.split(':')[0..1].join(':') }
83
+ errors.puts " -> in hook file:line, #{hooks_files.join("\n\t")}" unless hooks_files.empty?
84
+ else
85
+ errors.puts "\t#{error.backtrace.join("\n\t")}"
86
+ end
87
+
88
+ @success = false
89
+ ensure
90
+ @errors, @warnings = [errors, warnings].collect do |io|
91
+ io.rewind
92
+ io.read.split(/\n/)
93
+ end
94
+
95
+ $stdout, $stderr = STDOUT, STDERR
96
+ finished!
97
+ end
98
+
99
+ @success
100
+ end
101
+
102
+ def method_missing(method, *args, &block)
103
+ command = section.hook.find_command(method)
104
+ return super unless command
105
+ run_command(command, *args, &block)
106
+ end
107
+
108
+ # DSL Methods
109
+
110
+ def limit(what)
111
+ Repository::Limiter.new(what).tap do |limiter|
112
+ @limiters << limiter
113
+ end
114
+ end
115
+
116
+ def on_each_file(&block)
117
+ @on = -> { manifest.collect { |file| block.call(file) }.all? }
118
+ end
119
+
120
+ def on_all_files(&block)
121
+ @on = ->() { block.call manifest }
122
+ end
123
+
124
+ def on_argv(&block)
125
+ @on = ->() { block.call section.hook.args }
126
+ end
127
+
128
+ def on(*args, &block)
129
+ @on = ->() { block.call(*args) }
130
+ end
131
+
132
+ private
133
+
134
+ def run_command(command, *args, &block)
135
+ options = args.extract_options
136
+ prefix = options.delete(:prefix_output)
137
+ args << options
138
+
139
+ command.execute(*args, &block).tap do |res|
140
+ res.output.split(/\n/).each do |line|
141
+ $stdout.puts prefix ? "#{prefix}: #{line}" : line
142
+ end
143
+
144
+ res.error.split(/\n/).each do |line|
145
+ $stderr.puts prefix ? "#{prefix}: #{line}" : line
146
+ end
147
+ end.status.success?
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,93 @@
1
+ require 'thor'
2
+
3
+ module GitHooks
4
+ module CLI
5
+ autoload :Config, 'githooks/commands/config'
6
+
7
+ class Base < Thor
8
+ class_option :verbose, aliases: '-v', type: :boolean, desc: 'verbose output', default: false
9
+ class_option :debug, aliases: '-d', type: :boolean, desc: 'debug output', default: false
10
+
11
+ # githook attach [--hook <hook1,hookN>] [--script <path> | --path <path>] [--bootstrap <path>]
12
+ # -- attaches the listed hooks (or all if none specified) to the githook runner
13
+ # optionally sets the script XOR path
14
+ desc :attach, 'attach githooks to repository hooks'
15
+ method_option :bootstrap, type: :string, desc: 'Path to bootstrap script', default: nil
16
+ method_option :script, aliases: '-s', type: :string, desc: 'Path to script to run', default: nil
17
+ method_option :path, aliases: '-p', type: :string, desc: 'Path to library of tests', default: nil
18
+ method_option :repo, aliases: '-r', type: :string, desc: 'Path to repo to run tests on', default: Dir.getwd
19
+ method_option :hooks, { # rubocop:disable BracesAroundHashParameters
20
+ type: :array,
21
+ desc: 'Path to repo to run tests on',
22
+ enum: Hook::VALID_PHASES,
23
+ default: Hook::VALID_PHASES
24
+ }
25
+ def attach
26
+ GitHooks.verbose = !!options['verbose']
27
+ GitHooks.debug = !!options['debug']
28
+
29
+ unless options['script'] || options['path']
30
+ fail ArgumentError, %q|Neither 'path' nor 'script' were specified - please provide at least one.|
31
+ end
32
+
33
+ Runner.attach(options)
34
+ end
35
+
36
+ # githook dettach [--hook <hook1,hookN>]
37
+ # -- detaches the listed hooks, or all hooks if none specified
38
+ desc :detach, 'detach githooks from repository hooks'
39
+ method_option :repo, aliases: '-r', type: :string, desc: 'Path to repo to run tests on', default: Dir.getwd
40
+ method_option :hooks, { # rubocop:disable BracesAroundHashParameters
41
+ type: :array,
42
+ desc: 'Path to repo to run tests on',
43
+ enum: Hook::VALID_PHASES,
44
+ default: Hook::VALID_PHASES
45
+ }
46
+ def detach
47
+ GitHooks.verbose = !!options['verbose']
48
+ GitHooks.debug = !!options['debug']
49
+ Runner.detach(options['repo'], options['hooks'])
50
+ end
51
+
52
+ # githook list [--hook <hook1,hook2,hookN>]
53
+ # -- lists tests for the given hook(s), or all hooks if none specified
54
+ desc :list, 'list tests assigned to given repository hooks'
55
+ method_option :repo, aliases: '-r', type: :string, desc: 'Path to repo to run tests on', default: Dir.getwd
56
+ def list
57
+ GitHooks.verbose = !!options['verbose']
58
+ GitHooks.debug = !!options['debug']
59
+ Runner.list(options['repo'])
60
+ end
61
+
62
+ # githooks run [--hook <hook1,hookN>] [--[un]staged] [--args -- one two three ... fifty]
63
+ # -- runs the selected hooks (or pre-commit, if none specified) passing
64
+ # the argument list to the script
65
+
66
+ desc :execute, 'Runs the selected hooks, passing the argument list to the script'
67
+ method_option :unstaged, aliases: '-U', type: :boolean, desc: 'test unstaged files', default: false
68
+ method_option :untracked, aliases: '-T', type: :boolean, desc: 'test unstaged files', default: false
69
+ method_option :script, aliases: '-s', type: :string, desc: 'Path to script to run', default: nil
70
+ method_option :path, aliases: '-p', type: :string, desc: 'Path to library of tests', default: nil
71
+ method_option :repo, aliases: '-r', type: :string, desc: 'Path to repo to run tests on', default: Dir.getwd
72
+ method_option :'skip-pre', type: :boolean, desc: 'Skip PreRun Scripts', default: false
73
+ method_option :'skip-post', type: :boolean, desc: 'Skip PostRun Scripts', default: false
74
+ method_option :'skip-bundler', type: :boolean, desc: %Q|Don't load bundler gemfile|, default: false
75
+ method_option :args, type: :array, desc: 'Args to pass to pre/post scripts and main testing script', default: []
76
+ def execute(*args)
77
+ GitHooks.verbose = !!options['verbose']
78
+ GitHooks.debug = !!options['debug']
79
+
80
+ opts = (options).dup
81
+ if opts['untracked'] && !opts['unstaged']
82
+ warn %q|--untracked requires --unstaged. Dropping option --untracked...|
83
+ opts['untracked'] = false
84
+ end
85
+
86
+ GitHooks::Runner.run(opts)
87
+ end
88
+
89
+ desc :config, 'manage githooks configuration'
90
+ subcommand :config, Config
91
+ end
92
+ end
93
+ end