github_changelog_generator 1.3.11 → 1.4.0

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