github_changelog_generator 1.4.1 → 1.5.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.
@@ -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