git-commit-notifier 0.11.2 → 0.11.3

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.
@@ -2,32 +2,51 @@
2
2
 
3
3
  require 'premailer'
4
4
 
5
+ # Represents email sender.
5
6
  class GitCommitNotifier::Emailer
6
- DEFAULT_STYLESHEET_PATH = File.join(File.dirname(__FILE__), '/../../template/styles.css').freeze
7
- TEMPLATE = File.join(File.dirname(__FILE__), '/../../template/email.html.erb').freeze
8
- PARAMETERS = %w[project_path recipient from_address from_alias subject text_message html_message repo_name ref_name old_rev new_rev].freeze
7
+ # Default CSS stylesheet file path
8
+ DEFAULT_STYLESHEET_PATH = File.join(File.dirname(__FILE__), *'../../template/styles.css'.split('/')).freeze
9
+ # Default ERB template file path
10
+ TEMPLATE = File.join(File.dirname(__FILE__), *'../../template/email.html.erb'.split('/')).freeze
11
+ # Instance variable names
12
+ PARAMETERS = %w[project_path recipient from_address from_alias date subject text_message html_message repo_name ref_name old_rev new_rev].freeze
9
13
 
14
+ # Gets config.
15
+ # @return [Hash] Configuration
16
+ # @note Helper that represents class method in instance scope.
17
+ # @see GitCommitNotifier::Emailer.config
10
18
  def config
11
- @@config
19
+ GitCommitNotifier::Emailer.config
12
20
  end
13
21
 
14
22
  def initialize(config, options = {})
15
- @@config = config || {}
23
+ GitCommitNotifier::Emailer.config = config || {}
16
24
  PARAMETERS.each do |name|
17
25
  instance_variable_set("@#{name}".to_sym, options[name.to_sym])
18
26
  end
19
27
  end
20
28
 
21
29
  class << self
30
+ # [Hash] Gets or sets config.
31
+ attr_accessor :config
32
+
33
+ # Resets compiled template.
34
+ # @note Useful for tests.
35
+ # @return [NilClass] nil
22
36
  def reset_template
23
37
  @template = nil
24
38
  end
25
39
 
40
+ # Reads template source code from file system.
41
+ # @return [String] Template source code.
26
42
  def template_source
27
- template_file = @@config['custom_template'] || TEMPLATE
43
+ template_file = config['custom_template'] || TEMPLATE
28
44
  IO.read(template_file)
29
45
  end
30
46
 
47
+ # Gets or reads compiled template.
48
+ # @return [Object] Compiled template.
49
+ # @note Erubis used as template engine if present; ERB otherwise.
31
50
  def template
32
51
  unless @template
33
52
  source = template_source
@@ -41,8 +60,30 @@ class GitCommitNotifier::Emailer
41
60
  end
42
61
  @template
43
62
  end
63
+
64
+ # Resets CSS stylesheet source.
65
+ # @note Useful for tests.
66
+ # @return [NilClass] nil
67
+ def reset_stylesheet
68
+ @stylesheet = nil
69
+ end
70
+
71
+ # Reads CSS stylesheet source code.
72
+ # @return [String] Stylesheet source code.
73
+ def stylesheet_source
74
+ stylesheet = config['stylesheet'] || DEFAULT_STYLESHEET_PATH
75
+ IO.read(stylesheet)
76
+ end
77
+
78
+ # Gets or reads CSS stylesheet.
79
+ # @return [String] Stylesheet source code.
80
+ def stylesheet
81
+ @stylesheet ||= stylesheet_source
82
+ end
44
83
  end
45
84
 
85
+ # Gets HTML-formatted message.
86
+ # @return [String] HTML-formatted message.
46
87
  def mail_html_message
47
88
  html = GitCommitNotifier::Emailer.template.result(binding)
48
89
  if config['expand_css'].nil? || config['expand_css']
@@ -52,6 +93,16 @@ class GitCommitNotifier::Emailer
52
93
  html
53
94
  end
54
95
 
