github_changelog_generator 1.15.0.pre.rc → 1.15.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.
- checksums.yaml +5 -5
- data/LICENSE +1 -1
- data/README.md +126 -51
- data/bin/git-generate-changelog +1 -1
- data/lib/github_changelog_generator.rb +10 -6
- data/lib/github_changelog_generator/generator/entry.rb +218 -0
- data/lib/github_changelog_generator/generator/generator.rb +96 -119
- data/lib/github_changelog_generator/generator/generator_fetcher.rb +140 -21
- data/lib/github_changelog_generator/generator/generator_processor.rb +40 -10
- data/lib/github_changelog_generator/generator/generator_tags.rb +10 -12
- data/lib/github_changelog_generator/generator/section.rb +104 -0
- data/lib/github_changelog_generator/octo_fetcher.rb +113 -23
- data/lib/github_changelog_generator/options.rb +35 -4
- data/lib/github_changelog_generator/parser.rb +88 -49
- data/lib/github_changelog_generator/parser_file.rb +6 -2
- data/lib/github_changelog_generator/task.rb +2 -3
- data/lib/github_changelog_generator/version.rb +1 -1
- data/man/git-generate-changelog.1 +125 -51
- data/man/git-generate-changelog.1.html +145 -89
- data/man/git-generate-changelog.html +19 -7
- data/man/git-generate-changelog.md +141 -86
- data/spec/files/github-changelog-generator.md +114 -114
- data/spec/{install-gem-in-bundler.gemfile → install_gem_in_bundler.gemfile} +2 -0
- data/spec/spec_helper.rb +1 -5
- data/spec/unit/generator/entry_spec.rb +760 -0
- data/spec/unit/generator/generator_processor_spec.rb +9 -2
- data/spec/unit/generator/generator_tags_spec.rb +5 -21
- data/spec/unit/octo_fetcher_spec.rb +204 -197
- data/spec/unit/options_spec.rb +24 -0
- data/spec/unit/parse_file_spec.rb +2 -2
- data/spec/unit/reader_spec.rb +4 -4
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_commits/when_API_is_valid/returns_commits.json +1 -0
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_commits_before/when_API_is_valid/returns_commits.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_issue_with_proper_key/values.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_issues.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_issues_with_labels.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_pull_request_with_proper_key/values.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_issues_and_pr/when_API_call_is_valid/returns_pull_requests_with_labels.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_pull_requests/when_API_call_is_valid.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_pull_requests/when_API_call_is_valid/returns_correct_pull_request_keys.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_closed_pull_requests/when_API_call_is_valid/returns_pull_requests.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_commit/when_API_call_is_valid.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_commit/when_API_call_is_valid/returns_commit.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_date_of_tag/when_API_call_is_valid.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_date_of_tag/when_API_call_is_valid/returns_date.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_events_async/when_API_call_is_valid.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_fetch_events_async/when_API_call_is_valid/populates_issues.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_API_call_is_valid.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_API_call_is_valid/should_return_tags.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_API_call_is_valid/should_return_tags_count.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_wrong_token_provided.json +1 -1
- data/spec/vcr/GitHubChangelogGenerator_OctoFetcher/_github_fetch_tags/when_wrong_token_provided/should_raise_Unauthorized_error.json +1 -1
- metadata +17 -17
- data/bin/ghclgen +0 -5
- data/lib/github_changelog_generator/generator/generator_generation.rb +0 -181
- data/spec/unit/generator/generator_generation_spec.rb +0 -73
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module GitHubChangelogGenerator
|
4
4
|
class Generator
|
5
|
-
# delete all
|
5
|
+
# delete all issues with labels from options[:exclude_labels] array
|
6
6
|
# @param [Array] issues
|
7
7
|
# @return [Array] filtered array
|
8
8
|
def exclude_issues_by_labels(issues)
|
@@ -14,6 +14,19 @@ module GitHubChangelogGenerator
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
# Only include issues without labels if options[:add_issues_wo_labels]
|
18
|
+
# @param [Array] issues
|
19
|
+
# @return [Array] filtered array
|
20
|
+
def exclude_issues_without_labels(issues)
|
21
|
+
return issues if issues.empty?
|
22
|
+
return issues if issues.first.key?("pull_request") && options[:add_pr_wo_labels]
|
23
|
+
return issues if !issues.first.key?("pull_request") && options[:add_issues_wo_labels]
|
24
|
+
|
25
|
+
issues.reject do |issue|
|
26
|
+
issue["labels"].empty?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
17
30
|
# @return [Array] filtered issues accourding milestone
|
18
31
|
def filter_by_milestone(filtered_issues, tag_name, all_issues)
|
19
32
|
remove_issues_in_milestones(filtered_issues)
|
@@ -63,11 +76,22 @@ module GitHubChangelogGenerator
|
|
63
76
|
end
|
64
77
|
end
|
65
78
|
|
79
|
+
# Method filter issues, that belong only specified tag range
|
80
|
+
#
|
81
|
+
# @param [Array] issues issues to filter
|
82
|
+
# @param [Hash, Nil] newer_tag Tag to find PRs of. May be nil for unreleased section
|
83
|
+
# @return [Array] filtered issues
|
84
|
+
def filter_by_tag(issues, newer_tag = nil)
|
85
|
+
issues.select do |issue|
|
86
|
+
issue["first_occurring_tag"] == (newer_tag.nil? ? nil : newer_tag["name"])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
66
90
|
# Method filter issues, that belong only specified tag range
|
67
91
|
# @param [Array] issues issues to filter
|
68
92
|
# @param [Symbol] hash_key key of date value default is :actual_date
|
69
|
-
# @param [
|
70
|
-
# @param [
|
93
|
+
# @param [Hash, Nil] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag
|
94
|
+
# @param [Hash, Nil] newer_tag all issue after this tag will be excluded. May be nil for unreleased section
|
71
95
|
# @return [Array] filtered issues
|
72
96
|
def delete_by_time(issues, hash_key = "actual_date", older_tag = nil, newer_tag = nil)
|
73
97
|
# in case if not tags specified - return unchanged array
|
@@ -97,9 +121,11 @@ module GitHubChangelogGenerator
|
|
97
121
|
|
98
122
|
def ensure_older_tag(older_tag, newer_tag)
|
99
123
|
return older_tag if older_tag
|
124
|
+
|
100
125
|
idx = sorted_tags.index { |t| t["name"] == newer_tag["name"] }
|
101
126
|
# skip if we are already at the oldest element
|
102
127
|
return if idx == sorted_tags.size - 1
|
128
|
+
|
103
129
|
sorted_tags[idx - 1]
|
104
130
|
end
|
105
131
|
|
@@ -112,11 +138,11 @@ module GitHubChangelogGenerator
|
|
112
138
|
tag_in_range_new
|
113
139
|
end
|
114
140
|
|
115
|
-
def tag_newer_old_tag?(older_tag_time,
|
141
|
+
def tag_newer_old_tag?(older_tag_time, time)
|
116
142
|
tag_in_range_old = if older_tag_time.nil?
|
117
143
|
true
|
118
144
|
else
|
119
|
-
|
145
|
+
time > older_tag_time
|
120
146
|
end
|
121
147
|
tag_in_range_old
|
122
148
|
end
|
@@ -130,33 +156,37 @@ module GitHubChangelogGenerator
|
|
130
156
|
filtered_issues
|
131
157
|
end
|
132
158
|
|
133
|
-
# @
|
159
|
+
# @param [Array] issues Issues & PRs to filter when without labels
|
160
|
+
# @return [Array] Issues & PRs without labels or empty array if
|
161
|
+
# add_issues_wo_labels or add_pr_wo_labels are false
|
134
162
|
def filter_wo_labels(issues)
|
135
|
-
if options[:add_issues_wo_labels]
|
163
|
+
if (!issues.empty? && issues.first.key?("pull_requests") && options[:add_pr_wo_labels]) || options[:add_issues_wo_labels]
|
136
164
|
issues
|
137
165
|
else
|
138
166
|
issues.select { |issue| issue["labels"].map { |l| l["name"] }.any? }
|
139
167
|
end
|
140
168
|
end
|
141
169
|
|
170
|
+
# @todo Document this
|
142
171
|
def filter_by_include_labels(issues)
|
143
172
|
if options[:include_labels].nil?
|
144
173
|
issues
|
145
174
|
else
|
146
175
|
issues.select do |issue|
|
147
176
|
labels = issue["labels"].map { |l| l["name"] } & options[:include_labels]
|
148
|
-
labels.any?
|
177
|
+
labels.any? || issue["labels"].empty?
|
149
178
|
end
|
150
179
|
end
|
151
180
|
end
|
152
181
|
|
153
182
|
# General filtered function
|
154
183
|
#
|
155
|
-
# @param [Array] all_issues
|
184
|
+
# @param [Array] all_issues PRs or issues
|
156
185
|
# @return [Array] filtered issues
|
157
186
|
def filter_array_by_labels(all_issues)
|
158
187
|
filtered_issues = include_issues_by_labels(all_issues)
|
159
|
-
exclude_issues_by_labels(filtered_issues)
|
188
|
+
filtered_issues = exclude_issues_by_labels(filtered_issues)
|
189
|
+
exclude_issues_without_labels(filtered_issues)
|
160
190
|
end
|
161
191
|
|
162
192
|
# Filter issues according labels
|
@@ -4,8 +4,8 @@ module GitHubChangelogGenerator
|
|
4
4
|
class Generator
|
5
5
|
# fetch, filter tags, fetch dates and sort them in time order
|
6
6
|
def fetch_and_filter_tags
|
7
|
-
|
8
|
-
|
7
|
+
since_tag
|
8
|
+
due_tag
|
9
9
|
|
10
10
|
all_tags = @fetcher.get_all_tags
|
11
11
|
fetch_tags_dates(all_tags) # Creates a Hash @tag_times_hash
|
@@ -35,7 +35,7 @@ module GitHubChangelogGenerator
|
|
35
35
|
tag = section_tags[i]
|
36
36
|
|
37
37
|
# Don't create section header for the "since" tag
|
38
|
-
next if
|
38
|
+
next if since_tag && tag["name"] == since_tag
|
39
39
|
|
40
40
|
# Don't create a section header for the first tag in between_tags
|
41
41
|
next if options[:between_tags] && tag == section_tags.last
|
@@ -98,11 +98,11 @@ module GitHubChangelogGenerator
|
|
98
98
|
end
|
99
99
|
|
100
100
|
# @return [Object] try to find newest tag using #Reader and :base option if specified otherwise returns nil
|
101
|
-
def
|
101
|
+
def since_tag
|
102
102
|
@since_tag ||= options.fetch(:since_tag) { version_of_first_item }
|
103
103
|
end
|
104
104
|
|
105
|
-
def
|
105
|
+
def due_tag
|
106
106
|
@due_tag ||= options.fetch(:due_tag, nil)
|
107
107
|
end
|
108
108
|
|
@@ -125,7 +125,7 @@ module GitHubChangelogGenerator
|
|
125
125
|
# @return [Array] filtered tags according :since_tag option
|
126
126
|
def filter_since_tag(all_tags)
|
127
127
|
filtered_tags = all_tags
|
128
|
-
tag =
|
128
|
+
tag = since_tag
|
129
129
|
if tag
|
130
130
|
if all_tags.map { |t| t["name"] }.include? tag
|
131
131
|
idx = all_tags.index { |t| t["name"] == tag }
|
@@ -135,7 +135,7 @@ module GitHubChangelogGenerator
|
|
135
135
|
[]
|
136
136
|
end
|
137
137
|
else
|
138
|
-
|
138
|
+
raise ChangelogGeneratorError, "Error: can't find tag #{tag}, specified with --since-tag option."
|
139
139
|
end
|
140
140
|
end
|
141
141
|
filtered_tags
|
@@ -145,7 +145,7 @@ module GitHubChangelogGenerator
|
|
145
145
|
# @return [Array] filtered tags according :due_tag option
|
146
146
|
def filter_due_tag(all_tags)
|
147
147
|
filtered_tags = all_tags
|
148
|
-
tag =
|
148
|
+
tag = due_tag
|
149
149
|
if tag
|
150
150
|
if all_tags.any? && all_tags.map { |t| t["name"] }.include?(tag)
|
151
151
|
idx = all_tags.index { |t| t["name"] == tag }
|
@@ -155,7 +155,7 @@ module GitHubChangelogGenerator
|
|
155
155
|
[]
|
156
156
|
end
|
157
157
|
else
|
158
|
-
|
158
|
+
raise ChangelogGeneratorError, "Error: can't find tag #{tag}, specified with --due-tag option."
|
159
159
|
end
|
160
160
|
end
|
161
161
|
filtered_tags
|
@@ -208,9 +208,7 @@ module GitHubChangelogGenerator
|
|
208
208
|
end
|
209
209
|
|
210
210
|
def warn_if_tag_not_found(all_tags, tag)
|
211
|
-
unless all_tags.map { |t| t["name"] }.include?(tag)
|
212
|
-
Helper.log.warn "Warning: can't find tag #{tag}, specified with --exclude-tags option."
|
213
|
-
end
|
211
|
+
Helper.log.warn("Warning: can't find tag #{tag}, specified with --exclude-tags option.") unless all_tags.map { |t| t["name"] }.include?(tag)
|
214
212
|
end
|
215
213
|
end
|
216
214
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GitHubChangelogGenerator
|
4
|
+
# This class generates the content for a single section of a changelog entry.
|
5
|
+
# It turns the tagged issues and PRs into a well-formatted list of changes to
|
6
|
+
# be later incorporated into a changelog entry.
|
7
|
+
#
|
8
|
+
# @see GitHubChangelogGenerator::Entry
|
9
|
+
class Section
|
10
|
+
attr_accessor :name, :prefix, :issues, :labels, :body_only
|
11
|
+
|
12
|
+
def initialize(opts = {})
|
13
|
+
@name = opts[:name]
|
14
|
+
@prefix = opts[:prefix]
|
15
|
+
@labels = opts[:labels] || []
|
16
|
+
@issues = opts[:issues] || []
|
17
|
+
@options = opts[:options] || Options.new({})
|
18
|
+
@body_only = opts[:body_only] || false
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the content of a section.
|
22
|
+
#
|
23
|
+
# @return [String] Generate section content
|
24
|
+
def generate_content
|
25
|
+
content = ""
|
26
|
+
|
27
|
+
if @issues.any?
|
28
|
+
content += "#{@prefix}\n\n" unless @options[:simple_list] || @prefix.blank?
|
29
|
+
@issues.each do |issue|
|
30
|
+
merge_string = get_string_for_issue(issue)
|
31
|
+
content += "- " unless @body_only
|
32
|
+
content += "#{merge_string}\n"
|
33
|
+
end
|
34
|
+
content += "\n"
|
35
|
+
end
|
36
|
+
content
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Parse issue and generate single line formatted issue line.
|
42
|
+
#
|
43
|
+
# Example output:
|
44
|
+
# - Add coveralls integration [\#223](https://github.com/github-changelog-generator/github-changelog-generator/pull/223) (@github-changelog-generator)
|
45
|
+
#
|
46
|
+
# @param [Hash] issue Fetched issue from GitHub
|
47
|
+
# @return [String] Markdown-formatted single issue
|
48
|
+
def get_string_for_issue(issue)
|
49
|
+
encapsulated_title = encapsulate_string issue["title"]
|
50
|
+
|
51
|
+
title_with_number = "#{encapsulated_title} [\\##{issue['number']}](#{issue['html_url']})"
|
52
|
+
title_with_number = "#{title_with_number}#{line_labels_for(issue)}" if @options[:issue_line_labels].present?
|
53
|
+
line = issue_line_with_user(title_with_number, issue)
|
54
|
+
issue_line_with_body(line, issue)
|
55
|
+
end
|
56
|
+
|
57
|
+
def issue_line_with_body(line, issue)
|
58
|
+
return issue["body"] if @body_only && issue["body"].present?
|
59
|
+
return line if !@options[:issue_line_body] || issue["body"].blank?
|
60
|
+
|
61
|
+
# get issue body till first line break
|
62
|
+
body_paragraph = body_till_first_break(issue["body"])
|
63
|
+
# remove spaces from begining and end of the string
|
64
|
+
body_paragraph.rstrip!
|
65
|
+
# encapsulate to md
|
66
|
+
encapsulated_body = "\s\s\n" + encapsulate_string(body_paragraph)
|
67
|
+
|
68
|
+
"**#{line}** #{encapsulated_body}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def body_till_first_break(body)
|
72
|
+
body.split(/\n/).first
|
73
|
+
end
|
74
|
+
|
75
|
+
def issue_line_with_user(line, issue)
|
76
|
+
return line if !@options[:author] || issue["pull_request"].nil?
|
77
|
+
|
78
|
+
user = issue["user"]
|
79
|
+
return "#{line} ({Null user})" unless user
|
80
|
+
|
81
|
+
if @options[:usernames_as_github_logins]
|
82
|
+
"#{line} (@#{user['login']})"
|
83
|
+
else
|
84
|
+
"#{line} ([#{user['login']}](#{user['html_url']}))"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
ENCAPSULATED_CHARACTERS = %w(< > * _ \( \) [ ] #)
|
89
|
+
|
90
|
+
# Encapsulate characters to make Markdown look as expected.
|
91
|
+
#
|
92
|
+
# @param [String] string
|
93
|
+
# @return [String] encapsulated input string
|
94
|
+
def encapsulate_string(string)
|
95
|
+
string = string.gsub('\\', '\\\\')
|
96
|
+
|
97
|
+
ENCAPSULATED_CHARACTERS.each do |char|
|
98
|
+
string = string.gsub(char, "\\#{char}")
|
99
|
+
end
|
100
|
+
|
101
|
+
string
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -10,10 +10,10 @@ module GitHubChangelogGenerator
|
|
10
10
|
# fetcher = GitHubChangelogGenerator::OctoFetcher.new(options)
|
11
11
|
class OctoFetcher
|
12
12
|
PER_PAGE_NUMBER = 100
|
13
|
-
MAX_THREAD_NUMBER =
|
13
|
+
MAX_THREAD_NUMBER = 10
|
14
14
|
MAX_FORBIDDEN_RETRIES = 100
|
15
15
|
CHANGELOG_GITHUB_TOKEN = "CHANGELOG_GITHUB_TOKEN"
|
16
|
-
GH_RATE_LIMIT_EXCEEDED_MSG = "Warning: Can't finish operation: GitHub API rate limit exceeded,
|
16
|
+
GH_RATE_LIMIT_EXCEEDED_MSG = "Warning: Can't finish operation: GitHub API rate limit exceeded, changelog may be " \
|
17
17
|
"missing some issues. You can limit the number of issues fetched using the `--max-issues NUM` argument."
|
18
18
|
NO_TOKEN_PROVIDED = "Warning: No token provided (-t option) and variable $CHANGELOG_GITHUB_TOKEN was not found. " \
|
19
19
|
"This script can make only 50 requests to GitHub API per hour without token!"
|
@@ -33,6 +33,8 @@ module GitHubChangelogGenerator
|
|
33
33
|
@http_cache = @options[:http_cache]
|
34
34
|
@cache_file = nil
|
35
35
|
@cache_log = nil
|
36
|
+
@commits = []
|
37
|
+
@compares = {}
|
36
38
|
prepare_cache
|
37
39
|
configure_octokit_ssl
|
38
40
|
@client = Octokit::Client.new(github_options)
|
@@ -40,6 +42,7 @@ module GitHubChangelogGenerator
|
|
40
42
|
|
41
43
|
def prepare_cache
|
42
44
|
return unless @http_cache
|
45
|
+
|
43
46
|
@cache_file = @options.fetch(:cache_file) { File.join(Dir.tmpdir, "github-changelog-http-cache") }
|
44
47
|
@cache_log = @options.fetch(:cache_log) { File.join(Dir.tmpdir, "github-changelog-logger.log") }
|
45
48
|
init_cache
|
@@ -55,7 +58,7 @@ module GitHubChangelogGenerator
|
|
55
58
|
end
|
56
59
|
|
57
60
|
def configure_octokit_ssl
|
58
|
-
ca_file = @options[:ssl_ca_file] || ENV["SSL_CA_FILE"] || File.expand_path("
|
61
|
+
ca_file = @options[:ssl_ca_file] || ENV["SSL_CA_FILE"] || File.expand_path("ssl_certs/cacert.pem", __dir__)
|
59
62
|
Octokit.connection_options = { ssl: { ca_file: ca_file } }
|
60
63
|
end
|
61
64
|
|
@@ -138,7 +141,7 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
|
|
138
141
|
def fetch_closed_issues_and_pr
|
139
142
|
print "Fetching closed issues...\r" if @options[:verbose]
|
140
143
|
issues = []
|
141
|
-
page_i
|
144
|
+
page_i = 0
|
142
145
|
count_pages = calculate_pages(@client, "issues", closed_pr_options)
|
143
146
|
|
144
147
|
iterate_pages(@client, "issues", closed_pr_options) do |new_issues|
|
@@ -162,10 +165,6 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
|
|
162
165
|
pull_requests = []
|
163
166
|
options = { state: "closed" }
|
164
167
|
|
165
|
-
unless @options[:release_branch].nil?
|
166
|
-
options[:base] = @options[:release_branch]
|
167
|
-
end
|
168
|
-
|
169
168
|
page_i = 0
|
170
169
|
count_pages = calculate_pages(@client, "pull_requests", options)
|
171
170
|
|
@@ -211,38 +210,129 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
|
|
211
210
|
Helper.log.info "Fetching events for issues and PR: #{i}"
|
212
211
|
end
|
213
212
|
|
213
|
+
# Fetch comments for PRs and add them to "comments"
|
214
|
+
#
|
215
|
+
# @param [Array] prs The array of PRs.
|
216
|
+
# @return [Void] No return; PRs are updated in-place.
|
217
|
+
def fetch_comments_async(prs)
|
218
|
+
threads = []
|
219
|
+
|
220
|
+
prs.each_slice(MAX_THREAD_NUMBER) do |prs_slice|
|
221
|
+
prs_slice.each do |pr|
|
222
|
+
threads << Thread.new do
|
223
|
+
pr["comments"] = []
|
224
|
+
iterate_pages(@client, "issue_comments", pr["number"]) do |new_comment|
|
225
|
+
pr["comments"].concat(new_comment)
|
226
|
+
end
|
227
|
+
pr["comments"] = pr["comments"].map { |comment| stringify_keys_deep(comment.to_hash) }
|
228
|
+
end
|
229
|
+
end
|
230
|
+
threads.each(&:join)
|
231
|
+
threads = []
|
232
|
+
end
|
233
|
+
nil
|
234
|
+
end
|
235
|
+
|
214
236
|
# Fetch tag time from repo
|
215
237
|
#
|
216
238
|
# @param [Hash] tag GitHub data item about a Tag
|
217
239
|
#
|
218
240
|
# @return [Time] time of specified tag
|
219
241
|
def fetch_date_of_tag(tag)
|
220
|
-
commit_data =
|
242
|
+
commit_data = fetch_commit(tag["commit"]["sha"])
|
221
243
|
commit_data = stringify_keys_deep(commit_data.to_hash)
|
222
244
|
|
223
245
|
commit_data["commit"]["committer"]["date"]
|
224
246
|
end
|
225
247
|
|
248
|
+
# Fetch and cache comparison between two github refs
|
249
|
+
#
|
250
|
+
# @param [String] older The older sha/tag/branch.
|
251
|
+
# @param [String] newer The newer sha/tag/branch.
|
252
|
+
# @return [Hash] Github api response for comparison.
|
253
|
+
def fetch_compare(older, newer)
|
254
|
+
unless @compares["#{older}...#{newer}"]
|
255
|
+
compare_data = check_github_response { @client.compare(user_project, older, newer || "HEAD") }
|
256
|
+
raise StandardError, "Sha #{older} and sha #{newer} are not related; please file a github-changelog-generator issues and describe how to replicate this issue." if compare_data["status"] == "diverged"
|
257
|
+
|
258
|
+
@compares["#{older}...#{newer}"] = stringify_keys_deep(compare_data.to_hash)
|
259
|
+
end
|
260
|
+
@compares["#{older}...#{newer}"]
|
261
|
+
end
|
262
|
+
|
226
263
|
# Fetch commit for specified event
|
227
264
|
#
|
265
|
+
# @param [String] commit_id the SHA of a commit to fetch
|
228
266
|
# @return [Hash]
|
229
|
-
def fetch_commit(
|
230
|
-
|
231
|
-
commit
|
232
|
-
|
233
|
-
|
267
|
+
def fetch_commit(commit_id)
|
268
|
+
found = commits.find do |commit|
|
269
|
+
commit["sha"] == commit_id
|
270
|
+
end
|
271
|
+
if found
|
272
|
+
stringify_keys_deep(found.to_hash)
|
273
|
+
else
|
274
|
+
# cache miss; don't add to @commits because unsure of order.
|
275
|
+
check_github_response do
|
276
|
+
commit = @client.commit(user_project, commit_id)
|
277
|
+
commit = stringify_keys_deep(commit.to_hash)
|
278
|
+
commit
|
279
|
+
end
|
234
280
|
end
|
235
281
|
end
|
236
282
|
|
237
|
-
# Fetch all commits
|
283
|
+
# Fetch all commits
|
238
284
|
#
|
239
|
-
# @return [
|
240
|
-
def
|
241
|
-
commits
|
242
|
-
|
243
|
-
|
285
|
+
# @return [Array] Commits in a repo.
|
286
|
+
def commits
|
287
|
+
if @commits.empty?
|
288
|
+
iterate_pages(@client, "commits") do |new_commits|
|
289
|
+
@commits.concat(new_commits)
|
290
|
+
end
|
244
291
|
end
|
245
|
-
commits
|
292
|
+
@commits
|
293
|
+
end
|
294
|
+
|
295
|
+
# Return the oldest commit in a repo
|
296
|
+
#
|
297
|
+
# @return [Hash] Oldest commit in the github git history.
|
298
|
+
def oldest_commit
|
299
|
+
commits.last
|
300
|
+
end
|
301
|
+
|
302
|
+
# @return [String] Default branch of the repo
|
303
|
+
def default_branch
|
304
|
+
@default_branch ||= @client.repository(user_project)[:default_branch]
|
305
|
+
end
|
306
|
+
|
307
|
+
# Fetch all SHAs occurring in or before a given tag and add them to
|
308
|
+
# "shas_in_tag"
|
309
|
+
#
|
310
|
+
# @param [Array] tags The array of tags.
|
311
|
+
# @return [Nil] No return; tags are updated in-place.
|
312
|
+
def fetch_tag_shas_async(tags)
|
313
|
+
i = 0
|
314
|
+
threads = []
|
315
|
+
print_in_same_line("Fetching SHAs for tags: #{i}/#{tags.count}\r") if @options[:verbose]
|
316
|
+
|
317
|
+
tags.each_slice(MAX_THREAD_NUMBER) do |tags_slice|
|
318
|
+
tags_slice.each do |tag|
|
319
|
+
threads << Thread.new do
|
320
|
+
# Use oldest commit because comparing two arbitrary tags may be diverged
|
321
|
+
commits_in_tag = fetch_compare(oldest_commit["sha"], tag["name"])
|
322
|
+
tag["shas_in_tag"] = commits_in_tag["commits"].collect { |commit| commit["sha"] }
|
323
|
+
print_in_same_line("Fetching SHAs for tags: #{i + 1}/#{tags.count}") if @options[:verbose]
|
324
|
+
i += 1
|
325
|
+
end
|
326
|
+
end
|
327
|
+
threads.each(&:join)
|
328
|
+
threads = []
|
329
|
+
end
|
330
|
+
|
331
|
+
# to clear line from prev print
|
332
|
+
print_empty_line
|
333
|
+
|
334
|
+
Helper.log.info "Fetching SHAs for tags: #{i}"
|
335
|
+
nil
|
246
336
|
end
|
247
337
|
|
248
338
|
private
|
@@ -316,8 +406,8 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
|
|
316
406
|
end
|
317
407
|
|
318
408
|
# Presents the exception, and the aborts with the message.
|
319
|
-
def fail_with_message(
|
320
|
-
Helper.log.error("#{
|
409
|
+
def fail_with_message(error, message)
|
410
|
+
Helper.log.error("#{error.class}: #{error.message}")
|
321
411
|
sys_abort(message)
|
322
412
|
end
|
323
413
|
|