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