96
+ # Gets stylesheet string.
97
+ # @note This is helper to provide data from class context.
98
+ # @see GitCommitNotifier::Emailer.stylesheet
99
+ # @return [String] Stylesheet source code.
100
+ def stylesheet_string
101
+ GitCommitNotifier::Emailer.stylesheet
102
+ end
103
+
104
+ # Gets or creates email part boundary
105
+ # @return [String] Email part boundary.
55
106
  def boundary
56
107
  return @boundary if @boundary
57
108
  srand
@@ -59,17 +110,17 @@ class GitCommitNotifier::Emailer
59
110
  @boundary = Digest::SHA1.hexdigest(seed)
60
111
  end
61
112
 
62
- def stylesheet_string
63
- stylesheet = config['stylesheet'] || DEFAULT_STYLESHEET_PATH
64
- IO.read(stylesheet)
65
- end
66
-
113
+ # Performs email delivery in debug mode (to STDOUT).
114
+ # @return [NilClass] nil
67
115
  def perform_delivery_debug(content)
68
116
  content.each do |line|
69
117
  puts line
70
118
  end
119
+ nil
71
120
  end
72
121
 
122
+ # Performs email delivery through SMTP.
123
+ # @return [NilClass] nil
73
124
  def perform_delivery_smtp(content, smtp_settings)
74
125
  settings = { }
75
126
  %w(address port domain user_name password authentication enable_tls).each do |key|
@@ -94,8 +145,11 @@ class GitCommitNotifier::Emailer
94
145
  end
95
146
  end
96
147
  end
148
+ nil
97
149
  end
98
150
 
151
+ # Performs email delivery through Sendmail.
152
+ # @return [NilClass] nil
99
153
  def perform_delivery_sendmail(content, options = nil)
