Class: RubyGitHooks::Hook
- Inherits:
-
Object
- Object
- RubyGitHooks::Hook
- Defined in:
- lib/ruby_git_hooks.rb
Overview
applypatch-msg, pre-applypatch, post-applypatch prepare-commit-msg, commit-msg pre-rebase, post-checkout, post-merge, update, post-update, pre-auto-gc, post-rewrite
Direct Known Subclasses
AddWatermarkCommitHook, CaseClashHook, CopyrightCheckHook, EmailNotifyHook, JiraCommentAddHook, JiraReferenceCheckHook, MaxFileSizeHook, NonAsciiCharactersCheckHook, RubyDebugHook
Constant Summary collapse
- HOOK_INFO =
Instances of Hook delegate these methods to the class methods.
[ :files_changed, :file_contents, :file_diffs, :ls_files, :commits, :commit_message, :commit_message_file ]
- HOOK_TYPE_SETUP =
{ # Pre-receive gets no args, but STDIN with a list of changes. "pre-receive" => proc { changes = [] STDIN.each_line do |line| base, commit, ref = line.strip.split changes.push [base, commit, ref] end self.commits = [] self.files_changed = [] self.file_contents = {} self.file_diffs = {} changes.each do |base, commit, ref| no_base = false if base =~ /\A0+\z/ # if base is 000... (initial commit), then all files were added, and git diff won't work no_base = true files_with_status = Hook.shell!("git ls-tree --name-status -r #{commit}").split("\n") # put the A at the front files_with_status.map!{|filename| "A\t" + filename} else files_with_status = Hook.shell!("git diff --name-status #{base}..#{commit}").split("\n") end files_with_status.each do |f| status, file_changed = f.scan(/([ACDMRTUXB])\s+(\S+)$/).flatten self.files_changed << file_changed file_diffs[file_changed] = Hook.shell!("git log -p #{commit} -- #{file_changed}") begin file_contents[file_changed] = status == "D" ? "" : Hook.shell!("git show #{commit}:#{file_changed}") rescue # weird bug where some repos can't run the git show command even when it's not a deleted file. # example: noah-gibbs/barkeep/test/fixtures/text_git_repo I haven't figured out what's # weird about it yet but this fails, so put in a hack for now. May want to leave this since # we'd rather continue without the changes than fail, right? file_contents[file_changed] = "" end end commit_range = no_base ? commit : "#{base}..#{commit}" new_commits = Hook.shell!("git log --pretty=format:%H #{commit_range}").split("\n") self.commits = self.commits | new_commits end if !self.commits.empty? file_list_revision = self.commits.first # can't just use HEAD - remote may be on branch with no HEAD self.ls_files = Hook.shell!("git ls-tree --full-tree --name-only -r #{file_list_revision}").split("\n") # TODO should store ls_files per commit and ls_files with branch name (in case commits on multiple branches)? end }, "pre-commit" => proc { files_with_status = Hook.shell!("git diff --name-status --cached").split("\n") self.files_changed = [] self.file_contents = {} self.file_diffs = {} self.commits = [] files_with_status.each do |f| status, file_changed = f.scan(/([ACDMRTUXB])\s+(\S+)$/).flatten self.files_changed << file_changed file_diffs[file_changed] = Hook.shell!("git diff --cached -- #{file_changed}") file_contents[file_changed] = status == "D"? "": Hook.shell!("git show :#{file_changed}") end self.ls_files = Hook.shell!("git ls-files").split("\n") }, "post-commit" => proc { last_commit_files = Hook.shell!("git log --oneline --name-status -1") # Split, cut off leading line to get actual files with status files_with_status = last_commit_files.split("\n")[1..-1] self.files_changed = [] self.commits = [ Hook.shell!("git log -n 1 --pretty=format:%H").chomp ] self.file_contents = {} self.file_diffs = {} files_with_status.each do |f| status, file_changed = f.scan(/([ACDMRTUXB])\s+(\S+)$/).flatten self.files_changed << file_changed file_diffs[file_changed] = Hook.shell!("git log --oneline -p -1 -- #{file_changed}") file_contents[file_changed] = status == "D"? "": Hook.shell!("git show :#{file_changed}") end self.ls_files = Hook.shell!("git ls-files").split("\n") self. = Hook.shell!("git log -1 --pretty=%B") }, "commit-msg" => proc { files_with_status = Hook.shell!("git diff --name-status --cached").split("\n") self.files_changed = [] self.file_contents = {} self.file_diffs = {} self.commits = [] files_with_status.each do |f| status, file_changed = f.scan(/([ACDMRTUXB])\s+(\S+)$/).flatten self.files_changed << file_changed file_diffs[file_changed] = Hook.shell!("git diff --cached -- #{file_changed}") file_contents[file_changed] = status == "D"? "": Hook.shell!("git show :#{file_changed}") end self.ls_files = Hook.shell!("git ls-files").split("\n") self. = File.read(ARGV[0]) self. = ARGV[0] } }
Class Attribute Summary collapse
-
.commit_message ⇒ Object
Commit message for current commit.
-
.commit_message_file ⇒ Object
Commit message file for current commit.
-
.commits ⇒ Object
All current commits (sometimes empty).
-
.file_contents ⇒ Object
Latest contents of all changed files.
-
.file_diffs ⇒ Object
A human-readable diff per file.
-
.files_changed ⇒ Object
Array of what files were changed.
-
.has_run ⇒ Object
readonly
Whether .run has ever been called.
-
.ls_files ⇒ Object
All filenames in repo.
-
.registered_hooks ⇒ Object
readonly
What hooks are running.
-
.run_as ⇒ Object
readonly
What command line was run.
-
.run_as_hook ⇒ Object
readonly
What git hook is being run.
-
.run_from ⇒ Object
readonly
What directory to run from.
Class Method Summary collapse
- .get_hooks_to_run(hook_specs) ⇒ Object
- .initial_setup ⇒ Object
- .register(hook) ⇒ Object
-
.run(*hook_specs) ⇒ Object
Run takes a list of hook specifications.
- .run_as_specific_githook ⇒ Object
- .shell!(*args) ⇒ Object
Instance Method Summary collapse
Class Attribute Details
.commit_message ⇒ Object
Commit message for current commit
53 54 55 |
# File 'lib/ruby_git_hooks.rb', line 53 def @commit_message end |
.commit_message_file ⇒ Object
Commit message file for current commit
56 57 58 |
# File 'lib/ruby_git_hooks.rb', line 56 def @commit_message_file end |
.commits ⇒ Object
All current commits (sometimes empty)
50 51 52 |
# File 'lib/ruby_git_hooks.rb', line 50 def commits @commits end |
.file_contents ⇒ Object
Latest contents of all changed files
41 42 43 |
# File 'lib/ruby_git_hooks.rb', line 41 def file_contents @file_contents end |
.file_diffs ⇒ Object
A human-readable diff per file
44 45 46 |
# File 'lib/ruby_git_hooks.rb', line 44 def file_diffs @file_diffs end |
.files_changed ⇒ Object
Array of what files were changed
38 39 40 |
# File 'lib/ruby_git_hooks.rb', line 38 def files_changed @files_changed end |
.has_run ⇒ Object (readonly)
Whether .run has ever been called
35 36 37 |
# File 'lib/ruby_git_hooks.rb', line 35 def has_run @has_run end |
.ls_files ⇒ Object
All filenames in repo
47 48 49 |
# File 'lib/ruby_git_hooks.rb', line 47 def ls_files @ls_files end |
.registered_hooks ⇒ Object (readonly)
What hooks are running
23 24 25 |
# File 'lib/ruby_git_hooks.rb', line 23 def registered_hooks @registered_hooks end |
.run_as ⇒ Object (readonly)
What command line was run
26 27 28 |
# File 'lib/ruby_git_hooks.rb', line 26 def run_as @run_as end |
.run_as_hook ⇒ Object (readonly)
What git hook is being run
32 33 34 |
# File 'lib/ruby_git_hooks.rb', line 32 def run_as_hook @run_as_hook end |
.run_from ⇒ Object (readonly)
What directory to run from
29 30 31 |
# File 'lib/ruby_git_hooks.rb', line 29 def run_from @run_from end |
Class Method Details
.get_hooks_to_run(hook_specs) ⇒ Object
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/ruby_git_hooks.rb', line 205 def self.get_hooks_to_run(hook_specs) @registered_hooks ||= {} if hook_specs.empty? return @registered_hooks.values.inject([], &:+) end hook_specs.flat_map do |spec| if @registered_hooks[spec] @registered_hooks[spec] elsif spec.is_a?(Hook) [ spec ] elsif spec.is_a?(String) # A string is assumed to be a class name @registered_hooks[Object.const_get(spec)] else raise "Can't find hook for specification: #{spec.inspect}!" end end end |
.initial_setup ⇒ Object
189 190 191 192 193 194 |
# File 'lib/ruby_git_hooks.rb', line 189 def self.initial_setup return if @run_from @run_from = Dir.getwd @run_as = $0 end |
.register(hook) ⇒ Object
297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/ruby_git_hooks.rb', line 297 def self.register(hook) @registered_hooks ||= {} @registered_hooks[hook.class.name] ||= [] @registered_hooks[hook.class.name].push hook # Figure out when to set this up... #at_exit do # unless RubyGitHooks::Hook.has_run # STDERR.puts "No call to RubyGitHooks.run happened, so no hooks ran!" # end #end end |
.run(*hook_specs) ⇒ Object
Run takes a list of hook specifications. Those can be Hook classnames or instances of type Hook.
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/ruby_git_hooks.rb', line 231 def self.run(*hook_specs) if @has_run STDERR.puts <<ERR In this version, you can't call .run more than once. For now, please register your hooks individually and then call .run with no args, or else call .run with both as arguments. This may be fixed in a future version. Sorry! ERR exit 1 end @has_run = true initial_setup run_as_specific_githook # By default, run all hooks hooks_to_run = get_hooks_to_run(hook_specs.flatten) failed_hooks = [] val = nil hooks_to_run.each do |hook| begin hook.setup { val = hook.check } # Re-init each time, just in case failed_hooks.push(hook) unless val rescue # Failed. Return non-zero if that makes a difference. STDERR.puts "Hook #{hook.inspect} raised exception: #{$!.inspect}!\n#{$!.backtrace.join("\n")}" failed_hooks.push hook end end if CAN_FAIL_HOOKS.include?(@run_as_hook) && failed_hooks.size > 0 STDERR.puts "Hooks failed: #{failed_hooks}" STDERR.puts "Use 'git commit -eF .git/COMMIT_EDITMSG' to restore your commit message" if STDERR.puts "Exiting!" exit 1 end end |
.run_as_specific_githook ⇒ Object
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 |
# File 'lib/ruby_git_hooks.rb', line 271 def self.run_as_specific_githook return if @run_as_hook # Already did this self.initial_setup # Might have already done this if ARGV.include? "--hook" idx = ARGV.find_index "--hook" @run_as_hook = ARGV[idx + 1] 2.times { ARGV.delete_at(idx) } else @run_as_hook = HOOK_NAMES.detect { |hook| @run_as.include?(hook) } end unless @run_as_hook STDERR.puts "Name #{@run_as.inspect} doesn't include " + "any of: #{HOOK_NAMES.inspect}" exit 1 end unless HOOK_TYPE_SETUP[@run_as_hook] STDERR.puts "No setup defined for hook type #{@run_as_hook.inspect}!" exit 1 end self.instance_eval(&HOOK_TYPE_SETUP[@run_as_hook]) end |
.shell!(*args) ⇒ Object
310 311 312 313 314 315 316 317 318 319 320 |
# File 'lib/ruby_git_hooks.rb', line 310 def self.shell!(*args) output = `#{args.join(" ")}` unless $?.success? STDERR.puts "Job #{args.inspect} failed in dir #{Dir.getwd.inspect}" STDERR.puts "Failed job output:\n#{output}\n======" raise "Exec of #{args.inspect} failed: #{$?}!" end output end |