rabbitt-githooks 1.3.0 → 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/Gemfile.lock +29 -19
- data/Rakefile +20 -0
- data/bin/githooks +2 -1
- data/bin/githooks-runner +10 -2
- data/lib/githooks.rb +11 -7
- data/lib/githooks/action.rb +67 -53
- data/lib/githooks/cli.rb +9 -6
- data/lib/githooks/commands/config.rb +26 -19
- data/lib/githooks/core_ext.rb +2 -1
- data/lib/githooks/core_ext/array/extract_options.rb +5 -1
- data/lib/githooks/core_ext/array/min_max.rb +4 -4
- data/lib/githooks/core_ext/array/select_with_index.rb +1 -1
- data/lib/githooks/core_ext/colorize.rb +30 -0
- data/lib/githooks/core_ext/object.rb +8 -0
- data/lib/githooks/core_ext/pathname.rb +7 -6
- data/lib/githooks/core_ext/string.rb +1 -1
- data/lib/githooks/core_ext/string/sanitize.rb +51 -0
- data/lib/githooks/error.rb +1 -0
- data/lib/githooks/hook.rb +21 -26
- data/lib/githooks/repository.rb +31 -35
- data/lib/githooks/repository/config.rb +16 -24
- data/lib/githooks/repository/diff_index_entry.rb +3 -2
- data/lib/githooks/repository/file.rb +14 -7
- data/lib/githooks/runner.rb +38 -36
- data/lib/githooks/section.rb +13 -19
- data/lib/githooks/system_utils.rb +132 -47
- data/lib/githooks/version.rb +1 -1
- data/lib/tasks/dev.task +14 -0
- data/rabbitt-githooks.gemspec +18 -15
- metadata +58 -33
- data/.gitignore +0 -34
- data/.hooks/commit-messages.rb +0 -29
- data/.hooks/formatting.rb +0 -44
- data/.rubocop.yml +0 -87
- data/lib/githooks/core_ext/process.rb +0 -9
- data/lib/githooks/core_ext/string/strip_empty_lines.rb +0 -9
- data/lib/githooks/terminal_colors.rb +0 -62
- data/thoughts.txt +0 -56
data/lib/githooks/core_ext.rb
CHANGED
@@ -16,8 +16,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|
16
16
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
17
17
|
=end
|
18
18
|
|
19
|
+
require_relative 'core_ext/object'
|
19
20
|
require_relative 'core_ext/array'
|
21
|
+
require_relative 'core_ext/colorize'
|
20
22
|
require_relative 'core_ext/numbers'
|
21
23
|
require_relative 'core_ext/string'
|
22
24
|
require_relative 'core_ext/pathname'
|
23
|
-
require_relative 'core_ext/process'
|
@@ -19,17 +19,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|
19
19
|
require_relative '../numbers/infinity'
|
20
20
|
|
21
21
|
class Array
|
22
|
-
def
|
22
|
+
def minimum(&_block)
|
23
23
|
collection = block_given? ? collect { |obj| yield obj } : self
|
24
|
-
collection.inject(Infinity) do |min, num|
|
24
|
+
collection.inject(Infinity) do |min, num| # rubocop:disable Style/EachWithObject
|
25
25
|
min = num < min ? num : min
|
26
26
|
min
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
30
|
+
def maximum(&_block)
|
31
31
|
collection = block_given? ? collect { |obj| yield obj } : self
|
32
|
-
collection.inject(0) do |max, num|
|
32
|
+
collection.inject(0) do |max, num| # rubocop:disable Style/EachWithObject
|
33
33
|
max = num > max ? num : max
|
34
34
|
max
|
35
35
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
module Colorize
|
4
|
+
module ClassMethods
|
5
|
+
unless respond_to? :disable_colorization_without_tty_detection
|
6
|
+
alias_method :disable_colorization_without_tty_detection, :disable_colorization
|
7
|
+
end
|
8
|
+
|
9
|
+
def disable_colorization(value = nil)
|
10
|
+
# disable colorization when we don't have a tty on STDOUT
|
11
|
+
return true unless value || STDOUT.tty?
|
12
|
+
disable_colorization_without_tty_detection(value)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
def success!
|
18
|
+
light_green
|
19
|
+
end
|
20
|
+
|
21
|
+
def failure!
|
22
|
+
light_red
|
23
|
+
end
|
24
|
+
|
25
|
+
def unknown!
|
26
|
+
light_yellow
|
27
|
+
end
|
28
|
+
alias_method :warning!, :unknown!
|
29
|
+
end
|
30
|
+
end
|
@@ -3,10 +3,9 @@ require 'pathname'
|
|
3
3
|
if RUBY_ENGINE == 'jruby'
|
4
4
|
class Pathname
|
5
5
|
def realpath(basedir = nil)
|
6
|
-
|
7
|
-
fail Errno::ENOENT, path.to_s
|
6
|
+
java_realpath(basedir).tap do |path|
|
7
|
+
fail Errno::ENOENT, path.to_s unless path.exist?
|
8
8
|
end
|
9
|
-
path
|
10
9
|
end
|
11
10
|
|
12
11
|
def realdirpath(basedir = nil)
|
@@ -14,11 +13,13 @@ if RUBY_ENGINE == 'jruby'
|
|
14
13
|
end
|
15
14
|
|
16
15
|
def java_realpath(basedir = nil)
|
17
|
-
|
18
|
-
|
16
|
+
# rubocop:disable ElseAlignment, EndAlignment
|
17
|
+
path = if basedir && @path[0] != '/'
|
18
|
+
Pathname.new(basedir).realpath.join(@path)
|
19
19
|
else
|
20
|
-
|
20
|
+
@path.to_s
|
21
21
|
end
|
22
|
+
# rubocop:enable ElseAlignment, EndAlignment
|
22
23
|
|
23
24
|
self.class.new java.io.File.new(path.to_s).canonical_path
|
24
25
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative '../array/extract_options'
|
2
|
+
|
3
|
+
class String
|
4
|
+
def strip_empty_lines!
|
5
|
+
replace(strip_empty_lines)
|
6
|
+
end
|
7
|
+
|
8
|
+
def strip_empty_lines
|
9
|
+
split(/\n/).reject { |s| s !~ /\S/ }.join("\n")
|
10
|
+
end
|
11
|
+
|
12
|
+
def strip_non_printable!
|
13
|
+
replace(strip_non_printable)
|
14
|
+
end
|
15
|
+
|
16
|
+
def strip_non_printable
|
17
|
+
gsub(/[^[:print:] \n\t\x1b]/, '')
|
18
|
+
end
|
19
|
+
|
20
|
+
def strip_colors!
|
21
|
+
replace(strip_colors)
|
22
|
+
end
|
23
|
+
|
24
|
+
def strip_colors
|
25
|
+
gsub(/\x1b\[\d+(?:;\d+)?m/, '')
|
26
|
+
end
|
27
|
+
|
28
|
+
def sanitize!(*methods)
|
29
|
+
options = methods.extract_options!
|
30
|
+
|
31
|
+
map = {
|
32
|
+
strip: :strip!,
|
33
|
+
empty_lines: :strip_empty_lines!,
|
34
|
+
non_printable: :strip_non_printable!,
|
35
|
+
colors: :strip_colors!
|
36
|
+
}
|
37
|
+
|
38
|
+
methods = map.keys if methods.empty? || methods.include?(:all)
|
39
|
+
methods -= Array(options.delete(:except)) if options[:except]
|
40
|
+
|
41
|
+
methods.collect(&:to_sym).each do |method|
|
42
|
+
send(map[method]) if map[method]
|
43
|
+
end
|
44
|
+
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def sanitize(*methods)
|
49
|
+
dup.sanitize!(*methods)
|
50
|
+
end
|
51
|
+
end
|
data/lib/githooks/error.rb
CHANGED
data/lib/githooks/hook.rb
CHANGED
@@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|
17
17
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
18
18
|
=end
|
19
19
|
|
20
|
+
require_relative 'repository'
|
20
21
|
require_relative 'system_utils'
|
21
22
|
|
22
23
|
module GitHooks
|
@@ -27,24 +28,22 @@ module GitHooks
|
|
27
28
|
@__mutex__ = Mutex.new
|
28
29
|
|
29
30
|
class << self
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
alias_method :phases, :instances
|
31
|
+
attr_reader :__phases__
|
32
|
+
alias_method :phases, :__phases__
|
34
33
|
|
35
|
-
def instance(phase = 'pre-commit')
|
34
|
+
def instance(phase = 'pre-commit') # rubocop:disable AbcSize
|
36
35
|
phase = phase.to_s
|
37
36
|
unless VALID_PHASES.include? phase
|
38
|
-
|
39
|
-
fail ArgumentError, "Hook phase (#{phase.inspect}) must be one of #{valid_phases}"
|
37
|
+
fail ArgumentError, "Hook phase (#{phase}) must be one of #{VALID_PHASES.join(', ')}"
|
40
38
|
end
|
41
39
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
40
|
+
unless phases[phase]
|
41
|
+
@__mutex__.synchronize {
|
42
|
+
return phases[phase] if phases[phase]
|
43
|
+
phases[phase] = new(phase)
|
44
|
+
}
|
47
45
|
end
|
46
|
+
phases[phase]
|
48
47
|
end
|
49
48
|
private :instance
|
50
49
|
|
@@ -57,8 +56,8 @@ module GitHooks
|
|
57
56
|
end
|
58
57
|
|
59
58
|
def register(phase, &block)
|
60
|
-
fail ArgumentError, '
|
61
|
-
|
59
|
+
fail ArgumentError, 'expected block, received none' unless block_given?
|
60
|
+
instance(phase).instance_eval(&block)
|
62
61
|
end
|
63
62
|
end
|
64
63
|
|
@@ -66,7 +65,7 @@ module GitHooks
|
|
66
65
|
attr_accessor :args, :staged, :untracked, :tracked
|
67
66
|
|
68
67
|
def initialize(phase)
|
69
|
-
@phase = phase
|
68
|
+
@phase = phase.to_s
|
70
69
|
@sections = {}
|
71
70
|
@commands = []
|
72
71
|
@args = []
|
@@ -85,35 +84,31 @@ module GitHooks
|
|
85
84
|
@repository = Repository.new(path)
|
86
85
|
end
|
87
86
|
|
88
|
-
def manifest
|
87
|
+
def manifest
|
89
88
|
@manifest ||= Manifest.new(self)
|
90
89
|
end
|
91
90
|
|
92
91
|
def run
|
93
92
|
# only run sections that have actions matching files in the manifest
|
94
|
-
|
95
|
-
runable_sections.collect { |section| section.run }.all?
|
93
|
+
sections.reject { |s| s.actions.empty? }.collect(&:run).all?
|
96
94
|
end
|
97
95
|
|
98
96
|
def method_missing(method, *args, &block)
|
99
|
-
command = find_command(method)
|
100
|
-
return super unless command
|
97
|
+
return super unless command = find_command(method) # rubocop:disable AssignmentInCondition
|
101
98
|
command.execute(*args, &block)
|
102
99
|
end
|
103
100
|
|
104
101
|
def setup_command(name, options = {})
|
105
|
-
name = name.to_s.to_sym
|
106
|
-
|
107
102
|
@commands << SystemUtils::Command.new(
|
108
|
-
name,
|
109
|
-
|
110
|
-
|
103
|
+
name.to_sym,
|
104
|
+
chdir: options.delete(:chdir),
|
105
|
+
bin_path: options.delete(:bin_path)
|
111
106
|
)
|
112
107
|
end
|
113
108
|
private :setup_command
|
114
109
|
|
115
110
|
def find_command(name)
|
116
|
-
@commands.select { |command| command.
|
111
|
+
@commands.select { |command| command.name == name.to_s }.first
|
117
112
|
end
|
118
113
|
|
119
114
|
def sections
|
data/lib/githooks/repository.rb
CHANGED
@@ -17,12 +17,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|
17
17
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
18
18
|
=end
|
19
19
|
|
20
|
-
require 'ostruct'
|
21
|
-
require 'singleton'
|
22
|
-
require 'open3'
|
23
|
-
|
24
20
|
module GitHooks
|
25
21
|
class Repository # rubocop:disable ClassLength
|
22
|
+
extend SystemUtils
|
23
|
+
|
24
|
+
command :git
|
25
|
+
|
26
26
|
autoload :Config, 'githooks/repository/config'
|
27
27
|
autoload :File, 'githooks/repository/file'
|
28
28
|
autoload :Limiter, 'githooks/repository/limiter'
|
@@ -38,11 +38,11 @@ module GitHooks
|
|
38
38
|
}.freeze unless defined? CHANGE_TYPE_SYMBOLS
|
39
39
|
|
40
40
|
CHANGE_TYPES = CHANGE_TYPE_SYMBOLS.invert.freeze unless defined? CHANGE_TYPES
|
41
|
-
|
42
41
|
DEFAULT_DIFF_INDEX_OPTIONS = { staged: true } unless defined? DEFAULT_DIFF_INDEX_OPTIONS
|
43
42
|
|
44
43
|
@__instance__ = {}
|
45
44
|
@__mutex__ = Mutex.new
|
45
|
+
|
46
46
|
def self.instance(path = Dir.getwd)
|
47
47
|
path = Pathname.new(path).realpath
|
48
48
|
strpath = path.to_s
|
@@ -70,12 +70,8 @@ module GitHooks
|
|
70
70
|
@config ||= Repository::Config.new(root_path)
|
71
71
|
end
|
72
72
|
|
73
|
-
def git_command(*args)
|
74
|
-
git.execute(*args.flatten)
|
75
|
-
end
|
76
|
-
|
77
73
|
def get_root_path(path)
|
78
|
-
|
74
|
+
git('rev-parse', '--show-toplevel', chdir: path).tap do |result|
|
79
75
|
unless result.status.success? && result.output !~ /not a git repository/i
|
80
76
|
fail Error::NotAGitRepo, "Unable to find a valid git repo in #{path}"
|
81
77
|
end
|
@@ -83,30 +79,33 @@ module GitHooks
|
|
83
79
|
end
|
84
80
|
|
85
81
|
def stash
|
86
|
-
|
82
|
+
git(*%w( stash -q --keep-index -a)).status.success?
|
87
83
|
end
|
88
84
|
|
89
85
|
def unstash
|
90
|
-
|
86
|
+
git(*%w(stash pop -q)).status.success?
|
91
87
|
end
|
92
88
|
|
93
|
-
def manifest(options = {})
|
89
|
+
def manifest(options = {}) # rubocop:disable AbcSize
|
94
90
|
ref = options.delete(:ref)
|
95
|
-
|
96
91
|
return staged_manifest(ref: ref) if options.delete(:staged)
|
97
92
|
|
98
|
-
|
93
|
+
manifest_list = unstaged_manifest(ref: ref)
|
99
94
|
|
100
|
-
|
101
|
-
|
102
|
-
|
95
|
+
if options.delete(:tracked)
|
96
|
+
tracked_manifest(ref: ref).each_with_object(manifest_list) do |file, list|
|
97
|
+
list << file unless list.include?(file)
|
98
|
+
end
|
99
|
+
end
|
103
100
|
|
104
|
-
|
105
|
-
|
106
|
-
|
101
|
+
if options.delete(:untracked)
|
102
|
+
untracked_manifest(ref: ref).each_with_object(manifest_list) do |file, list|
|
103
|
+
list << file unless list.include?(file)
|
104
|
+
end
|
105
|
+
end
|
107
106
|
|
108
|
-
|
109
|
-
|
107
|
+
manifest_list.sort!
|
108
|
+
manifest_list.uniq { |f| f.path.to_s }
|
110
109
|
end
|
111
110
|
|
112
111
|
def staged_manifest(options = {})
|
@@ -119,18 +118,18 @@ module GitHooks
|
|
119
118
|
end
|
120
119
|
|
121
120
|
def tracked_manifest(*)
|
122
|
-
files =
|
121
|
+
files = git('ls-files', '--exclude-standard').output.strip.split(/\s*\n\s*/)
|
123
122
|
files.collect { |path| DiffIndexEntry.from_file_path(path, true).to_repo_file }
|
124
123
|
end
|
125
124
|
|
126
125
|
def untracked_manifest(*)
|
127
|
-
files =
|
126
|
+
files = git('ls-files', '--others', '--exclude-standard').output.strip.split(/\s*\n\s*/)
|
128
127
|
files.collect { |path| DiffIndexEntry.from_file_path(path).to_repo_file }
|
129
128
|
end
|
130
129
|
|
131
130
|
private
|
132
131
|
|
133
|
-
def diff_index(options = {})
|
132
|
+
def diff_index(options = {}) # rubocop:disable AbcSize
|
134
133
|
options = DEFAULT_DIFF_INDEX_OPTIONS.merge(options)
|
135
134
|
|
136
135
|
if $stdout.tty? && !options[:staged]
|
@@ -141,21 +140,18 @@ module GitHooks
|
|
141
140
|
cmd << (options.delete(:ref) || 'HEAD')
|
142
141
|
end
|
143
142
|
|
144
|
-
cmd.compact
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
def git
|
151
|
-
@git ||= SystemUtils::Command.new('git')
|
143
|
+
git(*cmd.compact.compact).output_lines.collect do |diff_data|
|
144
|
+
DiffIndexEntry.new(diff_data).to_repo_file
|
145
|
+
end
|
146
|
+
rescue
|
147
|
+
exit! 1
|
152
148
|
end
|
153
149
|
|
154
150
|
def while_stashed(&block)
|
155
151
|
fail ArgumentError, 'Missing required block' unless block_given?
|
156
152
|
begin
|
157
153
|
stash
|
158
|
-
|
154
|
+
block.call
|
159
155
|
ensure
|
160
156
|
unstash
|
161
157
|
end
|
@@ -17,18 +17,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|
17
17
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
18
18
|
=end
|
19
19
|
|
20
|
-
require 'ostruct'
|
21
|
-
require 'singleton'
|
22
|
-
require 'open3'
|
23
|
-
|
24
20
|
module GitHooks
|
25
21
|
class Repository
|
26
22
|
class Config # rubocop:disable ClassLength
|
27
23
|
OPTIONS = {
|
28
|
-
'path'
|
29
|
-
'script'
|
30
|
-
'pre-run-execute'
|
31
|
-
'post-run-execute' => { type: :path, multiple: true }
|
24
|
+
'path' => { type: :path, multiple: false },
|
25
|
+
'script' => { type: :path, multiple: false },
|
26
|
+
'pre-run-execute' => { type: :path, multiple: true },
|
27
|
+
'post-run-execute' => { type: :path, multiple: true },
|
32
28
|
}
|
33
29
|
|
34
30
|
def initialize(path = Dir.getwd)
|
@@ -49,7 +45,7 @@ module GitHooks
|
|
49
45
|
send(option.gsub('-', '_'))
|
50
46
|
end
|
51
47
|
|
52
|
-
def set(option, value, options = {}) # rubocop:disable CyclomaticComplexity, MethodLength
|
48
|
+
def set(option, value, options = {}) # rubocop:disable CyclomaticComplexity, MethodLength, PerceivedComplexity, AbcSize
|
53
49
|
unless OPTIONS.keys.include? option
|
54
50
|
fail ArgumentError, "Unexpected option '#{option}': expected one of: #{OPTIONS.keys.join(', ')}"
|
55
51
|
end
|
@@ -82,11 +78,11 @@ module GitHooks
|
|
82
78
|
|
83
79
|
if overwrite && !self[option].nil? && !self[option].empty?
|
84
80
|
puts "Overwrite requested for option '#{option}'" if GitHooks.verbose
|
85
|
-
unset(option,
|
81
|
+
unset(option, repo_chdir: repo, global: global)
|
86
82
|
end
|
87
83
|
|
88
84
|
option = "githooks.#{repo}.#{option}"
|
89
|
-
|
85
|
+
git(global ? '--global' : '--local', var_type, add_type, option, value, chdir: repo).tap do |result|
|
90
86
|
puts "Added option #{option} with value #{value}" if result.status.success?
|
91
87
|
end
|
92
88
|
end
|
@@ -95,10 +91,10 @@ module GitHooks
|
|
95
91
|
repo = options.delete(:repo_path) || repo_path
|
96
92
|
global = (opt = options.delete(:global)).nil? ? false : opt
|
97
93
|
option = "githooks.#{repo}"
|
98
|
-
|
94
|
+
git(global ? '--global' : '--local', '--remove-section', option, chdir: repo)
|
99
95
|
end
|
100
96
|
|
101
|
-
def unset(option, *args) # rubocop:disable
|
97
|
+
def unset(option, *args) # rubocop:disable CyclomaticComplexity, MethodLength, PerceivedComplexity, AbcSize
|
102
98
|
unless OPTIONS.keys.include? option
|
103
99
|
fail ArgumentError, "Unexpected option '#{option}': expected one of: #{OPTIONS.keys.join(', ')}"
|
104
100
|
end
|
@@ -111,9 +107,9 @@ module GitHooks
|
|
111
107
|
value_regex = args.first
|
112
108
|
|
113
109
|
if options.delete(:all) || value_regex.nil?
|
114
|
-
|
110
|
+
git(global ? '--global' : '--local', '--unset-all', option, chdir: repo)
|
115
111
|
else
|
116
|
-
|
112
|
+
git(global ? '--global' : '--local', '--unset', option, value_regex, chdir: repo)
|
117
113
|
end.tap do |result|
|
118
114
|
puts "Unset option #{option.git_option_path_split.last}" if result.status.success?
|
119
115
|
end
|
@@ -140,16 +136,16 @@ module GitHooks
|
|
140
136
|
@repository.root_path
|
141
137
|
end
|
142
138
|
|
143
|
-
def
|
139
|
+
def git(*args)
|
144
140
|
args = ['config', *args].flatten
|
145
|
-
@repository.
|
141
|
+
@repository.git(*args)
|
146
142
|
end
|
147
143
|
|
148
|
-
def config(path = nil) # rubocop:disable MethodLength,
|
144
|
+
def config(path = nil) # rubocop:disable CyclomaticComplexity, MethodLength, PerceivedComplexity, AbcSize
|
149
145
|
path ||= repo_path
|
150
146
|
|
151
|
-
raw_config =
|
152
|
-
raw_config.sort.uniq.
|
147
|
+
raw_config = git('--list', chdir: path).output.split("\n")
|
148
|
+
raw_config.sort.uniq.each_with_object({}) do |line, hash|
|
153
149
|
key, value = line.split(/\s*=\s*/)
|
154
150
|
key_parts = key.git_option_path_split
|
155
151
|
|
@@ -168,10 +164,6 @@ module GitHooks
|
|
168
164
|
hash
|
169
165
|
end
|
170
166
|
end
|
171
|
-
|
172
|
-
def git
|
173
|
-
@repository.git
|
174
|
-
end
|
175
167
|
end
|
176
168
|
end
|
177
169
|
end
|