rgithook 3.0.2

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.
Files changed (45) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +3 -0
  3. data/Manifest +42 -0
  4. data/README.rdoc +12 -0
  5. data/Rakefile +22 -0
  6. data/TODO.txt +4 -0
  7. data/bin/rgithook +13 -0
  8. data/lib/plugins/custom.rb +18 -0
  9. data/lib/plugins/db.rb +34 -0
  10. data/lib/plugins/email.rb +39 -0
  11. data/lib/plugins/html.rb +53 -0
  12. data/lib/plugins/html/commit.html.erb +26 -0
  13. data/lib/plugins/html/diff.html.erb +16 -0
  14. data/lib/plugins/rake.rb +14 -0
  15. data/lib/plugins/spec/rgithook_formatter.rb +74 -0
  16. data/lib/plugins/spec/spec_result.rb +5 -0
  17. data/lib/plugins/temp.rb +41 -0
  18. data/lib/plugins/test.rb +37 -0
  19. data/lib/plugins/twitter.rb +13 -0
  20. data/lib/rgithook.rb +36 -0
  21. data/lib/rgithook/command_line.rb +47 -0
  22. data/lib/rgithook/hook.rb +39 -0
  23. data/lib/rgithook/plugin.rb +105 -0
  24. data/lib/rgithook/rgithook.rb +229 -0
  25. data/lib/rgithook/runner.rb +178 -0
  26. data/lib/rgithook/templates/hook.rb +6 -0
  27. data/lib/rgithook/templates/rgithook.rb +18 -0
  28. data/lib/rgithook/test/unit.rb +70 -0
  29. data/rgithook.gemspec +42 -0
  30. data/test/fixtures/sample_plugin.rb +28 -0
  31. data/test/fixtures/sample_repo.zip +0 -0
  32. data/test/integration/test_install.rb +68 -0
  33. data/test/integration/test_pull_and_push.rb +20 -0
  34. data/test/plugins/test_email.rb +8 -0
  35. data/test/plugins/test_html.rb +6 -0
  36. data/test/plugins/test_spec.rb +6 -0
  37. data/test/plugins/test_test.rb +14 -0
  38. data/test/test_helper.rb +12 -0
  39. data/test/unit/test_command_line.rb +59 -0
  40. data/test/unit/test_hook.rb +31 -0
  41. data/test/unit/test_plugin.rb +36 -0
  42. data/test/unit/test_rgithook.rb +143 -0
  43. data/test/unit/test_runner.rb +95 -0
  44. metadata +174 -0
  45. metadata.gz.sig +2 -0
