ruby_git_hooks 0.0.31

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