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.
@@ -0,0 +1,70 @@
1
+ module GitHubChangelogGenerator
2
+ class Generator
3
+ # fetch, filter tags, fetch dates and sort them in time order
4
+ def fetch_and_filter_tags
5
+ @all_tags = get_filtered_tags(@fetcher.get_all_tags)
6
+ fetch_tags_dates
7
+ sort_tags_by_date
8
+ end
9
+
10
+ # Sort all tags by date
11
+ def sort_tags_by_date
12
+ puts "Sorting tags..." if @options[:verbose]
13
+ @all_tags.sort_by! { |x| @fetcher.get_time_of_tag(x) }.reverse!
14
+ end
15
+
16
+ # Detect link, name and time for specified tag.
17
+ #
18
+ # @param [Hash] newer_tag newer tag. Can be nil, if it's Unreleased section.
19
+ # @return [Array] link, name and time of the tag
20
+ def detect_link_tag_time(newer_tag)
21
+ # if tag is nil - set current time
22
+ newer_tag_time = newer_tag.nil? ? Time.new : @fetcher.get_time_of_tag(newer_tag)
23
+
24
+ # if it's future release tag - set this value
25
+ if newer_tag.nil? && @options[:future_release]
26
+ newer_tag_name = @options[:future_release]
27
+ newer_tag_link = @options[:future_release]
28
+ else
29
+ # put unreleased label if there is no name for the tag
30
+ newer_tag_name = newer_tag.nil? ? @options[:unreleased_label] : newer_tag["name"]
31
+ newer_tag_link = newer_tag.nil? ? "HEAD" : newer_tag_name
32
+ end
33
+ [newer_tag_link, newer_tag_name, newer_tag_time]
34
+ end
35
+
36
+ # Return tags after filtering tags in lists provided by option: --between-tags & --exclude-tags
37
+ #
38
+ # @return [Array]
39
+ def get_filtered_tags(all_tags)
40
+ filtered_tags = filter_between_tags(all_tags)
41
+ filter_excluded_tags(filtered_tags)
42
+ end
43
+
44
+ def filter_between_tags(all_tags)
45
+ filtered_tags = all_tags
46
+ if @options[:between_tags]
47
+ @options[:between_tags].each do |tag|
48
+ unless all_tags.include? tag
49
+ puts "Warning: can't find tag #{tag}, specified with --between-tags option.".yellow
50
+ end
51
+ end
52
+ filtered_tags = all_tags.select { |tag| @options[:between_tags].include? tag }
53
+ end
54
+ filtered_tags
55
+ end
56
+
57
+ def filter_excluded_tags(all_tags)
58
+ filtered_tags = all_tags
59
+ if @options[:exclude_tags]
60
+ @options[:exclude_tags].each do |tag|
61
+ unless all_tags.include? tag
62
+ puts "Warning: can't find tag #{tag}, specified with --between-tags option.".yellow
63
+ end
64
+ end
65
+ filtered_tags = all_tags.reject { |tag| @options[:exclude_tags].include? tag }
66
+ end
67
+ filtered_tags
68
+ end
69
+ end
70
+ end
@@ -5,34 +5,32 @@ require_relative "version"
5
5
 
6
6
  module GitHubChangelogGenerator
7
7
  class Parser
8
+ # parse options with optparse
8
9
  def self.parse_options
9
- options = {
10
- tag1: nil,
11
- tag2: nil,
12
- dateformat: "%Y-%m-%d",
13
- output: "CHANGELOG.md",
14
- issues: true,
15
- add_issues_wo_labels: true,
16
- add_pr_wo_labels: true,
17
- pulls: true,
18
- filter_issues_by_milestone: true,
19
- author: true,
20
- unreleased: true,
21
- unreleased_label: "Unreleased",
22
- compare_link: true,
23
- include_labels: %w(bug enhancement),
24
- exclude_labels: %w(duplicate question invalid wontfix),
25
- max_issues: nil,
26
- simple_list: false,
27
- verbose: true,
10
+ options = get_default_options
28
11
 
29
- merge_prefix: "**Merged pull requests:**",
30
- issue_prefix: "**Closed issues:**",
31
- bug_prefix: "**Fixed bugs:**",
32
- enhancement_prefix: "**Implemented enhancements:**",
33
- branch: "origin"
34
- }
12
+ parser = setup_parser(options)
13
+
14
+ parser.parse!
15
+
16
+ detect_user_and_project(options)
17
+
18
+ if !options[:user] || !options[:project]
19
+ puts parser.banner
20
+ exit
21
+ end
22
+
23
+ if options[:verbose]
24
+ puts "Performing task with options:"
25
+ pp options
26
+ puts ""
27
+ end
28
+
29
+ options
30
+ end
35
31
 
