rgithook 3.0.2

Sign up to get free protection for your applications and to get access to all the features.
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