captain_hoog 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +172 -0
- data/Rakefile +2 -0
- data/bin/githoog +110 -0
- data/captain_hoog.gemspec +38 -0
- data/features/failed_hook.feature +14 -0
- data/features/installing_hook.feature +35 -0
- data/features/moving_hooks.feature +34 -0
- data/features/passed_hook.feature +12 -0
- data/features/removing_hook.feature +33 -0
- data/features/support/env.rb +43 -0
- data/features/support/steps/hooks_steps.rb +63 -0
- data/features/support/world.rb +30 -0
- data/lib/captain_hoog/delegatable.rb +33 -0
- data/lib/captain_hoog/dependencies.rb +2 -0
- data/lib/captain_hoog/env.rb +17 -0
- data/lib/captain_hoog/errors/dsl_errors.rb +6 -0
- data/lib/captain_hoog/errors.rb +1 -0
- data/lib/captain_hoog/git.rb +72 -0
- data/lib/captain_hoog/helper_table.rb +23 -0
- data/lib/captain_hoog/plugin.rb +67 -0
- data/lib/captain_hoog/plugin_list.rb +29 -0
- data/lib/captain_hoog/pre_git.rb +125 -0
- data/lib/captain_hoog/templates/hoogfile.erb +39 -0
- data/lib/captain_hoog/templates/install.erb +20 -0
- data/lib/captain_hoog/version.rb +3 -0
- data/lib/captain_hoog.rb +14 -0
- data/spec/fixtures/code/helper.rb +13 -0
- data/spec/fixtures/plugins/shared/passing/simple_shared.rb +11 -0
- data/spec/fixtures/plugins/test_plugins/failing/simple.rb +15 -0
- data/spec/fixtures/plugins/test_plugins/passing/simple.rb +11 -0
- data/spec/fixtures/pre-commit-fail +25 -0
- data/spec/fixtures/pre-commit-pass +25 -0
- data/spec/lib/captain_hoog/env_spec.rb +31 -0
- data/spec/lib/captain_hoog/git_spec.rb +185 -0
- data/spec/lib/captain_hoog/plugin_list_spec.rb +42 -0
- data/spec/lib/captain_hoog/plugin_spec.rb +101 -0
- data/spec/lib/captain_hoog/pre_git_spec.rb +107 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/matchers/be_subclass_of.rb +13 -0
- metadata +236 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
module CaptainHoog
|
2
|
+
class Env < Hash
|
3
|
+
|
4
|
+
def method_missing(meth_name, *args, &block)
|
5
|
+
if self.has_key?(meth_name)
|
6
|
+
return self[meth_name]
|
7
|
+
else
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def respond_to_missing?(meth_name, include_private = false)
|
13
|
+
self.has_key?(meth_name) || super
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'errors/dsl_errors'
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module CaptainHoog
|
2
|
+
class Git
|
3
|
+
|
4
|
+
attr_accessor :env
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@helper_table = HelperTable.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test(&test_block)
|
11
|
+
if test_block
|
12
|
+
@test_block = test_block
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def message
|
17
|
+
if block_given?
|
18
|
+
@message = yield
|
19
|
+
unless @message.is_a?(String)
|
20
|
+
raise CaptainHoog::Errors::MessageResultNotValidError
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def helper(name,&block)
|
26
|
+
return if @helper_table.helper_defined?(name)
|
27
|
+
helper_proc = {}
|
28
|
+
helper_proc[name] = block
|
29
|
+
@helper_table.set(helper_proc)
|
30
|
+
end
|
31
|
+
|
32
|
+
def run(&run_block)
|
33
|
+
@test_result = true
|
34
|
+
@run_block = run_block
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Renders a table.
|
38
|
+
#
|
39
|
+
# rows - An Array of row contents
|
40
|
+
# headings - An Array of headlines
|
41
|
+
#
|
42
|
+
# Returns the table as String.
|
43
|
+
def render_table(rows, headings = [])
|
44
|
+
table = ::Terminal::Table.new(headings: headings, rows: rows)
|
45
|
+
|
46
|
+
table.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def method_missing(meth_name, *args, &block)
|
50
|
+
super unless @helper_table.helper_defined?(meth_name)
|
51
|
+
helper = @helper_table[meth_name]
|
52
|
+
fail ArgumentError unless helper[meth_name].arity == args.size
|
53
|
+
helper[meth_name].call(*args)
|
54
|
+
end
|
55
|
+
|
56
|
+
def respond_to_missing?(meth_name, include_private = false)
|
57
|
+
@helper_table.helper_defined?(meth_name) || super
|
58
|
+
end
|
59
|
+
|
60
|
+
def execute
|
61
|
+
if @test_block
|
62
|
+
@test_result = @test_block.call
|
63
|
+
else
|
64
|
+
# run #run
|
65
|
+
@run_block.call
|
66
|
+
end
|
67
|
+
unless @test_result.is_a?(FalseClass) or @test_result.is_a?(TrueClass)
|
68
|
+
raise CaptainHoog::Errors::TestResultNotValidError
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module CaptainHoog
|
2
|
+
class HelperTable < ::Array
|
3
|
+
|
4
|
+
def helper_defined?(helper_name)
|
5
|
+
defined_helpers.include?(helper_name)
|
6
|
+
end
|
7
|
+
|
8
|
+
def defined_helpers
|
9
|
+
self.map(&:keys).flatten
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](helper_name)
|
13
|
+
self.detect do |helper|
|
14
|
+
helper.keys.include?(helper_name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
alias_method :get, :[]
|
18
|
+
|
19
|
+
def set(helper)
|
20
|
+
self << helper
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module CaptainHoog
|
2
|
+
# Public: Class that evaluates a plugin from a given bunch of DSL code.
|
3
|
+
class Plugin
|
4
|
+
include Delegatable
|
5
|
+
|
6
|
+
attr_accessor :env
|
7
|
+
|
8
|
+
delegate_to :eigenplugin
|
9
|
+
|
10
|
+
# Public: Initializes the Plugin evaluator.
|
11
|
+
#
|
12
|
+
# code - the plugin code as String
|
13
|
+
# env - An instance of CaptainHoog::Env containing some accessible
|
14
|
+
# environment variables (context is limited to CaptainHoog)
|
15
|
+
def initialize(code,env)
|
16
|
+
@code = code
|
17
|
+
@env = env
|
18
|
+
@git = CaptainHoog::Git.new
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Yields the code given in @code.
|
22
|
+
def git
|
23
|
+
eigenplugin
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Evaluates the plugin by 'reading' the dsl. Did not execute
|
27
|
+
# anything.
|
28
|
+
#
|
29
|
+
# Returns nothing
|
30
|
+
def eval_plugin
|
31
|
+
instance_eval(@code)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Public: Executes a plugin and stores the results in a Hash.
|
35
|
+
#
|
36
|
+
# Returns a Hash containing the test result and the failure message.
|
37
|
+
def execute
|
38
|
+
eigenplugin.execute
|
39
|
+
{
|
40
|
+
:result => @git.instance_variable_get(:@test_result),
|
41
|
+
:message => @git.instance_variable_get(:@message)
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def eigenplugin
|
48
|
+
@eigenplugin ||= Class.new do
|
49
|
+
include Delegatable
|
50
|
+
attr_reader :plugin_name
|
51
|
+
|
52
|
+
delegate_to :git
|
53
|
+
|
54
|
+
def initialize(git)
|
55
|
+
@git = git
|
56
|
+
end
|
57
|
+
|
58
|
+
def describe(name)
|
59
|
+
@plugin_name = name
|
60
|
+
yield(@git) if block_given?
|
61
|
+
end
|
62
|
+
|
63
|
+
end.new(@git)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CaptainHoog
|
2
|
+
class PluginList
|
3
|
+
|
4
|
+
attr_reader :plugins
|
5
|
+
|
6
|
+
def initialize(type='pre-commit', config: {})
|
7
|
+
@config = config
|
8
|
+
@type = type
|
9
|
+
build_list
|
10
|
+
end
|
11
|
+
|
12
|
+
def has?(plugin)
|
13
|
+
@plugins.include?(plugin.plugin_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def build_list
|
19
|
+
plugins_for_type = @config.fetch(@type, [])
|
20
|
+
excluded_plugins = @config.fetch('exclude', [])
|
21
|
+
|
22
|
+
plugins_for_type = [] if plugins_for_type.nil?
|
23
|
+
excluded_plugins = [] if excluded_plugins.nil?
|
24
|
+
|
25
|
+
@plugins = plugins_for_type - excluded_plugins
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module CaptainHoog
|
2
|
+
# Public: Entry class for handling a Pre-Something with plugins.
|
3
|
+
class PreGit
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_accessor :project_dir,
|
7
|
+
:plugins_dir,
|
8
|
+
:headline_on_success,
|
9
|
+
:headline_on_failure,
|
10
|
+
:suppress_headline
|
11
|
+
end
|
12
|
+
|
13
|
+
# Public: Runs the hook.
|
14
|
+
#
|
15
|
+
# Inits a new instance of self and evaluates the plugins (if found some)
|
16
|
+
#
|
17
|
+
# Returns an instance of self (CaptainHoog::PreGit)
|
18
|
+
def self.run(plugins_list: nil)
|
19
|
+
pre_git = self.new(plugins_list)
|
20
|
+
pre_git.plugins_eval
|
21
|
+
pre_git
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public: Configures the hook by calling the class' accessors.
|
25
|
+
#
|
26
|
+
# Returns nothing.
|
27
|
+
def self.configure
|
28
|
+
yield(self) if block_given?
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(plugins_list = nil)
|
32
|
+
env = prepare_env
|
33
|
+
@plugins = []
|
34
|
+
@plugins_list = plugins_list
|
35
|
+
if self.class.plugins_dir
|
36
|
+
read_plugins_from_dir(self.class.plugins_dir, env)
|
37
|
+
end
|
38
|
+
if shared_plugins_dir_present?
|
39
|
+
read_plugins_from_dir(shared_plugins_dir, env)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Public: Evaluates all plugins that are found in plugins_dir.
|
44
|
+
#
|
45
|
+
# If any plugin contains a test that returns false (means it fails)
|
46
|
+
# it displays the plugins failure messages and exits with code 1
|
47
|
+
#
|
48
|
+
def plugins_eval
|
49
|
+
raw_results = available_plugins.inject([]) do |result, plugin|
|
50
|
+
result << plugin.execute
|
51
|
+
end
|
52
|
+
@results = raw_results.select{ |result| result.is_a?(Hash) }
|
53
|
+
tests = @results.map{ |result| result[:result] }
|
54
|
+
if tests.any?{ |test| not test }
|
55
|
+
message_on_failure
|
56
|
+
exit 1 unless ENV["PREGIT_ENV"] == "test"
|
57
|
+
else
|
58
|
+
message_on_success
|
59
|
+
exit 0 unless ENV["PREGIT_ENV"] == "test"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def available_plugins
|
66
|
+
@plugins.inject([]) do |result, item|
|
67
|
+
item.eval_plugin
|
68
|
+
result << item if @plugins_list.has?(item)
|
69
|
+
result
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def prepare_env
|
74
|
+
env = Env.new
|
75
|
+
env[:project_dir] = self.class.project_dir
|
76
|
+
env
|
77
|
+
end
|
78
|
+
|
79
|
+
def message_on_success
|
80
|
+
puts defined_message_on(:success).green unless self.class.suppress_headline
|
81
|
+
end
|
82
|
+
|
83
|
+
def message_on_failure
|
84
|
+
unless self.class.suppress_headline
|
85
|
+
puts defined_message_on(:failure).red
|
86
|
+
puts "\n"
|
87
|
+
@results.select{|result| not result[:result] }.each do |result|
|
88
|
+
puts result[:message].red
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def defined_message_on(type)
|
94
|
+
if self.class.send("headline_on_#{type}")
|
95
|
+
self.class.send("headline_on_#{type}")
|
96
|
+
else
|
97
|
+
default_messages[type]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def default_messages
|
102
|
+
{
|
103
|
+
success: "All tests passed. No reason to prevent the commit.",
|
104
|
+
failure: "Commit failed. See errors below."
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def shared_plugins_dir_present?
|
109
|
+
File.exists?(shared_plugins_dir)
|
110
|
+
end
|
111
|
+
|
112
|
+
def shared_plugins_dir
|
113
|
+
File.join(self.class.plugins_dir, "..", "shared")
|
114
|
+
end
|
115
|
+
|
116
|
+
def read_plugins_from_dir(dir, env)
|
117
|
+
Dir["#{dir}/**/**.rb"].each do |plugin|
|
118
|
+
code = File.read(plugin)
|
119
|
+
@plugins << Plugin.new(code,env)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# This file will be created after installing.
|
2
|
+
# It only includes
|
3
|
+
#
|
4
|
+
# * project_dir
|
5
|
+
# * plugin_dir
|
6
|
+
#
|
7
|
+
# at the beginning (if they're provided during installation).
|
8
|
+
|
9
|
+
# Exclude plugins you don't want to be executed within every
|
10
|
+
# hook type. This has no effect in v1.x
|
11
|
+
#
|
12
|
+
# The name is taken from the plugin's name identifier.
|
13
|
+
# exclude:
|
14
|
+
# - cucumber
|
15
|
+
# - log
|
16
|
+
|
17
|
+
# Specify plugins you want to have executed within a pre-commit hook.
|
18
|
+
# If no list is specfied, all plugins are executed (if the pre-commit
|
19
|
+
# hook is installed).
|
20
|
+
#
|
21
|
+
# The name is taken from the plugin's name identifier.
|
22
|
+
# pre-commit:
|
23
|
+
# - pry
|
24
|
+
# - rspec
|
25
|
+
|
26
|
+
# Specify plugins you want to have executed within a pre-push hook.
|
27
|
+
# If no list is specfied, all plugins are executed (if the pre-push
|
28
|
+
# hook is installed).
|
29
|
+
#
|
30
|
+
# The name is taken from the plugin's name identifier.
|
31
|
+
# pre-push:
|
32
|
+
# - mat
|
33
|
+
|
34
|
+
# path in which the hooks should be executed. It's Git root by default.
|
35
|
+
# This is going into env.
|
36
|
+
project_dir: "<%= config[:project_dir] %>"
|
37
|
+
# path to plugins
|
38
|
+
# This is going into env.
|
39
|
+
plugins_dir: "<%= config[:plugins_dir] %>"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'captain_hoog'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
# read config
|
6
|
+
hoog_config = YAML.load_file(File.join('<%= config[:root_dir] %>',
|
7
|
+
'hoogfile.yml'))
|
8
|
+
|
9
|
+
# Adjust settings
|
10
|
+
CaptainHoog::PreGit.configure do |config|
|
11
|
+
config.plugins_dir = hoog_config['plugins_dir']
|
12
|
+
config.project_dir = hoog_config['project_dir']
|
13
|
+
#config.headline_on_success = "All is fine."
|
14
|
+
#config.headline_on_failure = "Oops. Something went wrong"
|
15
|
+
end
|
16
|
+
|
17
|
+
plugin_list = CaptainHoog::PluginList.new('<%=config[:type] %>',
|
18
|
+
config: hoog_config)
|
19
|
+
|
20
|
+
CaptainHoog::PreGit.run(plugins_list: plugin_list)
|
data/lib/captain_hoog.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "captain_hoog/version"
|
2
|
+
require "captain_hoog/dependencies"
|
3
|
+
require "captain_hoog/errors"
|
4
|
+
require "captain_hoog/delegatable"
|
5
|
+
require "captain_hoog/helper_table"
|
6
|
+
require "captain_hoog/env"
|
7
|
+
require "captain_hoog/git"
|
8
|
+
require "captain_hoog/plugin"
|
9
|
+
require "captain_hoog/pre_git"
|
10
|
+
require "captain_hoog/plugin_list"
|
11
|
+
|
12
|
+
module CaptainHoog
|
13
|
+
# Your code goes here...
|
14
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'captain_hoog'
|
3
|
+
|
4
|
+
plugins_path = File.expand_path(File.join(File.dirname(__FILE__),
|
5
|
+
"plugins",
|
6
|
+
"test_plugins",
|
7
|
+
"failing"))
|
8
|
+
|
9
|
+
hoog_config = {
|
10
|
+
'exclude' => [],
|
11
|
+
'plugins_dir' => plugins_path,
|
12
|
+
'project_dir' => 'with_git',
|
13
|
+
'pre-commit' => ['foo']
|
14
|
+
}
|
15
|
+
|
16
|
+
plugin_list = CaptainHoog::PluginList.new('pre-commit',
|
17
|
+
config: hoog_config)
|
18
|
+
|
19
|
+
# Adjust settings
|
20
|
+
CaptainHoog::PreGit.configure do |config|
|
21
|
+
config.plugins_dir = hoog_config['plugins_dir']
|
22
|
+
config.project_dir = hoog_config['project_dir']
|
23
|
+
end
|
24
|
+
|
25
|
+
CaptainHoog::PreGit.run(plugins_list: plugin_list)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'captain_hoog'
|
3
|
+
|
4
|
+
plugins_path = File.expand_path(File.join(File.dirname(__FILE__),
|
5
|
+
"plugins",
|
6
|
+
"test_plugins",
|
7
|
+
"passing"))
|
8
|
+
|
9
|
+
hoog_config = {
|
10
|
+
'exclude' => [],
|
11
|
+
'plugins_dir' => plugins_path,
|
12
|
+
'project_dir' => 'with_git',
|
13
|
+
'pre-commit' => ['simple']
|
14
|
+
}
|
15
|
+
|
16
|
+
plugin_list = CaptainHoog::PluginList.new('pre-commit',
|
17
|
+
config: hoog_config)
|
18
|
+
|
19
|
+
# Adjust settings
|
20
|
+
CaptainHoog::PreGit.configure do |config|
|
21
|
+
config.plugins_dir = hoog_config['plugins_dir']
|
22
|
+
config.project_dir = hoog_config['project_dir']
|
23
|
+
end
|
24
|
+
|
25
|
+
CaptainHoog::PreGit.run(plugins_list: plugin_list)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CaptainHoog::Env do
|
4
|
+
|
5
|
+
it "is inherits from Hash" do
|
6
|
+
expect(subject.class).to be_subclass_of(Hash)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "treats keys as methods" do
|
10
|
+
|
11
|
+
let(:env){ CaptainHoog::Env.new }
|
12
|
+
|
13
|
+
before do
|
14
|
+
env[:foo] = "bar"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "raises not a NoMethodError" do
|
18
|
+
expect { env.foo }.to_not raise_error
|
19
|
+
end
|
20
|
+
|
21
|
+
it "responds to the key as method" do
|
22
|
+
expect(env).to respond_to(:foo)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns the value for the key" do
|
26
|
+
expect(env.foo).to eq "bar"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|