32
+ # setup parsing options
33
+ def self.setup_parser(options)
36
34
  parser = OptionParser.new do |opts|
37
35
  opts.banner = "Usage: github_changelog_generator [options]"
38
36
  opts.on("-u", "--user [USER]", "Username of the owner of target GitHub repo") do |last|
@@ -45,7 +43,7 @@ module GitHubChangelogGenerator
45
43
  options[:token] = last
46
44
  end
47
45
  opts.on("-f", "--date-format [FORMAT]", "Date format. Default is %Y-%m-%d") do |last|
48
- options[:dateformat] = last
46
+ options[:date_format] = last
49
47
  end
50
48
  opts.on("-o", "--output [NAME]", "Output file. Default is CHANGELOG.md") do |last|
51
49
  options[:output] = last
@@ -86,6 +84,12 @@ module GitHubChangelogGenerator
86
84
  opts.on("--exclude-labels x,y,z", Array, 'Issues with the specified labels will be always excluded from changelog. Default is \'duplicate,question,invalid,wontfix\'') do |list|
87
85
  options[:exclude_labels] = list
88
86
  end
87
+ opts.on("--between-tags x,y,z", Array, "Change log will be filled only between specified tags") do |list|
88
+ options[:between_tags] = list
89
+ end
90
+ opts.on("--exclude-tags x,y,z", Array, "Change log will be exclude specified tags") do |list|
91
+ options[:exclude_tags] = list
92
+ end
89
93
  opts.on("--max-issues [NUMBER]", Integer, "Max number of issues to fetch from GitHub. Default is unlimited") do |max|
90
94
  options[:max_issues] = max
91
95
  end
@@ -113,79 +117,120 @@ module GitHubChangelogGenerator
113
117
  exit
114
118
  end
115
119
  end
120
+ parser
121
+ end
116
122
 
117
- parser.parse!
123
+ # just get default options
124
+ def self.get_default_options
125
+ options = {
126
+ tag1: nil,
127
+ tag2: nil,
128
+ date_format: "%Y-%m-%d",
129
+ output: "CHANGELOG.md",
130
+ issues: true,
131
+ add_issues_wo_labels: true,
132
+ add_pr_wo_labels: true,
133
+ pulls: true,
134
+ filter_issues_by_milestone: true,
135
+ author: true,
136
+ unreleased: true,
137
+ unreleased_label: "Unreleased",
138
+ compare_link: true,
139
+ include_labels: %w(bug enhancement),
140
+ exclude_labels: %w(duplicate question invalid wontfix),
141
+ max_issues: nil,
142
+ simple_list: false,
143
+ verbose: true,
144
+ merge_prefix: "**Merged pull requests:**",
145
+ issue_prefix: "**Closed issues:**",
146
+ bug_prefix: "**Fixed bugs:**",
147
+ enhancement_prefix: "**Implemented enhancements:**",
148
+ git_remote: "origin"
149
+ }
118
150
 
119
- detect_user_and_project(options)
151
+ options
152
+ end
120
153
 
154
+ # Detects user and project from git
155
+ def self.detect_user_and_project(options)
156
+ options[:user], options[:project] = user_project_from_option(ARGV[0], ARGV[1], options[:github_site])
121
157
  if !options[:user] || !options[:project]
122
- puts parser.banner
123
- exit
124
- end
125
-
126
- if ARGV[1]
127
- options[:tag1] = ARGV[0]
128
- options[:tag2] = ARGV[1]
129
- end
130
-
131
- if options[:verbose]
132
- puts "Performing task with options:"
133
- pp options
134
- puts ""
158
+ if ENV["RUBYLIB"] =~ /ruby-debug-ide/
159
+ options[:user] = "skywinder"
160
+ options[:project] = "changelog_test"
161
+ else
162
+ remote = `git config --get remote.#{options[:git_remote]}.url`
163
+ options[:user], options[:project] = user_project_from_remote(remote)
164
+ end
135
165
  end
136
-
137
- options
138
166
  end
139
167
 
140
- def self.detect_user_and_project(options)
141
- if ARGV[0] && !ARGV[1]
142
- github_site = options[:github_site] ? options[:github_site] : "github.com"
168
+ # Try to find user and project name from git remote output
169
+ #
170
+ # @param [String] output of git remote command
171
+ # @return [Array] user and project
172
+ def self.user_project_from_option(arg0, arg1, github_site = nil)
173
+ user = nil
174
+ project = nil
175
+ github_site ||= "github.com"
176
+ if arg0 && !arg1
143
177
  # this match should parse strings such "https://github.com/skywinder/Github-Changelog-Generator" or "skywinder/Github-Changelog-Generator" to user and name
