github_changelog_generator 1.1.2 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 'httparty'
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
- class ChangelogGenerator
12
+ attr_accessor :options, :all_tags, :github
10
13
 
11
- attr_accessor :options, :all_tags, :github
14
+ def initialize
12
15
 
13
- def initialize()
16
+ @options = Parser.parse_options
14
17
 
15
- @options = Parser.parse_options
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
- def print_json(json)
29
- puts JSON.pretty_generate(json)
30
- end
20
+ if @github_token.nil?
21
+ @github = Github.new
22
+ else
23
+ @github = Github.new oauth_token: @github_token
24
+ end
31
25
 
32
- def exec_command(cmd)
33
- exec_cmd = "cd #{$project_path} && #{cmd}"
34
- %x[#{exec_cmd}]
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
- def get_all_closed_pull_requests
39
- request = @github.pull_requests.list @options[:user], @options[:project], :state => 'closed'
40
- pull_requests = request.body
33
+ def print_json(json)
34
+ puts JSON.pretty_generate(json)
35
+ end
41
36
 
42
- if @options[:verbose]
43
- puts 'Receive all pull requests'
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
- def compund_changelog
50
- if @options[:verbose]
51
- puts 'Generating changelog:'
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
- log = "# Changelog\n\n"
58
+ def compund_changelog
59
+ if @options[:verbose]
60
+ puts 'Generating changelog:'
61
+ end
62
+
63
+ log = "# Changelog\n\n"
55
64
 
56
- if @options[:last]
57
- log += self.generate_log_between_tags(self.all_tags[0], self.all_tags[1])
58
- elsif @options[:tag1] && @options[:tag2]
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
- tag1 = @options[:tag1]
61
- tag2 = @options[:tag2]
62
- tags_strings = []
63
- self.all_tags.each { |x| tags_strings.push(x['name']) }
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
- if tags_strings.include?(tag1)
66
- if tags_strings.include?(tag2)
67
- hash = Hash[tags_strings.map.with_index.to_a]
68
- index1 = hash[tag1]
69
- index2 = hash[tag2]
70
- log += self.generate_log_between_tags(self.all_tags[index1], self.all_tags[index2])
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 #{tag2} -> exit"
85
+ puts "Can't find tag #{tag1} -> exit"
73
86
  exit
74
87
  end
75
88
  else
76
- puts "Can't find tag #{tag1} -> exit"
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
- log += "\n\n\\* *This changelog was generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*"
92
+ log += "\n\n\\* *This changelog was generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*"
84
93
 
85
- output_filename = "#{@options[:output]}"
86
- File.open(output_filename, 'w') { |file| file.write(log) }
94
+ output_filename = "#{@options[:output]}"
95
+ File.open(output_filename, 'w') { |file| file.write(log) }
87
96
 
88
- puts "Done! Generated log placed in #{output_filename}"
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
- log += self.generate_log_before_tag(self.all_tags.last)
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
- def is_megred(number)
104
- @github.pull_requests.merged? @options[:user], @options[:project], number
105
- end
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
- def get_all_tags
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
- url = "https://api.github.com/repos/#{@options[:user]}/#{@options[:project]}/tags"
120
+ log += self.generate_log_before_tag(self.all_tags.last)
110
121
 
111
- if @options[:verbose]
112
- puts "Receive tags for repo #{url}"
122
+ log
113
123
  end
114
124
 
115
- if @options[:token]
116
- response = HTTParty.get(url,
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
- json_parse = JSON.parse(response.body)
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
- if @options[:verbose]
127
- puts "Found #{json_parse.count} tags"
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
- json_parse
131
- end
152
+ def github_token
153
+ if @options[:token]
154
+ return @github_token ||= @options[:token]
155
+ end
132
156
 
133
- def generate_log_between_tags(since_tag, till_tag)
134
- since_tag_time = self.get_time_of_tag(since_tag)
135
- till_tag_time = self.get_time_of_tag(till_tag)
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
- pull_requests = Array.new(@pull_requests)
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
- pull_requests.delete_if { |req|
148
- if req[:merged_at]
149
- t = Time.parse(req[:merged_at]).utc
150
- tag_is_later_since = t > since_tag_time
151
- tag_is_before_till = t <= till_tag_time
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
- in_range = (tag_is_later_since) && (tag_is_before_till)
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
- issues = Array.new(@issues)
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
- issues.delete_if { |issue|
164
- if issue[:closed_at]
165
- t = Time.parse(issue[:closed_at]).utc
166
- tag_is_later_since = t > since_tag_time
167
- tag_is_before_till = t <= till_tag_time
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
- in_range = (tag_is_later_since) && (tag_is_before_till)
170
- !in_range
171
- else
172
- true
173
- end
196
+ }
174
197
 
175
- }
198
+ issues = Array.new(@issues)
176
199
 
177
- self.create_log(pull_requests, issues, till_tag_name, till_tag_time)
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
- end
206
+ in_range = (tag_is_later_since) && (tag_is_before_till)
207
+ !in_range
208
+ else
209
+ true
210
+ end
180
211
 
181
- def generate_log_before_tag(tag)
182
- tag_time = self.get_time_of_tag(tag)
183
- tag_name = tag['name']
212
+ }
184
213
 
185
- pull_requests = Array.new(@pull_requests)
214
+ self.create_log(pull_requests, issues, newer_tag_name, newer_tag_time)
186
215
 
187
- pull_requests.delete_if { |req|
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
- issues = Array.new(@issues)
222
+ pull_requests = Array.new(@pull_requests)
198
223
 
199
- issues.delete_if { |issue|
200
- if issue[:closed_at]
201
- t = Time.parse(issue[:closed_at]).utc
202
- t > tag_time
203
- else
204
- true
205
- end
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
- self.create_log(pull_requests, issues, tag_name, tag_time)
232
+ }
209
233
 
210
- end
234
+ issues = Array.new(@issues)
211
235
 
212
- def create_log(pull_requests, issues, tag_name, tag_time)
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
- # Generate tag name and link
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
- #Generate date string:
219
- time_string = tag_time.strftime @options[:format]
220
- log += "#### #{time_string}\n"
247
+ end
221
248
 
222
- if @options[:pulls]
223
- # Generate pull requests:
224
- if pull_requests
225
- pull_requests.each { |dict|
226
- merge = "#{@options[:merge_prefix]}#{dict[:title]} [\\##{dict[:number]}](#{dict.html_url})\n\n"
227
- log += "- #{merge}"
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
- if @options[:issues]
233
- # Generate issues:
234
- if issues
235
- issues.sort! { |x, y|
236
- if x.labels.any? && y.labels.any?
237
- x.labels[0].name <=> y.labels[0].name
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 y.labels.any?
243
- -1
276
+ if x.labels.any?
277
+ 1
244
278
  else
245
- 0
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
- }.reverse!
250
- end
251
- issues.each { |dict|
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
- intro = 'Closed issue'
264
- if is_bug
265
- intro = 'Fixed bug'
266
- end
316
+ def get_time_of_tag(prev_tag)
267
317
 
268
- if is_enhancement
269
- intro = 'Implemented enhancement'
270
- end
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
- def get_time_of_tag(prev_tag)
322
+ if @options[:verbose]
323
+ puts "Getting time for tag #{prev_tag['name']}"
324
+ end
280
325
 
281
- if @tag_times_hash[prev_tag['name']]
282
- return @tag_times_hash[prev_tag['name']]
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
- if @options[:verbose]
286
- puts "Get time for tag #{prev_tag['name']}"
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
- def get_all_issues
296
- all_issues = []
336
+ issues = []
337
+ response.each_page do |page|
338
+ issues.concat(page)
339
+ end
297
340
 
298
- issues_req = @github.issues.list user: @options[:user], repo: @options[:project], state: 'closed', filter: 'all', labels: nil
341
+ # remove pull request from issues:
342
+ issues.select! { |x|
343
+ x.pull_request == nil
344
+ }
299
345
 
300
- filtered_issues = issues_req.body.select { |issues|
301
- #compare is there any labels from @options[:labels] array
302
- (issues.labels.map { |issue| issue.name } & @options[:labels]).any?
303
- }
346
+ if @options[:verbose]
347
+ puts "Receive all closed issues: #{issues.count}"
348
+ end
304
349
 
305
- if @options[:add_issues_wo_labels]
306
- issues_wo_labels = issues_req.body.select {
307
- |issues| !issues.labels.map { |issue| issue.name }.any?
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
- # remove pull request from issues:
313
- filtered_issues.select! { |x|
314
- x.pull_request == nil
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
- if @options[:verbose]
318
- puts "Receive all closed issues with labels #{@options[:labels]}: #{all_issues.count} issues"
319
- end
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
- filtered_issues
367
+ filtered_issues
368
+
369
+ end
322
370
 
323
371
  end
324
372
 
325
- end
373
+ if __FILE__ == $0
374
+ GitHubChangelogGenerator::ChangelogGenerator.new.compund_changelog
375
+ end
326
376
 
327
- if __FILE__ == $0
328
- changelog_generator = ChangelogGenerator.new
329
- changelog_generator.compund_changelog
330
- end
377
+ end