githooker 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +20 -0
- data/README.md +4 -0
- data/README.rdoc +19 -0
- data/Rakefile +43 -0
- data/VERSION +1 -0
- data/features/githooker.feature +9 -0
- data/features/step_definitions/githooker_steps.rb +0 -0
- data/features/support/env.rb +15 -0
- data/githooker.gemspec +91 -0
- data/lib/githooker.rb +76 -0
- data/lib/githooker/action.rb +72 -0
- data/lib/githooker/core_ext.rb +3 -0
- data/lib/githooker/core_ext/array.rb +1 -0
- data/lib/githooker/core_ext/array/min_max.rb +17 -0
- data/lib/githooker/core_ext/numbers.rb +1 -0
- data/lib/githooker/core_ext/numbers/infinity.rb +2 -0
- data/lib/githooker/core_ext/string.rb +1 -0
- data/lib/githooker/core_ext/string/inflections.rb +51 -0
- data/lib/githooker/hook.rb +48 -0
- data/lib/githooker/repo.rb +125 -0
- data/lib/githooker/runner.rb +41 -0
- data/lib/githooker/section.rb +36 -0
- data/lib/githooker/terminal_colors.rb +45 -0
- data/test/helper.rb +18 -0
- data/test/test_githooker.rb +7 -0
- metadata +223 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "awesome_print", ">= 0"
|
10
|
+
gem "shoulda", ">= 0"
|
11
|
+
gem "yard", "~> 0.7"
|
12
|
+
gem "rdoc", "~> 3.12"
|
13
|
+
gem "cucumber", ">= 0"
|
14
|
+
gem "bundler", ">= 0"
|
15
|
+
gem "jeweler", "~> 1.8.4"
|
16
|
+
gem "simplecov", ">= 0"
|
17
|
+
end
|
18
|
+
|
19
|
+
gem "inifile", "~> 2.0"
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Carl P. Corliss
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= githooker
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to githooker
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
+
* Fork the project.
|
10
|
+
* Start a feature/bugfix branch.
|
11
|
+
* Commit and push until you are happy with your contribution.
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2013 Carl P. Corliss. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
begin
|
8
|
+
Bundler.setup(:default, :development)
|
9
|
+
rescue Bundler::BundlerError => e
|
10
|
+
$stderr.puts e.message
|
11
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
12
|
+
exit e.status_code
|
13
|
+
end
|
14
|
+
require 'rake'
|
15
|
+
|
16
|
+
require 'jeweler'
|
17
|
+
Jeweler::Tasks.new do |gem|
|
18
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
19
|
+
gem.name = "githooker"
|
20
|
+
gem.homepage = "http://github.com/rabbitt/githooker"
|
21
|
+
gem.license = "GPLv2"
|
22
|
+
gem.summary = %Q{framework for building git hooks tests}
|
23
|
+
gem.description = %Q{GitHooker provides a framework for building test that can be used with git hooks}
|
24
|
+
gem.email = "rabbitt@gmail.com"
|
25
|
+
gem.authors = ["Carl P. Corliss"]
|
26
|
+
# dependencies defined in Gemfile
|
27
|
+
end
|
28
|
+
Jeweler::RubygemsDotOrgTasks.new
|
29
|
+
|
30
|
+
require 'rake/testtask'
|
31
|
+
Rake::TestTask.new(:test) do |test|
|
32
|
+
test.libs << 'lib' << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
|
37
|
+
require 'cucumber/rake/task'
|
38
|
+
Cucumber::Rake::Task.new(:features)
|
39
|
+
|
40
|
+
task :default => :test
|
41
|
+
|
42
|
+
require 'yard'
|
43
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
File without changes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
begin
|
3
|
+
Bundler.setup(:default, :development)
|
4
|
+
rescue Bundler::BundlerError => e
|
5
|
+
$stderr.puts e.message
|
6
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
7
|
+
exit e.status_code
|
8
|
+
end
|
9
|
+
|
10
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
11
|
+
require 'githooker'
|
12
|
+
|
13
|
+
require 'test/unit/assertions'
|
14
|
+
|
15
|
+
World(Test::Unit::Assertions)
|
data/githooker.gemspec
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "githooker"
|
8
|
+
s.version = "0.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Carl P. Corliss"]
|
12
|
+
s.date = "2013-03-11"
|
13
|
+
s.description = "GitHooker provides a framework for building test that can be used with git hooks"
|
14
|
+
s.email = "rabbitt@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md",
|
18
|
+
"README.rdoc"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".document",
|
22
|
+
"Gemfile",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.md",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"features/githooker.feature",
|
29
|
+
"features/step_definitions/githooker_steps.rb",
|
30
|
+
"features/support/env.rb",
|
31
|
+
"githooker.gemspec",
|
32
|
+
"lib/githooker.rb",
|
33
|
+
"lib/githooker/action.rb",
|
34
|
+
"lib/githooker/core_ext.rb",
|
35
|
+
"lib/githooker/core_ext/array.rb",
|
36
|
+
"lib/githooker/core_ext/array/min_max.rb",
|
37
|
+
"lib/githooker/core_ext/numbers.rb",
|
38
|
+
"lib/githooker/core_ext/numbers/infinity.rb",
|
39
|
+
"lib/githooker/core_ext/string.rb",
|
40
|
+
"lib/githooker/core_ext/string/inflections.rb",
|
41
|
+
"lib/githooker/hook.rb",
|
42
|
+
"lib/githooker/repo.rb",
|
43
|
+
"lib/githooker/runner.rb",
|
44
|
+
"lib/githooker/section.rb",
|
45
|
+
"lib/githooker/terminal_colors.rb",
|
46
|
+
"test/helper.rb",
|
47
|
+
"test/test_githooker.rb"
|
48
|
+
]
|
49
|
+
s.homepage = "http://github.com/rabbitt/githooker"
|
50
|
+
s.licenses = ["GPLv2"]
|
51
|
+
s.require_paths = ["lib"]
|
52
|
+
s.rubygems_version = "1.8.23"
|
53
|
+
s.summary = "framework for building git hooks tests"
|
54
|
+
|
55
|
+
if s.respond_to? :specification_version then
|
56
|
+
s.specification_version = 3
|
57
|
+
|
58
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
59
|
+
s.add_runtime_dependency(%q<inifile>, ["~> 2.0"])
|
60
|
+
s.add_development_dependency(%q<awesome_print>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<yard>, ["~> 0.7"])
|
63
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
64
|
+
s.add_development_dependency(%q<cucumber>, [">= 0"])
|
65
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
67
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
68
|
+
else
|
69
|
+
s.add_dependency(%q<inifile>, ["~> 2.0"])
|
70
|
+
s.add_dependency(%q<awesome_print>, [">= 0"])
|
71
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
72
|
+
s.add_dependency(%q<yard>, ["~> 0.7"])
|
73
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
74
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
75
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
76
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
77
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
78
|
+
end
|
79
|
+
else
|
80
|
+
s.add_dependency(%q<inifile>, ["~> 2.0"])
|
81
|
+
s.add_dependency(%q<awesome_print>, [">= 0"])
|
82
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
83
|
+
s.add_dependency(%q<yard>, ["~> 0.7"])
|
84
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
85
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
86
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
87
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
88
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
data/lib/githooker.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'githooker/core_ext'
|
5
|
+
|
6
|
+
module GitHooker
|
7
|
+
autoload :Hook, 'githooker/hook'
|
8
|
+
autoload :Repo, 'githooker/repo'
|
9
|
+
autoload :Runner, 'githooker/runner'
|
10
|
+
autoload :Section, 'githooker/section'
|
11
|
+
autoload :Action, 'githooker/action'
|
12
|
+
autoload :TerminalColors, 'githooker/terminal_colors'
|
13
|
+
autoload :RegistrationError, 'githooker/action'
|
14
|
+
|
15
|
+
LIB_PATH = Pathname.new(__FILE__).dirname
|
16
|
+
GEM_PATH = LIB_PATH.parent
|
17
|
+
|
18
|
+
SCRIPT_NAME = Pathname.new($0).basename
|
19
|
+
SCRIPT_DIR = Pathname.new($0).dirname.realdirpath
|
20
|
+
SCRIPT_PATH = SCRIPT_DIR + SCRIPT_NAME
|
21
|
+
|
22
|
+
REPO_ROOT = Pathname.new(%x{git rev-parse --show-toplevel})
|
23
|
+
|
24
|
+
HOOK_NAME = SCRIPT_NAME.to_s.underscore.to_sym
|
25
|
+
|
26
|
+
VERSION = IO.read(GEM_PATH + 'VERSION')
|
27
|
+
end
|
28
|
+
|
29
|
+
# # # --- commit_hooks.rb
|
30
|
+
# #!/usr/bin/env ruby
|
31
|
+
# require 'githooker'
|
32
|
+
|
33
|
+
# GitHooker::Hook.register(:pre_commit) do
|
34
|
+
|
35
|
+
# section :generic
|
36
|
+
# exit_on_error true
|
37
|
+
|
38
|
+
# perform "Valid Puppet Syntax" do
|
39
|
+
# on :name => /\.pp$/, :call => Puppet.method(:parser_test)
|
40
|
+
# end
|
41
|
+
|
42
|
+
# perform "Validate Zone File" do
|
43
|
+
# on :name => /\.db$/ do |file|
|
44
|
+
# system("named-checkzone #{file}") == 0
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
|
48
|
+
# section :policy
|
49
|
+
|
50
|
+
# perform "No Leading Tabs" do
|
51
|
+
# on :change => [:added, :modified] { |file_path|
|
52
|
+
# IO.read(file_path).split(/\n/).tap { |contents|
|
53
|
+
# contents.each_with_index do |line, index|
|
54
|
+
# if line.match(/^[ ]*\t/)
|
55
|
+
# $stderr.printf "%#{contents.length.to_s.size}d: %s\n", index, line
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
# }.none? { |line| line.match(/^[ ]*\t/) }
|
59
|
+
# }
|
60
|
+
# end
|
61
|
+
|
62
|
+
# perform "Leading Spaces Multiple of 2" do
|
63
|
+
# on :change => [:added, :modified] { |file_path|
|
64
|
+
# IO.read(file_path).split(/\n/).tap { |contents|
|
65
|
+
# contents.each_with_index do |line, index|
|
66
|
+
# if line.scan(/^[ ]+/).first.to_s.size % 2 > 0
|
67
|
+
# $stderr.printf "%#{contents.length.to_s.size}d: %s\n", index, line
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
# }.none? { |line| line.scan(/^[ ]+/).first.to_s.size % 2 > 0 }
|
71
|
+
# }
|
72
|
+
# end
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'stringio'
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
module GitHooker
|
6
|
+
class Action
|
7
|
+
include TerminalColors
|
8
|
+
include Repo
|
9
|
+
|
10
|
+
attr_reader :errors, :warnings, :title, :success
|
11
|
+
|
12
|
+
def initialize(title, &block)
|
13
|
+
raise ArgumentError, "Missing required block for Action#new" unless block_given?
|
14
|
+
|
15
|
+
@title = title.to_s.titleize
|
16
|
+
@success = true
|
17
|
+
@errors = []
|
18
|
+
@warnings = []
|
19
|
+
@action = block
|
20
|
+
end
|
21
|
+
|
22
|
+
def success?() @success; end
|
23
|
+
|
24
|
+
def title()
|
25
|
+
success_colored @title
|
26
|
+
end
|
27
|
+
|
28
|
+
def state_symbol
|
29
|
+
symbol = success? ? "✓" : 'X'
|
30
|
+
success_colored symbol
|
31
|
+
end
|
32
|
+
|
33
|
+
def success_colored(text)
|
34
|
+
success? ? bright_green(text) : bright_red(text)
|
35
|
+
end
|
36
|
+
|
37
|
+
def on(options = {}, &block)
|
38
|
+
block = block || options.delete(:call)
|
39
|
+
Repo.match_files_on(options).collect { |file|
|
40
|
+
block.call(file)
|
41
|
+
}.all? # test that they all returned true
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute(cmd, output_line_prefix=nil)
|
45
|
+
Open3.popen3(cmd) { |i, o, e, t|
|
46
|
+
|
47
|
+
o.read.split(/\n/).each do |line|
|
48
|
+
$stdout.puts output_line_prefix ? "#{output_line_prefix}: #{line}" : line
|
49
|
+
end
|
50
|
+
|
51
|
+
e.read.split(/\n/).each do |line|
|
52
|
+
$stderr.puts output_line_prefix ? "#{output_line_prefix}: #{line}" : line
|
53
|
+
end
|
54
|
+
|
55
|
+
t.value
|
56
|
+
}.success?
|
57
|
+
end
|
58
|
+
|
59
|
+
def run
|
60
|
+
warnings, errors = StringIO.new, StringIO.new
|
61
|
+
begin
|
62
|
+
$stdout, $stderr = warnings, errors
|
63
|
+
@success &= instance_eval(&@action)
|
64
|
+
return @success
|
65
|
+
ensure
|
66
|
+
@errors = errors.tap {|e| e.rewind}.read.split(/\n(?:[\t ]*)/)
|
67
|
+
@warnings = warnings.tap {|w| w.rewind}.read.split(/\n(?:[\t ]*)/)
|
68
|
+
$stdout, $stderr = STDOUT, STDERR
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'githooker/core_ext/array/min_max'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'githooker/core_ext/numbers/infinity'
|
2
|
+
|
3
|
+
class Array
|
4
|
+
def min(&block)
|
5
|
+
collection = block_given? ? collect { |obj| yield obj } : self
|
6
|
+
collection.inject(Infinity) { |min, num|
|
7
|
+
min = num < min ? num : min; min
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def max(&block)
|
12
|
+
collection = block_given? ? collect { |obj| yield obj } : self
|
13
|
+
collection.inject(0) { |max, num|
|
14
|
+
max = num > max ? num : max; max
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'githooker/core_ext/numbers/infinity'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'githooker/core_ext/string/inflections'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class String
|
2
|
+
def constantize()
|
3
|
+
names = self.split('::')
|
4
|
+
names.shift if names.empty? || names.first.empty?
|
5
|
+
|
6
|
+
names.inject(Object) { |obj, name|
|
7
|
+
obj = obj.const_defined?(name) ? obj.const_get(name) : obj.const_missing(name)
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def camelize()
|
12
|
+
self.dup.camelize!
|
13
|
+
end
|
14
|
+
|
15
|
+
def camelize!()
|
16
|
+
self.replace(self.split(/[\W_]+/).collect(&:capitalize).join)
|
17
|
+
end
|
18
|
+
|
19
|
+
def underscore!()
|
20
|
+
self.tap {|word|
|
21
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
22
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
23
|
+
word.tr!("-", "_")
|
24
|
+
word.downcase!
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def titleize!
|
29
|
+
self.tap { |title|
|
30
|
+
title.replace(title.split(/\b/).collect(&:capitalize).join)
|
31
|
+
}
|
32
|
+
end
|
33
|
+
alias :titlize! :titleize!
|
34
|
+
|
35
|
+
def titleize
|
36
|
+
self.dup.titleize!
|
37
|
+
end
|
38
|
+
alias :titlize :titleize
|
39
|
+
|
40
|
+
def underscore()
|
41
|
+
self.dup.underscore!
|
42
|
+
end
|
43
|
+
|
44
|
+
def dasherize!()
|
45
|
+
self.underscore!.gsub!(/_/, '-')
|
46
|
+
end
|
47
|
+
|
48
|
+
def dasherize()
|
49
|
+
self.dup.dasherize!
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module GitHooker
|
4
|
+
class RegistrationError < StandardError; end
|
5
|
+
class Hook
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
def self.method_missing(*args, &block)
|
9
|
+
return super unless self.instance.respond_to? args.first
|
10
|
+
self.instance.public_send(*args, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
@hooks.all? { |hook| hook.run }
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@hooks = []
|
19
|
+
@section = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def register(&block)
|
23
|
+
raise ArgumentError, "Missing required block to #register" unless block_given?
|
24
|
+
instance_eval(&block)
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def section(name)
|
29
|
+
@hooks << (@section = Section.new(name))
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def sections
|
34
|
+
@hooks
|
35
|
+
end
|
36
|
+
|
37
|
+
def exit_on_error(value)
|
38
|
+
raise RegistrationError, "#exit_on_error called before section defined" unless @section
|
39
|
+
@section.exit_on_error = value
|
40
|
+
end
|
41
|
+
|
42
|
+
def perform(title, &block)
|
43
|
+
raise RegistrationError, "#perform called before section defined" unless @section
|
44
|
+
raise ArgumentError, "Missing required block to #perform" unless block_given?
|
45
|
+
@section << Action.new(title, &block)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module GitHooker
|
4
|
+
module Repo
|
5
|
+
extend self
|
6
|
+
|
7
|
+
CHANGE_TYPE_SYMBOLS = {
|
8
|
+
:added => 'A', :copied => 'C', :deleted => 'D', :modified => 'M',
|
9
|
+
:renamed => 'R', :retyped => 'T', :unknown => 'U', :unmerged => 'X',
|
10
|
+
:broken => 'B', :any => '*'
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
CHANGE_TYPES = CHANGE_TYPE_SYMBOLS.invert.freeze
|
14
|
+
|
15
|
+
DEFAULT_DIFF_INDEX_OPTIONS = { :staged => true, :ref => 'HEAD' }
|
16
|
+
|
17
|
+
public
|
18
|
+
|
19
|
+
def while_stashed
|
20
|
+
raise ArgumentError, "Missing required block" unless block_given?
|
21
|
+
begin
|
22
|
+
stash
|
23
|
+
return yield
|
24
|
+
ensure
|
25
|
+
unstash
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def run_while_stashed(cmd)
|
30
|
+
while_stashed { system(cmd) }
|
31
|
+
$? == 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def stash()
|
35
|
+
%x{ git stash -q --keep-index }
|
36
|
+
end
|
37
|
+
|
38
|
+
def unstash()
|
39
|
+
%x{ git stash pop -q }
|
40
|
+
end
|
41
|
+
|
42
|
+
def use_unstaged?
|
43
|
+
ENV['UNSTAGED']
|
44
|
+
end
|
45
|
+
|
46
|
+
def diff_index(options = {})
|
47
|
+
options = DEFAULT_DIFF_INDEX_OPTIONS.merge(options)
|
48
|
+
|
49
|
+
cmd = %w(git diff-index -C -M -B)
|
50
|
+
cmd << '--cached' if options[:staged]
|
51
|
+
cmd << options.delete(:ref) || 'HEAD'
|
52
|
+
|
53
|
+
%x{ #{cmd.join(' ')} }
|
54
|
+
end
|
55
|
+
|
56
|
+
def staged_manifest
|
57
|
+
parse_diff_index_data(diff_index(:staged => true, :ref => 'HEAD'))
|
58
|
+
end
|
59
|
+
alias :commit_manifest :staged_manifest
|
60
|
+
|
61
|
+
def unstaged_manifest
|
62
|
+
parse_diff_index_data(diff_index(:staged => false, :ref => 'HEAD'))
|
63
|
+
end
|
64
|
+
|
65
|
+
def match_files_on(options)
|
66
|
+
raise ArgumentError, "options should be a hash" unless options.is_a? Hash
|
67
|
+
match(use_unstaged? ? unstaged_manifest : staged_manifest, options.to_a)
|
68
|
+
end
|
69
|
+
|
70
|
+
# returns the intersection of all file filters
|
71
|
+
def match(manifest_files, filters)
|
72
|
+
manifest_files.tap { |files|
|
73
|
+
filters.each {|type, value| files.select! { |name, data| match_file(data, type, value) } }
|
74
|
+
}.values
|
75
|
+
end
|
76
|
+
|
77
|
+
def match_file(file, matchtype, matchvalue)
|
78
|
+
attr_value = case matchtype
|
79
|
+
when :name then file.path
|
80
|
+
when :type then file.type
|
81
|
+
when :mode then file.to.mode
|
82
|
+
when :sha then file.to.sha
|
83
|
+
when :score then file.score
|
84
|
+
else raise ArgumentError, "Invalid match type '#{matchtype}' - expected one of: :name, :type, :mode, :sha, or :score"
|
85
|
+
end
|
86
|
+
|
87
|
+
return matchvalue.call(attr_value) if matchvalue.respond_to? :call
|
88
|
+
|
89
|
+
case matchtype
|
90
|
+
when :name then
|
91
|
+
matchvalue.is_a?(Regexp) ? attr_value =~ matchvalue : attr_value == matchvalue
|
92
|
+
when :type then
|
93
|
+
matchvalue.is_a?(Array) ? matchvalue.include?(attr_value) : matchvalue == attr_value
|
94
|
+
when :mode then
|
95
|
+
matchvalue & attr_value == matchvalue
|
96
|
+
when :sha then
|
97
|
+
attr_value == matchvalue
|
98
|
+
when :score then
|
99
|
+
attr_value == matchvalue
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
@filters = []
|
107
|
+
|
108
|
+
def parse_diff_index_data(index)
|
109
|
+
index.split(/\n+/).inject({}) do |files,data|
|
110
|
+
orig_mode, new_mode, orig_sha, new_sha, change_type, file_path, rename_path = data.split(/\s+/)
|
111
|
+
change_type, score = change_type.split(/(\d+)/)
|
112
|
+
path = rename_path || file_path
|
113
|
+
|
114
|
+
files[path] = OpenStruct.new({
|
115
|
+
:from => OpenStruct.new({ :mode => orig_mode[1..-1].to_i, :sha => orig_sha, :path => file_path }),
|
116
|
+
:to => OpenStruct.new({ :mode => new_mode.to_i, :sha => new_sha, :path => path }),
|
117
|
+
:type => CHANGE_TYPES[change_type],
|
118
|
+
:score => score.to_i,
|
119
|
+
:path => path
|
120
|
+
}); files
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module GitHooker
|
2
|
+
module Runner
|
3
|
+
extend TerminalColors
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def start
|
7
|
+
max_section_length = Hook.sections.max {|s| s.name.length }
|
8
|
+
|
9
|
+
success = Hook.run
|
10
|
+
|
11
|
+
Hook.sections.each do |section|
|
12
|
+
hash_tail_length = (max_section_length - section.name.length)
|
13
|
+
printf "===== %s %s=====\n", section.name, ("=" * hash_tail_length)
|
14
|
+
|
15
|
+
section.each_with_index do |action, index|
|
16
|
+
printf " %d. [ %s ] %s\n", (index + 1), action.state_symbol, action.title
|
17
|
+
|
18
|
+
action.errors.each do |error|
|
19
|
+
printf " %s %s\n", bright_red('-->'), error
|
20
|
+
end unless action.errors.empty?
|
21
|
+
|
22
|
+
action.warnings.each do |warning|
|
23
|
+
printf " %s %s\n", ( action.success? ? bright_green('-->') : bright_yellow('-->') ), warning
|
24
|
+
end unless action.warnings.empty?
|
25
|
+
|
26
|
+
exit 1 if section.exit_on_error and not action.errors.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
puts
|
30
|
+
end
|
31
|
+
|
32
|
+
success = 1 if ENV['FORCE_ERROR']
|
33
|
+
|
34
|
+
if not success
|
35
|
+
$stderr.puts "Commit failed due to errors listed above. Please fix and attempt your commit again."
|
36
|
+
end
|
37
|
+
|
38
|
+
exit(success ? 0 : 1)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module GitHooker
|
4
|
+
class Section < DelegateClass(Array)
|
5
|
+
include TerminalColors
|
6
|
+
|
7
|
+
attr_reader :name, :status
|
8
|
+
attr_accessor :exit_on_error
|
9
|
+
|
10
|
+
attr_reader :actions
|
11
|
+
alias :__getobj__ :actions
|
12
|
+
|
13
|
+
def initialize(name)
|
14
|
+
@name = name.to_s.titleize
|
15
|
+
@success = true
|
16
|
+
@exit_on_error = false
|
17
|
+
@actions = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def name()
|
21
|
+
unless @success
|
22
|
+
bright_red @name
|
23
|
+
else
|
24
|
+
bright_green @name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def run()
|
29
|
+
@actions.each do |action|
|
30
|
+
@success &= action.run
|
31
|
+
return false if not @success and @exit_on_error
|
32
|
+
end
|
33
|
+
return @success
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module GitHooker
|
2
|
+
module TerminalColors
|
3
|
+
extend self
|
4
|
+
|
5
|
+
NORMAL = "\033[0;0m"
|
6
|
+
|
7
|
+
def color(name)
|
8
|
+
return "" unless $stdout.tty? && $stderr.tty?
|
9
|
+
return NORMAL if !!name.to_s.match(/norm/)
|
10
|
+
|
11
|
+
light = !!name.to_s.match(/(light|bright)/) ? "1" : "0"
|
12
|
+
blink = !!name.to_s.match(/blink/)
|
13
|
+
|
14
|
+
color_code = 30 + case name.to_s
|
15
|
+
when /black/, /gray/ then 0
|
16
|
+
when /red/ then 1
|
17
|
+
when /green/ then 2
|
18
|
+
when /yellow/ then 3
|
19
|
+
when /blue/ then 4
|
20
|
+
when /magenta/,/purple/ then 5
|
21
|
+
when /cyan/ then 6
|
22
|
+
when /white/ then 7
|
23
|
+
else return NORMAL
|
24
|
+
end
|
25
|
+
|
26
|
+
return "\033[#{light};5;#{color_code}m" if blink
|
27
|
+
return "\033[#{light};#{color_code}m"
|
28
|
+
end
|
29
|
+
|
30
|
+
['light', 'bright', 'dark', ''].each do |shade|
|
31
|
+
['blink', 'blinking', ''].each do |style|
|
32
|
+
%w(black red green yellow blue magenta purple cyan white).each do |color|
|
33
|
+
name = "#{style}_#{shade}_#{color}".gsub(/(^_+|_+$)/, '').gsub(/_{2,}/, '_')
|
34
|
+
const_set(name.upcase, color(name))
|
35
|
+
define_method(name) { |text=''| "#{self.color(name)}#{text}#{self.color(:normal)}" }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
if $0 == __FILE__
|
43
|
+
include GitHooker::TerminalColors
|
44
|
+
puts send(ARGV.shift, ARGV.join(" ")) unless ARGV.empty?
|
45
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'githooker'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: githooker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Carl P. Corliss
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: inifile
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: awesome_print
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: shoulda
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: yard
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0.7'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0.7'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rdoc
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '3.12'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '3.12'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: cucumber
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: bundler
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: jeweler
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 1.8.4
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 1.8.4
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: simplecov
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
description: GitHooker provides a framework for building test that can be used with
|
159
|
+
git hooks
|
160
|
+
email: rabbitt@gmail.com
|
161
|
+
executables: []
|
162
|
+
extensions: []
|
163
|
+
extra_rdoc_files:
|
164
|
+
- LICENSE.txt
|
165
|
+
- README.md
|
166
|
+
- README.rdoc
|
167
|
+
files:
|
168
|
+
- .document
|
169
|
+
- Gemfile
|
170
|
+
- LICENSE.txt
|
171
|
+
- README.md
|
172
|
+
- README.rdoc
|
173
|
+
- Rakefile
|
174
|
+
- VERSION
|
175
|
+
- features/githooker.feature
|
176
|
+
- features/step_definitions/githooker_steps.rb
|
177
|
+
- features/support/env.rb
|
178
|
+
- githooker.gemspec
|
179
|
+
- lib/githooker.rb
|
180
|
+
- lib/githooker/action.rb
|
181
|
+
- lib/githooker/core_ext.rb
|
182
|
+
- lib/githooker/core_ext/array.rb
|
183
|
+
- lib/githooker/core_ext/array/min_max.rb
|
184
|
+
- lib/githooker/core_ext/numbers.rb
|
185
|
+
- lib/githooker/core_ext/numbers/infinity.rb
|
186
|
+
- lib/githooker/core_ext/string.rb
|
187
|
+
- lib/githooker/core_ext/string/inflections.rb
|
188
|
+
- lib/githooker/hook.rb
|
189
|
+
- lib/githooker/repo.rb
|
190
|
+
- lib/githooker/runner.rb
|
191
|
+
- lib/githooker/section.rb
|
192
|
+
- lib/githooker/terminal_colors.rb
|
193
|
+
- test/helper.rb
|
194
|
+
- test/test_githooker.rb
|
195
|
+
homepage: http://github.com/rabbitt/githooker
|
196
|
+
licenses:
|
197
|
+
- GPLv2
|
198
|
+
post_install_message:
|
199
|
+
rdoc_options: []
|
200
|
+
require_paths:
|
201
|
+
- lib
|
202
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
203
|
+
none: false
|
204
|
+
requirements:
|
205
|
+
- - ! '>='
|
206
|
+
- !ruby/object:Gem::Version
|
207
|
+
version: '0'
|
208
|
+
segments:
|
209
|
+
- 0
|
210
|
+
hash: -1407532413498943980
|
211
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
212
|
+
none: false
|
213
|
+
requirements:
|
214
|
+
- - ! '>='
|
215
|
+
- !ruby/object:Gem::Version
|
216
|
+
version: '0'
|
217
|
+
requirements: []
|
218
|
+
rubyforge_project:
|
219
|
+
rubygems_version: 1.8.23
|
220
|
+
signing_key:
|
221
|
+
specification_version: 3
|
222
|
+
summary: framework for building git hooks tests
|
223
|
+
test_files: []
|