144
- match = /(?:.+#{Regexp.escape(github_site)}\/)?(.+)\/(.+)/.match(ARGV[0])
178
+ puts arg0
179
+ match = /(?:.+#{Regexp.escape(github_site)}\/)?(.+)\/(.+)/.match(arg0)
145
180
 
146
181
  begin
147
182
  param = match[2].nil?
148
183
  rescue
149
- puts "Can't detect user and name from first parameter: '#{ARGV[0]}' -> exit'"
184
+ puts "Can't detect user and name from first parameter: '#{arg0}' -> exit'"
150
185
  exit
151
186
  end
152
187
  if param
153
188
  exit
154
189
  else
155
- options[:user] = match[1]
156
- options[:project] = match[2]
190
+ user = match[1]
191
+ project = match[2]
157
192
  end
158
-
159
193
  end
194
+ [user, project]
195
+ end
160
196
 
161
- if !options[:user] && !options[:project]
162
- if ENV["RUBYLIB"] =~ /ruby-debug-ide/
163
- options[:user] = "skywinder"
164
- options[:project] = "changelog_test"
165
- else
166
- remote = `git config --get remote.#{options[:branch]}.url`
167
- # try to find repo in format:
168
- # origin git@github.com:skywinder/Github-Changelog-Generator.git (fetch)
169
- # git@github.com:skywinder/Github-Changelog-Generator.git
170
- match = /.*(?:[:\/])((?:-|\w|\.)*)\/((?:-|\w|\.)*)(?:\.git).*/.match(remote)
171
-
172
- if match && match[1] && match[2]
173
- puts "Detected user:#{match[1]}, project:#{match[2]}"
174
- options[:user] = match[1]
175
- options[:project] = match[2]
176
- else
177
- # try to find repo in format:
178
- # origin https://github.com/skywinder/ChangelogMerger (fetch)
179
- # https://github.com/skywinder/ChangelogMerger
180
- match = /.*\/((?:-|\w|\.)*)\/((?:-|\w|\.)*).*/.match(remote)
181
- if match && match[1] && match[2]
182
- puts "Detected user:#{match[1]}, project:#{match[2]}"
183
- options[:user] = match[1]
184
- options[:project] = match[2]
185
- end
186
- end
197
+ # Try to find user and project name from git remote output
198
+ #
199
+ # @param [String] output of git remote command
200
+ # @return [Array] user and project
201
+ def self.user_project_from_remote(remote)
202
+ # try to find repo in format:
203
+ # origin git@github.com:skywinder/Github-Changelog-Generator.git (fetch)
204
+ # git@github.com:skywinder/Github-Changelog-Generator.git
205
+ regex1 = /.*(?:[:\/])((?:-|\w|\.)*)\/((?:-|\w|\.)*)(?:\.git).*/
206
+
207
+ # try to find repo in format:
208
+ # origin https://github.com/skywinder/ChangelogMerger (fetch)
209
+ # https://github.com/skywinder/ChangelogMerger
210
+ regex2 = /.*\/((?:-|\w|\.)*)\/((?:-|\w|\.)*).*/
211
+
212
+ remote_structures = [regex1, regex2]
213
+
214
+ user = nil
215
+ project = nil
216
+ remote_structures.each do |regex|
217
+ matches = Regexp.new(regex).match(remote)
218
+
219
+ if matches && matches[1] && matches[2]
220
+ puts "Detected user:#{matches[1]}, project:#{matches[2]}"
221
+ user = matches[1]
222
+ project = matches[2]
187
223
  end
224
+
225
+ break unless matches.nil?
188
226
  end
227
+
228
+ [user, project]
189
229
  end
190
230
  end
231
+
232
+ if __FILE__ == $PROGRAM_NAME
233
+ remote = "invalid reference to project"
234
+ p user_project_from_option(ARGV[0], ARGV[1], remote)
235
+ end
191
236
  end
@@ -1,3 +1,3 @@
1
1
  module GitHubChangelogGenerator
2
- VERSION = "1.4.1"
2
+ VERSION = "1.5.0"
3
3
  end
@@ -0,0 +1,69 @@
1
+ describe GitHubChangelogGenerator::Generator do
2
+ describe "#filter_between_tags" do
3
+ context "when between_tags nil" do
4
+ before do
5
+ @generator = GitHubChangelogGenerator::Generator.new(between_tags: nil)
6
+ end
7
+
8
+ subject do
9
+ @generator.get_filtered_tags(%w(1 2 3))
10
+ end
11
+ it { is_expected.to be_a(Array) }
12
+ it { is_expected.to match_array(%w(1 2 3)) }
13
+ end
14
+ context "when between_tags same as input array" do
15
+ before do
16
+ @generator = GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2 3))
17
+ end
18
+ subject do
19
+ @generator.get_filtered_tags(%w(1 2 3))
20
+ end
21
+ it { is_expected.to be_a(Array) }
22
+ it { is_expected.to match_array(%w(1 2 3)) }
23
+ end
24
+
25
+ context "when between_tags filled with correct values" do
26
+ before do
27
+ @generator = GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2))
28
+ end
29
+ subject do
30
+ @generator.get_filtered_tags(%w(1 2 3))
31
+ end
32
+ it { is_expected.to be_a(Array) }
33
+ it { is_expected.to match_array(%w(1 2)) }
34
+ end
35
+
36
+ context "when between_tags filled with invalid values" do
37
+ before do
38
+ @generator = GitHubChangelogGenerator::Generator.new(between_tags: %w(1 q w))
39
+ end
40
+
41
+ subject do
42
+ @generator.get_filtered_tags(%w(1 2 3))
43
+ end
44
+ it { is_expected.to be_a(Array) }
45
+ it { is_expected.to match_array(%w(1)) }
46
+ end
47
+ end
48
+
49
+ describe "#get_filtered_tags" do
50
+ subject { generator.get_filtered_tags(%w(1 2 3 4 5)) }
51
+ # before { generator.get_filtered_tags(%w(1 2 3 4 5)) }
52
+
53
+ context "with excluded and between tags" do
54
+ let(:generator) { GitHubChangelogGenerator::Generator.new(between_tags: %w(1 2 3), exclude_tags: %w(2)) }
55
+ it { is_expected.to be_a Array }
56
+ it { is_expected.to match_array(%w(1 3)) }
57
+ end
58
+ end
59
+
60
+ describe "#filter_excluded_tags" do
61
+ subject { generator.filter_excluded_tags(%w(1 2 3)) }
62
+
63
+ context "with valid excluded tags" do
64
+ let(:generator) { GitHubChangelogGenerator::Generator.new(exclude_tags: %w(3)) }
65
+ it { is_expected.to be_a Array }
66
+ it { is_expected.to match_array(%w(1 2)) }
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,60 @@
1
+ describe GitHubChangelogGenerator::Parser do
2
+ describe ".user_project_from_remote" do
3
+ context "when remote is 1" do
4
+ subject { GitHubChangelogGenerator::Parser.user_project_from_remote("origin https://github.com/skywinder/ActionSheetPicker-3.0 (fetch)") }
5
+ it { is_expected.to be_a(Array) }
6
+ it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
7
+ end
8
+ context "when remote is 2" do
9
+ subject { GitHubChangelogGenerator::Parser.user_project_from_remote("https://github.com/skywinder/ActionSheetPicker-3.0") }
10
+ it { is_expected.to be_a(Array) }
11
+ it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
12
+ end
13
+ context "when remote is 3" do
14
+ subject { GitHubChangelogGenerator::Parser.user_project_from_remote("https://github.com/skywinder/ActionSheetPicker-3.0") }
15
+ it { is_expected.to be_a(Array) }
16
+ it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
17
+ end
18
+ context "when remote is 4" do
19
+ subject { GitHubChangelogGenerator::Parser.user_project_from_remote("origin git@github.com:skywinder/ActionSheetPicker-3.0.git (fetch)") }
20
+ it { is_expected.to be_a(Array) }
21
+ it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
22
+ end
23
+ context "when remote is invalid" do
24
+ subject { GitHubChangelogGenerator::Parser.user_project_from_remote("some invalid text") }
25
+ it { is_expected.to be_a(Array) }
26
+ it { is_expected.to match_array([nil, nil]) }
27
+ end
28
+ end
29
+ describe ".user_project_from_option" do
30
+ # context "when option is invalid" do
31
+ # it("should exit") { expect { GitHubChangelogGenerator::Parser.user_project_from_option("blah", nil) }.to raise_error(SystemExit) }
32
+ # end
33
+
34
+ context "when option is valid" do
35
+ subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", nil) }
36
+ it { is_expected.to be_a(Array) }
37
+ it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
38
+ end
39
+ context "when option nil" do
40
+ subject { GitHubChangelogGenerator::Parser.user_project_from_option(nil, nil) }
41
+ it { is_expected.to be_a(Array) }
42
+ it { is_expected.to match_array([nil, nil]) }
43
+ end
44
+ context "when site is nil" do
45
+ subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", nil, nil) }
46
+ it { is_expected.to be_a(Array) }
47
+ it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
48
+ end
49
+ context "when site is valid" do
50
+ subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", nil, "https://codeclimate.com") }
51
+ it { is_expected.to be_a(Array) }
52
+ it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
53
+ end
54
+ context "when second arg is not nil" do
55
+ subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", "blah", nil) }
56
+ it { is_expected.to be_a(Array) }
57
+ it { is_expected.to match_array([nil, nil]) }
58
+ end
59
+ end
60
+ end