github_changelog_generator 1.1.2 → 1.1.4
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +61 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +40 -0
- data/README.md +85 -0
- data/bin/github_changelog_generator +2 -3
- data/bump_gemfile.rb +227 -0
- data/github_changelog_generator.gemspec +32 -0
- data/lib/github_changelog_generator.rb +277 -230
- data/lib/github_changelog_generator/parser.rb +71 -64
- data/lib/github_changelog_generator/version.rb +3 -0
- metadata +43 -7
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'github_changelog_generator/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "github_changelog_generator"
|
9
|
+
spec.version = GitHubChangelogGenerator::VERSION
|
10
|
+
spec.default_executable = "github_changelog_generator"
|
11
|
+
|
12
|
+
spec.required_ruby_version = '>= 1.9.3'
|
13
|
+
spec.authors = ["Petr Korolev"]
|
14
|
+
spec.email = %q{sky4winder+github_changelog_generator@gmail.com}
|
15
|
+
spec.date = `date +"%Y-%m-%d"`.strip!
|
16
|
+
spec.summary = %q{Script, that automatically generate change-log from your tags and pull-requests.}
|
17
|
+
spec.description = %q{Script, that automatically generate change-log from your tags and pull-requests}
|
18
|
+
spec.homepage = %q{https://github.com/skywinder/Github-Changelog-Generator}
|
19
|
+
spec.license = "MIT"
|
20
|
+
|
21
|
+
spec.files = `git ls-files -z`.split("\x0")
|
22
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
23
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
|
29
|
+
spec.add_runtime_dependency(%q<github_api>, ["~> 0.12"])
|
30
|
+
spec.add_runtime_dependency(%q<colorize>, ["~> 0.7"])
|
31
|
+
|
32
|
+
end
|
@@ -2,329 +2,376 @@
|
|
2
2
|
|
3
3
|
require 'github_api'
|
4
4
|
require 'json'
|
5
|
-
require '
|
5
|
+
require 'colorize'
|
6
6
|
require_relative 'github_changelog_generator/parser'
|
7
|
+
require_relative 'github_changelog_generator/version'
|
7
8
|
|
9
|
+
module GitHubChangelogGenerator
|
10
|
+
class ChangelogGenerator
|
8
11
|
|
9
|
-
|
12
|
+
attr_accessor :options, :all_tags, :github
|
10
13
|
|
11
|
-
|
14
|
+
def initialize
|
12
15
|
|
13
|
-
|
16
|
+
@options = Parser.parse_options
|
14
17
|
|
15
|
-
|
16
|
-
if @options[:token]
|
17
|
-
@github = Github.new oauth_token: @options[:token]
|
18
|
-
else
|
19
|
-
@github = Github.new
|
20
|
-
end
|
21
|
-
@all_tags = self.get_all_tags
|
22
|
-
@pull_requests = self.get_all_closed_pull_requests
|
23
|
-
@issues = self.get_all_issues
|
24
|
-
|
25
|
-
@tag_times_hash = {}
|
26
|
-
end
|
18
|
+
github_token
|
27
19
|
|
28
|
-
|
29
|
-
|
30
|
-
|
20
|
+
if @github_token.nil?
|
21
|
+
@github = Github.new
|
22
|
+
else
|
23
|
+
@github = Github.new oauth_token: @github_token
|
24
|
+
end
|
31
25
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
26
|
+
@all_tags = self.get_all_tags
|
27
|
+
@pull_requests = self.get_all_closed_pull_requests
|
28
|
+
@issues = self.get_all_issues
|
36
29
|
|
30
|
+
@tag_times_hash = {}
|
31
|
+
end
|
37
32
|
|
38
|
-
|
39
|
-
|
40
|
-
|
33
|
+
def print_json(json)
|
34
|
+
puts JSON.pretty_generate(json)
|
35
|
+
end
|
41
36
|
|
42
|
-
|
43
|
-
|
37
|
+
def exec_command(cmd)
|
38
|
+
exec_cmd = "cd #{$project_path} && #{cmd}"
|
39
|
+
%x[#{exec_cmd}]
|
44
40
|
end
|
45
41
|
|
46
|
-
pull_requests
|
47
|
-
end
|
48
42
|
|
49
|
-
|
50
|
-
|
51
|
-
|
43
|
+
def get_all_closed_pull_requests
|
44
|
+
response = @github.pull_requests.list @options[:user], @options[:project], :state => 'closed'
|
45
|
+
|
46
|
+
pull_requests = []
|
47
|
+
response.each_page do |page|
|
48
|
+
pull_requests.concat(page)
|
49
|
+
end
|
50
|
+
|
51
|
+
if @options[:verbose]
|
52
|
+
puts "Receive all pull requests: #{pull_requests.count}"
|
53
|
+
end
|
54
|
+
|
55
|
+
pull_requests
|
52
56
|
end
|
53
57
|
|
54
|
-
|
58
|
+
def compund_changelog
|
59
|
+
if @options[:verbose]
|
60
|
+
puts 'Generating changelog:'
|
61
|
+
end
|
62
|
+
|
63
|
+
log = "# Changelog\n\n"
|
55
64
|
|
56
|
-
|
57
|
-
|
58
|
-
|
65
|
+
if @options[:last]
|
66
|
+
log += self.generate_log_between_tags(self.all_tags[0], self.all_tags[1])
|
67
|
+
elsif @options[:tag1] && @options[:tag2]
|
59
68
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
69
|
+
tag1 = @options[:tag1]
|
70
|
+
tag2 = @options[:tag2]
|
71
|
+
tags_strings = []
|
72
|
+
self.all_tags.each { |x| tags_strings.push(x['name']) }
|
64
73
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
74
|
+
if tags_strings.include?(tag1)
|
75
|
+
if tags_strings.include?(tag2)
|
76
|
+
hash = Hash[tags_strings.map.with_index.to_a]
|
77
|
+
index1 = hash[tag1]
|
78
|
+
index2 = hash[tag2]
|
79
|
+
log += self.generate_log_between_tags(self.all_tags[index1], self.all_tags[index2])
|
80
|
+
else
|
81
|
+
puts "Can't find tag #{tag2} -> exit"
|
82
|
+
exit
|
83
|
+
end
|
71
84
|
else
|
72
|
-
puts "Can't find tag #{
|
85
|
+
puts "Can't find tag #{tag1} -> exit"
|
73
86
|
exit
|
74
87
|
end
|
75
88
|
else
|
76
|
-
|
77
|
-
exit
|
89
|
+
log += self.generate_log_for_all_tags
|
78
90
|
end
|
79
|
-
else
|
80
|
-
log += self.generate_log_for_all_tags
|
81
|
-
end
|
82
91
|
|
83
|
-
|
92
|
+
log += "\n\n\\* *This changelog was generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*"
|
84
93
|
|
85
|
-
|
86
|
-
|
94
|
+
output_filename = "#{@options[:output]}"
|
95
|
+
File.open(output_filename, 'w') { |file| file.write(log) }
|
87
96
|
|
88
|
-
|
97
|
+
puts "Done! Generated log placed in #{output_filename}"
|
89
98
|
|
90
|
-
end
|
91
|
-
|
92
|
-
def generate_log_for_all_tags
|
93
|
-
log = ''
|
94
|
-
for index in 1 ... self.all_tags.size
|
95
|
-
log += self.generate_log_between_tags(self.all_tags[index-1], self.all_tags[index])
|
96
99
|
end
|
97
100
|
|
98
|
-
|
101
|
+
def generate_log_for_all_tags
|
102
|
+
log = ''
|
103
|
+
@all_tags.each { |tag| self.get_time_of_tag(tag) }
|
99
104
|
|
100
|
-
log
|
101
|
-
end
|
102
105
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
+
if @options[:verbose]
|
107
|
+
puts "Sorting tags.."
|
108
|
+
end
|
109
|
+
|
110
|
+
@all_tags.sort_by! { |x| self.get_time_of_tag(x) }.reverse!
|
111
|
+
|
112
|
+
if @options[:verbose]
|
113
|
+
puts "Generating log.."
|
114
|
+
end
|
106
115
|
|
107
|
-
|
116
|
+
for index in 1 ... self.all_tags.size
|
117
|
+
log += self.generate_log_between_tags(self.all_tags[index], self.all_tags[index-1])
|
118
|
+
end
|
108
119
|
|
109
|
-
|
120
|
+
log += self.generate_log_before_tag(self.all_tags.last)
|
110
121
|
|
111
|
-
|
112
|
-
puts "Receive tags for repo #{url}"
|
122
|
+
log
|
113
123
|
end
|
114
124
|
|
115
|
-
|
116
|
-
|
117
|
-
:headers => {'Authorization' => "token #{@options[:token]}",
|
118
|
-
'User-Agent' => 'Changelog-Generator'})
|
119
|
-
else
|
120
|
-
response = HTTParty.get(url,
|
121
|
-
:headers => {'User-Agent' => 'Changelog-Generator'})
|
125
|
+
def is_megred(number)
|
126
|
+
@github.pull_requests.merged? @options[:user], @options[:project], number
|
122
127
|
end
|
123
128
|
|
124
|
-
|
129
|
+
def get_all_tags
|
130
|
+
|
131
|
+
url = "https://api.github.com/repos/#{@options[:user]}/#{@options[:project]}/tags"
|
132
|
+
|
133
|
+
if @options[:verbose]
|
134
|
+
|
135
|
+
puts "Receive tags for repo #{url}"
|
136
|
+
end
|
137
|
+
|
138
|
+
response = @github.repos.tags @options[:user], @options[:project]
|
125
139
|
|
126
|
-
|
127
|
-
|
140
|
+
tags = []
|
141
|
+
response.each_page do |page|
|
142
|
+
tags.concat(page)
|
143
|
+
end
|
144
|
+
|
145
|
+
if @options[:verbose]
|
146
|
+
puts "Found #{tags.count} tags"
|
147
|
+
end
|
148
|
+
|
149
|
+
tags
|
128
150
|
end
|
129
151
|
|
130
|
-
|
131
|
-
|
152
|
+
def github_token
|
153
|
+
if @options[:token]
|
154
|
+
return @github_token ||= @options[:token]
|
155
|
+
end
|
132
156
|
|
133
|
-
|
134
|
-
|
135
|
-
|
157
|
+
env_var = ENV.fetch 'CHANGELOG_GITHUB_TOKEN', nil
|
158
|
+
|
159
|
+
unless env_var
|
160
|
+
puts "Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found.".yellow
|
161
|
+
puts "This script can make only 50 requests to GitHub API per hour without token!".yellow
|
162
|
+
end
|
163
|
+
|
164
|
+
@github_token ||= env_var
|
136
165
|
|
137
|
-
# if we mix up tags order - lits fix it!
|
138
|
-
if since_tag_time > till_tag_time
|
139
|
-
since_tag, till_tag = till_tag, since_tag
|
140
|
-
since_tag_time, till_tag_time = till_tag_time, since_tag_time
|
141
166
|
end
|
142
167
|
|
143
|
-
till_tag_name = till_tag['name']
|
144
168
|
|
145
|
-
|
169
|
+
def generate_log_between_tags(older_tag, newer_tag)
|
170
|
+
older_tag_time = self.get_time_of_tag(older_tag)
|
171
|
+
newer_tag_time = self.get_time_of_tag(newer_tag)
|
146
172
|
|
147
|
-
|
148
|
-
if
|
149
|
-
|
150
|
-
|
151
|
-
|
173
|
+
# if we mix up tags order - lits fix it!
|
174
|
+
# if older_tag_time < newer_tag_time
|
175
|
+
# older_tag, newer_tag = newer_tag, older_tag
|
176
|
+
# older_tag_time, newer_tag_time = newer_tag_time, older_tag_time
|
177
|
+
# puts "Swap tags!"
|
178
|
+
# end
|
152
179
|
|
153
|
-
|
154
|
-
!in_range
|
155
|
-
else
|
156
|
-
true
|
157
|
-
end
|
180
|
+
newer_tag_name = newer_tag['name']
|
158
181
|
|
159
|
-
|
182
|
+
pull_requests = Array.new(@pull_requests)
|
160
183
|
|
161
|
-
|
184
|
+
pull_requests.delete_if { |req|
|
185
|
+
if req[:merged_at]
|
186
|
+
t = Time.parse(req[:merged_at]).utc
|
187
|
+
tag_is_older_of_older = t > older_tag_time
|
188
|
+
tag_is_newer_than_new = t <= newer_tag_time
|
162
189
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
190
|
+
tag_not_in_range = (tag_is_older_of_older) && (tag_is_newer_than_new)
|
191
|
+
!tag_not_in_range
|
192
|
+
else
|
193
|
+
true
|
194
|
+
end
|
168
195
|
|
169
|
-
|
170
|
-
!in_range
|
171
|
-
else
|
172
|
-
true
|
173
|
-
end
|
196
|
+
}
|
174
197
|
|
175
|
-
|
198
|
+
issues = Array.new(@issues)
|
176
199
|
|
177
|
-
|
200
|
+
issues.delete_if { |issue|
|
201
|
+
if issue[:closed_at]
|
202
|
+
t = Time.parse(issue[:closed_at]).utc
|
203
|
+
tag_is_later_since = t > older_tag_time
|
204
|
+
tag_is_before_till = t <= newer_tag_time
|
178
205
|
|
179
|
-
|
206
|
+
in_range = (tag_is_later_since) && (tag_is_before_till)
|
207
|
+
!in_range
|
208
|
+
else
|
209
|
+
true
|
210
|
+
end
|
180
211
|
|
181
|
-
|
182
|
-
tag_time = self.get_time_of_tag(tag)
|
183
|
-
tag_name = tag['name']
|
212
|
+
}
|
184
213
|
|
185
|
-
|
214
|
+
self.create_log(pull_requests, issues, newer_tag_name, newer_tag_time)
|
186
215
|
|
187
|
-
|
188
|
-
if req[:merged_at]
|
189
|
-
t = Time.parse(req[:merged_at]).utc
|
190
|
-
t > tag_time
|
191
|
-
else
|
192
|
-
true
|
193
|
-
end
|
216
|
+
end
|
194
217
|
|
195
|
-
|
218
|
+
def generate_log_before_tag(tag)
|
219
|
+
tag_time = self.get_time_of_tag(tag)
|
220
|
+
tag_name = tag['name']
|
196
221
|
|
197
|
-
|
222
|
+
pull_requests = Array.new(@pull_requests)
|
198
223
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
}
|
224
|
+
pull_requests.delete_if { |req|
|
225
|
+
if req[:merged_at]
|
226
|
+
t = Time.parse(req[:merged_at]).utc
|
227
|
+
t > tag_time
|
228
|
+
else
|
229
|
+
true
|
230
|
+
end
|
207
231
|
|
208
|
-
|
232
|
+
}
|
209
233
|
|
210
|
-
|
234
|
+
issues = Array.new(@issues)
|
211
235
|
|
212
|
-
|
236
|
+
issues.delete_if { |issue|
|
237
|
+
if issue[:closed_at]
|
238
|
+
t = Time.parse(issue[:closed_at]).utc
|
239
|
+
t > tag_time
|
240
|
+
else
|
241
|
+
true
|
242
|
+
end
|
243
|
+
}
|
213
244
|
|
214
|
-
|
215
|
-
trimmed_tag = tag_name.tr('v', '')
|
216
|
-
log = "## [#{trimmed_tag}] (https://github.com/#{@options[:user]}/#{@options[:project]}/tree/#{tag_name})\n"
|
245
|
+
self.create_log(pull_requests, issues, tag_name, tag_time)
|
217
246
|
|
218
|
-
|
219
|
-
time_string = tag_time.strftime @options[:format]
|
220
|
-
log += "#### #{time_string}\n"
|
247
|
+
end
|
221
248
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
249
|
+
def create_log(pull_requests, issues, tag_name, tag_time)
|
250
|
+
|
251
|
+
# Generate tag name and link
|
252
|
+
trimmed_tag = tag_name.tr('v', '')
|
253
|
+
log = "## [#{trimmed_tag}] (https://github.com/#{@options[:user]}/#{@options[:project]}/tree/#{tag_name})\n"
|
254
|
+
|
255
|
+
#Generate date string:
|
256
|
+
time_string = tag_time.strftime @options[:format]
|
257
|
+
log += "#### #{time_string}\n"
|
258
|
+
|
259
|
+
if @options[:pulls]
|
260
|
+
# Generate pull requests:
|
261
|
+
if pull_requests
|
262
|
+
pull_requests.each { |dict|
|
263
|
+
merge = "#{@options[:merge_prefix]}#{dict[:title]} [\\##{dict[:number]}](#{dict.html_url})\n\n"
|
264
|
+
log += "- #{merge}"
|
265
|
+
}
|
266
|
+
end
|
229
267
|
end
|
230
|
-
end
|
231
268
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
else
|
239
|
-
if x.labels.any?
|
240
|
-
1
|
269
|
+
if @options[:issues]
|
270
|
+
# Generate issues:
|
271
|
+
if issues
|
272
|
+
issues.sort! { |x, y|
|
273
|
+
if x.labels.any? && y.labels.any?
|
274
|
+
x.labels[0].name <=> y.labels[0].name
|
241
275
|
else
|
242
|
-
if
|
243
|
-
|
276
|
+
if x.labels.any?
|
277
|
+
1
|
244
278
|
else
|
245
|
-
|
279
|
+
if y.labels.any?
|
280
|
+
-1
|
281
|
+
else
|
282
|
+
0
|
283
|
+
end
|
246
284
|
end
|
247
285
|
end
|
286
|
+
}.reverse!
|
287
|
+
end
|
288
|
+
issues.each { |dict|
|
289
|
+
is_bug = false
|
290
|
+
is_enhancement = false
|
291
|
+
dict.labels.each { |label|
|
292
|
+
if label.name == 'bug'
|
293
|
+
is_bug = true
|
294
|
+
end
|
295
|
+
if label.name == 'enhancement'
|
296
|
+
is_enhancement = true
|
297
|
+
end
|
298
|
+
}
|
299
|
+
|
300
|
+
intro = 'Closed issue'
|
301
|
+
if is_bug
|
302
|
+
intro = 'Fixed bug'
|
248
303
|
end
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
is_bug = false
|
253
|
-
is_enhancement = false
|
254
|
-
dict.labels.each { |label|
|
255
|
-
if label.name == 'bug'
|
256
|
-
is_bug = true
|
257
|
-
end
|
258
|
-
if label.name == 'enhancement'
|
259
|
-
is_enhancement = true
|
304
|
+
|
305
|
+
if is_enhancement
|
306
|
+
intro = 'Implemented enhancement'
|
260
307
|
end
|
308
|
+
|
309
|
+
merge = "*#{intro}:* #{dict[:title]} [\\##{dict[:number]}](#{dict.html_url})\n\n"
|
310
|
+
log += "- #{merge}"
|
261
311
|
}
|
312
|
+
end
|
313
|
+
log
|
314
|
+
end
|
262
315
|
|
263
|
-
|
264
|
-
if is_bug
|
265
|
-
intro = 'Fixed bug'
|
266
|
-
end
|
316
|
+
def get_time_of_tag(prev_tag)
|
267
317
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
merge = "*#{intro}:* #{dict[:title]} [\\##{dict[:number]}](#{dict.html_url})\n\n"
|
273
|
-
log += "- #{merge}"
|
274
|
-
}
|
275
|
-
end
|
276
|
-
log
|
277
|
-
end
|
318
|
+
if @tag_times_hash[prev_tag['name']]
|
319
|
+
return @tag_times_hash[prev_tag['name']]
|
320
|
+
end
|
278
321
|
|
279
|
-
|
322
|
+
if @options[:verbose]
|
323
|
+
puts "Getting time for tag #{prev_tag['name']}"
|
324
|
+
end
|
280
325
|
|
281
|
-
|
282
|
-
|
326
|
+
github_git_data_commits_get = @github.git_data.commits.get @options[:user], @options[:project], prev_tag['commit']['sha']
|
327
|
+
time_string = github_git_data_commits_get['committer']['date']
|
328
|
+
Time.parse(time_string)
|
329
|
+
@tag_times_hash[prev_tag['name']] = Time.parse(time_string)
|
283
330
|
end
|
284
331
|
|
285
|
-
|
286
|
-
|
287
|
-
end
|
332
|
+
def get_all_issues
|
333
|
+
response = @github.issues.list user: @options[:user], repo: @options[:project], state: 'closed', filter: 'all', labels: nil
|
288
334
|
|
289
|
-
github_git_data_commits_get = @github.git_data.commits.get @options[:user], @options[:project], prev_tag['commit']['sha']
|
290
|
-
time_string = github_git_data_commits_get['committer']['date']
|
291
|
-
Time.parse(time_string)
|
292
|
-
@tag_times_hash[prev_tag['name']] = Time.parse(time_string)
|
293
|
-
end
|
294
335
|
|
295
|
-
|
296
|
-
|
336
|
+
issues = []
|
337
|
+
response.each_page do |page|
|
338
|
+
issues.concat(page)
|
339
|
+
end
|
297
340
|
|
298
|
-
|
341
|
+
# remove pull request from issues:
|
342
|
+
issues.select! { |x|
|
343
|
+
x.pull_request == nil
|
344
|
+
}
|
299
345
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
}
|
346
|
+
if @options[:verbose]
|
347
|
+
puts "Receive all closed issues: #{issues.count}"
|
348
|
+
end
|
304
349
|
|
305
|
-
|
306
|
-
|
307
|
-
|
350
|
+
filtered_issues = issues.select { |issue|
|
351
|
+
#compare is there any labels from @options[:labels] array
|
352
|
+
(issue.labels.map { |label| label.name } & @options[:labels]).any?
|
308
353
|
}
|
309
|
-
filtered_issues.concat(issues_wo_labels)
|
310
|
-
end
|
311
354
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
355
|
+
if @options[:add_issues_wo_labels]
|
356
|
+
issues_wo_labels = issues.select {
|
357
|
+
# add issues without any labels
|
358
|
+
|issue| !issue.labels.map { |label| label.name }.any?
|
359
|
+
}
|
360
|
+
filtered_issues.concat(issues_wo_labels)
|
361
|
+
end
|
316
362
|
|
317
|
-
|
318
|
-
|
319
|
-
|
363
|
+
if @options[:verbose]
|
364
|
+
puts "Filter issues with labels #{@options[:labels]}#{@options[:add_issues_wo_labels] ? ' and w/o labels' : ''}: #{filtered_issues.count} issues"
|
365
|
+
end
|
320
366
|
|
321
|
-
|
367
|
+
filtered_issues
|
368
|
+
|
369
|
+
end
|
322
370
|
|
323
371
|
end
|
324
372
|
|
325
|
-
|
373
|
+
if __FILE__ == $0
|
374
|
+
GitHubChangelogGenerator::ChangelogGenerator.new.compund_changelog
|
375
|
+
end
|
326
376
|
|
327
|
-
|
328
|
-
changelog_generator = ChangelogGenerator.new
|
329
|
-
changelog_generator.compund_changelog
|
330
|
-
end
|
377
|
+
end
|