github_changelog_generator 1.3.11 → 1.4.0

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.
@@ -1,21 +1,21 @@
1
1
  # coding: utf-8
2
2
 
3
- lib = File.expand_path('../lib', __FILE__)
3
+ lib = File.expand_path("../lib", __FILE__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'github_changelog_generator/version'
5
+ require "github_changelog_generator/version"
6
6
 
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = "github_changelog_generator"
9
9
  spec.version = GitHubChangelogGenerator::VERSION
10
10
  spec.default_executable = "github_changelog_generator"
11
11
 
12
- spec.required_ruby_version = '>= 1.9.3'
12
+ spec.required_ruby_version = ">= 1.9.3"
13
13
  spec.authors = ["Petr Korolev"]
14
- spec.email = %q{sky4winder+github_changelog_generator@gmail.com}
14
+ spec.email = "sky4winder+github_changelog_generator@gmail.com"
15
15
  spec.date = `date +"%Y-%m-%d"`.strip!
16
- spec.summary = %q{Script, that automatically generate changelog from your tags, issues, labels and pull requests.}
17
- spec.description = %q{Changelog generation has never been so easy. Fully automate changelog generation - this gem generate change log file based on tags, issues and merged pull requests from Github issue tracker.}
18
- spec.homepage = %q{https://github.com/skywinder/Github-Changelog-Generator}
16
+ spec.summary = "Script, that automatically generate changelog from your tags, issues, labels and pull requests."
17
+ spec.description = "Changelog generation has never been so easy. Fully automate changelog generation - this gem generate change log file based on tags, issues and merged pull requests from Github issue tracker."
18
+ spec.homepage = "https://github.com/skywinder/Github-Changelog-Generator"
19
19
  spec.license = "MIT"
20
20
 
21
21
  spec.files = `git ls-files -z`.split("\x0")
@@ -26,7 +26,6 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency "bundler", "~> 1.7"
27
27
  spec.add_development_dependency "rake", "~> 10.0"
28
28
 
29
- spec.add_runtime_dependency(%q<github_api>, ["~> 0.12"])
30
- spec.add_runtime_dependency(%q<colorize>, ["~> 0.7"])
31
-
29
+ spec.add_runtime_dependency("github_api", ["~> 0.12"])
30
+ spec.add_runtime_dependency("colorize", ["~> 0.7"])
32
31
  end
@@ -6,6 +6,8 @@
6
6
 
7
7
  **Merged pull requests:**
8
8
 
9
+ - This PR SHOULD NOT appear in change log! [\#6](https://github.com/skywinder/changelog_test/pull/6) ([skywinder](https://github.com/skywinder))
10
+
9
11
  - Add automatically generated change log file. [\#5](https://github.com/skywinder/changelog_test/pull/5) ([skywinder](https://github.com/skywinder))
10
12
 
11
13
  ## [v0.0.3](https://github.com/skywinder/changelog_test/tree/v0.0.3) (2015-03-04)
@@ -1,62 +1,65 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'github_api'
4
- require 'json'
5
- require 'colorize'
6
- require 'benchmark'
3
+ require "github_api"
4
+ require "json"
5
+ require "colorize"
6
+ require "benchmark"
7
7
 
8
- require_relative 'github_changelog_generator/parser'
9
- require_relative 'github_changelog_generator/generator'
10
- require_relative 'github_changelog_generator/version'
8
+ require_relative "github_changelog_generator/parser"
9
+ require_relative "github_changelog_generator/generator"
10
+ require_relative "github_changelog_generator/version"
11
+ require_relative "github_changelog_generator/reader"
12
+ require_relative "github_changelog_generator/fetcher"
11
13
 
12
14
  module GitHubChangelogGenerator
13
- class ChangelogGenerator
15
+ # Default error for ChangelogGenerator
16
+ class ChangelogGeneratorError < StandardError
17
+ end
14
18
 
19
+ # Main class and entry point for this script.
20
+ class ChangelogGenerator
15
21
  attr_accessor :options, :all_tags, :github
16
22
 
17
- PER_PAGE_NUMBER = 30
18
-
23
+ # Class, responsible for whole change log generation cycle
24
+ # @return initialised instance of ChangelogGenerator
19
25
  def initialize
20
-
21
26
  @options = Parser.parse_options
22
27
 
23
- fetch_github_token
28
+ @fetcher = GitHubChangelogGenerator::Fetcher.new @options
24
29
 
25
- github_options = {per_page: PER_PAGE_NUMBER}
26
- github_options[:oauth_token] = @github_token unless @github_token.nil?
27
- github_options[:endpoint] = options[:github_endpoint] unless options[:github_endpoint].nil?
28
- github_options[:site] = options[:github_endpoint] unless options[:github_site].nil?
30
+ @generator = Generator.new @options
29
31
 
30
- begin
31
- @github = Github.new github_options
32
- rescue
33
- puts "Warning: GitHub API rate limit exceed (5000 per hour), change log may not contain some issues.".yellow
34
- end
32
+ # @all_tags = get_filtered_tags
33
+ @all_tags = @fetcher.get_all_tags
35
34
 
36
- @generator = Generator.new(@options)
35
+ @issues, @pull_requests = @fetcher.fetch_issues_and_pull_requests
37
36
 
38
- @all_tags = self.get_all_tags
39
- @issues, @pull_requests = self.fetch_issues_and_pull_requests
37
+ @pull_requests = @options[:pulls] ? get_filtered_pull_requests : []
40
38
 
41
- if @options[:pulls]
42
- @pull_requests = self.get_filtered_pull_requests
43
- else
44
- @pull_requests = []
45
- end
46
-
47
- if @options[:issues]
48
- @issues = self.get_filtered_issues
49
- else
50
- @issues = []
51
- end
39
+ @issues = @options[:issues] ? get_filtered_issues : []
52
40
 
53
41
  fetch_event_for_issues_and_pr
54
42
  detect_actual_closed_dates
55
- @tag_times_hash = {}
56
43
  end
57
44
 
58
- def detect_actual_closed_dates
45
+ # Return tags after filtering tags in lists provided by option: --between-tags & --exclude-tags
46
+ #
47
+ # @return [Array]
48
+ def get_filtered_tags
49
+ all_tags = @fetcher.get_all_tags
50
+ filtered_tags = []
51
+ if @options[:between_tags]
52
+ @options[:between_tags].each do |tag|
53
+ unless all_tags.include? tag
54
+ puts "Warning: can't find tag #{tag}, specified with --between-tags option.".yellow
55
+ end
56
+ end
57
+ filtered_tags = all_tags.select { |tag| @options[:between_tags].include? tag }
58
+ end
59
+ filtered_tags
60
+ end
59
61
 
62
+ def detect_actual_closed_dates
60
63
  if @options[:verbose]
61
64
  print "Fetching closed dates for issues...\r"
62
65
  end
@@ -74,32 +77,30 @@ rescue
74
77
  find_closed_date_by_commit(pull_request)
75
78
  }
76
79
  }
77
- threads.each { |thr| thr.join }
80
+ threads.each(&:join)
78
81
 
79
82
  if @options[:verbose]
80
- puts 'Fetching closed dates for issues: Done!'
83
+ puts "Fetching closed dates for issues: Done!"
81
84
  end
82
85
  end
83
86
 
87
+ # Fill :actual_date parameter of specified issue by closed date of the commit, it it was closed by commit.
88
+ # @param [Hash] issue
84
89
  def find_closed_date_by_commit(issue)
85
- unless issue['events'].nil?
86
- #if it's PR -> then find "merged event", in case of usual issue -> fond closed date
87
- compare_string = issue[:merged_at].nil? ? 'closed' : 'merged'
90
+ unless issue["events"].nil?
91
+ # if it's PR -> then find "merged event", in case of usual issue -> fond closed date
92
+ compare_string = issue[:merged_at].nil? ? "closed" : "merged"
88
93
  # reverse! - to find latest closed event. (event goes in date order)
89
- issue['events'].reverse!.each { |event|
94
+ issue["events"].reverse!.each { |event|
90
95
  if event[:event].eql? compare_string
91
96
  if event[:commit_id].nil?
92
97
  issue[:actual_date] = issue[:closed_at]
93
98
  else
94
99
  begin
95
- begin
96
- commit = @github.git_data.commits.get @options[:user], @options[:project], event[:commit_id]
97
- rescue
98
- puts "Warning: GitHub API rate limit exceed (5000 per hour), change log may not contain some issues.".yellow
99
- end
100
+ commit = @fetcher.fetch_commit(event)
100
101
  issue[:actual_date] = commit[:author][:date]
101
102
  rescue
102
- puts "Warning: can't fetch commit #{event[:commit_id]} probably it referenced from another repo."
103
+ puts "Warning: Can't fetch commit #{event[:commit_id]}. It is probably referenced from another repo.".yellow
103
104
  issue[:actual_date] = issue[:closed_at]
104
105
  end
105
106
  end
@@ -107,95 +108,92 @@ rescue
107
108
  end
108
109
  }
109
110
  end
110
- #TODO: assert issues, that remain without 'actual_date' hash for some reason.
111
+ # TODO: assert issues, that remain without 'actual_date' hash for some reason.
111
112
  end
112
113
 
113
114
  def print_json(json)
114
115
  puts JSON.pretty_generate(json)
115
116
  end
116
117
 
117
- def fetch_merged_at_pull_requests
118
+ # This method fetches missing params for PR and filter them by specified options
119
+ # It include add all PR's with labels from @options[:include_labels] array
120
+ # And exclude all from :exclude_labels array.
121
+ # @return [Array] filtered PR's
122
+ def get_filtered_pull_requests
123
+ fetch_merged_at_pull_requests
124
+
125
+ filtered_pull_requests = include_issues_by_labels(@pull_requests)
126
+
127
+ filtered_pull_requests = exclude_issues_by_labels(filtered_pull_requests)
128
+
118
129
  if @options[:verbose]
119
- print "Fetching merged dates...\r"
130
+ puts "Filtered pull requests: #{filtered_pull_requests.count}"
120
131
  end
121
- pull_requests = []
122
- begin
123
- response = @github.pull_requests.list @options[:user], @options[:project], :state => 'closed'
124
- page_i = 0
125
- response.each_page do |page|
126
- page_i += PER_PAGE_NUMBER
127
- count_pages = response.count_pages
128
- print "Fetching merged dates... #{page_i}/#{count_pages * PER_PAGE_NUMBER}\r"
129
- pull_requests.concat(page)
130
- end
131
- rescue
132
- puts "Warning: GitHub API rate limit exceed (5000 per hour), change log may not contain some issues.".yellow
133
- end
134
-
135
132
 
133
+ filtered_pull_requests
134
+ end
136
135
 
137
- print " \r"
136
+ # This method fetch missing required attributes for pull requests
137
+ # :merged_at - is a date, when issue PR was merged.
138
+ # More correct to use this date, not closed date.
139
+ def fetch_merged_at_pull_requests
140
+ if @options[:verbose]
141
+ print "Fetching merged dates...\r"
142
+ end
143
+ pull_requests = @fetcher.fetch_pull_requests
138
144
 
139
145
  @pull_requests.each { |pr|
140
146
  fetched_pr = pull_requests.find { |fpr|
141
- fpr.number == pr.number }
147
+ fpr.number == pr.number
148
+ }
142
149
  pr[:merged_at] = fetched_pr[:merged_at]
143
150
  pull_requests.delete(fetched_pr)
144
151
  }
145
152
 
146
153
  if @options[:verbose]
147
- puts 'Fetching merged dates... Done!'
154
+ puts "Fetching merged dates: Done!"
148
155
  end
149
-
150
156
  end
151
157
 
152
- def get_filtered_pull_requests
153
-
154
- self.fetch_merged_at_pull_requests
155
-
156
- filtered_pull_requests = @pull_requests.select {|pr| pr[:merged_at] != nil }
158
+ # Include issues with labels, specified in :include_labels
159
+ # @param [Array] issues to filter
160
+ # @return [Array] filtered array of issues
161
+ def include_issues_by_labels(issues)
162
+ filtered_issues = @options[:include_labels].nil? ? issues : issues.select { |issue| (issue.labels.map(&:name) & @options[:include_labels]).any? }
157
163
 
158
- unless @options[:include_labels].nil?
159
- filtered_pull_requests = @pull_requests.select { |issue|
160
- #add all labels from @options[:incluse_labels] array
161
- (issue.labels.map { |label| label.name } & @options[:include_labels]).any?
164
+ if @options[:add_issues_wo_labels]
165
+ issues_wo_labels = issues.select { |issue|
166
+ !issue.labels.map(&:name).any?
162
167
  }
168
+ filtered_issues |= issues_wo_labels
163
169
  end
170
+ filtered_issues
171
+ end
164
172
 
173
+ # delete all labels with labels from @options[:exclude_labels] array
174
+ # @param [Array] issues
175
+ # @return [Array] filtered array
176
+ def exclude_issues_by_labels(issues)
165
177
  unless @options[:exclude_labels].nil?
166
- filtered_pull_requests = filtered_pull_requests.select { |issue|
167
- #delete all labels from @options[:exclude_labels] array
168
- !(issue.labels.map { |label| label.name } & @options[:exclude_labels]).any?
178
+ issues = issues.select { |issue|
179
+ !(issue.labels.map(&:name) & @options[:exclude_labels]).any?
169
180
  }
170
181
  end
171
-
172
- if @options[:add_issues_wo_labels]
173
- issues_wo_labels = @pull_requests.select {
174
- # add issues without any labels
175
- |issue| !issue.labels.map { |label| label.name }.any?
176
- }
177
- filtered_pull_requests |= issues_wo_labels
178
- end
179
-
180
-
181
- if @options[:verbose]
182
- puts "Filtered pull requests: #{filtered_pull_requests.count}"
183
- end
184
-
185
- filtered_pull_requests
182
+ issues
186
183
  end
187
184
 
188
- def compund_changelog
189
-
185
+ # The entry point of this script to generate change log
186
+ # @raise (ChangelogGeneratorError) Is thrown when one of specified tags was not found in list of tags.
187
+ def compound_changelog
190
188
  log = "# Change Log\n\n"
191
189
 
192
190
  if @options[:unreleased_only]
193
- log += self.generate_log_between_tags(self.all_tags[0], nil)
191
+ log += generate_log_between_tags(all_tags[0], nil)
194
192
  elsif @options[:tag1] and @options[:tag2]
195
193
  tag1 = @options[:tag1]
196
194
  tag2 = @options[:tag2]
197
195
  tags_strings = []
198
- self.all_tags.each { |x| tags_strings.push(x['name']) }
196
+ all_tags.each { |x| tags_strings.push(x["name"]) }
199
197
 
200
198
  if tags_strings.include?(tag1)
201
199
  if tags_strings.include?(tag2)
@@ -203,65 +201,63 @@ rescue
203
201
  hash = Hash[to_a]
204
202
  index1 = hash[tag1]
205
203
  index2 = hash[tag2]
206
- log += self.generate_log_between_tags(self.all_tags[index1], self.all_tags[index2])
204
+ log += generate_log_between_tags(all_tags[index1], all_tags[index2])
207
205
  else
208
- puts "Can't find tag #{tag2} -> exit"
209
- exit
206
+ fail ChangelogGeneratorError, "Can't find tag #{tag2} -> exit".red
210
207
  end
211
208
  else
212
- puts "Can't find tag #{tag1} -> exit"
213
- exit
209
+ fail ChangelogGeneratorError, "Can't find tag #{tag1} -> exit".red
214
210
  end
215
211
  else
216
- log += self.generate_log_for_all_tags
212
+ log += generate_log_for_all_tags
217
213
  end
218
214
 
219
215
  log += "\n\n\\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*"
220
216
 
221
217
  output_filename = "#{@options[:output]}"
222
- File.open(output_filename, 'w') { |file| file.write(log) }
223
- puts 'Done!'
218
+ File.open(output_filename, "w") { |file| file.write(log) }
219
+ puts "Done!"
224
220
  puts "Generated log placed in #{`pwd`.strip!}/#{output_filename}"
225
-
226
221
  end
227
222
 
223
+ # The full cycle of generation for whole project
224
+ # @return [String] The complete change log
228
225
  def generate_log_for_all_tags
229
-
230
226
  fetch_tags_dates
231
227
 
232
228
  if @options[:verbose]
233
- puts "Sorting tags.."
229
+ puts "Sorting tags..."
234
230
  end
235
231
 
236
- @all_tags.sort_by! { |x| self.get_time_of_tag(x) }.reverse!
232
+ @all_tags.sort_by! { |x| @fetcher.get_time_of_tag(x) }.reverse!
237
233
 
238
234
  if @options[:verbose]
239
- puts "Generating log.."
235
+ puts "Generating log..."
240
236
  end
241
237
 
242
-
243
- log = ''
238
+ log = ""
244
239
 
245
240
  if @options[:unreleased] && @all_tags.count != 0
246
- unreleased_log = self.generate_log_between_tags(self.all_tags[0], nil)
241
+ unreleased_log = generate_log_between_tags(all_tags[0], nil)
247
242
  if unreleased_log
248
243
  log += unreleased_log
249
244
  end
250
245
  end
251
246
 
252
- (1 ... self.all_tags.size).each { |index|
253
- log += self.generate_log_between_tags(self.all_tags[index], self.all_tags[index-1])
247
+ (1...all_tags.size).each { |index|
248
+ log += generate_log_between_tags(all_tags[index], all_tags[index - 1])
254
249
  }
255
250
  if @all_tags.count != 0
256
- log += generate_log_between_tags(nil, self.all_tags.last)
251
+ log += generate_log_between_tags(nil, all_tags.last)
257
252
  end
258
253
 
259
254
  log
260
255
  end
261
256
 
257
+ # Async fetching of all tags dates
262
258
  def fetch_tags_dates
263
259
  if @options[:verbose]
264
- print "Fetching tags dates..\r"
260
+ print "Fetching tag dates...\r"
265
261
  end
266
262
 
267
263
  # Async fetching tags:
@@ -269,96 +265,46 @@ rescue
269
265
  i = 0
270
266
  all = @all_tags.count
271
267
  @all_tags.each { |tag|
272
- # explicit set @tag_times_hash to write data safety.
273
268
  threads << Thread.new {
274
- self.get_time_of_tag(tag, @tag_times_hash)
269
+ @fetcher.get_time_of_tag(tag)
275
270
  if @options[:verbose]
276
- print "Fetching tags dates: #{i+1}/#{all}\r"
277
- i+=1
271
+ print "Fetching tags dates: #{i + 1}/#{all}\r"
272
+ i += 1
278
273
  end
279
-
280
274
  }
281
275
  }
282
276
 
283
277
  print " \r"
284
278
 
285
- threads.each { |thr| thr.join }
286
-
287
- if @options[:verbose]
288
- puts 'Fetching tags: Done!'
289
- end
290
- end
291
-
292
- def is_megred(number)
293
- begin
294
- @github.pull_requests.merged? @options[:user], @options[:project], number
295
- rescue
296
- puts "Warning: GitHub API rate limit exceed (5000 per hour), change log may not contain some issues.".yellow
297
- end
298
- end
299
-
300
- def get_all_tags
279
+ threads.each(&:join)
301
280
 
302
281
  if @options[:verbose]
303
- print "Fetching tags...\r"
304
- end
305
-
306
- tags = []
307
-
308
- begin
309
- response = @github.repos.tags @options[:user], @options[:project]
310
- page_i = 0
311
- count_pages = response.count_pages
312
- response.each_page do |page|
313
- page_i += PER_PAGE_NUMBER
314
- print "Fetching tags... #{page_i}/#{count_pages * PER_PAGE_NUMBER}\r"
315
- tags.concat(page)
316
- end
317
- print " \r"
318
- if @options[:verbose]
319
- puts "Found #{tags.count} tags"
320
- end
321
- rescue
322
- puts "Warning: GitHub API rate limit exceed (5000 per hour), change log may not contain some issues.".yellow
323
- end
324
-
325
-
326
-
327
- tags
328
- end
329
-
330
- def fetch_github_token
331
- env_var = @options[:token] ? @options[:token] : (ENV.fetch 'CHANGELOG_GITHUB_TOKEN', nil)
332
-
333
- unless env_var
334
- puts "Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found.".yellow
335
- puts "This script can make only 50 requests to GitHub API per hour without token!".yellow
282
+ puts "Fetching tags dates: #{i} Done!"
336
283
  end
337
-
338
- @github_token ||= env_var
339
-
340
284
  end
341
285
 
286
+ # Generate log only between 2 specified tags
287
+ # @param [String] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag
288
+ # @param [String] newer_tag all issue after this tag will be excluded. May be nil for unreleased section
342
289
  def generate_log_between_tags(older_tag, newer_tag)
343
- # older_tag nil - means it's first tag, newer_tag nil - means it unreleased section
344
290
  filtered_pull_requests = delete_by_time(@pull_requests, :actual_date, older_tag, newer_tag)
345
291
  filtered_issues = delete_by_time(@issues, :actual_date, older_tag, newer_tag)
346
292
 
347
- newer_tag_name = newer_tag.nil? ? nil : newer_tag['name']
348
- older_tag_name = older_tag.nil? ? nil : older_tag['name']
293
+ newer_tag_name = newer_tag.nil? ? nil : newer_tag["name"]
294
+ older_tag_name = older_tag.nil? ? nil : older_tag["name"]
349
295
 
350
296
  if @options[:filter_issues_by_milestone]
351
- #delete excess irrelevant issues (according milestones)
297
+ # delete excess irrelevant issues (according milestones)
352
298
  filtered_issues = filter_by_milestone(filtered_issues, newer_tag_name, @issues)
353
299
  filtered_pull_requests = filter_by_milestone(filtered_pull_requests, newer_tag_name, @pull_requests)
354
300
  end
355
301
 
356
302
  if filtered_issues.empty? && filtered_pull_requests.empty? && newer_tag.nil?
357
303
  # do not generate empty unreleased section
358
- return ''
304
+ return ""
359
305
  end
360
306
 
361
- self.create_log(filtered_pull_requests, filtered_issues, newer_tag, older_tag_name)
307
+ create_log(filtered_pull_requests, filtered_issues, newer_tag, older_tag_name)
362
308
  end
363
309
 
364
310
  def filter_by_milestone(filtered_issues, newer_tag_name, src_array)
@@ -367,18 +313,18 @@ rescue
367
313
  if issue.milestone.nil?
368
314
  true
369
315
  else
370
- #check, that this milestone in tag list:
316
+ # check, that this milestone in tag list:
371
317
  @all_tags.find { |tag| tag.name == issue.milestone.title }.nil?
372
318
  end
373
319
  }
374
320
  unless newer_tag_name.nil?
375
321
 
376
- #add missed issues (according milestones)
322
+ # add missed issues (according milestones)
377
323
  issues_to_add = src_array.select { |issue|
378
324
  if issue.milestone.nil?
379
325
  false
380
326
  else
381
- #check, that this milestone in tag list:
327
+ # check, that this milestone in tag list:
382
328
  milestone_is_tag = @all_tags.find { |tag|
383
329
  tag.name == issue.milestone.title
384
330
  }
@@ -396,12 +342,17 @@ rescue
396
342
  filtered_issues
397
343
  end
398
344
 
399
- def delete_by_time(array, hash_key, older_tag = nil, newer_tag = nil)
345
+ # Method filter issues, that belong only specified tag range
346
+ # @param [Array] array of issues to filter
347
+ # @param [Symbol] hash_key key of date value default is :actual_date
348
+ # @param [String] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag
349
+ # @param [String] newer_tag all issue after this tag will be excluded. May be nil for unreleased section
350
+ # @return [Array] filtered issues
351
+ def delete_by_time(array, hash_key = :actual_date, older_tag = nil, newer_tag = nil)
352
+ fail ChangelogGeneratorError, "At least one of the tags should be not nil!".red if older_tag.nil? && newer_tag.nil?
400
353
 
401
- raise 'At least one of the tags should be not nil!' if (older_tag.nil? && newer_tag.nil?)
402
-
403
- newer_tag_time = self.get_time_of_tag(newer_tag)
404
- older_tag_time = self.get_time_of_tag(older_tag)
354
+ newer_tag_time = newer_tag && @fetcher.get_time_of_tag(newer_tag)
355
+ older_tag_time = older_tag && @fetcher.get_time_of_tag(older_tag)
405
356
 
406
357
  array.select { |req|
407
358
  if req[hash_key]
@@ -419,7 +370,6 @@ rescue
419
370
  tag_in_range_new = t <= newer_tag_time
420
371
  end
421
372
 
422
-
423
373
  tag_in_range = (tag_in_range_old) && (tag_in_range_new)
424
374
 
425
375
  tag_in_range
@@ -429,45 +379,38 @@ rescue
429
379
  }
430
380
  end
431
381
 
432
- # @param [Array] pull_requests
433
- # @param [Array] issues
434
- # @param [String] older_tag_name
435
- # @return [String]
382
+ # Generates log for section with header and body
383
+ #
384
+ # @param [Array] pull_requests List or PR's in new section
385
+ # @param [Array] issues List of issues in new section
386
+ # @param [String] newer_tag Name of the newer tag. Could be nil for `Unreleased` section
387
+ # @param [String] older_tag_name Older tag, used for the links. Could be nil for last tag.
388
+ # @return [String] Ready and parsed section
436
389
  def create_log(pull_requests, issues, newer_tag, older_tag_name = nil)
390
+ newer_tag_time = newer_tag.nil? ? Time.new : @fetcher.get_time_of_tag(newer_tag)
391
+ newer_tag_name = newer_tag.nil? ? @options[:unreleased_label] : newer_tag["name"]
392
+ newer_tag_link = newer_tag.nil? ? "HEAD" : newer_tag_name
437
393
 
438
- newer_tag_time = newer_tag.nil? ? nil : self.get_time_of_tag(newer_tag)
439
- newer_tag_name = newer_tag.nil? ? nil : newer_tag['name']
440
-
441
- github_site = options[:github_site] || 'https://github.com'
394
+ github_site = options[:github_site] || "https://github.com"
442
395
  project_url = "#{github_site}/#{@options[:user]}/#{@options[:project]}"
443
396
 
444
- if newer_tag.nil?
445
- newer_tag_name = @options[:unreleased_label]
446
- newer_tag_link = 'HEAD'
447
- newer_tag_time = Time.new
448
- else
449
- newer_tag_link = newer_tag_name
450
- end
451
-
452
- log = ''
453
-
454
- log += generate_header(log, newer_tag_name, newer_tag_link, newer_tag_time, older_tag_name, project_url)
397
+ log = generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_name, project_url)
455
398
 
456
399
  if @options[:issues]
457
400
  # Generate issues:
458
401
  issues_a = []
459
402
  enhancement_a = []
460
- bugs_a =[]
403
+ bugs_a = []
461
404
 
462
405
  issues.each { |dict|
463
406
  added = false
464
407
  dict.labels.each { |label|
465
- if label.name == 'bug'
408
+ if label.name == "bug"
466
409
  bugs_a.push dict
467
410
  added = true
468
411
  next
469
412
  end
470
- if label.name == 'enhancement'
413
+ if label.name == "enhancement"
471
414
  enhancement_a.push dict
472
415
  added = true
473
416
  next
@@ -478,22 +421,26 @@ rescue
478
421
  end
479
422
  }
480
423
 
481
- log += generate_log_from_array(enhancement_a, @options[:enhancement_prefix])
482
- log += generate_log_from_array(bugs_a, @options[:bug_prefix])
483
- log += generate_log_from_array(issues_a, @options[:issue_prefix])
424
+ log += generate_sub_section(enhancement_a, @options[:enhancement_prefix])
425
+ log += generate_sub_section(bugs_a, @options[:bug_prefix])
426
+ log += generate_sub_section(issues_a, @options[:issue_prefix])
484
427
  end
485
428
 
486
429
  if @options[:pulls]
487
430
  # Generate pull requests:
488
- log += generate_log_from_array(pull_requests, @options[:merge_prefix])
431
+ log += generate_sub_section(pull_requests, @options[:merge_prefix])
489
432
  end
490
433
 
491
434
  log
492
435
  end
493
436
 
494
- def generate_log_from_array(issues, prefix)
495
- log = ''
496
- if options[:simple_list].nil? && issues.any?
437
+ # @param [Array] issues List of issues on sub-section
438
+ # @param [String] prefix Nae of sub-section
439
+ # @return [String] Generate ready-to-go sub-section
440
+ def generate_sub_section(issues, prefix)
441
+ log = ""
442
+
443
+ if options[:simple_list] != true && issues.any?
497
444
  log += "#{prefix}\n\n"
498
445
  end
499
446
 
@@ -506,119 +453,51 @@ rescue
506
453
  log
507
454
  end
508
455
 
509
- def generate_header(log, newer_tag_name, newer_tag_name2, newer_tag_time, older_tag_name, project_url)
456
+ # It generate one header for section with specific parameters.
457
+ #
458
+ # @param [String] newer_tag_name - name of newer tag
459
+ # @param [String] newer_tag_link - used for links. Could be same as #newer_tag_name or some specific value, like HEAD
460
+ # @param [Time] newer_tag_time - time, when newer tag created
461
+ # @param [String] older_tag_link - tag name, used for links.
462
+ # @param [String] project_url - url for current project.
463
+ # @return [String] - Generate one ready-to-add section.
464
+ def generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_link, project_url)
465
+ log = ""
510
466
 
511
- #Generate date string:
512
- time_string = newer_tag_time.strftime @options[:format]
467
+ # Generate date string:
468
+ time_string = newer_tag_time.strftime @options[:dateformat]
513
469
 
514
470
  # Generate tag name and link
515
471
  if newer_tag_name.equal? @options[:unreleased_label]
516
- log += "## [#{newer_tag_name}](#{project_url}/tree/#{newer_tag_name2})\n\n"
472
+ log += "## [#{newer_tag_name}](#{project_url}/tree/#{newer_tag_link})\n\n"
517
473
  else
518
- log += "## [#{newer_tag_name}](#{project_url}/tree/#{newer_tag_name2}) (#{time_string})\n\n"
474
+ log += "## [#{newer_tag_name}](#{project_url}/tree/#{newer_tag_link}) (#{time_string})\n\n"
519
475
  end
520
476
 
521
- if @options[:compare_link] && older_tag_name
477
+ if @options[:compare_link] && older_tag_link
522
478
  # Generate compare link
523
- log += "[Full Changelog](#{project_url}/compare/#{older_tag_name}...#{newer_tag_name2})\n\n"
479
+ log += "[Full Changelog](#{project_url}/compare/#{older_tag_link}...#{newer_tag_link})\n\n"
524
480
  end
525
481
 
526
482
  log
527
483
  end
528
484
 
529
- def get_time_of_tag(tag_name, tag_times_hash = @tag_times_hash)
530
-
531
- if tag_name.nil?
532
- return nil
533
- end
534
-
535
- if tag_times_hash[tag_name['name']]
536
- return @tag_times_hash[tag_name['name']]
537
- end
538
-
539
- begin
540
- github_git_data_commits_get = @github.git_data.commits.get @options[:user], @options[:project], tag_name['commit']['sha']
541
- rescue
542
- puts "Warning: GitHub API rate limit exceed (5000 per hour), change log may not contain some issues.".yellow
543
- end
544
- time_string = github_git_data_commits_get['committer']['date']
545
- @tag_times_hash[tag_name['name']] = Time.parse(time_string)
546
- end
547
-
485
+ # Filter issues according labels
486
+ # @return [Array] Filtered issues
548
487
  def get_filtered_issues
488
+ filtered_issues = include_issues_by_labels(@issues)
549
489
 
550
- issues = @issues
551
-
552
- filtered_issues = issues
553
-
554
- unless @options[:include_labels].nil?
555
- filtered_issues = issues.select { |issue|
556
- #add all labels from @options[:incluse_labels] array
557
- (issue.labels.map { |label| label.name } & @options[:include_labels]).any?
558
- }
559
- end
560
-
561
- unless @options[:exclude_labels].nil?
562
- filtered_issues = filtered_issues.select { |issue|
563
- #delete all labels from @options[:exclude_labels] array
564
- !(issue.labels.map { |label| label.name } & @options[:exclude_labels]).any?
565
- }
566
- end
567
-
568
- if @options[:add_issues_wo_labels]
569
- issues_wo_labels = issues.select {
570
- # add issues without any labels
571
- |issue| !issue.labels.map { |label| label.name }.any?
572
- }
573
- filtered_issues |= issues_wo_labels
574
- end
575
-
490
+ filtered_issues = exclude_issues_by_labels(filtered_issues)
576
491
 
577
492
  if @options[:verbose]
578
493
  puts "Filtered issues: #{filtered_issues.count}"
579
494
  end
580
495
 
581
496
  filtered_issues
582
-
583
- end
584
-
585
- def fetch_issues_and_pull_requests
586
- if @options[:verbose]
587
- print "Fetching closed issues...\r"
588
- end
589
- issues = []
590
-
591
- begin
592
- response = @github.issues.list user: @options[:user], repo: @options[:project], state: 'closed', filter: 'all', labels: nil
593
- page_i = 0
594
- count_pages = response.count_pages
595
- response.each_page do |page|
596
- page_i += PER_PAGE_NUMBER
597
- print "Fetching issues... #{page_i}/#{count_pages * PER_PAGE_NUMBER}\r"
598
- issues.concat(page)
599
- end
600
- rescue
601
- puts "Warning: GitHub API rate limit exceed (5000 per hour), change log may not contain some issues.".yellow
602
- end
603
-
604
-
605
-
606
- print " \r"
607
-
608
- if @options[:verbose]
609
- puts "Received issues: #{issues.count}"
610
- end
611
-
612
- # remove pull request from issues:
613
- issues_wo_pr = issues.select { |x|
614
- x.pull_request == nil
615
- }
616
- pull_requests = issues.select { |x|
617
- x.pull_request != nil
618
- }
619
- return issues_wo_pr, pull_requests
620
497
  end
621
498
 
499
+ # Fetch event for issues and pull requests
500
+ # @return [Array] array of fetched issues
622
501
  def fetch_event_for_issues_and_pr
623
502
  if @options[:verbose]
624
503
  print "Fetching events for issues and PR: 0/#{@issues.count + @pull_requests.count}\r"
@@ -626,42 +505,11 @@ rescue
626
505
 
627
506
  # Async fetching events:
628
507
 
629
- fetch_events_async(@issues + @pull_requests)
630
-
631
- #to clear line from prev print
632
- print " \r"
633
-
634
- if @options[:verbose]
635
- puts 'Fetching events for issues and PR: Done!'
636
- end
637
- end
638
-
639
- def fetch_events_async(issues)
640
- i = 0
641
- max_thread_number = 50
642
- threads = []
643
- issues.each_slice(max_thread_number) { |issues_slice|
644
- issues_slice.each { |issue|
645
- threads << Thread.new {
646
- begin
647
- obj = @github.issues.events.list user: @options[:user], repo: @options[:project], issue_number: issue['number']
648
- rescue
649
- puts "Warning: GitHub API rate limit exceed (5000 per hour), change log may not contain some issues.".yellow
650
- end
651
- issue[:events] = obj.body
652
- print "Fetching events for issues and PR: #{i+1}/#{@issues.count + @pull_requests.count}\r"
653
- i +=1
654
- }
655
- }
656
- threads.each { |thr| thr.join }
657
- threads = []
658
- }
508
+ @fetcher.fetch_events_async(@issues + @pull_requests)
659
509
  end
660
-
661
510
  end
662
511
 
663
- if __FILE__ == $0
664
- GitHubChangelogGenerator::ChangelogGenerator.new.compund_changelog
512
+ if __FILE__ == $PROGRAM_NAME
513
+ GitHubChangelogGenerator::ChangelogGenerator.new.compound_changelog
665
514
  end
666
-
667
515
  end