changelog_jira 1.12.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 +7 -0
- data/README.md +233 -0
- data/Rakefile +39 -0
- data/bin/git-generate-changelog +4 -0
- data/bin/github_changelog_generator +4 -0
- data/lib/CHANGELOG.md +58 -0
- data/lib/github_changelog_generator.rb +41 -0
- data/lib/github_changelog_generator/fetcher.rb +221 -0
- data/lib/github_changelog_generator/generator/generator.rb +143 -0
- data/lib/github_changelog_generator/generator/generator_fetcher.rb +83 -0
- data/lib/github_changelog_generator/generator/generator_generation.rb +190 -0
- data/lib/github_changelog_generator/generator/generator_processor.rb +193 -0
- data/lib/github_changelog_generator/generator/generator_tags.rb +184 -0
- data/lib/github_changelog_generator/helper.rb +37 -0
- data/lib/github_changelog_generator/parser.rb +285 -0
- data/lib/github_changelog_generator/parser_file.rb +103 -0
- data/lib/github_changelog_generator/reader.rb +84 -0
- data/lib/github_changelog_generator/task.rb +67 -0
- data/lib/github_changelog_generator/version.rb +3 -0
- data/man/git-generate-changelog.1 +252 -0
- data/man/git-generate-changelog.html +262 -0
- data/man/git-generate-changelog.md +179 -0
- data/spec/files/angular.js.md +9395 -0
- data/spec/files/bundler.md +1911 -0
- data/spec/files/github-changelog-generator.md +305 -0
- data/spec/install-gem-in-bundler.gemfile +3 -0
- data/spec/spec_helper.rb +55 -0
- data/spec/unit/fetcher_spec.rb +59 -0
- data/spec/unit/generator/generator_processor_spec.rb +28 -0
- data/spec/unit/generator/generator_tags_spec.rb +243 -0
- data/spec/unit/parse_file_spec.rb +73 -0
- data/spec/unit/parser_spec.rb +60 -0
- data/spec/unit/reader_spec.rb +113 -0
- metadata +190 -0
@@ -0,0 +1,143 @@
|
|
1
|
+
require_relative "../fetcher"
|
2
|
+
require_relative "generator_generation"
|
3
|
+
require_relative "generator_fetcher"
|
4
|
+
require_relative "generator_processor"
|
5
|
+
require_relative "generator_tags"
|
6
|
+
|
7
|
+
module GitHubChangelogGenerator
|
8
|
+
# Default error for ChangelogGenerator
|
9
|
+
class ChangelogGeneratorError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class Generator
|
13
|
+
attr_accessor :options, :filtered_tags, :github
|
14
|
+
|
15
|
+
# A Generator responsible for all logic, related with change log generation from ready-to-parse issues
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
# generator = GitHubChangelogGenerator::Generator.new
|
19
|
+
# content = generator.compound_changelog
|
20
|
+
def initialize(options = nil)
|
21
|
+
@options = options || {}
|
22
|
+
@tag_times_hash = {}
|
23
|
+
@fetcher = GitHubChangelogGenerator::Fetcher.new @options
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch_issues_and_pr
|
27
|
+
issues, pull_requests = @fetcher.fetch_closed_issues_and_pr
|
28
|
+
|
29
|
+
@pull_requests = @options[:pulls] ? get_filtered_pull_requests(pull_requests) : []
|
30
|
+
|
31
|
+
@issues = @options[:issues] ? get_filtered_issues(issues) : []
|
32
|
+
|
33
|
+
fetch_events_for_issues_and_pr
|
34
|
+
detect_actual_closed_dates(@issues + @pull_requests)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Encapsulate characters to make markdown look as expected.
|
38
|
+
#
|
39
|
+
# @param [String] string
|
40
|
+
# @return [String] encapsulated input string
|
41
|
+
def encapsulate_string(string)
|
42
|
+
string.gsub! '\\', '\\\\'
|
43
|
+
|
44
|
+
encpas_chars = %w(< > * _ \( \) [ ] #)
|
45
|
+
encpas_chars.each do |char|
|
46
|
+
string.gsub! char, "\\#{char}"
|
47
|
+
end
|
48
|
+
|
49
|
+
string
|
50
|
+
end
|
51
|
+
|
52
|
+
# Generates log for section with header and body
|
53
|
+
#
|
54
|
+
# @param [Array] pull_requests List or PR's in new section
|
55
|
+
# @param [Array] issues List of issues in new section
|
56
|
+
# @param [String] newer_tag Name of the newer tag. Could be nil for `Unreleased` section
|
57
|
+
# @param [String] older_tag_name Older tag, used for the links. Could be nil for last tag.
|
58
|
+
# @return [String] Ready and parsed section
|
59
|
+
def create_log_for_tag(pull_requests, issues, newer_tag, older_tag_name = nil)
|
60
|
+
newer_tag_link, newer_tag_name, newer_tag_time = detect_link_tag_time(newer_tag)
|
61
|
+
|
62
|
+
github_site = options[:github_site] || "https://github.com"
|
63
|
+
project_url = "#{github_site}/#{@options[:user]}/#{@options[:project]}"
|
64
|
+
|
65
|
+
log = generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_name, project_url)
|
66
|
+
|
67
|
+
if @options[:issues]
|
68
|
+
# Generate issues:
|
69
|
+
log += issues_to_log(issues, pull_requests)
|
70
|
+
end
|
71
|
+
|
72
|
+
if @options[:pulls]
|
73
|
+
# Generate pull requests:
|
74
|
+
log += generate_sub_section(pull_requests, @options[:merge_prefix])
|
75
|
+
end
|
76
|
+
|
77
|
+
log
|
78
|
+
end
|
79
|
+
|
80
|
+
# Generate ready-to-paste log from list of issues and pull requests.
|
81
|
+
#
|
82
|
+
# @param [Array] issues
|
83
|
+
# @param [Array] pull_requests
|
84
|
+
# @return [String] generated log for issues
|
85
|
+
def issues_to_log(issues, pull_requests)
|
86
|
+
log = ""
|
87
|
+
bugs_a, enhancement_a, issues_a = parse_by_sections(issues, pull_requests)
|
88
|
+
|
89
|
+
log += generate_sub_section(enhancement_a, @options[:enhancement_prefix])
|
90
|
+
log += generate_sub_section(bugs_a, @options[:bug_prefix])
|
91
|
+
log += generate_sub_section(issues_a, @options[:issue_prefix])
|
92
|
+
log
|
93
|
+
end
|
94
|
+
|
95
|
+
# This method sort issues by types
|
96
|
+
# (bugs, features, or just closed issues) by labels
|
97
|
+
#
|
98
|
+
# @param [Array] issues
|
99
|
+
# @param [Array] pull_requests
|
100
|
+
# @return [Array] tuple of filtered arrays: (Bugs, Enhancements Issues)
|
101
|
+
def parse_by_sections(issues, pull_requests)
|
102
|
+
issues_a = []
|
103
|
+
enhancement_a = []
|
104
|
+
bugs_a = []
|
105
|
+
|
106
|
+
issues.each do |dict|
|
107
|
+
added = false
|
108
|
+
dict.labels.each do |label|
|
109
|
+
if @options[:bug_labels].include? label.name
|
110
|
+
bugs_a.push dict
|
111
|
+
added = true
|
112
|
+
next
|
113
|
+
end
|
114
|
+
if @options[:enhancement_labels].include? label.name
|
115
|
+
enhancement_a.push dict
|
116
|
+
added = true
|
117
|
+
next
|
118
|
+
end
|
119
|
+
end
|
120
|
+
issues_a.push dict unless added
|
121
|
+
end
|
122
|
+
|
123
|
+
added_pull_requests = []
|
124
|
+
pull_requests.each do |dict|
|
125
|
+
dict.labels.each do |label|
|
126
|
+
if @options[:bug_labels].include? label.name
|
127
|
+
bugs_a.push dict
|
128
|
+
added_pull_requests.push dict
|
129
|
+
next
|
130
|
+
end
|
131
|
+
if @options[:enhancement_labels].include? label.name
|
132
|
+
enhancement_a.push dict
|
133
|
+
added_pull_requests.push dict
|
134
|
+
next
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
added_pull_requests.each { |p| pull_requests.delete(p) }
|
139
|
+
|
140
|
+
[bugs_a, enhancement_a, issues_a]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module GitHubChangelogGenerator
|
2
|
+
class Generator
|
3
|
+
# Fetch event for issues and pull requests
|
4
|
+
# @return [Array] array of fetched issues
|
5
|
+
def fetch_events_for_issues_and_pr
|
6
|
+
if @options[:verbose]
|
7
|
+
print "Fetching events for issues and PR: 0/#{@issues.count + @pull_requests.count}\r"
|
8
|
+
end
|
9
|
+
|
10
|
+
# Async fetching events:
|
11
|
+
@fetcher.fetch_events_async(@issues + @pull_requests)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Async fetching of all tags dates
|
15
|
+
def fetch_tags_dates
|
16
|
+
print "Fetching tag dates...\r" if @options[:verbose]
|
17
|
+
# Async fetching tags:
|
18
|
+
threads = []
|
19
|
+
i = 0
|
20
|
+
all = @filtered_tags.count
|
21
|
+
@filtered_tags.each do |tag|
|
22
|
+
print " \r"
|
23
|
+
threads << Thread.new do
|
24
|
+
get_time_of_tag(tag)
|
25
|
+
print "Fetching tags dates: #{i + 1}/#{all}\r" if @options[:verbose]
|
26
|
+
i += 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
threads.each(&:join)
|
30
|
+
puts "Fetching tags dates: #{i}" if @options[:verbose]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Find correct closed dates, if issues was closed by commits
|
34
|
+
def detect_actual_closed_dates(issues)
|
35
|
+
print "Fetching closed dates for issues...\r" if @options[:verbose]
|
36
|
+
|
37
|
+
max_thread_number = 50
|
38
|
+
issues.each_slice(max_thread_number) do |issues_slice|
|
39
|
+
threads = []
|
40
|
+
issues_slice.each do |issue|
|
41
|
+
threads << Thread.new { find_closed_date_by_commit(issue) }
|
42
|
+
end
|
43
|
+
threads.each(&:join)
|
44
|
+
end
|
45
|
+
puts "Fetching closed dates for issues: Done!" if @options[:verbose]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Fill :actual_date parameter of specified issue by closed date of the commit, if it was closed by commit.
|
49
|
+
# @param [Hash] issue
|
50
|
+
def find_closed_date_by_commit(issue)
|
51
|
+
unless issue[:events].nil?
|
52
|
+
# if it's PR -> then find "merged event", in case of usual issue -> fond closed date
|
53
|
+
compare_string = issue[:merged_at].nil? ? "closed" : "merged"
|
54
|
+
# reverse! - to find latest closed event. (event goes in date order)
|
55
|
+
issue[:events].reverse!.each do |event|
|
56
|
+
if event[:event].eql? compare_string
|
57
|
+
set_date_from_event(event, issue)
|
58
|
+
break
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
# TODO: assert issues, that remain without 'actual_date' hash for some reason.
|
63
|
+
end
|
64
|
+
|
65
|
+
# Set closed date from this issue
|
66
|
+
#
|
67
|
+
# @param [Hash] event
|
68
|
+
# @param [Hash] issue
|
69
|
+
def set_date_from_event(event, issue)
|
70
|
+
if event[:commit_id].nil?
|
71
|
+
issue[:actual_date] = issue[:closed_at]
|
72
|
+
else
|
73
|
+
begin
|
74
|
+
commit = @fetcher.fetch_commit(event)
|
75
|
+
issue[:actual_date] = commit[:author][:date]
|
76
|
+
rescue
|
77
|
+
puts "Warning: Can't fetch commit #{event[:commit_id]}. It is probably referenced from another repo.".yellow
|
78
|
+
issue[:actual_date] = issue[:closed_at]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
module GitHubChangelogGenerator
|
2
|
+
class Generator
|
3
|
+
# Main function to start change log generation
|
4
|
+
#
|
5
|
+
# @return [String] Generated change log file
|
6
|
+
def compound_changelog
|
7
|
+
fetch_and_filter_tags
|
8
|
+
sort_tags_by_date(@filtered_tags)
|
9
|
+
fetch_issues_and_pr
|
10
|
+
|
11
|
+
log = ""
|
12
|
+
log += @options[:frontmatter] if @options[:frontmatter]
|
13
|
+
log += "#{@options[:header]}\n\n"
|
14
|
+
|
15
|
+
log += if @options[:unreleased_only]
|
16
|
+
generate_log_between_tags(filtered_tags[0], nil)
|
17
|
+
else
|
18
|
+
generate_log_for_all_tags
|
19
|
+
end
|
20
|
+
|
21
|
+
log += File.read(@options[:base]) if File.file?(@options[:base])
|
22
|
+
|
23
|
+
log += "\n"
|
24
|
+
@log = log
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String] temp method should be removed soon
|
28
|
+
def generate_for_2_tags(log)
|
29
|
+
tag1 = @options[:tag1]
|
30
|
+
tag2 = @options[:tag2]
|
31
|
+
tags_strings = []
|
32
|
+
filtered_tags.each { |x| tags_strings.push(x["name"]) }
|
33
|
+
|
34
|
+
if tags_strings.include?(tag1)
|
35
|
+
if tags_strings.include?(tag2)
|
36
|
+
to_a = tags_strings.map.with_index.to_a
|
37
|
+
hash = Hash[to_a]
|
38
|
+
index1 = hash[tag1]
|
39
|
+
index2 = hash[tag2]
|
40
|
+
log += generate_log_between_tags(all_tags[index1], all_tags[index2])
|
41
|
+
else
|
42
|
+
raise ChangelogGeneratorError, "Can't find tag #{tag2} -> exit".red
|
43
|
+
end
|
44
|
+
else
|
45
|
+
raise ChangelogGeneratorError, "Can't find tag #{tag1} -> exit".red
|
46
|
+
end
|
47
|
+
log
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param [Array] issues List of issues on sub-section
|
51
|
+
# @param [String] prefix Nae of sub-section
|
52
|
+
# @return [String] Generate ready-to-go sub-section
|
53
|
+
def generate_sub_section(issues, prefix)
|
54
|
+
log = ""
|
55
|
+
|
56
|
+
if issues.any?
|
57
|
+
log += "#{prefix}\n\n" unless options[:simple_list]
|
58
|
+
issues.each do |issue|
|
59
|
+
|
60
|
+
if issue[:title].include? " :: "
|
61
|
+
t = issue[:title].split(" :: ")
|
62
|
+
merge_string = "[#{t[0]}](https://jira.netwerven.nl/browse/" + t[0] + ") :: " + get_string_for_issue(issue).sub("#{t[0]} :: ", "")
|
63
|
+
else
|
64
|
+
merge_string = get_string_for_issue(issue)
|
65
|
+
end
|
66
|
+
log += "- #{merge_string}\n"
|
67
|
+
end
|
68
|
+
log += "\n"
|
69
|
+
end
|
70
|
+
log
|
71
|
+
end
|
72
|
+
|
73
|
+
# It generate one header for section with specific parameters.
|
74
|
+
#
|
75
|
+
# @param [String] newer_tag_name - name of newer tag
|
76
|
+
# @param [String] newer_tag_link - used for links. Could be same as #newer_tag_name or some specific value, like HEAD
|
77
|
+
# @param [Time] newer_tag_time - time, when newer tag created
|
78
|
+
# @param [String] older_tag_link - tag name, used for links.
|
79
|
+
# @param [String] project_url - url for current project.
|
80
|
+
# @return [String] - Generate one ready-to-add section.
|
81
|
+
def generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_link, project_url)
|
82
|
+
log = ""
|
83
|
+
|
84
|
+
# Generate date string:
|
85
|
+
time_string = newer_tag_time.strftime @options[:date_format]
|
86
|
+
|
87
|
+
# Generate tag name and link
|
88
|
+
release_url = if @options[:release_url]
|
89
|
+
format(@options[:release_url], newer_tag_link)
|
90
|
+
else
|
91
|
+
"#{project_url}/tree/#{newer_tag_link}"
|
92
|
+
end
|
93
|
+
log += if newer_tag_name.equal? @options[:unreleased_label]
|
94
|
+
"## [#{newer_tag_name}](#{release_url})\n\n"
|
95
|
+
else
|
96
|
+
"## [#{newer_tag_name}](#{release_url}) (#{time_string})\n"
|
97
|
+
end
|
98
|
+
|
99
|
+
if @options[:compare_link] && older_tag_link
|
100
|
+
# Generate compare link
|
101
|
+
log += "[Full Changelog](#{project_url}/compare/#{older_tag_link}...#{newer_tag_link})\n\n"
|
102
|
+
end
|
103
|
+
|
104
|
+
log
|
105
|
+
end
|
106
|
+
|
107
|
+
# Generate log only between 2 specified tags
|
108
|
+
# @param [String] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag
|
109
|
+
# @param [String] newer_tag all issue after this tag will be excluded. May be nil for unreleased section
|
110
|
+
def generate_log_between_tags(older_tag, newer_tag)
|
111
|
+
filtered_issues, filtered_pull_requests = filter_issues_for_tags(newer_tag, older_tag)
|
112
|
+
|
113
|
+
older_tag_name = older_tag.nil? ? detect_since_tag : older_tag["name"]
|
114
|
+
|
115
|
+
if newer_tag.nil? && filtered_issues.empty? && filtered_pull_requests.empty?
|
116
|
+
# do not generate empty unreleased section
|
117
|
+
return ""
|
118
|
+
end
|
119
|
+
|
120
|
+
create_log_for_tag(filtered_pull_requests, filtered_issues, newer_tag, older_tag_name)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Apply all filters to issues and pull requests
|
124
|
+
#
|
125
|
+
# @return [Array] filtered issues and pull requests
|
126
|
+
def filter_issues_for_tags(newer_tag, older_tag)
|
127
|
+
filtered_pull_requests = delete_by_time(@pull_requests, :actual_date, older_tag, newer_tag)
|
128
|
+
filtered_issues = delete_by_time(@issues, :actual_date, older_tag, newer_tag)
|
129
|
+
|
130
|
+
newer_tag_name = newer_tag.nil? ? nil : newer_tag["name"]
|
131
|
+
|
132
|
+
if @options[:filter_issues_by_milestone]
|
133
|
+
# delete excess irrelevant issues (according milestones). Issue #22.
|
134
|
+
filtered_issues = filter_by_milestone(filtered_issues, newer_tag_name, @issues)
|
135
|
+
filtered_pull_requests = filter_by_milestone(filtered_pull_requests, newer_tag_name, @pull_requests)
|
136
|
+
end
|
137
|
+
[filtered_issues, filtered_pull_requests]
|
138
|
+
end
|
139
|
+
|
140
|
+
# The full cycle of generation for whole project
|
141
|
+
# @return [String] The complete change log
|
142
|
+
def generate_log_for_all_tags
|
143
|
+
puts "Generating log..." if @options[:verbose]
|
144
|
+
|
145
|
+
log = generate_unreleased_section
|
146
|
+
|
147
|
+
(1...filtered_tags.size).each do |index|
|
148
|
+
log += generate_log_between_tags(filtered_tags[index], filtered_tags[index - 1])
|
149
|
+
end
|
150
|
+
if @filtered_tags.count != 0
|
151
|
+
log += generate_log_between_tags(nil, filtered_tags.last)
|
152
|
+
end
|
153
|
+
|
154
|
+
log
|
155
|
+
end
|
156
|
+
|
157
|
+
def generate_unreleased_section
|
158
|
+
log = ""
|
159
|
+
if @options[:unreleased]
|
160
|
+
unreleased_log = generate_log_between_tags(filtered_tags[0], nil)
|
161
|
+
log += unreleased_log if unreleased_log
|
162
|
+
end
|
163
|
+
log
|
164
|
+
end
|
165
|
+
|
166
|
+
# Parse issue and generate single line formatted issue line.
|
167
|
+
#
|
168
|
+
# Example output:
|
169
|
+
# - Add coveralls integration [\#223](https://github.com/skywinder/github-changelog-generator/pull/223) ([skywinder](https://github.com/skywinder))
|
170
|
+
#
|
171
|
+
# @param [Hash] issue Fetched issue from GitHub
|
172
|
+
# @return [String] Markdown-formatted single issue
|
173
|
+
def get_string_for_issue(issue)
|
174
|
+
encapsulated_title = encapsulate_string issue[:title]
|
175
|
+
|
176
|
+
title_with_number = "#{encapsulated_title} [\\##{issue[:number]}](#{issue.html_url})"
|
177
|
+
|
178
|
+
unless issue.pull_request.nil?
|
179
|
+
if @options[:author]
|
180
|
+
title_with_number += if issue.user.nil?
|
181
|
+
" ({Null user})"
|
182
|
+
else
|
183
|
+
" ([#{issue.user.login}](#{issue.user.html_url}))"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
title_with_number
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module GitHubChangelogGenerator
|
2
|
+
class Generator
|
3
|
+
# delete all labels with labels from @options[:exclude_labels] array
|
4
|
+
# @param [Array] issues
|
5
|
+
# @return [Array] filtered array
|
6
|
+
def exclude_issues_by_labels(issues)
|
7
|
+
return issues if !@options[:exclude_labels] || @options[:exclude_labels].empty?
|
8
|
+
|
9
|
+
issues.reject do |issue|
|
10
|
+
labels = issue.labels.map(&:name)
|
11
|
+
(labels & @options[:exclude_labels]).any?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Array] filtered issues accourding milestone
|
16
|
+
def filter_by_milestone(filtered_issues, tag_name, all_issues)
|
17
|
+
remove_issues_in_milestones(filtered_issues)
|
18
|
+
unless tag_name.nil?
|
19
|
+
# add missed issues (according milestones)
|
20
|
+
issues_to_add = find_issues_to_add(all_issues, tag_name)
|
21
|
+
|
22
|
+
filtered_issues |= issues_to_add
|
23
|
+
end
|
24
|
+
filtered_issues
|
25
|
+
end
|
26
|
+
|
27
|
+
# Add all issues, that should be in that tag, according milestone
|
28
|
+
#
|
29
|
+
# @param [Array] all_issues
|
30
|
+
# @param [String] tag_name
|
31
|
+
# @return [Array] issues with milestone #tag_name
|
32
|
+
def find_issues_to_add(all_issues, tag_name)
|
33
|
+
all_issues.select do |issue|
|
34
|
+
if issue.milestone.nil?
|
35
|
+
false
|
36
|
+
else
|
37
|
+
# check, that this milestone in tag list:
|
38
|
+
milestone_is_tag = @filtered_tags.find do |tag|
|
39
|
+
tag.name == issue.milestone.title
|
40
|
+
end
|
41
|
+
|
42
|
+
if milestone_is_tag.nil?
|
43
|
+
false
|
44
|
+
else
|
45
|
+
issue.milestone.title == tag_name
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Array] array with removed issues, that contain milestones with same name as a tag
|
52
|
+
def remove_issues_in_milestones(filtered_issues)
|
53
|
+
filtered_issues.select! do |issue|
|
54
|
+
# leave issues without milestones
|
55
|
+
if issue.milestone.nil?
|
56
|
+
true
|
57
|
+
else
|
58
|
+
# check, that this milestone in tag list:
|
59
|
+
@filtered_tags.find { |tag| tag.name == issue.milestone.title }.nil?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Method filter issues, that belong only specified tag range
|
65
|
+
# @param [Array] issues issues to filter
|
66
|
+
# @param [Symbol] hash_key key of date value default is :actual_date
|
67
|
+
# @param [String] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag
|
68
|
+
# @param [String] newer_tag all issue after this tag will be excluded. May be nil for unreleased section
|
69
|
+
# @return [Array] filtered issues
|
70
|
+
def delete_by_time(issues, hash_key = :actual_date, older_tag = nil, newer_tag = nil)
|
71
|
+
# in case if not tags specified - return unchanged array
|
72
|
+
return issues if older_tag.nil? && newer_tag.nil?
|
73
|
+
|
74
|
+
newer_tag_time = newer_tag && get_time_of_tag(newer_tag)
|
75
|
+
older_tag_time = older_tag && get_time_of_tag(older_tag)
|
76
|
+
|
77
|
+
issues.select do |issue|
|
78
|
+
if issue[hash_key]
|
79
|
+
time = Time.parse(issue[hash_key]).utc
|
80
|
+
|
81
|
+
tag_in_range_old = tag_newer_old_tag?(older_tag_time, time)
|
82
|
+
|
83
|
+
tag_in_range_new = tag_older_new_tag?(newer_tag_time, time)
|
84
|
+
|
85
|
+
tag_in_range = tag_in_range_old && tag_in_range_new
|
86
|
+
|
87
|
+
tag_in_range
|
88
|
+
else
|
89
|
+
false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def tag_older_new_tag?(newer_tag_time, time)
|
95
|
+
tag_in_range_new = if newer_tag_time.nil?
|
96
|
+
true
|
97
|
+
else
|
98
|
+
time <= newer_tag_time
|
99
|
+
end
|
100
|
+
tag_in_range_new
|
101
|
+
end
|
102
|
+
|
103
|
+
def tag_newer_old_tag?(older_tag_time, t)
|
104
|
+
tag_in_range_old = if older_tag_time.nil?
|
105
|
+
true
|
106
|
+
else
|
107
|
+
t > older_tag_time
|
108
|
+
end
|
109
|
+
tag_in_range_old
|
110
|
+
end
|
111
|
+
|
112
|
+
# Include issues with labels, specified in :include_labels
|
113
|
+
# @param [Array] issues to filter
|
114
|
+
# @return [Array] filtered array of issues
|
115
|
+
def include_issues_by_labels(issues)
|
116
|
+
filtered_issues = filter_by_include_labels(issues)
|
117
|
+
filtered_issues |= filter_wo_labels(issues)
|
118
|
+
filtered_issues
|
119
|
+
end
|
120
|
+
|
121
|
+
# @return [Array] issues without labels or empty array if add_issues_wo_labels is false
|
122
|
+
def filter_wo_labels(issues)
|
123
|
+
if @options[:add_issues_wo_labels]
|
124
|
+
issues_wo_labels = issues.select do |issue|
|
125
|
+
!issue.labels.map(&:name).any?
|
126
|
+
end
|
127
|
+
return issues_wo_labels
|
128
|
+
end
|
129
|
+
[]
|
130
|
+
end
|
131
|
+
|
132
|
+
def filter_by_include_labels(issues)
|
133
|
+
filtered_issues = @options[:include_labels].nil? ? issues : issues.select do |issue|
|
134
|
+
labels = issue.labels.map(&:name) & @options[:include_labels]
|
135
|
+
labels.any?
|
136
|
+
end
|
137
|
+
filtered_issues
|
138
|
+
end
|
139
|
+
|
140
|
+
# General filtered function
|
141
|
+
#
|
142
|
+
# @param [Array] all_issues
|
143
|
+
# @return [Array] filtered issues
|
144
|
+
def filter_array_by_labels(all_issues)
|
145
|
+
filtered_issues = include_issues_by_labels(all_issues)
|
146
|
+
exclude_issues_by_labels(filtered_issues)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Filter issues according labels
|
150
|
+
# @return [Array] Filtered issues
|
151
|
+
def get_filtered_issues(issues)
|
152
|
+
issues = filter_array_by_labels(issues)
|
153
|
+
puts "Filtered issues: #{issues.count}" if @options[:verbose]
|
154
|
+
issues
|
155
|
+
end
|
156
|
+
|
157
|
+
# This method fetches missing params for PR and filter them by specified options
|
158
|
+
# It include add all PR's with labels from @options[:include_labels] array
|
159
|
+
# And exclude all from :exclude_labels array.
|
160
|
+
# @return [Array] filtered PR's
|
161
|
+
def get_filtered_pull_requests(pull_requests)
|
162
|
+
pull_requests = filter_array_by_labels(pull_requests)
|
163
|
+
pull_requests = filter_merged_pull_requests(pull_requests)
|
164
|
+
puts "Filtered pull requests: #{pull_requests.count}" if @options[:verbose]
|
165
|
+
pull_requests
|
166
|
+
end
|
167
|
+
|
168
|
+
# This method filter only merged PR and
|
169
|
+
# fetch missing required attributes for pull requests
|
170
|
+
# :merged_at - is a date, when issue PR was merged.
|
171
|
+
# More correct to use merged date, rather than closed date.
|
172
|
+
def filter_merged_pull_requests(pull_requests)
|
173
|
+
print "Fetching merged dates...\r" if @options[:verbose]
|
174
|
+
closed_pull_requests = @fetcher.fetch_closed_pull_requests
|
175
|
+
|
176
|
+
pull_requests.each do |pr|
|
177
|
+
fetched_pr = closed_pull_requests.find do |fpr|
|
178
|
+
fpr.number == pr.number
|
179
|
+
end
|
180
|
+
if fetched_pr
|
181
|
+
pr[:merged_at] = fetched_pr[:merged_at]
|
182
|
+
closed_pull_requests.delete(fetched_pr)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
pull_requests.select! do |pr|
|
187
|
+
!pr[:merged_at].nil?
|
188
|
+
end
|
189
|
+
|
190
|
+
pull_requests
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|