@@ -0,0 +1,37 @@
1
+ module RGitHook
2
+ class TestSuite < Plugin
3
+
4
+ module RunnerMethods
5
+ def test(commit)
6
+ commit.properties['testing'] = true
7
+ commit.save_properties
8
+ in_temp_commit(commit) do |new_repo|
9
+ # Some hacks to migrate rails app if found.
10
+ if File.file?('./config/environment.rb')
11
+ commit.properties['db:migrate'] = rake('db:migrate')
12
+ commit.properties['db:test:prepare'] = rake('db:test:prepare')
13
+ end
14
+ commit.properties['spec'] = test_spec(repo) if File.directory? File.join(repo.working_dir,'spec')
15
+ commit.properties['cucumber'] = test_cucumber(repo) if File.directory? File.join(repo.working_dir,'features')
16
+ commit.properties['test_unit'] = test_unit(repo) if File.directory? File.join(repo.working_dir,'test')
17
+ end
18
+ commit.properties["status"] = %w(spec cucumber test_unit).map{|t|commit.properties[t][1].exitstatus}.max
19
+ ensure
20
+ commit.properties.delete 'testing'
21
+ commit.save_properties
22
+ end
23
+
24
+ def test_spec(repo)
25
+ [%x(spec spec/),$?]
26
+ end
27
+
28
+ def test_cucumber(repo)
29
+ [%x(cucumber features/),$?]
30
+ end
31
+
32
+ def test_unit(repo)
33
+ [%x(testrb `find test -name '*.rb'`),$?]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,13 @@
1
+
2
+
3
+ class Twitter < RGitHook::Plugin
4
+
5
+ option :user, 'Username', nil, String
6
+ option :password, 'Password', nil, String
7
+
8
+ module RunnerMethods
9
+ def twitter(msg)
10
+ %(curl http://twitter.com -u #{options[:Twitter][:user]} -)
11
+ end
12
+ end
13
+ end
data/lib/rgithook.rb ADDED
@@ -0,0 +1,36 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ begin
4
+
5
+ module RGitHook
6
+ VERSION='3.0.2'
7
+ PATH=File.dirname(File.expand_path(__FILE__))
8
+ end
9
+
10
+ require 'rubygems'
11
+ gem 'mojombo-grit'
12
+ require 'grit'
13
+
14
+ require File.join('rgithook', 'runner')
15
+
16
+ require File.join('rgithook', 'rgithook')
17
+ require File.join('rgithook', 'hook')
18
+ RGitHook.autoload(:Plugin, File.join(RGitHook::PATH,'rgithook','plugin'))
19
+ RGitHook.autoload(:CommandLine, File.join(RGitHook::PATH,'rgithook','command_line'))
20
+
21
+ Dir.glob(File.join(File.dirname(__FILE__),'plugins','*.rb')).each do |plugin|
22
+ begin
23
+ require plugin
24
+ rescue LoadError => e
25
+ puts "Failed to load some plugin #{plugin}\n#{e.to_s}"
26
+ end
27
+ end
28
+
29
+
30
+ rescue LoadError => e
31
+ puts 'Imposible to load some gem'
32
+ puts e.message
33
+ exit -1
34
+ end
35
+
36
+
@@ -0,0 +1,47 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+ require 'fileutils'
4
+
5
+ module RGitHook
6
+ class CommandLine
7
+
8
+ # Parse command line options and execute
9
+ def self.execute(args)
10
+ options = parse_options(args)
11
+
12
+ case options.command
13
+ when :install,:fetch,:edit
14
+ ret_val = RGitHook.new(options.path).send(options.command)
15
+ exit 0
16
+ when :version
17
+ puts 'Version 3.0.0'
18
+ exit 0
19
+ end
20
+
21
+ rescue Grit::InvalidGitRepositoryError
22
+ puts "Invalid Git repository:\n#{@options.path}"
23
+ exit 1
24
+ end
25
+
26
+ private
27
+ def self.parse_options(args)
28
+ options = OpenStruct.new
29
+ options.command = :help
30
+ options.path = Dir.pwd
31
+
32
+ opts = OptionParser.new do |opts|
33
+ opts.banner = "Usage #{File.basename $0} [options]"
34
+ opts.on_head("-i","--install", "Install hooks on current dir") { options.command = :install }
35
+ opts.on_head("-f","--fetch", "Fetch remote origin and run hooks") { options.command = :fetch }
36
+ opts.on_head("-e","--edit", "Edit your hooks file with your EDITOR") { options.command = :edit }
37
+ opts.on("-p","--path=[path]", "Run in another path insted of current path") { |path| options.path = path }
38
+ opts.on_tail("--version", "Print current version and exit") {options.command = :version }
39
+ opts.on_tail("-h","--help", "Print help message")
40
+ end
41
+ opts.parse!(args)
42
+ (puts opts and exit 0) if options.command == :help
43
+ options
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,39 @@
1
+ module RGitHook
2
+ # Interface from git-commands to RGitHook
3
+ class Hook #::nodoc::
4
+ def self.execute
5
+ puts "Executing #{File.basename($0)} hook"
6
+ self.send File.basename($0).tr('-','_')
7
+ end
8
+
9
+ def self.post_receive
10
+ packs = STDIN.read.split("\n")
11
+ ret_vals = []
12
+
13
+ rgithook = RGitHook.new repo_path
14
+
15
+ packs.each do |pack|
16
+ oldrev, newrev, ref = pack.split(' ')
17
+ ret_vals << rgithook.post_receive(oldrev,newrev,ref).first[1].to_i
18
+ end
19
+ return ret_vals.max
20
+ end
21
+
22
+ def self.method_missing(method_name)
23
+ RGitHook.new(repo_path).send(method_name)
24
+ 0
25
+ rescue
26
+ -1
27
+ end
28
+
29
+ private
30
+ def self.repo_path
31
+ if File.basename(File.expand_path(ENV['GIT_DIR'])) == '.git'
32
+ repo_path = File.join(ENV['GIT_DIR'],'..')
33
+ else
34
+ repo_path = ENV['GIT_DIR']
35
+ end
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,105 @@
1
+
2
+ module RGitHook
3
+
4
+ class Plugin
5
+ include ::RGitHook
6
+ class Option #::nodoc::
7
+ attr_reader :name,:description,:default,:type
8
+ def initialize(name,description,default,type)
9
+ @name, @description, @default, @type = name,description,default,type
10
+ end
11
+ end
12
+
13
+ class NoOptionError < Exception ; end
14
+
15
+ class OptionGroup #::nodoc::
16
+ # Todo: Make OptionGroup be an option of OptionGroup
17
+ attr_reader :name,:description
18
+ attr_reader :options
19
+ def initialize(name,description)
20
+ @name,@description = name,description
21
+ @options = []
22
+ end
23
+
24
+ def option(name,desc,default,type)
25
+ @options << Option.new(name,desc,default,type)
26
+ end
27
+
28
+ # Rertun a hash with default options
29
+ #
30
+ def options_to_hash
31
+ group_options = {}
32
+ @options.each do |option|
33
+ group_options[option.name]= option.default
34
+ end
35
+ group_options
36
+ end
37
+ end
38
+
39
+ # ::nodoc::
40
+ # Return raw options
41
+ def self.options
42
+ @@options
43
+ end
44
+
45
+ # Initialize Plugin system by including each plugin modules to the Module
46
+ # It also load the defaults from a yaml file.
47
+ # You can change the default yaml file to load another file
48
+ def self.load!
49
+ @@plugins ||=[]
50
+ @@plugins.each do |plugin|
51
+ if defined?(plugin::RunnerMethods)
52
+ Runner.class_eval do
53
+ include plugin::RunnerMethods
54
+ end
55
+ end
56
+
57
+ if defined?(plugin::GritCommit)
58
+ ::Grit::Commit.class_eval do
59
+ include plugin::GritCommit
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+ # Return a hash with the default options
67
+ def self.options_to_hash
68
+ options = {}
69
+ @@options.each do |plugin,plugin_options|
70
+ plugin_opts = {}
71
+ plugin_options.each do |option|
72
+ plugin_opts[option.name]=option.default.is_a?(OptionGroup) ? option.default.options_to_hash : option.default
73
+ end
74
+ options[plugin]=plugin_opts
75
+ end
76
+ options
77
+ end
78
+
79
+ private
80
+
81
+ # Define a new option
82
+ def self.option(name,desc,default,type)
83
+ @@options[get_plugin_name(self)] << Option.new(name,desc,default,type)
84
+ end
85
+
86
+ def self.option_group(name,desc,&block)
87
+ opt_group = OptionGroup.new(name,desc)
88
+ yield opt_group
89
+ option(name,desc,opt_group,OptionGroup)
90
+ end
91
+
92
+ def self.inherited(base)
93
+ @@plugins ||=[]
94
+ @@plugins << base
95
+ @@options ||={}
96
+ @@options[get_plugin_name(base)]=[]
97
+ end
98
+
99
+ private
100
+ def self.get_plugin_name(kmod)
101
+ kmod.to_s[/(.*\::)?(.*)/,2].to_sym
102
+ end
103
+ end
104
+ end
105
+
@@ -0,0 +1,229 @@
1
+ module RGitHook
2
+ HOOKS = %w(applypatch_msg
3
+ commit_msg
4
+ post_commit
5
+ post_receive
6
+ post_update
7
+ pre_applypatch
8
+ pre_commit
9
+ pre_rebase
10
+ prepare_commit_msg
11
+ update)
12
+
13
+ class RGitHook
14
+ attr_reader :repo
15
+
16
+ # Install RGitHook in the given repo/path
17
+ # if confirmation_needed it will ask to overwrite the existing hooks
18
+ def self.install(path_or_repo, verbose=true)
19
+ repo = parse_path path_or_repo
20
+
21
+ if verbose
22
+ puts "Welcome to RGitHook.
23
+
24
+ These will install rgithook for the next Git Repo
25
+ /Users/guillermo/Sites/
26
+
27
+ These will overwrite all the current hooks and
28
+ create .git/hooks/rgithook.rb file.
29
+
30
+ In that file you will define your hooks in ruby.
31
+ ".gsub(/^ /,'')
32
+ return -1 unless prompt('Continue?')
33
+ end
34
+ hooks_dir = File.join(repo.path,'hooks')
35
+ FileUtils.mkdir hooks_dir unless File.exist? hooks_dir
36
+
37
+ install_template(repo,'rgithook.rb')
38
+
39
+ HOOKS.each do |hook_name|
40
+ install_template(repo,'hook.rb',hook_name.tr('_','-'),0555)
41
+ end
42
+
43
+ if verbose
44
+ puts "RGitHook Installed.
45
+ Now you can:
46
+ * Edit the hooks file by running
47
+ rgithook --edit
48
+
49
+ * Install the hooks file inside the repo. So all
50
+ contributors can run hooks.
51
+ mv .git/hooks/rgithook.rb ./config/rgithook.rb && \
52
+ ln -s ./config/rgithook.rb
53
+
54
+ or
55
+
56
+ mv .git/hooks/rgithook.rb ./config/rgithook.rb && \
57
+ ln -s ./.rgithook.rb
58
+
59
+ * Fetch changes from origin
60
+ rgithook --fetch
61
+ ".gsub(/^ /,'')
62
+ end
63
+ end
64
+
65
+ # Install RGitHook in the given repo/path
66
+ # if confirmation_needed it will ask to overwrite the existing hooks
67
+ def install(confirmation_needed = true)
68
+ self.class.install(@repo,confirmation_needed)
69
+ end
70
+
71
+ # Return true if rgithook is installed in the given repo
72
+ def self.installed? (path_or_repo)
73
+ File.file?(hooks_file(path_or_repo))
74
+ end
75
+
76
+ # Return true if rgithook is installed
77
+ def installed?
78
+ self.class.installed?(@repo)
79
+ end
80
+
81
+ #Extract the projects name
82
+ def project_name
83
+ if @repo.bare
84
+ File.basename(@repo.path) =~ /(.*).git/
85
+ $1 || @repo.bare
86
+ else
87
+ File.basename(@repo.working_dir)
88
+ end
89
+ end
90
+
91
+ # Returns the path of the current repo
92
+ def path
93
+ @repo.path
94
+ end
95
+
96
+ # Returns the path of the working dir or nil if it is a bare repo
97
+ def working_path
98
+ @repo.working_dir
99
+ end
100
+
101
+ # Returns true on bare repo.
102
+ def bare?
103
+ @repo.bare
104
+ end
105
+
106
+ # Open the editor with the config file
107
+ def self.call_editor(path_or_repo)
108
+ repo = parse_path path_or_repo
109
+ system(ENV['EDITOR'] || 'vi',conf_file)
110
+ end
111
+
112
+ # Open the editor with the config file
113
+ def call_editor
114
+ self.class.call_editor(@repo)
115
+ end
116
+
117
+ # Get the config file of the current repo or path
118
+ def self.conf_file(path_or_repo)
119
+ repo = parse_path path_or_repo
120
+ File.join(repo.path,'hooks','rgithook.rb')
121
+ end
122
+
123
+ # Get the config file of the current repo or path
124
+ def conf_file
125
+ self.class.conf_file(@repo)
126
+ end
127
+
128
+ #TODO: Make a reload method, to reload conf_file and hooks_file
129
+
130
+ HOOKS.each do |hook_name|
131
+ start_line = __LINE__
132
+ hook_methods =<<-EOHM
133
+ def self.#{hook_name}(path_or_repo,*args)
134
+ RGitHook.new(path_or_repo).#{hook_name}(*args)
135
+ end
136
+
137
+ def #{hook_name}(*args)
138
+ @runner.run_hooks(:#{hook_name.to_sym},*args)
139
+ end
140
+
141
+ EOHM
142
+ eval(hook_methods,binding,__FILE__,start_line+2)
143
+ end
144
+
145
+
146
+ # Call post_receive hooks
147
+ def self.post_receive(path_or_repo,oldrev,newrev,ref)
148
+ r = RGitHook.new(path_or_repo)
149
+ r.post_receive(oldrev,newrev,ref)
150
+ end
151
+
152
+ # Call post_receive hooks
153
+ def post_receive(oldrev,newrev,ref)
154
+ @runner.run_hooks(:post_receive,oldrev,newrev,ref)
155
+ end
156
+
157
+ def save_plugin_options
158
+ File.open(plugin_conf_file,'w') {|f| f.write @runner.options.to_yaml }
159
+ end
160
+
161
+ # Run some code in the runner binding
162
+ def run(code,file=nil,line=nil)
163
+ @runner.run(code,file,line)
164
+ end
165
+
166
+ def self.hooks_file(repo_or_path)
167
+ File.join(parse_path(repo_or_path).path,'hooks','rgithook.rb')
168
+ end
169
+
170
+ def hooks_file
171
+ self.class.hooks_file(@repo)
172
+ end
173
+
174
+ def self.plugin_conf_file(repo_or_path)
175
+ File.join(parse_path(repo_or_path).path,'hooks','rgithook.yaml')
176
+ end
177
+
178
+ def plugin_conf_file
179
+ self.class.plugin_conf_file(@repo)
180
+ end
181
+
182
+
183
+ private
184
+
185
+ def self.prompt(message)
186
+ while true
187
+ puts message+' [yn]'
188
+ return 'yY'.include?($1) ? true : false if $stdin.gets.strip =~ /([yYnN])/
189
+ end
190
+ end
191
+
192
+
193
+ def self.parse_path(path)
194
+ case path
195
+ when ::Grit::Repo
196
+ path
197
+ when String
198
+ ::Grit::Repo.new(path)
199
+ end
200
+ rescue Grit::InvalidGitRepositoryError
201
+ raise ArgumentError, 'path is not a valid git repo'
202
+ end
203
+
204
+
205
+ # Contains the current runner with the hooks
206
+ attr_reader :runner
207
+
208
+
209
+ def self.install_template(path_or_repo, from,to=nil,mode = 0600)
210
+ to = from if to.nil?
211
+ repo = parse_path(path_or_repo)
212
+
213
+ from = File.join(PATH,'rgithook','templates',from)
214
+ to = File.join(repo.path,'hooks',to)
215
+ FileUtils.install(from,to, :mode => mode)
216
+ end
217
+
218
+ # Initialize a new instance of rgithook in the given repo or path
219
+ def initialize(repo_or_path)
220
+ @repo = self.class.parse_path(repo_or_path)
221
+ Plugin.load!
222
+ @runner = Runner.new(@repo)
223
+ @runner.load_options(plugin_conf_file)
224
+ @runner.load(hooks_file)
225
+ end
226
+
227
+
228
+ end
229
+ end