100
154
  sendmail_settings = {
101
155
  'location' => "/usr/sbin/sendmail",
@@ -108,28 +162,38 @@ class GitCommitNotifier::Emailer
108
162
  end
109
163
  f.flush
110
164
  end
165
+ nil
111
166
  end
112
167
 
168
+ # Performs email delivery through NNTP.
169
+ # @return [NilClass] nil
113
170
  def perform_delivery_nntp(content, nntp_settings)
114
171
  require 'nntp'
115
172
  Net::NNTP.start(nntp_settings['address'], nntp_settings['port']) do |nntp|
116
173
  nntp.post content
117
174
  end
175
+ nil
118
176
  end
119
177
 
178
+ # Creates email message and sends it using configured delivery method.
179
+ # @return [NilClass] nil
120
180
  def send
121
181
  to_tag = config['delivery_method'] == 'nntp' ? 'Newsgroups' : 'To'
122
182
  quoted_from_alias = !@from_alias.nil? ? quote_if_necessary("#{@from_alias}",'utf-8') : nil
123
183
  from = (@from_alias.nil? || @from_alias.empty?) ? @from_address : "#{quoted_from_alias} <#{@from_address}>"
124
-
184
+
125
185
  plaintext = if config['add_plaintext'].nil? || config['add_plaintext']
126
186
  @text_message
127
187
  else
128
188
  "Plain text part omitted. Consider setting add_plaintext in configuration."
129
189
  end
130
-
190
+
131
191
  content = []
132
- content << "From: #{from}" if !from.nil?
192
+ content << "From: #{from}" unless from.nil?
193
+
194
+ # Setting the email date from the commit date is undesired by those
195
+ # who sort their email by send date instead of receive date
196
+ #content << "Date: #{@date}" if !@date.nil?
133
197
 
134
198
  content.concat [
135
199
  "#{to_tag}: #{quote_if_necessary(@recipient, 'utf-8')}",
@@ -168,10 +232,11 @@ class GitCommitNotifier::Emailer
168
232
  else # sendmail
169
233
  perform_delivery_sendmail(content, config['sendmail_options'])
170
234
  end
235
+ nil
171
236
  end
172
237
 
173
238
  # Convert a message into quoted printable encoding,
174
- # limiting line length to 76 characters per spec
239
+ # limiting line length to 76 characters per spec.
175
240
  # Encoding messages in this way ensures that they
176
241
  # won't violate rules for maximum line length, which
177
242
  # can result in the MTA breaking lines at inconvenient points,
@@ -180,7 +245,7 @@ class GitCommitNotifier::Emailer
180
245
  StringIO.open("", "w") do |output|
181
246
  # Character encoding of output string can be plain US-ASCII since quoted-printable is plain ASCII
182
247
  output.string.force_encoding("US-ASCII") if output.string.respond_to?(:force_encoding)
183
-
248
+
184
249
  line_max = 76
185
250
  line_len = 0
186
251
 
@@ -239,3 +304,5 @@ class GitCommitNotifier::Emailer
239
304
  end
240
305
  end
241
306
 
307
+
308
+
@@ -1,7 +1,12 @@
1
- # -*- coding: utf-8; mode: ruby; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- vim:fenc=utf-8:filetype=ruby:et:sw=2:ts=2:sts=2
1
+ # -*- coding: utf-8; mode: ruby; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2
2
+ # -*- vim:fenc=utf-8:filetype=ruby:et:sw=2:ts=2:sts=2
2
3
 
4
+ # Provides content escaping helpers.
3
5
  module GitCommitNotifier::EscapeHelper
4
-
6
+ # Expand tabs into spaces.
7
+ # @param [String] s Text to be expanded.
8
+ # @param [FixNum] tabWidth One tab indentation (in count of spaces).
9
+ # @return [String] Expanded text.
5
10
  def expand_tabs(s, tabWidth)
6
11
  delta = 0
7
12
  s.gsub(/\t/) do |m|
@@ -11,6 +16,10 @@ module GitCommitNotifier::EscapeHelper
11
16
  end
12
17
  end
13
18
 
19
+ # Escapes expanded content using CGI.escapeHTML and {#expand_tabs}.
20
+ # @param [String] s Text to be expanded and extended.
21
+ # @return [String] Escaped and expanded text.
22
+ # @see #expand_tabs
14
23
  def escape_content(s)
15
24
  CGI.escapeHTML(expand_tabs(s, 4)).gsub(" ", "&nbsp;")
16
25
  end
@@ -21,7 +21,7 @@ module GitCommitNotifier
21
21
  GitCommitNotifier::CommitHook.show_error("No data given on standard input")
22
22
  return
23
23
  end
24
-
24
+
25
25
  # Note that there may be multiple lines on stdin, such
26
26
  # as in the case of multiple tags being pushed
27
27
  $stdin.each_line do |line|
@@ -5,17 +5,28 @@ require 'set'
5
5
  # Git methods
6
6
  class GitCommitNotifier::Git
7
7
  class << self
8
- # Runs specified command.
8
+ # Runs specified command and gets its output.
9
9
  # @return (String) Shell command STDOUT (forced to UTF-8)
10
10
  # @raise [ArgumentError] when command exits with nonzero status.
11
11
  def from_shell(cmd)
12
12
  r = `#{cmd}`
13
- raise ArgumentError.new("#{cmd} failed") unless $?.exitstatus.zero?
13
+ raise ArgumentError.new("#{cmd} failed") unless $?.exitstatus.zero?
14
14
  r.force_encoding(Encoding::UTF_8) if r.respond_to?(:force_encoding)
15
15
  r
16
16
  end
17
17
 
18
- # runs `git show`
18
+ # Runs specified command and gets its output as array of lines.
19
+ # @return (Enumerable(String)) Shell command STDOUT (forced to UTF-8) as enumerable lines.
20
+ # @raise [ArgumentError] when command exits with nonzero status.
21
+ # @see from_shell
22
+ def lines_from_shell(cmd)
23
+ lines = from_shell(cmd)
24
+ # Ruby 1.9 tweak.
25
+ lines = lines.lines if lines.respond_to?(:lines)
26
+ lines
27
+ end
28
+
29
+ # Runs `git show`
19
30
  # @note uses "--pretty=fuller" option.
20
31
  # @return [String] Its output
21
32
  # @see from_shell
@@ -23,34 +34,30 @@ class GitCommitNotifier::Git
23
34
  # @param [Hash] opts Options
24
35
  # @option opts [Boolean] :ignore_whitespaces Ignore whitespaces or not
25
36
  def show(rev, opts = {})
26
- gitopt = ""
37
+ gitopt = " --date=rfc2822"
27
38
  gitopt += " --pretty=fuller"
28
39
  gitopt += " -w" if opts[:ignore_whitespaces]
29
- data = from_shell("git show #{rev.strip}#{gitopt}")
30
- data
40
+ from_shell("git show #{rev.strip}#{gitopt}")
31
41
  end
32
42
 
33
- # runs `git log`
43
+ # Runs `git log`
34
44
  # @note uses "--pretty=fuller" option.
35
45
  # @return [String] Its output
36
46
  # @see from_shell
37
47
  # @param [String] rev1 First revision
38
48
  # @param [String] rev2 Second revision
39
49
  def log(rev1, rev2)
40
- data = from_shell("git log --pretty=fuller #{rev1}..#{rev2}").strip
41
- data
50
+ from_shell("git log --pretty=fuller #{rev1}..#{rev2}").strip
42
51
  end
43
52
 
44
- # runs `git log` and extract filenames only
53
+ # Runs `git log` and extract filenames only
45
54
  # @note uses "--pretty=fuller" and "--name-status" options.
46
55
  # @return [Array(String)] File names
47
- # @see from_shell
56
+ # @see lines_from_shell
48
57
  # @param [String] rev1 First revision
49
58
  # @param [String] rev2 Second revision
50
59
  def changed_files(rev1, rev2)
51
- output = ""
52
- lines = from_shell("git log #{rev1}..#{rev2} --name-status --pretty=oneline")
53
- lines = lines.lines if lines.respond_to?(:lines)
60
+ lines = lines_from_shell("git log #{rev1}..#{rev2} --name-status --pretty=oneline")
54
61
  lines = lines.select {|line| line =~ /^\w{1}\s+\w+/} # grep out only filenames
55
62
  lines.uniq
56
63
  end
@@ -59,18 +66,16 @@ class GitCommitNotifier::Git
59
66
  args = branch_heads - [ branch_head(treeish) ]
60
67
  args.map! { |tree| "^#{tree}" }
61
68
  args << treeish
62
- lines = from_shell("git rev-list #{args.join(' ')}")
63
- lines = lines.lines if lines.respond_to?(:lines)
69
+ lines = lines_from_shell("git rev-list #{args.join(' ')}")
64
70
  lines.to_a.map { |commit| commit.chomp }
65
71
  end
66
72
 
67
73
  def branch_heads
68
- lines = from_shell("git rev-parse --branches")
69
- lines = lines.lines if lines.respond_to?(:lines)
74
+ lines = lines_from_shell("git rev-parse --branches")
70
75
  lines.to_a.map { |head| head.chomp }
71
76
  end
72
77
 
73
- def git_dir()
78
+ def git_dir
74
79
  from_shell("git rev-parse --git-dir").strip
75
80
  end
76
81
 
@@ -81,37 +86,36 @@ class GitCommitNotifier::Git
81
86
  def branch_head(treeish)
82
87
  from_shell("git rev-parse #{treeish}").strip
83
88
  end
84
-
89
+
85
90
  def new_commits(oldrev, newrev, refname, unique_to_current_branch)
86
91
  # We want to get the set of commits (^B1 ^B2 ... ^oldrev newrev)
87
92
  # Where B1, B2, ..., are any other branch
88
-
89
93
  s = Set.new
90
-
94
+
91
95
  # If we want to include only those commits that are
92
96
  # unique to this branch, then exclude commits that occur on
93
97
  # other branches
94
98
  if unique_to_current_branch
95
99
  # Make a set of all branches, not'd (^BCURRENT ^B1 ^B2...)
96
- not_branches = from_shell("git rev-parse --not --branches")
97
- s.merge(not_branches.lines.map {|l| l.chomp}.to_set)
98
-
100
+ not_branches = lines_from_shell("git rev-parse --not --branches")
101
+ s.merge(not_branches.to_a.map { |l| l.chomp }.to_set)
102
+
99
103
  # Remove the current branch (^BCURRENT) from the set
100
104
  current_branch = rev_parse(refname)
101
105
  s.delete("^#{current_branch}")
102
106
  end
103
-
107
+
104
108
  # Add not'd oldrev (^oldrev)
105
- s.add("^#{oldrev}") unless oldrev =~ /^0+$/
109
+ s.add("^#{oldrev}") unless oldrev =~ /^0+$/
106
110
 
107
111
  # Add newrev
108
112
  s.add(newrev)
109
-
113
+
110
114
  # We should now have ^B1... ^oldrev newrev
111
115
 
112
116
  # Get all the commits that match that specification
113
- lines = from_shell("git rev-list #{s.to_a.join(' ')}")
114
- commits = lines.lines.map {|l| l.chomp}
117
+ lines = lines_from_shell("git rev-list --reverse #{s.to_a.join(' ')}")
118
+ commits = lines.to_a.map { |l| l.chomp }
115
119
  end
116
120
 
117
121
  def rev_type(rev)
@@ -119,7 +123,7 @@ class GitCommitNotifier::Git
119
123
  rescue ArgumentError
120
124
  nil
121
125
  end
122
-
126
+
123
127
  def tag_info(refname)
124
128
  fields = [
125
129
  ':tagobject => %(*objectname)',
@@ -134,16 +138,23 @@ class GitCommitNotifier::Git
134
138
  eval(hash_script)
135
139
  end
136
140
 
141
+ # Gets repository name.
142
+ # @note Tries to gets human readable repository name through `git config hooks.emailprefix` call.
143
+ # If it's not specified then returns directory name (except '.git' suffix if exists).
144
+ # @return [String] Human readable repository name.
137
145
  def repo_name
138
146
  git_prefix = begin
139
147
  from_shell("git config hooks.emailprefix").strip
140
148
  rescue ArgumentError
141
149
  ''
142
150
  end
143
- return git_prefix unless git_prefix.empty?
151
+ return git_prefix unless git_prefix.empty?
144
152
  File.expand_path(git_dir).split("/").last.sub(/\.git$/, '')
145
153
  end
146
154
 
155
+ # Gets mailing list address.
156
+ # @note mailing list address retrieved through `git config hooks.mailinglist` call.
157
+ # @return [String] Mailing list address if exists; otherwise nil.
147
158
  def mailing_list_address
148
159
  from_shell("git config hooks.mailinglist").strip
149
160
  rescue ArgumentError
@@ -4,19 +4,21 @@ require 'cgi'
4
4
  require 'git_commit_notifier/escape_helper'
5
5
 
6
6
  module GitCommitNotifier
7
+ # Processes diff result
8
+ # input (loaded in @diff) is an array having Hash elements:
9
+ # { :action => action, :token => string }
10
+ # action can be :discard_a, :discard_b or :match
11
+ # output: two formatted html strings, one for the removals and one for the additions
7
12
  class ResultProcessor
8
13
  include EscapeHelper
9
- # input (loaded in @diff) is an array having Hash elements:
10
- # { :action => action, :token => string }
11
- # action can be :discard_a, :discard_b or :match
12
-
13
- # output: two formatted html strings, one for the removals and one for the additions
14
14
 
15
15
  def results
16
16
  close_tags # close last tag
17
17
  [array_of_lines(@result[:removal]), array_of_lines(@result[:addition])]
18
18
  end
19
19
 
20
+ # Gets length of tokenized diff in characters.
21
+ # @return [FixNum] Length of the tokenized diff in characters.
20
22
  def length_in_chars(diff)
21
23
  diff.inject(0) do |length, s|
22
24
  token = s[:token]
@@ -74,7 +74,7 @@ describe GitCommitNotifier::CommitHook do
74
74
  def expect_repository_access
75
75
  mock(GitCommitNotifier::Git).rev_type(REVISIONS.first) { "commit" }
76
76
  mock(GitCommitNotifier::Git).rev_type(REVISIONS.last) { "commit" }
77
- mock(GitCommitNotifier::Git).new_commits(anything, anything, anything, anything) { REVISIONS }
77
+ mock(GitCommitNotifier::Git).new_commits(anything, anything, anything, anything) { REVISIONS }
78
78
  mock(GitCommitNotifier::Git).mailing_list_address { 'recipient@test.com' }
79
79
  mock(GitCommitNotifier::Git).repo_name { 'testproject' }
80
80
  mock(GitCommitNotifier::Git).changed_files('7e4f6b4', '4f13525') { [] }