github_changelog_generator 1.4.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -0
- data/.rubocop_todo.yml +11 -43
- data/CHANGELOG.md +24 -4
- data/README.md +29 -24
- data/bin/github_changelog_generator +1 -1
- data/lib/CHANGELOG.md +6 -2
- data/lib/github_changelog_generator.rb +5 -488
- data/lib/github_changelog_generator/fetcher.rb +6 -12
- data/lib/github_changelog_generator/generator/generator.rb +123 -0
- data/lib/github_changelog_generator/generator/generator_fetcher.rb +83 -0
- data/lib/github_changelog_generator/generator/generator_generation.rb +174 -0
- data/lib/github_changelog_generator/generator/generator_processor.rb +192 -0
- data/lib/github_changelog_generator/generator/generator_tags.rb +70 -0
- data/lib/github_changelog_generator/parser.rb +122 -77
- data/lib/github_changelog_generator/version.rb +1 -1
- data/spec/unit/generator/generator_tags_spec.rb +69 -0
- data/spec/unit/parser_spec.rb +60 -0
- metadata +11 -3
- data/lib/github_changelog_generator/generator.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 563c813256e21ca9658e33cdd86ce3078b496478
|
4
|
+
data.tar.gz: 9f07193e18dcf30878f25f10d0b44a84b04f264a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c9e6456e97e7d1c1e1161d6cfd21d80066e28130df7879f84bd2930db791b102ff55064b4426c0fa2de8c34894dce297ab48b565f0072cfdcf6d21d48d91726
|
7
|
+
data.tar.gz: 80e4f40a783cdb79a6498e4af85ed9a227663ff7fa4d55730442aad114343faf70cc199731c7f4480a5c0b66e4112e28de0b461fdd5f1bdd9070e6feb42ea960
|
data/.rubocop.yml
CHANGED
@@ -6,3 +6,11 @@ Metrics/LineLength:
|
|
6
6
|
#http://viget.com/extend/just-use-double-quoted-ruby-strings
|
7
7
|
Style/StringLiterals:
|
8
8
|
EnforcedStyle: double_quotes
|
9
|
+
|
10
|
+
# Configuration parameters: CountComments.
|
11
|
+
Metrics/ClassLength:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
# Configuration parameters: CountComments.
|
15
|
+
Metrics/MethodLength:
|
16
|
+
Enabled: false
|
data/.rubocop_todo.yml
CHANGED
@@ -1,68 +1,36 @@
|
|
1
1
|
# This configuration was generated by `rubocop --auto-gen-config`
|
2
|
-
# on 2015-05-
|
2
|
+
# on 2015-05-26 16:00:55 +0300 using RuboCop version 0.31.0.
|
3
3
|
# The point is for the user to remove these configuration records
|
4
4
|
# one by one as the offenses are removed from the code base.
|
5
5
|
# Note that changes in the inspected code, or installation of new
|
6
6
|
# versions of RuboCop, may require this file to be generated again.
|
7
7
|
|
8
|
-
# Offense count:
|
8
|
+
# Offense count: 14
|
9
9
|
Metrics/AbcSize:
|
10
|
-
|
11
|
-
|
12
|
-
# Offense count: 2
|
13
|
-
Metrics/BlockNesting:
|
14
|
-
Max: 4
|
10
|
+
Max: 59
|
15
11
|
|
16
|
-
# Offense count:
|
17
|
-
# Configuration parameters: CountComments.
|
18
|
-
Metrics/ClassLength:
|
19
|
-
Max: 337
|
20
|
-
|
21
|
-
# Offense count: 5
|
12
|
+
# Offense count: 1
|
22
13
|
Metrics/CyclomaticComplexity:
|
23
|
-
Max:
|
14
|
+
Max: 7
|
24
15
|
|
25
|
-
# Offense count:
|
26
|
-
# Configuration parameters: CountComments.
|
27
|
-
Metrics/MethodLength:
|
28
|
-
Enabled: false
|
29
|
-
|
30
|
-
# Offense count: 5
|
16
|
+
# Offense count: 1
|
31
17
|
Metrics/PerceivedComplexity:
|
32
|
-
Max:
|
18
|
+
Max: 8
|
33
19
|
|
34
|
-
# Offense count:
|
20
|
+
# Offense count: 2
|
35
21
|
Style/AccessorMethodName:
|
36
22
|
Enabled: false
|
37
23
|
|
38
|
-
# Offense count:
|
39
|
-
# Cop supports --auto-correct.
|
40
|
-
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
41
|
-
Style/AndOr:
|
42
|
-
Enabled: false
|
43
|
-
|
44
|
-
# Offense count: 19
|
45
|
-
# Cop supports --auto-correct.
|
46
|
-
# Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods.
|
47
|
-
Style/BlockDelimiters:
|
48
|
-
Enabled: false
|
49
|
-
|
50
|
-
# Offense count: 4
|
24
|
+
# Offense count: 8
|
51
25
|
Style/Documentation:
|
52
26
|
Enabled: false
|
53
27
|
|
54
|
-
# Offense count:
|
28
|
+
# Offense count: 1
|
55
29
|
# Configuration parameters: MinBodyLength.
|
56
30
|
Style/GuardClause:
|
57
31
|
Enabled: false
|
58
32
|
|
59
|
-
# Offense count:
|
60
|
-
# Cop supports --auto-correct.
|
61
|
-
# Configuration parameters: MaxLineLength.
|
62
|
-
Style/IfUnlessModifier:
|
63
|
-
Enabled: false
|
64
|
-
|
65
|
-
# Offense count: 2
|
33
|
+
# Offense count: 1
|
66
34
|
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
|
67
35
|
Style/Next:
|
68
36
|
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [1.4.1](https://github.com/skywinder/github-changelog-generator/tree/1.4.1) (2015-05-19)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/skywinder/github-changelog-generator/compare/1.4.0...1.4.1)
|
6
|
+
|
7
|
+
**Implemented enhancements:**
|
8
|
+
|
9
|
+
- Trees/Archives with missing change log notes for the current tag. [\#230](https://github.com/skywinder/github-changelog-generator/issues/230)
|
10
|
+
|
11
|
+
**Fixed bugs:**
|
12
|
+
|
13
|
+
- github\_changelog\_generator.rb:220:in ``': No such file or directory - pwd \(Errno::ENOENT\) [\#237](https://github.com/skywinder/github-changelog-generator/issues/237)
|
14
|
+
|
15
|
+
- Doesnot generator changelog [\#235](https://github.com/skywinder/github-changelog-generator/issues/235)
|
16
|
+
|
17
|
+
- Exclude closed \(not merged\) PR's from changelog. [\#69](https://github.com/skywinder/github-changelog-generator/issues/69)
|
18
|
+
|
19
|
+
**Merged pull requests:**
|
20
|
+
|
21
|
+
- Wrap GitHub requests in function check\_github\_response [\#238](https://github.com/skywinder/github-changelog-generator/pull/238) ([skywinder](https://github.com/skywinder))
|
22
|
+
|
23
|
+
- Add fetch token tests [\#236](https://github.com/skywinder/github-changelog-generator/pull/236) ([skywinder](https://github.com/skywinder))
|
24
|
+
|
25
|
+
- Add future release option [\#231](https://github.com/skywinder/github-changelog-generator/pull/231) ([sildur](https://github.com/sildur))
|
26
|
+
|
3
27
|
## [1.4.0](https://github.com/skywinder/github-changelog-generator/tree/1.4.0) (2015-05-07)
|
4
28
|
|
5
29
|
[Full Changelog](https://github.com/skywinder/github-changelog-generator/compare/1.3.11...1.4.0)
|
@@ -62,10 +86,6 @@
|
|
62
86
|
|
63
87
|
[Full Changelog](https://github.com/skywinder/github-changelog-generator/compare/1.3.6...1.3.8)
|
64
88
|
|
65
|
-
**Merged pull requests:**
|
66
|
-
|
67
|
-
- Fix `git remote` parsing in case, when script running without parameters inside destination directory [\#61](https://github.com/skywinder/github-changelog-generator/pull/61) ([skywinder](https://github.com/skywinder))
|
68
|
-
|
69
89
|
## [1.3.6](https://github.com/skywinder/github-changelog-generator/tree/1.3.6) (2015-03-05)
|
70
90
|
|
71
91
|
[Full Changelog](https://github.com/skywinder/github-changelog-generator/compare/1.3.5...1.3.6)
|
data/README.md
CHANGED
@@ -82,30 +82,35 @@ As output you will get `CHANGELOG.md` file with pretty *Markdown-formatted* chan
|
|
82
82
|
### Params
|
83
83
|
Type `github_changelog_generator --help` for detailed usage.
|
84
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
|
-
|
85
|
+
Usage: github_changelog_generator [options]
|
86
|
+
-u, --user [USER] Username of the owner of target GitHub repo
|
87
|
+
-p, --project [PROJECT] Name of project on GitHub
|
88
|
+
-t, --token [TOKEN] To make more than 50 requests per hour your GitHub token is required. You can generate it at: https://github.com/settings/tokens/new
|
89
|
+
-f, --date-format [FORMAT] Date format. Default is %Y-%m-%d
|
90
|
+
-o, --output [NAME] Output file. Default is CHANGELOG.md
|
91
|
+
--[no-]issues Include closed issues in changelog. Default is true
|
92
|
+
--[no-]issues-wo-labels Include closed issues without labels in changelog. Default is true
|
93
|
+
--[no-]pr-wo-labels Include pull requests without labels in changelog. Default is true
|
94
|
+
--[no-]pull-requests Include pull-requests in changelog. Default is true
|
95
|
+
--[no-]filter-by-milestone Use milestone to detect when issue was resolved. Default is true
|
96
|
+
--[no-]author Add author of pull-request in the end. Default is true
|
97
|
+
--unreleased-only Generate log from unreleased closed issues only.
|
98
|
+
--[no-]unreleased Add to log unreleased closed issues. Default is true
|
99
|
+
--unreleased-label [label] Add to log unreleased closed issues. Default is true
|
100
|
+
--[no-]compare-link Include compare link (Full Changelog) between older version and newer version. Default is true
|
101
|
+
--include-labels x,y,z Only issues with the specified labels will be included in the changelog. Default is 'bug,enhancement'
|
102
|
+
--exclude-labels x,y,z Issues with the specified labels will be always excluded from changelog. Default is 'duplicate,question,invalid,wontfix'
|
103
|
+
--between-tags x,y,z Change log will be filled only between specified tags
|
104
|
+
--exclude-tags x,y,z Change log will be exclude specified tags
|
105
|
+
--max-issues [NUMBER] Max number of issues to fetch from GitHub. Default is unlimited
|
106
|
+
--github-site [URL] The Enterprise Github site on which your project is hosted.
|
107
|
+
--github-api [URL] The enterprise endpoint to use for your Github API.
|
108
|
+
--simple-list Create simple list from issues and pull requests. Default is false.
|
109
|
+
--future-release [RELEASE-VERSION]
|
110
|
+
Put the unreleased changes in the specified release number.
|
111
|
+
--[no-]verbose Run verbosely. Default is true
|
112
|
+
-v, --version Print version number
|
113
|
+
-h, --help Displays Help
|
109
114
|
|
110
115
|
|
111
116
|
### GitHub token
|
data/lib/CHANGELOG.md
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## [
|
3
|
+
## [0.0.4](https://github.com/skywinder/changelog_test/tree/0.0.4) (2015-05-22)
|
4
4
|
|
5
|
-
[Full Changelog](https://github.com/skywinder/changelog_test/compare/v0.0.3...
|
5
|
+
[Full Changelog](https://github.com/skywinder/changelog_test/compare/v0.0.3...0.0.4)
|
6
|
+
|
7
|
+
**Closed issues:**
|
8
|
+
|
9
|
+
- Test issue, that should appear in 0.0.4 [\#3](https://github.com/skywinder/changelog_test/issues/3)
|
6
10
|
|
7
11
|
**Merged pull requests:**
|
8
12
|
|
@@ -6,517 +6,34 @@ require "colorize"
|
|
6
6
|
require "benchmark"
|
7
7
|
|
8
8
|
require_relative "github_changelog_generator/parser"
|
9
|
-
require_relative "github_changelog_generator/generator"
|
9
|
+
require_relative "github_changelog_generator/generator/generator"
|
10
10
|
require_relative "github_changelog_generator/version"
|
11
11
|
require_relative "github_changelog_generator/reader"
|
12
|
-
require_relative "github_changelog_generator/fetcher"
|
13
12
|
|
13
|
+
# The main module, where placed all classes (now, at least)
|
14
14
|
module GitHubChangelogGenerator
|
15
|
-
# Default error for ChangelogGenerator
|
16
|
-
class ChangelogGeneratorError < StandardError
|
17
|
-
end
|
18
|
-
|
19
15
|
# Main class and entry point for this script.
|
20
16
|
class ChangelogGenerator
|
21
|
-
attr_accessor :options, :all_tags, :github
|
22
|
-
|
23
17
|
# Class, responsible for whole change log generation cycle
|
24
18
|
# @return initialised instance of ChangelogGenerator
|
25
19
|
def initialize
|
26
20
|
@options = Parser.parse_options
|
27
|
-
|
28
|
-
@fetcher = GitHubChangelogGenerator::Fetcher.new @options
|
29
|
-
|
30
21
|
@generator = Generator.new @options
|
31
|
-
|
32
|
-
# @all_tags = get_filtered_tags
|
33
|
-
@all_tags = @fetcher.get_all_tags
|
34
|
-
|
35
|
-
# TODO: refactor this double asssign of @issues and @pull_requests and move all logic in one method
|
36
|
-
@issues, @pull_requests = @fetcher.fetch_closed_issues_and_pr
|
37
|
-
|
38
|
-
@pull_requests = @options[:pulls] ? get_filtered_pull_requests : []
|
39
|
-
|
40
|
-
@issues = @options[:issues] ? get_filtered_issues : []
|
41
|
-
|
42
|
-
fetch_event_for_issues_and_pr
|
43
|
-
detect_actual_closed_dates
|
44
|
-
end
|
45
|
-
|
46
|
-
# Return tags after filtering tags in lists provided by option: --between-tags & --exclude-tags
|
47
|
-
#
|
48
|
-
# @return [Array]
|
49
|
-
def get_filtered_tags
|
50
|
-
all_tags = @fetcher.get_all_tags
|
51
|
-
filtered_tags = []
|
52
|
-
if @options[:between_tags]
|
53
|
-
@options[:between_tags].each do |tag|
|
54
|
-
unless all_tags.include? tag
|
55
|
-
puts "Warning: can't find tag #{tag}, specified with --between-tags option.".yellow
|
56
|
-
end
|
57
|
-
end
|
58
|
-
filtered_tags = all_tags.select { |tag| @options[:between_tags].include? tag }
|
59
|
-
end
|
60
|
-
filtered_tags
|
61
|
-
end
|
62
|
-
|
63
|
-
def detect_actual_closed_dates
|
64
|
-
if @options[:verbose]
|
65
|
-
print "Fetching closed dates for issues...\r"
|
66
|
-
end
|
67
|
-
|
68
|
-
threads = []
|
69
|
-
|
70
|
-
@issues.each { |issue|
|
71
|
-
threads << Thread.new {
|
72
|
-
find_closed_date_by_commit(issue)
|
73
|
-
}
|
74
|
-
}
|
75
|
-
|
76
|
-
@pull_requests.each { |pull_request|
|
77
|
-
threads << Thread.new {
|
78
|
-
find_closed_date_by_commit(pull_request)
|
79
|
-
}
|
80
|
-
}
|
81
|
-
threads.each(&:join)
|
82
|
-
|
83
|
-
if @options[:verbose]
|
84
|
-
puts "Fetching closed dates for issues: Done!"
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Fill :actual_date parameter of specified issue by closed date of the commit, it it was closed by commit.
|
89
|
-
# @param [Hash] issue
|
90
|
-
def find_closed_date_by_commit(issue)
|
91
|
-
unless issue["events"].nil?
|
92
|
-
# if it's PR -> then find "merged event", in case of usual issue -> fond closed date
|
93
|
-
compare_string = issue[:merged_at].nil? ? "closed" : "merged"
|
94
|
-
# reverse! - to find latest closed event. (event goes in date order)
|
95
|
-
issue["events"].reverse!.each { |event|
|
96
|
-
if event[:event].eql? compare_string
|
97
|
-
if event[:commit_id].nil?
|
98
|
-
issue[:actual_date] = issue[:closed_at]
|
99
|
-
else
|
100
|
-
begin
|
101
|
-
commit = @fetcher.fetch_commit(event)
|
102
|
-
issue[:actual_date] = commit[:author][:date]
|
103
|
-
rescue
|
104
|
-
puts "Warning: Can't fetch commit #{event[:commit_id]}. It is probably referenced from another repo.".yellow
|
105
|
-
issue[:actual_date] = issue[:closed_at]
|
106
|
-
end
|
107
|
-
end
|
108
|
-
break
|
109
|
-
end
|
110
|
-
}
|
111
|
-
end
|
112
|
-
# TODO: assert issues, that remain without 'actual_date' hash for some reason.
|
113
|
-
end
|
114
|
-
|
115
|
-
def print_json(json)
|
116
|
-
puts JSON.pretty_generate(json)
|
117
|
-
end
|
118
|
-
|
119
|
-
# This method fetches missing params for PR and filter them by specified options
|
120
|
-
# It include add all PR's with labels from @options[:include_labels] array
|
121
|
-
# And exclude all from :exclude_labels array.
|
122
|
-
# @return [Array] filtered PR's
|
123
|
-
def get_filtered_pull_requests
|
124
|
-
filter_merged_pull_requests
|
125
|
-
|
126
|
-
filtered_pull_requests = include_issues_by_labels(@pull_requests)
|
127
|
-
|
128
|
-
filtered_pull_requests = exclude_issues_by_labels(filtered_pull_requests)
|
129
|
-
|
130
|
-
if @options[:verbose]
|
131
|
-
puts "Filtered pull requests: #{filtered_pull_requests.count}"
|
132
|
-
end
|
133
|
-
|
134
|
-
filtered_pull_requests
|
135
|
-
end
|
136
|
-
|
137
|
-
# This method filter only merged PR and
|
138
|
-
# fetch missing required attributes for pull requests
|
139
|
-
# :merged_at - is a date, when issue PR was merged.
|
140
|
-
# More correct to use merged date, rather than closed date.
|
141
|
-
def filter_merged_pull_requests
|
142
|
-
if @options[:verbose]
|
143
|
-
print "Fetching merged dates...\r"
|
144
|
-
end
|
145
|
-
pull_requests = @fetcher.fetch_closed_pull_requests
|
146
|
-
|
147
|
-
@pull_requests.each { |pr|
|
148
|
-
fetched_pr = pull_requests.find { |fpr|
|
149
|
-
fpr.number == pr.number
|
150
|
-
}
|
151
|
-
pr[:merged_at] = fetched_pr[:merged_at]
|
152
|
-
pull_requests.delete(fetched_pr)
|
153
|
-
}
|
154
|
-
|
155
|
-
@pull_requests.select! do |pr|
|
156
|
-
!pr[:merged_at].nil?
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
# Include issues with labels, specified in :include_labels
|
161
|
-
# @param [Array] issues to filter
|
162
|
-
# @return [Array] filtered array of issues
|
163
|
-
def include_issues_by_labels(issues)
|
164
|
-
filtered_issues = @options[:include_labels].nil? ? issues : issues.select { |issue| (issue.labels.map(&:name) & @options[:include_labels]).any? }
|
165
|
-
|
166
|
-
if @options[:add_issues_wo_labels]
|
167
|
-
issues_wo_labels = issues.select { |issue|
|
168
|
-
!issue.labels.map(&:name).any?
|
169
|
-
}
|
170
|
-
filtered_issues |= issues_wo_labels
|
171
|
-
end
|
172
|
-
filtered_issues
|
173
|
-
end
|
174
|
-
|
175
|
-
# delete all labels with labels from @options[:exclude_labels] array
|
176
|
-
# @param [Array] issues
|
177
|
-
# @return [Array] filtered array
|
178
|
-
def exclude_issues_by_labels(issues)
|
179
|
-
unless @options[:exclude_labels].nil?
|
180
|
-
issues = issues.select { |issue|
|
181
|
-
!(issue.labels.map(&:name) & @options[:exclude_labels]).any?
|
182
|
-
}
|
183
|
-
end
|
184
|
-
issues
|
185
22
|
end
|
186
23
|
|
187
24
|
# The entry point of this script to generate change log
|
188
25
|
# @raise (ChangelogGeneratorError) Is thrown when one of specified tags was not found in list of tags.
|
189
|
-
def
|
190
|
-
log =
|
191
|
-
|
192
|
-
if @options[:unreleased_only]
|
193
|
-
log += generate_log_between_tags(all_tags[0], nil)
|
194
|
-
elsif @options[:tag1] and @options[:tag2]
|
195
|
-
tag1 = @options[:tag1]
|
196
|
-
tag2 = @options[:tag2]
|
197
|
-
tags_strings = []
|
198
|
-
all_tags.each { |x| tags_strings.push(x["name"]) }
|
199
|
-
|
200
|
-
if tags_strings.include?(tag1)
|
201
|
-
if tags_strings.include?(tag2)
|
202
|
-
to_a = tags_strings.map.with_index.to_a
|
203
|
-
hash = Hash[to_a]
|
204
|
-
index1 = hash[tag1]
|
205
|
-
index2 = hash[tag2]
|
206
|
-
log += generate_log_between_tags(all_tags[index1], all_tags[index2])
|
207
|
-
else
|
208
|
-
fail ChangelogGeneratorError, "Can't find tag #{tag2} -> exit".red
|
209
|
-
end
|
210
|
-
else
|
211
|
-
fail ChangelogGeneratorError, "Can't find tag #{tag1} -> exit".red
|
212
|
-
end
|
213
|
-
else
|
214
|
-
log += generate_log_for_all_tags
|
215
|
-
end
|
216
|
-
|
217
|
-
log += "\n\n\\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*"
|
26
|
+
def run
|
27
|
+
log = @generator.compound_changelog
|
218
28
|
|
219
29
|
output_filename = "#{@options[:output]}"
|
220
30
|
File.open(output_filename, "w") { |file| file.write(log) }
|
221
31
|
puts "Done!"
|
222
32
|
puts "Generated log placed in #{Dir.pwd}/#{output_filename}"
|
223
33
|
end
|
224
|
-
|
225
|
-
# The full cycle of generation for whole project
|
226
|
-
# @return [String] The complete change log
|
227
|
-
def generate_log_for_all_tags
|
228
|
-
fetch_tags_dates
|
229
|
-
|
230
|
-
if @options[:verbose]
|
231
|
-
puts "Sorting tags..."
|
232
|
-
end
|
233
|
-
|
234
|
-
@all_tags.sort_by! { |x| @fetcher.get_time_of_tag(x) }.reverse!
|
235
|
-
|
236
|
-
if @options[:verbose]
|
237
|
-
puts "Generating log..."
|
238
|
-
end
|
239
|
-
|
240
|
-
log = ""
|
241
|
-
|
242
|
-
if @options[:unreleased] && @all_tags.count != 0
|
243
|
-
unreleased_log = generate_log_between_tags(all_tags[0], nil)
|
244
|
-
if unreleased_log
|
245
|
-
log += unreleased_log
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
(1...all_tags.size).each { |index|
|
250
|
-
log += generate_log_between_tags(all_tags[index], all_tags[index - 1])
|
251
|
-
}
|
252
|
-
if @all_tags.count != 0
|
253
|
-
log += generate_log_between_tags(nil, all_tags.last)
|
254
|
-
end
|
255
|
-
|
256
|
-
log
|
257
|
-
end
|
258
|
-
|
259
|
-
# Async fetching of all tags dates
|
260
|
-
def fetch_tags_dates
|
261
|
-
if @options[:verbose]
|
262
|
-
print "Fetching tag dates...\r"
|
263
|
-
end
|
264
|
-
|
265
|
-
# Async fetching tags:
|
266
|
-
threads = []
|
267
|
-
i = 0
|
268
|
-
all = @all_tags.count
|
269
|
-
@all_tags.each { |tag|
|
270
|
-
threads << Thread.new {
|
271
|
-
@fetcher.get_time_of_tag(tag)
|
272
|
-
if @options[:verbose]
|
273
|
-
print "Fetching tags dates: #{i + 1}/#{all}\r"
|
274
|
-
i += 1
|
275
|
-
end
|
276
|
-
}
|
277
|
-
}
|
278
|
-
|
279
|
-
print " \r"
|
280
|
-
|
281
|
-
threads.each(&:join)
|
282
|
-
|
283
|
-
if @options[:verbose]
|
284
|
-
puts "Fetching tags dates: #{i}"
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
# Generate log only between 2 specified tags
|
289
|
-
# @param [String] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag
|
290
|
-
# @param [String] newer_tag all issue after this tag will be excluded. May be nil for unreleased section
|
291
|
-
def generate_log_between_tags(older_tag, newer_tag)
|
292
|
-
filtered_pull_requests = delete_by_time(@pull_requests, :actual_date, older_tag, newer_tag)
|
293
|
-
filtered_issues = delete_by_time(@issues, :actual_date, older_tag, newer_tag)
|
294
|
-
|
295
|
-
newer_tag_name = newer_tag.nil? ? nil : newer_tag["name"]
|
296
|
-
older_tag_name = older_tag.nil? ? nil : older_tag["name"]
|
297
|
-
|
298
|
-
if @options[:filter_issues_by_milestone]
|
299
|
-
# delete excess irrelevant issues (according milestones)
|
300
|
-
filtered_issues = filter_by_milestone(filtered_issues, newer_tag_name, @issues)
|
301
|
-
filtered_pull_requests = filter_by_milestone(filtered_pull_requests, newer_tag_name, @pull_requests)
|
302
|
-
end
|
303
|
-
|
304
|
-
if filtered_issues.empty? && filtered_pull_requests.empty? && newer_tag.nil?
|
305
|
-
# do not generate empty unreleased section
|
306
|
-
return ""
|
307
|
-
end
|
308
|
-
|
309
|
-
create_log(filtered_pull_requests, filtered_issues, newer_tag, older_tag_name)
|
310
|
-
end
|
311
|
-
|
312
|
-
def filter_by_milestone(filtered_issues, newer_tag_name, src_array)
|
313
|
-
filtered_issues.select! { |issue|
|
314
|
-
# leave issues without milestones
|
315
|
-
if issue.milestone.nil?
|
316
|
-
true
|
317
|
-
else
|
318
|
-
# check, that this milestone in tag list:
|
319
|
-
@all_tags.find { |tag| tag.name == issue.milestone.title }.nil?
|
320
|
-
end
|
321
|
-
}
|
322
|
-
unless newer_tag_name.nil?
|
323
|
-
|
324
|
-
# add missed issues (according milestones)
|
325
|
-
issues_to_add = src_array.select { |issue|
|
326
|
-
if issue.milestone.nil?
|
327
|
-
false
|
328
|
-
else
|
329
|
-
# check, that this milestone in tag list:
|
330
|
-
milestone_is_tag = @all_tags.find { |tag|
|
331
|
-
tag.name == issue.milestone.title
|
332
|
-
}
|
333
|
-
|
334
|
-
if milestone_is_tag.nil?
|
335
|
-
false
|
336
|
-
else
|
337
|
-
issue.milestone.title == newer_tag_name
|
338
|
-
end
|
339
|
-
end
|
340
|
-
}
|
341
|
-
|
342
|
-
filtered_issues |= issues_to_add
|
343
|
-
end
|
344
|
-
filtered_issues
|
345
|
-
end
|
346
|
-
|
347
|
-
# Method filter issues, that belong only specified tag range
|
348
|
-
# @param [Array] array of issues to filter
|
349
|
-
# @param [Symbol] hash_key key of date value default is :actual_date
|
350
|
-
# @param [String] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag
|
351
|
-
# @param [String] newer_tag all issue after this tag will be excluded. May be nil for unreleased section
|
352
|
-
# @return [Array] filtered issues
|
353
|
-
def delete_by_time(array, hash_key = :actual_date, older_tag = nil, newer_tag = nil)
|
354
|
-
fail ChangelogGeneratorError, "At least one of the tags should be not nil!".red if older_tag.nil? && newer_tag.nil?
|
355
|
-
|
356
|
-
newer_tag_time = newer_tag && @fetcher.get_time_of_tag(newer_tag)
|
357
|
-
older_tag_time = older_tag && @fetcher.get_time_of_tag(older_tag)
|
358
|
-
|
359
|
-
array.select { |req|
|
360
|
-
if req[hash_key]
|
361
|
-
t = Time.parse(req[hash_key]).utc
|
362
|
-
|
363
|
-
if older_tag_time.nil?
|
364
|
-
tag_in_range_old = true
|
365
|
-
else
|
366
|
-
tag_in_range_old = t > older_tag_time
|
367
|
-
end
|
368
|
-
|
369
|
-
if newer_tag_time.nil?
|
370
|
-
tag_in_range_new = true
|
371
|
-
else
|
372
|
-
tag_in_range_new = t <= newer_tag_time
|
373
|
-
end
|
374
|
-
|
375
|
-
tag_in_range = (tag_in_range_old) && (tag_in_range_new)
|
376
|
-
|
377
|
-
tag_in_range
|
378
|
-
else
|
379
|
-
false
|
380
|
-
end
|
381
|
-
}
|
382
|
-
end
|
383
|
-
|
384
|
-
# Generates log for section with header and body
|
385
|
-
#
|
386
|
-
# @param [Array] pull_requests List or PR's in new section
|
387
|
-
# @param [Array] issues List of issues in new section
|
388
|
-
# @param [String] newer_tag Name of the newer tag. Could be nil for `Unreleased` section
|
389
|
-
# @param [String] older_tag_name Older tag, used for the links. Could be nil for last tag.
|
390
|
-
# @return [String] Ready and parsed section
|
391
|
-
def create_log(pull_requests, issues, newer_tag, older_tag_name = nil)
|
392
|
-
newer_tag_time = newer_tag.nil? ? Time.new : @fetcher.get_time_of_tag(newer_tag)
|
393
|
-
if newer_tag.nil? && @options[:future_release]
|
394
|
-
newer_tag_name = @options[:future_release]
|
395
|
-
newer_tag_link = @options[:future_release]
|
396
|
-
else
|
397
|
-
newer_tag_name = newer_tag.nil? ? @options[:unreleased_label] : newer_tag["name"]
|
398
|
-
newer_tag_link = newer_tag.nil? ? "HEAD" : newer_tag_name
|
399
|
-
end
|
400
|
-
|
401
|
-
github_site = options[:github_site] || "https://github.com"
|
402
|
-
project_url = "#{github_site}/#{@options[:user]}/#{@options[:project]}"
|
403
|
-
|
404
|
-
log = generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_name, project_url)
|
405
|
-
|
406
|
-
if @options[:issues]
|
407
|
-
# Generate issues:
|
408
|
-
issues_a = []
|
409
|
-
enhancement_a = []
|
410
|
-
bugs_a = []
|
411
|
-
|
412
|
-
issues.each { |dict|
|
413
|
-
added = false
|
414
|
-
dict.labels.each { |label|
|
415
|
-
if label.name == "bug"
|
416
|
-
bugs_a.push dict
|
417
|
-
added = true
|
418
|
-
next
|
419
|
-
end
|
420
|
-
if label.name == "enhancement"
|
421
|
-
enhancement_a.push dict
|
422
|
-
added = true
|
423
|
-
next
|
424
|
-
end
|
425
|
-
}
|
426
|
-
unless added
|
427
|
-
issues_a.push dict
|
428
|
-
end
|
429
|
-
}
|
430
|
-
|
431
|
-
log += generate_sub_section(enhancement_a, @options[:enhancement_prefix])
|
432
|
-
log += generate_sub_section(bugs_a, @options[:bug_prefix])
|
433
|
-
log += generate_sub_section(issues_a, @options[:issue_prefix])
|
434
|
-
end
|
435
|
-
|
436
|
-
if @options[:pulls]
|
437
|
-
# Generate pull requests:
|
438
|
-
log += generate_sub_section(pull_requests, @options[:merge_prefix])
|
439
|
-
end
|
440
|
-
|
441
|
-
log
|
442
|
-
end
|
443
|
-
|
444
|
-
# @param [Array] issues List of issues on sub-section
|
445
|
-
# @param [String] prefix Nae of sub-section
|
446
|
-
# @return [String] Generate ready-to-go sub-section
|
447
|
-
def generate_sub_section(issues, prefix)
|
448
|
-
log = ""
|
449
|
-
|
450
|
-
if options[:simple_list] != true && issues.any?
|
451
|
-
log += "#{prefix}\n\n"
|
452
|
-
end
|
453
|
-
|
454
|
-
if issues.any?
|
455
|
-
issues.each { |issue|
|
456
|
-
merge_string = @generator.get_string_for_issue(issue)
|
457
|
-
log += "- #{merge_string}\n\n"
|
458
|
-
}
|
459
|
-
end
|
460
|
-
log
|
461
|
-
end
|
462
|
-
|
463
|
-
# It generate one header for section with specific parameters.
|
464
|
-
#
|
465
|
-
# @param [String] newer_tag_name - name of newer tag
|
466
|
-
# @param [String] newer_tag_link - used for links. Could be same as #newer_tag_name or some specific value, like HEAD
|
467
|
-
# @param [Time] newer_tag_time - time, when newer tag created
|
468
|
-
# @param [String] older_tag_link - tag name, used for links.
|
469
|
-
# @param [String] project_url - url for current project.
|
470
|
-
# @return [String] - Generate one ready-to-add section.
|
471
|
-
def generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_link, project_url)
|
472
|
-
log = ""
|
473
|
-
|
474
|
-
# Generate date string:
|
475
|
-
time_string = newer_tag_time.strftime @options[:dateformat]
|
476
|
-
|
477
|
-
# Generate tag name and link
|
478
|
-
if newer_tag_name.equal? @options[:unreleased_label]
|
479
|
-
log += "## [#{newer_tag_name}](#{project_url}/tree/#{newer_tag_link})\n\n"
|
480
|
-
else
|
481
|
-
log += "## [#{newer_tag_name}](#{project_url}/tree/#{newer_tag_link}) (#{time_string})\n\n"
|
482
|
-
end
|
483
|
-
|
484
|
-
if @options[:compare_link] && older_tag_link
|
485
|
-
# Generate compare link
|
486
|
-
log += "[Full Changelog](#{project_url}/compare/#{older_tag_link}...#{newer_tag_link})\n\n"
|
487
|
-
end
|
488
|
-
|
489
|
-
log
|
490
|
-
end
|
491
|
-
|
492
|
-
# Filter issues according labels
|
493
|
-
# @return [Array] Filtered issues
|
494
|
-
def get_filtered_issues
|
495
|
-
filtered_issues = include_issues_by_labels(@issues)
|
496
|
-
|
497
|
-
filtered_issues = exclude_issues_by_labels(filtered_issues)
|
498
|
-
|
499
|
-
if @options[:verbose]
|
500
|
-
puts "Filtered issues: #{filtered_issues.count}"
|
501
|
-
end
|
502
|
-
|
503
|
-
filtered_issues
|
504
|
-
end
|
505
|
-
|
506
|
-
# Fetch event for issues and pull requests
|
507
|
-
# @return [Array] array of fetched issues
|
508
|
-
def fetch_event_for_issues_and_pr
|
509
|
-
if @options[:verbose]
|
510
|
-
print "Fetching events for issues and PR: 0/#{@issues.count + @pull_requests.count}\r"
|
511
|
-
end
|
512
|
-
|
513
|
-
# Async fetching events:
|
514
|
-
|
515
|
-
@fetcher.fetch_events_async(@issues + @pull_requests)
|
516
|
-
end
|
517
34
|
end
|
518
35
|
|
519
36
|
if __FILE__ == $PROGRAM_NAME
|
520
|
-
GitHubChangelogGenerator::ChangelogGenerator.new.
|
37
|
+
GitHubChangelogGenerator::ChangelogGenerator.new.run
|
521
38
|
end
|
522
39
|
end
|