ruby_git_hooks 0.0.31

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.
@@ -0,0 +1,30 @@
1
+ # Copyright (C) 2013 OL2, Inc. See LICENSE.txt for details.
2
+
3
+ require "ruby_git_hooks"
4
+
5
+ # This hook checks whether a similar file exists with the same name
6
+ # except for uppercase/lowercase. It's useful when Mac OS and Unix
7
+ # people need to coexist in a single Git repository. You can be sure
8
+ # that the Linux people can't check in files that the Mac people can
9
+ # neither see nor delete.
10
+
11
+ class CaseClashHook < RubyGitHooks::Hook
12
+ def check
13
+ downcase_hash = {}
14
+
15
+ ls_files.map(&:strip).each do |filename|
16
+ downcase_hash[filename.downcase] ||= []
17
+ downcase_hash[filename.downcase].push filename
18
+ end
19
+
20
+ okay = true
21
+ downcase_hash.each do |_, filenames|
22
+ if filenames.length > 1
23
+ okay = false
24
+ STDERR.puts "Duplicate-except-case files detected: #{filenames.inspect}"
25
+ end
26
+ end
27
+
28
+ okay
29
+ end
30
+ end
@@ -0,0 +1,181 @@
1
+ # Copyright (C) 2013 OL2, Inc. See LICENSE.txt for details.
2
+
3
+ require "ruby_git_hooks"
4
+
5
+ # This hook checks whether a proper copyright notice exists at the top
6
+ # of each of your files. Right now it hardcodes most of the Copyright
7
+ # notice format, though you can customize the company name. If you'd
8
+ # prefer a more flexible format, we accept pull requests!
9
+
10
+ class CopyrightCheckHook < RubyGitHooks::Hook
11
+ COPYRIGHT_REGEXP = /Copyright\s+\(C\)\s*(?<pre_year>.*)-?(?<cur_year>\d{4})\s+(?<company>.+)/i
12
+
13
+ # Only check files with known checkable extensions
14
+ EXTENSIONS = [
15
+ "c", "cpp", "cc", "cp",
16
+ "h", "hp", "hpp",
17
+ "m", "mm",
18
+ "java",
19
+ "bat",
20
+ "sh",
21
+ "ps1",
22
+ "rb",
23
+ ]
24
+
25
+ OPTIONS = [ "domain", "from", "subject", "via", "via_options", "intro",
26
+ "no_send", "company_check", "exclude_files" ]
27
+
28
+ Hook = RubyGitHooks::Hook
29
+
30
+ attr_accessor :cur_year
31
+
32
+ def initialize(options = {})
33
+ bad_options = options.keys - OPTIONS
34
+ raise "CopyrightCheckHook created with unrecognized options: " +
35
+ "#{bad_options.inspect}!" if bad_options.size > 0
36
+
37
+ @options = options
38
+ @options["domain"] ||= "mydomain.com"
39
+ @options["from"] ||= "Copyright Cop <noreply@#{@options["domain"]}>"
40
+ @options["subject"] ||= "Copyright Your Files, Please!"
41
+ @options["via"] ||= "no_send"
42
+ @options["via_options"] ||= {}
43
+ @options["exclude_files"] ||= [] # for generated files, etc.
44
+ @cur_year = Time.now.strftime("%Y")
45
+ end
46
+
47
+ def check
48
+ no_notice = []
49
+ outdated_notice = []
50
+ outdated_company = []
51
+
52
+ cur_year = Time.now.strftime("%Y")
53
+
54
+ files_changed.each do |filename|
55
+ extension = (filename.split(".") || [])[-1]
56
+ next unless EXTENSIONS.include?(extension)
57
+ next if file_contents[filename] == "" # for now this is how we recognize a deleted file.
58
+ next if @options["exclude_files"].include? filename
59
+ if file_contents[filename] =~ COPYRIGHT_REGEXP
60
+ parsed_cur_year = $~["cur_year"]
61
+ parsed_company = $~["company"]
62
+
63
+ unless parsed_cur_year == cur_year
64
+ outdated_notice << filename
65
+ end
66
+
67
+ # If there is a "company_check" option, either a string
68
+ # or regexp, make sure that the detected company name
69
+ # matches it.
70
+ if @options["company_check"] &&
71
+ !(parsed_company[@options["company_check"]])
72
+ outdated_company << filename
73
+ end
74
+ else
75
+ no_notice << filename
76
+ end
77
+ end
78
+
79
+ bad_num = no_notice.size + outdated_notice.size + outdated_company.size
80
+ return true if bad_num < 1
81
+
82
+ desc = build_description(no_notice, outdated_notice, outdated_company)
83
+
84
+ recipients = {}
85
+ self.commits.each do |commit|
86
+ author = Hook.shell!("git log -n 1 --pretty=format:\"%aE %aN\" #{commit}")
87
+ email, name = author.chomp.split(" ", 2)
88
+ recipients[name] = email
89
+ end
90
+
91
+ STDERR.puts "Warnings for commit from Copyright Check:\n--#{desc}\n--"
92
+
93
+ unless @options["no_send"] || @options["via"] == "no_send"
94
+ require "pony" # wait until we need it
95
+ # NOTE: Pony breaks on Windows so don't use this option in Windows.
96
+ recipients.each do |name, email|
97
+ email_str = "#{name} <#{email}>"
98
+ STDERR.puts "Sending warning email to #{email_str}"
99
+ ret = Pony.mail :to => email_str,
100
+ :from => @options["from"],
101
+ :subject => @options["subject"],
102
+ :body => desc,
103
+ :via => @options["via"],
104
+ :via_options => @options["via_options"]
105
+ end
106
+ end
107
+ # Block commit if installed as a pre-commit or pre-receive hook
108
+ false
109
+ end
110
+
111
+ protected
112
+
113
+ def commit_list
114
+ # return the list of commits to display. We don't want to show them all
115
+ # (it looks scary when there's a lot)
116
+
117
+ # return a list of the shortened SHAH1s for readability
118
+ # tried listing commit.last..commit.first but that didn't seem right either
119
+ # problem is really when there's dozens of commits.
120
+ self.commits.map {|c| c[0..6]}.join(", ")
121
+ end
122
+
123
+
124
+ def current_repo
125
+ # which repository are these commits in
126
+ # Since we are running as a hook, we may get back the .git directory
127
+
128
+ full_path = Hook.shell!("git rev-parse --show-toplevel").chomp
129
+ # like "/path/to/repos/test-repo" or maybe "/path/to/repos/test-repo/.git"
130
+ # return "test-repo"
131
+ File.basename full_path.sub(/.git\z/, "")
132
+ end
133
+ #
134
+ # Return an appropriate email based on the set of files with
135
+ # problems. If you need a different format, please inherit from
136
+ # CopyrightCheckHook and override this method.
137
+ #
138
+ def build_description(no_notice, outdated_notice, outdated_company)
139
+
140
+ description = @options["intro"] || ""
141
+ description.concat <<DESCRIPTION
142
+
143
+ In repository: #{current_repo}
144
+ commit(s): #{commit_list}
145
+
146
+ You have outdated, inaccurate or missing copyright notices.
147
+
148
+ Specifically:
149
+ =============
150
+ DESCRIPTION
151
+
152
+ if outdated_notice.size > 0
153
+ description.concat <<DESCRIPTION
154
+ The following files do not list #{cur_year} as the most recent copyright year:
155
+
156
+ #{outdated_notice.join("\n ")}
157
+ -----
158
+ DESCRIPTION
159
+ end
160
+
161
+ if outdated_company.size > 0
162
+ description.concat <<DESCRIPTION
163
+ The following files do not properly list your company as the holder of copyright:
164
+
165
+ #{outdated_company.join("\n ")}
166
+
167
+ DESCRIPTION
168
+ end
169
+
170
+ if no_notice.size > 0
171
+ description.concat <<DESCRIPTION
172
+ The following files have no notice or a notice I didn't recognize:
173
+
174
+ #{no_notice.join("\n ")}
175
+
176
+ DESCRIPTION
177
+ end
178
+
179
+ description
180
+ end
181
+ end
@@ -0,0 +1,50 @@
1
+ # Copyright (C) 2013 OL2, Inc. See LICENSE.txt for details.
2
+
3
+ require "ruby_git_hooks"
4
+
5
+ # This hook sends out email notification of commits to a configurable
6
+ # list of recipients via the Pony gem, which uses the Ruby Mail gem.
7
+
8
+ class EmailNotifyHook < RubyGitHooks::Hook
9
+ LEGAL_OPTIONS = [ "no_send", "via", "via_options", "max_lines", "recipients" ]
10
+
11
+ def initialize(options = {})
12
+ bad_opts = options.keys - LEGAL_OPTIONS
13
+ unless bad_opts.empty?
14
+ STDERR.puts "Called EmailNotifyHook with bad options: #{bad_opts.inspect}!"
15
+ exit 1
16
+ end
17
+
18
+ @options = options
19
+ @options["max_lines"] ||= 300
20
+
21
+ unless @options["recipients"]
22
+ raise "Must specify at least one recipient to EmailNotifyHook!"
23
+ end
24
+ end
25
+
26
+ def check
27
+ content = file_diffs.flat_map { |path, diff| ["", path, diff] }.join("\n")
28
+ if content.split("\n").size > options["max_lines"]
29
+ content = "Diffs are too big. Skipping them.\nFiles:\n" +
30
+ file_diffs.keys.join("\n")
31
+ end
32
+
33
+ recipients = @options.recipients.split /,|;/
34
+
35
+ unless @options["no_send"] || @options["via"] == "no_send"
36
+ require "pony"
37
+
38
+ recipients.each do |name, email|
39
+ ret = Pony.mail :to => email,
40
+ :from => @options["from"],
41
+ :subject => @options["subject"],
42
+ :body => desc,
43
+ :via => @options["via"],
44
+ :via_options => @options["via_options"]
45
+ end
46
+ end
47
+
48
+ true
49
+ end
50
+ end
@@ -0,0 +1,93 @@
1
+ # Copyright (C) 2013 OL2, Inc. See LICENSE.txt for details.
2
+
3
+ # This is a set of Git operations, run via shell. It permits much
4
+ # cleaner unit tests. Initially it was written in this way because it
5
+ # was very small and simple. Now it should be converted to Grit or a
6
+ # similar tool or library.
7
+
8
+ module RubyGitHooks; end
9
+
10
+ module RubyGitHooks::GitOps
11
+ extend self
12
+
13
+ Hook = RubyGitHooks::Hook
14
+
15
+ def new_bare_repo(name = "parent_repo.git")
16
+ Hook.shell! "mkdir #{name} && cd #{name} && git init --bare"
17
+ end
18
+
19
+ def clone_repo(parent_name = "parent_repo.git", child_name = "child_repo")
20
+ Hook.shell! "git clone #{parent_name} #{child_name}"
21
+ end
22
+
23
+ def add_hook(repo_name, hook_name, contents)
24
+ # We're adding to either a normal or bare directory.
25
+ # Check for hooks directory.
26
+ if File.exist? "#{repo_name}/.git/hooks"
27
+ hooks_dir = "#{repo_name}/.git/hooks"
28
+ elsif File.exist? "#{repo_name}/hooks"
29
+ hooks_dir = "#{repo_name}/hooks"
30
+ else
31
+ raise "Can't locate hooks directory under #{repo_name.inspect}!"
32
+ end
33
+
34
+ filename = File.join(hooks_dir, hook_name)
35
+ File.open(filename, "w") do |f|
36
+ f.write(contents)
37
+ end
38
+ Hook.shell!("chmod +x #{filename}")
39
+ end
40
+
41
+ def new_commit(repo_name, filename, contents = "Contents", commit_message = "Single-file commit of #{filename}")
42
+ if !contents.nil?
43
+ File.open(File.join(repo_name, filename), "w") do |f|
44
+ f.write(contents)
45
+ end
46
+ end
47
+
48
+ Hook.shell! "cd #{repo_name} && git add #{filename} && git commit -m \"#{commit_message}\""
49
+ end
50
+
51
+ def new_single_file_commit(repo_name = "child_repo", contents = "Single-file commit")
52
+ @single_file_counter ||= 1
53
+
54
+ filename = "test_file_#{@single_file_counter}"
55
+
56
+ new_commit(repo_name, filename, contents)
57
+
58
+ @single_file_counter += 1
59
+ end
60
+ system("git mv child_repo/file_to_rename child_repo/renamed_file")
61
+
62
+ def git_delete(repo_name="child_repo", filename="file_to_delete")
63
+ # filename is a file that has already been added and commited to the repo
64
+ Hook.shell! "cd #{repo_name} && git rm #{filename}"
65
+ end
66
+
67
+ def git_rename(repo_name="child_repo", filename="file_to_rename", new_filename)
68
+ # filename is a file that has already been added and commited to the repo
69
+ Hook.shell! "cd #{repo_name} && git mv #{filename} #{new_filename}"
70
+ end
71
+
72
+
73
+ def last_commit_sha(repo_name = "child_repo")
74
+ Hook.shell!("cd #{repo_name} && git log -n 1 --format=%H").chomp
75
+ end
76
+
77
+ def git_commit(repo_name = "child_repo", commit_message = "Generic commit message")
78
+ Hook.shell! "cd #{repo_name} && git commit -m \"#{commit_message}\""
79
+ end
80
+
81
+ def git_push(repo_name = "child_repo", remote = "origin", branch = "master")
82
+ Hook.shell! "cd #{repo_name} && git push #{remote} #{branch}"
83
+ end
84
+
85
+ def rewind_one_commit(repo_name = "child_repo")
86
+ Hook.shell! "cd #{repo_name} && git reset --hard HEAD~"
87
+ end
88
+
89
+ def last_commit_message(repo_name = "child_repo")
90
+ Hook.shell!("cd #{repo_name} && git log -1").chomp
91
+ end
92
+
93
+ end