-
-
Notifications
You must be signed in to change notification settings - Fork 844
/
generator_tags.rb
210 lines (173 loc) · 6.95 KB
/
generator_tags.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# frozen_string_literal: true
module GitHubChangelogGenerator
class Generator
# fetch, filter tags, fetch dates and sort them in time order
def fetch_and_filter_tags
since_tag
due_tag
all_tags = @fetcher.fetch_all_tags
fetch_tags_dates(all_tags) # Creates a Hash @tag_times_hash
all_sorted_tags = sort_tags_by_date(all_tags)
@sorted_tags = filter_included_tags(all_sorted_tags)
@sorted_tags = filter_excluded_tags(@sorted_tags)
@filtered_tags = get_filtered_tags(@sorted_tags)
@tag_section_mapping = build_tag_section_mapping(@filtered_tags, @filtered_tags)
@filtered_tags
end
# @param [Array] section_tags are the tags that need a subsection output
# @param [Array] filtered_tags is the list of filtered tags ordered from newest -> oldest
# @return [Hash] key is the tag to output, value is an array of [Left Tag, Right Tag]
# PRs to include in this section will be >= [Left Tag Date] and <= [Right Tag Date]
# rubocop:disable Style/For - for allows us to be more concise
def build_tag_section_mapping(section_tags, filtered_tags)
tag_mapping = {}
for i in 0..(section_tags.length - 1)
tag = section_tags[i]
# Don't create section header for the "since" tag
next if since_tag && tag["name"] == since_tag
# Don't create a section header for the first tag in between_tags
next if options[:between_tags] && tag == section_tags.last
# Don't create a section header for excluded tags
next unless filtered_tags.include?(tag)
older_tag = section_tags[i + 1]
tag_mapping[tag] = [older_tag, tag]
end
tag_mapping
end
# rubocop:enable Style/For
# Sort all tags by date, newest to oldest
def sort_tags_by_date(tags)
puts "Sorting tags..." if options[:verbose]
tags.sort_by! do |x|
get_time_of_tag(x)
end.reverse!
end
# Returns date for given GitHub Tag hash
#
# Memoize the date by tag name.
#
# @param [Hash] tag_name
#
# @return [Time] time of specified tag
def get_time_of_tag(tag_name)
raise ChangelogGeneratorError, "tag_name is nil" if tag_name.nil?
name_of_tag = tag_name.fetch("name")
time_for_tag_name = @tag_times_hash[name_of_tag]
return time_for_tag_name if time_for_tag_name
@fetcher.fetch_date_of_tag(tag_name).tap do |time_string|
@tag_times_hash[name_of_tag] = time_string
end
end
# Detect link, name and time for specified tag.
#
# @param [Hash] newer_tag newer tag. Can be nil, if it's Unreleased section.
# @return [Array] link, name and time of the tag
def detect_link_tag_time(newer_tag)
# if tag is nil - set current time
newer_tag_time = newer_tag.nil? ? Time.new.getutc : get_time_of_tag(newer_tag)
# if it's future release tag - set this value
if newer_tag.nil? && options[:future_release]
newer_tag_name = options[:future_release]
newer_tag_link = options[:future_release]
else
# put unreleased label if there is no name for the tag
newer_tag_name = newer_tag.nil? ? options[:unreleased_label] : newer_tag["name"]
newer_tag_link = newer_tag.nil? ? "HEAD" : newer_tag_name
end
[newer_tag_link, newer_tag_name, newer_tag_time]
end
# @return [Object] try to find newest tag using #Reader and :base option if specified otherwise returns nil
def since_tag
@since_tag ||= options.fetch(:since_tag) { version_of_first_item }
end
def due_tag
@due_tag ||= options.fetch(:due_tag, nil)
end
def version_of_first_item
return unless File.file?(options[:base].to_s)
sections = GitHubChangelogGenerator::Reader.new.read(options[:base])
sections.first["version"] if sections && sections.any?
end
# Return tags after filtering tags in lists provided by option: --exclude-tags
#
# @return [Array]
def get_filtered_tags(all_tags)
filtered_tags = filter_since_tag(all_tags)
filter_due_tag(filtered_tags)
end
# @param [Array] all_tags all tags
# @return [Array] filtered tags according :since_tag option
def filter_since_tag(all_tags)
return all_tags unless (tag = since_tag)
raise ChangelogGeneratorError, "Error: can't find tag #{tag}, specified with --since-tag option." if all_tags.none? { |t| t["name"] == tag }
if (idx = all_tags.index { |t| t["name"] == tag })
all_tags[0..idx]
else
[]
end
end
# @param [Array] all_tags all tags
# @return [Array] filtered tags according :due_tag option
def filter_due_tag(all_tags)
return all_tags unless (tag = due_tag)
raise ChangelogGeneratorError, "Error: can't find tag #{tag}, specified with --due-tag option." if all_tags.none? { |t| t["name"] == tag }
if (idx = all_tags.index { |t| t["name"] == tag }) > 0
all_tags[(idx + 1)..]
else
[]
end
end
# @param [Array] all_tags all tags
# @return [Array] filtered tags according to :include_tags_regex option
def filter_included_tags(all_tags)
if options[:include_tags_regex]
regex = Regexp.new(options[:include_tags_regex])
all_tags.select { |tag| regex =~ tag["name"] }
else
all_tags
end
end
# @param [Array] all_tags all tags
# @return [Array] filtered tags according :exclude_tags or :exclude_tags_regex option
def filter_excluded_tags(all_tags)
if options[:exclude_tags]
apply_exclude_tags(all_tags)
elsif options[:exclude_tags_regex]
apply_exclude_tags_regex(all_tags)
else
all_tags
end
end
private
def apply_exclude_tags(all_tags)
if options[:exclude_tags].is_a?(Regexp)
filter_tags_with_regex(all_tags, options[:exclude_tags], "--exclude-tags")
else
filter_exact_tags(all_tags)
end
end
def apply_exclude_tags_regex(all_tags)
regex = Regexp.new(options[:exclude_tags_regex])
filter_tags_with_regex(all_tags, regex, "--exclude-tags-regex")
end
def filter_tags_with_regex(all_tags, regex, regex_option_name)
warn_if_nonmatching_regex(all_tags, regex, regex_option_name)
all_tags.reject { |tag| regex =~ tag["name"] }
end
def filter_exact_tags(all_tags)
options[:exclude_tags].each do |tag|
warn_if_tag_not_found(all_tags, tag)
end
all_tags.reject { |tag| options[:exclude_tags].include?(tag["name"]) }
end
def warn_if_nonmatching_regex(all_tags, regex, regex_option_name)
return if all_tags.any? { |t| regex.match?(t["name"]) }
Helper.log.warn "Warning: unable to reject any tag, using regex "\
"#{regex.inspect} in #{regex_option_name} option."
end
def warn_if_tag_not_found(all_tags, tag)
return if all_tags.any? { |t| t["name"] == tag }
Helper.log.warn("Warning: can't find tag #{tag}, specified with --exclude-tags option.")
end
end
end