github_changelog_generator 1.15.0.pre.alpha → 1.15.0.pre.beta

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90e44c8d99a9213765a5d46605dd584956f79ca0
4
- data.tar.gz: e42720ae60fc9c8096c41cb51456e0aacdcc8b69
3
+ metadata.gz: 55def2416dfef54779832b68f186965d4c62bbd0
4
+ data.tar.gz: c9f978a6b01aab0356b676e7749f1b25d4aacc93
5
5
  SHA512:
6
- metadata.gz: fafd2adab89a40c20fc1975da3a569f7a559c377d564c8b79268d6630472daf54f00ed52389c3fc351c93f645106305454943bdbdde740d51b23eb508dec57fb
7
- data.tar.gz: 6143d6c610624b8ac836292698d01e6adb941c2fa21399ab85e7831982587d7b329a802c7fba0c51b986b85b58860850b1c2cb9034ffac7bdcc2329c0f0f01f6
6
+ metadata.gz: acdbbbae3679145b85639750184fffd39efe3d94ea2447f3429341655bd9ea94860f1a1eea65f970437ae2894aeffc145685ef34cda687acf90123d45dc7b6b0
7
+ data.tar.gz: 7cf8cf180df005695f5d3882299ce4ad4ef3b504dd0bfbba9806efe925fc1ebc666224b00eacbeda63add590ccd3b74ad19b40032c4fb70df07778cca444c29a
data/LICENSE CHANGED
@@ -1,5 +1,5 @@
1
1
  The MIT License (MIT)
2
- Copyright (c) 2016 Petr Korolev
2
+ Copyright (c) 2016-2017 Petr Korolev
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
5
 
data/README.md CHANGED
@@ -30,9 +30,14 @@ GitHub Changelog Generator ![GitHub Logo](../master/images/logo.jpg)
30
30
  Since you don't have to fill your `CHANGELOG.md` manually now: just run the script, relax and take a cup of :coffee: before your next release! :tada:
31
31
 
32
32
  ### *What’s the point of a change log?*
33
+
33
34
  To make it easier for users and contributors to see precisely what notable changes have been made between each release (or version) of the project.
35
+
34
36
  ### *Why should I care?*
35
- Because software tools are for people. If you don’t care, why are you contributing to open source? Surely, there must be a kernel (ha!) of care somewhere in that lovely little brain of yours.
37
+
38
+ Because software tools are for _people_. "Changelogs make it easier for users and
39
+ contributors to see precisely what notable changes have been made between each
40
+ release (or version) of the project."
36
41
 
37
42
  :arrow_right: *[http://keepachangelog.com](http://keepachangelog.com)*
38
43
 
@@ -71,32 +76,31 @@ See also Troubleshooting.
71
76
 
72
77
 
73
78
  ## Usage
74
- **It's really simple!**
75
79
 
76
- - If your **`git remote`** `origin` refers to your GitHub repo, just go to your project folder and run:
80
+ - Run this:
77
81
 
78
- github_changelog_generator
79
-
80
- - Or, run this from anywhere:
81
82
  `github_changelog_generator -u github_username -p github_project`
82
83
  `github_changelog_generator github_username/github_project`
83
84
 
84
- - If you are running it against a repository on a Github Enterprise install, you must specify *both* `--github-site` and `--github-api` command line options:
85
+ - For Github Enterprise repos, specify *both* `--github-site` and `--github-api` options:
85
86
 
86
87
  github_changelog_generator --github-site="https://github.yoursite.com" \
87
88
  --github-api="https://github.yoursite.com/api/v3/"
88
89
 
89
- This generates a changelog to the `CHANGELOG.md` file, with pretty markdown formatting.
90
+ This generates a `CHANGELOG.md`, with pretty Markdown formatting.
90
91
 
91
92
  ### Params
93
+
92
94
  Type `github_changelog_generator --help` for details.
93
95
 
94
96
  For more details about params, read the Wiki page: [**Advanced change log generation examples**](https://github.com/skywinder/github-changelog-generator/wiki/Advanced-change-log-generation-examples)
95
97
 
96
98
  ### Params File
99
+
97
100
  In your project root, you can put a params file named `.github_changelog_generator` to override default params:
98
101
 
99
102
  Example:
103
+
100
104
  ```
101
105
  unreleased=false
102
106
  future-release=5.0.0
@@ -106,6 +110,7 @@ since-tag=1.0.0
106
110
  ### GitHub token
107
111
 
108
112
  GitHub only allows 50 unauthenticated requests per hour.
113
+
109
114
  Therefore, it's recommended to run this script with authentication by using a **token**.
110
115
 
111
116
  Here's how:
@@ -143,7 +148,7 @@ If you have a `HISTORY.md` file in your project, it will automatically be picked
143
148
  You love `rake`? We do, too! So, we've made it even easier for you:
144
149
  we've provided a `rake` task library for your changelog generation.
145
150
 
146
- Just put something like this in your `Rakefile`:
151
+ Configure the task in your `Rakefile`:
147
152
 
148
153
  ```ruby
149
154
  require 'github_changelog_generator/task'
@@ -154,11 +159,14 @@ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
154
159
  end
155
160
  ```
156
161
 
157
- All command line options can be passed to the `rake` task as `config` parameters. And since you're naming the `rake` task yourself, you can create as many as you want.
162
+ All command-line options can be passed to the `rake` task as `config`
163
+ parameters. And since you're naming the `rake` task yourself, you can create
164
+ as many as you want.
158
165
 
159
166
  You can look for params names from the [parser source code (#setup_parser)](https://github.com/skywinder/github-changelog-generator/blob/master/lib/github_changelog_generator/parser.rb). For example, to translate the bugs label to Portuguese, instead of setting `config.bugs_label`, you have to set `config.bug_prefix`, and so on.
160
167
 
161
168
  ## Features and advantages of this project
169
+
162
170
  - Generate canonical, neat change log file, followed by [basic change log guidelines](http://keepachangelog.com) :gem:
163
171
  - Optionally generate **Unreleased** changes (closed issues that have not released yet) :dizzy:
164
172
  - **GitHub Enterprise support** via command line options! :factory:
@@ -179,12 +187,14 @@ You can look for params names from the [parser source code (#setup_parser)](http
179
187
 
180
188
 
181
189
  ### Alternatives
190
+
182
191
  Here is a [wikipage list of alternatives](https://github.com/skywinder/Github-Changelog-Generator/wiki/Alternatives) that I found. But none satisfied my requirements.
183
192
 
184
193
  *If you know other projects, feel free to edit this Wiki page!*
185
194
 
186
195
 
187
196
  ### Projects using this library
197
+
188
198
  Here's a [wikipage list of projects](https://github.com/skywinder/Github-Changelog-Generator/wiki/Projects-using-Github-Changelog-Generator).
189
199
 
190
200
  If you've used this project in a live app, please let me know! Nothing makes me happier than seeing someone else take my work and go wild with it.
data/Rakefile CHANGED
@@ -9,12 +9,11 @@ require "fileutils"
9
9
  require "overcommit"
10
10
 
11
11
  RuboCop::RakeTask.new
12
- RSpec::Core::RakeTask.new(:rspec)
12
+ RSpec::Core::RakeTask.new
13
13
 
14
14
  desc "When releasing the gem, re-fetch latest cacert.pem from curl.haxx.se. Developer task."
15
15
  task :update_ssl_ca_file do
16
16
  `pushd lib/github_changelog_generator/ssl_certs && curl --remote-name --time-cond cacert.pem https://curl.haxx.se/ca/cacert.pem && popd`
17
17
  end
18
18
 
19
- task checks: %i[rubocop rspec]
20
- task default: %i[rubocop rspec]
19
+ task default: %i[rubocop spec]
@@ -95,12 +95,13 @@ module GitHubChangelogGenerator
95
95
  # @param [Array] pull_requests
96
96
  # @return [String] generated log for issues
97
97
  def issues_to_log(issues, pull_requests)
98
- log = ""
99
- bugs_a, enhancement_a, issues_a = parse_by_sections(issues, pull_requests)
98
+ sections = parse_by_sections(issues, pull_requests)
100
99
 
101
- log += generate_sub_section(enhancement_a, options[:enhancement_prefix])
102
- log += generate_sub_section(bugs_a, options[:bug_prefix])
103
- log += generate_sub_section(issues_a, options[:issue_prefix])
100
+ log = ""
101
+ log += generate_sub_section(sections[:breaking], options[:breaking_prefix])
102
+ log += generate_sub_section(sections[:enhancements], options[:enhancement_prefix])
103
+ log += generate_sub_section(sections[:bugs], options[:bug_prefix])
104
+ log += generate_sub_section(sections[:issues], options[:issue_prefix])
104
105
  log
105
106
  end
106
107
 
@@ -109,47 +110,69 @@ module GitHubChangelogGenerator
109
110
  #
110
111
  # @param [Array] issues
111
112
  # @param [Array] pull_requests
112
- # @return [Array] tuple of filtered arrays: (Bugs, Enhancements Issues)
113
+ # @return [Hash] Mapping of filtered arrays: (Bugs, Enhancements, Breaking stuff, Issues)
113
114
  def parse_by_sections(issues, pull_requests)
114
- issues_a = []
115
- enhancement_a = []
116
- bugs_a = []
115
+ sections = {
116
+ issues: [],
117
+ enhancements: [],
118
+ bugs: [],
119
+ breaking: []
120
+ }
117
121
 
118
122
  issues.each do |dict|
119
123
  added = false
124
+
120
125
  dict["labels"].each do |label|
121
126
  if options[:bug_labels].include?(label["name"])
122
- bugs_a.push(dict)
127
+ sections[:bugs] << dict
123
128
  added = true
124
- next
125
- end
126
- if options[:enhancement_labels].include?(label["name"])
127
- enhancement_a.push(dict)
129
+ elsif options[:enhancement_labels].include?(label["name"])
130
+ sections[:enhancements] << dict
131
+ added = true
132
+ elsif options[:breaking_labels].include?(label["name"])
133
+ sections[:breaking] << dict
128
134
  added = true
129
- next
130
135
  end
136
+
137
+ break if added
131
138
  end
132
- issues_a.push(dict) unless added
139
+
140
+ sections[:issues] << dict unless added
133
141
  end
134
142
 
143
+ sort_pull_requests(pull_requests, sections)
144
+ end
145
+
146
+ # This method iterates through PRs and sorts them into sections
147
+ #
148
+ # @param [Array] pull_requests
149
+ # @param [Hash] sections
150
+ # @return [Hash] sections
151
+ def sort_pull_requests(pull_requests, sections)
135
152
  added_pull_requests = []
136
153
  pull_requests.each do |pr|
154
+ added = false
155
+
137
156
  pr["labels"].each do |label|
138
157
  if options[:bug_labels].include?(label["name"])
139
- bugs_a.push(pr)
140
- added_pull_requests.push(pr)
141
- next
142
- end
143
- if options[:enhancement_labels].include?(label["name"])
144
- enhancement_a.push(pr)
145
- added_pull_requests.push(pr)
146
- next
158
+ sections[:bugs] << pr
159
+ added_pull_requests << pr
160
+ added = true
161
+ elsif options[:enhancement_labels].include?(label["name"])
162
+ sections[:enhancements] << pr
163
+ added_pull_requests << pr
164
+ added = true
165
+ elsif options[:breaking_labels].include?(label["name"])
166
+ sections[:breaking] << pr
167
+ added_pull_requests << pr
168
+ added = true
147
169
  end
170
+
171
+ break if added
148
172
  end
149
173
  end
150
174
  added_pull_requests.each { |p| pull_requests.delete(p) }
151
-
152
- [bugs_a, enhancement_a, issues_a]
175
+ sections
153
176
  end
154
177
  end
155
178
  end
@@ -129,7 +129,7 @@ module GitHubChangelogGenerator
129
129
  if tag
130
130
  if all_tags.map { |t| t["name"] }.include? tag
131
131
  idx = all_tags.index { |t| t["name"] == tag }
132
- filtered_tags = if idx > 0
132
+ filtered_tags = if idx
133
133
  all_tags[0..idx]
134
134
  else
135
135
  []
@@ -31,21 +31,27 @@ module GitHubChangelogGenerator
31
31
  @project = @options[:project]
32
32
  @since = @options[:since]
33
33
  @http_cache = @options[:http_cache]
34
- if @http_cache
35
- @cache_file = @options.fetch(:cache_file) { File.join(Dir.tmpdir, "github-changelog-http-cache") }
36
- @cache_log = @options.fetch(:cache_log) { File.join(Dir.tmpdir, "github-changelog-logger.log") }
37
- init_cache
38
- end
39
- @github_token = fetch_github_token
40
-
41
- @request_options = { per_page: PER_PAGE_NUMBER }
42
- @github_options = {}
43
- @github_options[:access_token] = @github_token unless @github_token.nil?
44
- @github_options[:api_endpoint] = @options[:github_endpoint] unless @options[:github_endpoint].nil?
45
-
34
+ @cache_file = nil
35
+ @cache_log = nil
36
+ prepare_cache
46
37
  configure_octokit_ssl
38
+ @client = Octokit::Client.new(github_options)
39
+ end
40
+
41
+ def prepare_cache
42
+ return unless @http_cache
43
+ @cache_file = @options.fetch(:cache_file) { File.join(Dir.tmpdir, "github-changelog-http-cache") }
44
+ @cache_log = @options.fetch(:cache_log) { File.join(Dir.tmpdir, "github-changelog-logger.log") }
45
+ init_cache
46
+ end
47
47
 
48
- @client = Octokit::Client.new(@github_options)
48
+ def github_options
49
+ result = {}
50
+ github_token = fetch_github_token
51
+ result[:access_token] = github_token if github_token
52
+ endpoint = @options[:github_endpoint]
53
+ result[:api_endpoint] = endpoint if endpoint
54
+ result
49
55
  end
50
56
 
51
57
  def configure_octokit_ssl
@@ -54,21 +60,19 @@ module GitHubChangelogGenerator
54
60
  end
55
61
 
56
62
  def init_cache
57
- middleware_opts = {
58
- serializer: Marshal,
59
- store: ActiveSupport::Cache::FileStore.new(@cache_file),
60
- logger: Logger.new(@cache_log),
61
- shared_cache: false
62
- }
63
- stack = Faraday::RackBuilder.new do |builder|
64
- builder.use Faraday::HttpCache, middleware_opts
63
+ Octokit.middleware = Faraday::RackBuilder.new do |builder|
64
+ builder.use(Faraday::HttpCache, serializer: Marshal,
65
+ store: ActiveSupport::Cache::FileStore.new(@cache_file),
66
+ logger: Logger.new(@cache_log),
67
+ shared_cache: false)
65
68
  builder.use Octokit::Response::RaiseError
66
69
  builder.adapter Faraday.default_adapter
67
70
  # builder.response :logger
68
71
  end
69
- Octokit.middleware = stack
70
72
  end
71
73
 
74
+ DEFAULT_REQUEST_OPTIONS = { per_page: PER_PAGE_NUMBER }
75
+
72
76
  # Fetch all tags from repo
73
77
  #
74
78
  # @return [Array <Hash>] array of tags
@@ -84,7 +88,7 @@ module GitHubChangelogGenerator
84
88
  def calculate_pages(client, method, request_options)
85
89
  # Makes the first API call so that we can call last_response
86
90
  check_github_response do
87
- client.send(method, user_project, @request_options.merge(request_options))
91
+ client.send(method, user_project, DEFAULT_REQUEST_OPTIONS.merge(request_options))
88
92
  end
89
93
 
90
94
  last_response = client.last_response
@@ -104,7 +108,7 @@ module GitHubChangelogGenerator
104
108
  page_i = 0
105
109
  count_pages = calculate_pages(@client, "tags", {})
106
110
 
107
- iterate_pages(@client, "tags", {}) do |new_tags|
111
+ iterate_pages(@client, "tags") do |new_tags|
108
112
  page_i += PER_PAGE_NUMBER
109
113
  print_in_same_line("Fetching tags... #{page_i}/#{count_pages * PER_PAGE_NUMBER}")
110
114
  tags.concat(new_tags)
@@ -118,8 +122,13 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
118
122
  Helper.log.info "Found #{tags.count} tags"
119
123
  end
120
124
  # tags are a Sawyer::Resource. Convert to hash
121
- tags = tags.map { |h| stringify_keys_deep(h.to_hash) }
122
- tags
125
+ tags.map { |resource| stringify_keys_deep(resource.to_hash) }
126
+ end
127
+
128
+ def closed_pr_options
129
+ @closed_pr_options ||= {
130
+ filter: "all", labels: nil, state: "closed"
131
+ }.tap { |options| options[:since] = @since if @since }
123
132
  end
124
133
 
125
134
  # This method fetch all closed issues and separate them to pull requests and pure issues
@@ -129,17 +138,10 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
129
138
  def fetch_closed_issues_and_pr
130
139
  print "Fetching closed issues...\r" if @options[:verbose]
131
140
  issues = []
132
- options = {
133
- state: "closed",
134
- filter: "all",
135
- labels: nil
136
- }
137
- options[:since] = @since unless @since.nil?
138
-
139
141
  page_i = 0
140
- count_pages = calculate_pages(@client, "issues", options)
142
+ count_pages = calculate_pages(@client, "issues", closed_pr_options)
141
143
 
142
- iterate_pages(@client, "issues", options) do |new_issues|
144
+ iterate_pages(@client, "issues", closed_pr_options) do |new_issues|
143
145
  page_i += PER_PAGE_NUMBER
144
146
  print_in_same_line("Fetching issues... #{page_i}/#{count_pages * PER_PAGE_NUMBER}")
145
147
  issues.concat(new_issues)
@@ -148,12 +150,9 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
148
150
  print_empty_line
149
151
  Helper.log.info "Received issues: #{issues.count}"
150
152
 
151
- issues = issues.map { |h| stringify_keys_deep(h.to_hash) }
152
-
153
153
  # separate arrays of issues and pull requests:
154
- issues.partition do |x|
155
- x["pull_request"].nil?
156
- end
154
+ issues.map { |issue| stringify_keys_deep(issue.to_hash) }
155
+ .partition { |issue_or_pr| issue_or_pr["pull_request"].nil? }
157
156
  end
158
157
 
159
158
  # Fetch all pull requests. We need them to detect :merged_at parameter
@@ -179,8 +178,7 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
179
178
  print_empty_line
180
179
 
181
180
  Helper.log.info "Pull Request count: #{pull_requests.count}"
182
- pull_requests = pull_requests.map { |h| stringify_keys_deep(h.to_hash) }
183
- pull_requests
181
+ pull_requests.map { |pull_request| stringify_keys_deep(pull_request.to_hash) }
184
182
  end
185
183
 
186
184
  # Fetch event for all issues and add them to 'events'
@@ -195,10 +193,10 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
195
193
  issues_slice.each do |issue|
196
194
  threads << Thread.new do
197
195
  issue["events"] = []
198
- iterate_pages(@client, "issue_events", issue["number"], {}) do |new_event|
196
+ iterate_pages(@client, "issue_events", issue["number"]) do |new_event|
199
197
  issue["events"].concat(new_event)
200
198
  end
201
- issue["events"] = issue["events"].map { |h| stringify_keys_deep(h.to_hash) }
199
+ issue["events"] = issue["events"].map { |event| stringify_keys_deep(event.to_hash) }
202
200
  print_in_same_line("Fetching events for issues and PR: #{i + 1}/#{issues.count}")
203
201
  i += 1
204
202
  end
@@ -256,14 +254,15 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
256
254
  stringify_keys_deep(value)
257
255
  end
258
256
  when Hash
259
- indata.each_with_object({}) do |(k, v), output|
260
- output[k.to_s] = stringify_keys_deep(v)
257
+ indata.each_with_object({}) do |(key, value), output|
258
+ output[key.to_s] = stringify_keys_deep(value)
261
259
  end
262
260
  else
263
261
  indata
264
262
  end
265
263
  end
266
264
 
265
+ # Exception raised to warn about moved repositories.
267
266
  MovedPermanentlyError = Class.new(RuntimeError)
268
267
 
269
268
  # Iterates through all pages until there are no more :next pages to follow
@@ -274,29 +273,21 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
274
273
  #
275
274
  # @yield [Sawyer::Resource] An OctoKit-provided response (which can be empty)
276
275
  #
277
- # @return [Integer] total number of pages
276
+ # @return [void]
278
277
  def iterate_pages(client, method, *args)
279
- request_opts = extract_request_args(args)
280
- args.push(@request_options.merge(request_opts))
281
-
282
- number_of_pages = 1
278
+ args << DEFAULT_REQUEST_OPTIONS.merge(extract_request_args(args))
283
279
 
284
280
  check_github_response { client.send(method, user_project, *args) }
285
- last_response = client.last_response
286
- if last_response.status == 301
287
- raise MovedPermanentlyError, last_response.data[:url]
281
+ last_response = client.last_response.tap do |response|
282
+ raise(MovedPermanentlyError, response.data[:url]) if response.status == 301
288
283
  end
289
284
 
290
285
  yield(last_response.data)
291
286
 
292
287
  until (next_one = last_response.rels[:next]).nil?
293
- number_of_pages += 1
294
-
295
288
  last_response = check_github_response { next_one.get }
296
289
  yield(last_response.data)
297
290
  end
298
-
299
- number_of_pages
300
291
  end
301
292
 
302
293
  def extract_request_args(args)
@@ -317,14 +308,17 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
317
308
  yield
318
309
  end
319
310
  rescue MovedPermanentlyError => e
320
- Helper.log.error("#{e.class}: #{e.message}")
321
- sys_abort("The repository has moved, please update your configuration")
311
+ fail_with_message(e, "The repository has moved, update your configuration")
322
312
  rescue Octokit::Forbidden => e
323
- Helper.log.error("#{e.class}: #{e.message}")
324
- sys_abort("Exceeded retry limit")
313
+ fail_with_message(e, "Exceeded retry limit")
325
314
  rescue Octokit::Unauthorized => e
315
+ fail_with_message(e, "Error: wrong GitHub token")
316
+ end
317
+
318
+ # Presents the exception, and the aborts with the message.
319
+ def fail_with_message(e, message)
326
320
  Helper.log.error("#{e.class}: #{e.message}")
327
- sys_abort("Error: wrong GitHub token")
321
+ sys_abort(message)
328
322
  end
329
323
 
330
324
  # Exponential backoff
@@ -373,7 +367,7 @@ Make sure, that you push tags to remote repo via 'git push --tags'"
373
367
  #
374
368
  # @return [String]
375
369
  def fetch_github_token
376
- env_var = @options[:token] ? @options[:token] : (ENV.fetch CHANGELOG_GITHUB_TOKEN, nil)
370
+ env_var = @options[:token].presence || ENV["CHANGELOG_GITHUB_TOKEN"]
377
371
 
378
372
  Helper.log.warn NO_TOKEN_PROVIDED unless env_var
379
373
 
@@ -20,13 +20,14 @@ module GitHubChangelogGenerator
20
20
  due_tag
21
21
  enhancement_labels
22
22
  enhancement_prefix
23
+ breaking_labels
24
+ breaking_prefix
23
25
  exclude_labels
24
26
  exclude_tags
25
27
  exclude_tags_regex
26
28
  filter_issues_by_milestone
27
29
  frontmatter
28
30
  future_release
29
- git_remote
30
31
  github_endpoint
31
32
  github_site
32
33
  header
@@ -19,8 +19,6 @@ module GitHubChangelogGenerator
19
19
  abort [e, parser].join("\n")
20
20
  end
21
21
 
22
- fetch_user_and_project(options)
23
-
24
22
  abort(parser.banner) unless options[:user] && options[:project]
25
23
 
26
24
  print_options(options)
@@ -72,6 +70,9 @@ module GitHubChangelogGenerator
72
70
  opts.on("--enhancement-label [LABEL]", "Setup custom label for enhancements section. Default is \"**Implemented enhancements:**\"") do |v|
73
71
  options[:enhancement_prefix] = v
74
72
  end
73
+ opts.on("--breaking-label [LABEL]", "Setup custom label for the breaking changes section. Default is \"**Breaking changes:**\"") do |v|
74
+ options[:breaking_prefix] = v
75
+ end
75
76
  opts.on("--issues-label [LABEL]", "Setup custom label for closed-issues section. Default is \"**Closed issues:**\"") do |v|
76
77
  options[:issue_prefix] = v
77
78
  end
@@ -129,6 +130,9 @@ module GitHubChangelogGenerator
129
130
  opts.on("--enhancement-labels x,y,z", Array, 'Issues with the specified labels will be always added to "Implemented enhancements" section. Default is \'enhancement,Enhancement\'') do |list|
130
131
  options[:enhancement_labels] = list
131
132
  end
133
+ opts.on("--breaking-labels x,y,z", Array, 'Issues with these labels will be added to a new section, called "Breaking Changes". Default is \'backwards-incompatible\'') do |list|
134
+ options[:breaking_labels] = list
135
+ end
132
136
  opts.on("--issue-line-labels x,y,z", Array, 'The specified labels will be shown in brackets next to each matching issue. Use "ALL" to show all labels. Default is [].') do |list|
133
137
  options[:issue_line_labels] = list
134
138
  end
@@ -210,6 +214,7 @@ module GitHubChangelogGenerator
210
214
  enhancement_labels: ["enhancement", "Enhancement", "Type: Enhancement"],
211
215
  bug_labels: ["bug", "Bug", "Type: Bug"],
212
216
  exclude_labels: ["duplicate", "question", "invalid", "wontfix", "Duplicate", "Question", "Invalid", "Wontfix", "Meta: Exclude From Changelog"],
217
+ breaking_labels: %w[backwards-incompatible breaking],
213
218
  issue_line_labels: [],
214
219
  max_issues: nil,
215
220
  simple_list: false,
@@ -220,114 +225,9 @@ module GitHubChangelogGenerator
220
225
  issue_prefix: "**Closed issues:**",
221
226
  bug_prefix: "**Fixed bugs:**",
222
227
  enhancement_prefix: "**Implemented enhancements:**",
223
- git_remote: "origin",
228
+ breaking_prefix: "**Breaking changes:**",
224
229
  http_cache: true
225
230
  )
226
231
  end
227
-
228
- # If `:user` or `:project` not set in options, try setting them
229
- # Valid unnamed parameters:
230
- # 1) in 1 param: repo_name/project
231
- # 2) in 2 params: repo name project
232
- def self.fetch_user_and_project(options)
233
- if options[:user].nil? || options[:project].nil?
234
- user, project = user_and_project_from_git(options, ARGV[0], ARGV[1])
235
- options[:user] ||= user
236
- options[:project] ||= project
237
- end
238
- end
239
-
240
- # Sets `:user` and `:project` in `options` from CLI arguments or `git remote`
241
- # @param [String] arg0 first argument in cli
242
- # @param [String] arg1 second argument in cli
243
- # @return [Array<String>] user and project, or nil if unsuccessful
244
- def self.user_and_project_from_git(options, arg0 = nil, arg1 = nil)
245
- user, project = user_project_from_option(arg0, arg1, options[:github_site])
246
- unless user && project
247
- if ENV["RUBYLIB"] =~ /ruby-debug-ide/
248
- user = "skywinder"
249
- project = "changelog_test"
250
- else
251
- remote = `git config --get remote.#{options[:git_remote]}.url`
252
- user, project = user_project_from_remote(remote)
253
- end
254
- end
255
-
256
- [user, project]
257
- end
258
-
259
- # Returns GitHub username and project from CLI arguments
260
- #
261
- # @param arg0 [String] This parameter takes two forms: Either a full
262
- # GitHub URL, or a 'username/projectname', or
263
- # simply a GitHub username
264
- # @param arg1 [String] If arg0 is given as a username,
265
- # then arg1 can given as a projectname
266
- # @param github_site [String] Domain name of GitHub site
267
- #
268
- # @return [Array, nil] user and project, or nil if unsuccessful
269
- def self.user_project_from_option(arg0, arg1, github_site)
270
- user = nil
271
- project = nil
272
- github_site ||= "github.com"
273
- if arg0 && !arg1
274
- # this match should parse strings such "https://github.com/skywinder/Github-Changelog-Generator" or
275
- # "skywinder/Github-Changelog-Generator" to user and name
276
- match = /(?:.+#{Regexp.escape(github_site)}\/)?(.+)\/(.+)/.match(arg0)
277
-
278
- begin
279
- param = match[2].nil?
280
- rescue StandardError
281
- puts "Can't detect user and name from first parameter: '#{arg0}' -> exit'"
282
- return
283
- end
284
- if param
285
- return
286
- else
287
- user = match[1]
288
- project = match[2]
289
- end
290
- end
291
- [user, project]
292
- end
293
-
294
- # These patterns match these formats:
295
- #
296
- # ```
297
- # origin git@github.com:skywinder/Github-Changelog-Generator.git (fetch)
298
- # git@github.com:skywinder/Github-Changelog-Generator.git
299
- # ```
300
- #
301
- # and
302
- #
303
- # ```
304
- # origin https://github.com/skywinder/ChangelogMerger (fetch)
305
- # https://github.com/skywinder/ChangelogMerger
306
- # ```
307
- GIT_REMOTE_PATTERNS = [
308
- /.*(?:[:\/])(?<user>(?:-|\w|\.)*)\/(?<project>(?:-|\w|\.)*)(?:\.git).*/,
309
- /.*\/(?<user>(?:-|\w|\.)*)\/(?<project>(?:-|\w|\.)*).*/
310
- ]
311
-
312
- # Returns GitHub username and project from git remote output
313
- #
314
- # @param git_remote_output [String] Output of git remote command
315
- #
316
- # @return [Array] user and project
317
- def self.user_project_from_remote(git_remote_output)
318
- user = nil
319
- project = nil
320
- GIT_REMOTE_PATTERNS.each do |git_remote_pattern|
321
- git_remote_pattern =~ git_remote_output
322
-
323
- if Regexp.last_match
324
- user = Regexp.last_match(:user)
325
- project = Regexp.last_match(:project)
326
- break
327
- end
328
- end
329
-
330
- [user, project]
331
- end
332
232
  end
333
233
  end
@@ -67,7 +67,7 @@ module GitHubChangelogGenerator
67
67
  end
68
68
 
69
69
  KNOWN_ARRAY_KEYS = %i[exclude_labels include_labels bug_labels
70
- enhancement_labels issue_line_labels between_tags exclude_tags]
70
+ enhancement_labels breaking_labels issue_line_labels between_tags exclude_tags]
71
71
  KNOWN_INTEGER_KEYS = [:max_issues]
72
72
 
73
73
  def convert_value(value, option_name)
@@ -91,6 +91,7 @@ module GitHubChangelogGenerator
91
91
  header_label: :header,
92
92
  front_matter: :frontmatter,
93
93
  pr_label: :merge_prefix,
94
+ breaking_label: :breaking_prefix,
94
95
  issues_wo_labels: :add_issues_wo_labels,
95
96
  pr_wo_labels: :add_pr_wo_labels,
96
97
  pull_requests: :pulls,
@@ -48,13 +48,11 @@ module GitHubChangelogGenerator
48
48
  # mimick parse_options
49
49
  options = Parser.default_options
50
50
 
51
- Parser.fetch_user_and_project(options)
52
-
53
51
  OPTIONS.each do |o|
54
52
  v = instance_variable_get("@#{o}")
55
53
  options[o.to_sym] = v unless v.nil?
56
54
  end
57
-
55
+ abort "user and project are required." unless options[:user] && options[:project]
58
56
  generator = Generator.new options
59
57
 
60
58
  log = generator.compound_changelog
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GitHubChangelogGenerator
4
- VERSION = "1.15.0-alpha"
4
+ VERSION = "1.15.0-beta"
5
5
  end
@@ -13,5 +13,61 @@ module GitHubChangelogGenerator
13
13
  end.not_to raise_error
14
14
  end
15
15
  end
16
+
17
+ describe "#parse_by_sections" do
18
+ def label(name)
19
+ { "name" => name }
20
+ end
21
+
22
+ def issue(title, labels)
23
+ { "title" => "issue #{title}", "labels" => labels.map { |l| label(l) } }
24
+ end
25
+
26
+ def pr(title, labels)
27
+ { "title" => "pr #{title}", "labels" => labels.map { |l| label(l) } }
28
+ end
29
+
30
+ def get_titles(issues)
31
+ issues.map { |issue| issue["title"] }
32
+ end
33
+
34
+ let(:options) do
35
+ {
36
+ bug_labels: ["bug"],
37
+ enhancement_labels: ["enhancement"],
38
+ breaking_labels: ["breaking"]
39
+ }
40
+ end
41
+
42
+ let(:issues) do
43
+ [
44
+ issue("no labels", []),
45
+ issue("enhancement", ["enhancement"]),
46
+ issue("bug", ["bug"]),
47
+ issue("breaking", ["breaking"]),
48
+ issue("all the labels", %w[enhancement bug breaking])
49
+ ]
50
+ end
51
+
52
+ let(:pull_requests) do
53
+ [
54
+ pr("no labels", []),
55
+ pr("enhancement", ["enhancement"]),
56
+ pr("bug", ["bug"]),
57
+ pr("breaking", ["breaking"]),
58
+ pr("all the labels", %w[enhancement bug breaking])
59
+ ]
60
+ end
61
+
62
+ it "works" do
63
+ sections = described_class.new(options).parse_by_sections(issues, pull_requests)
64
+
65
+ expect(get_titles(sections[:issues])).to eq(["issue no labels"])
66
+ expect(get_titles(sections[:enhancements])).to eq(["issue enhancement", "issue all the labels", "pr enhancement", "pr all the labels"])
67
+ expect(get_titles(sections[:bugs])).to eq(["issue bug", "pr bug"])
68
+ expect(get_titles(sections[:breaking])).to eq(["issue breaking", "pr breaking"])
69
+ expect(get_titles(pull_requests)).to eq(["pr no labels"])
70
+ end
71
+ end
16
72
  end
17
73
  end
@@ -182,6 +182,11 @@ describe GitHubChangelogGenerator::Generator do
182
182
  let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "2") }
183
183
  it { is_expected.to be_a Array }
184
184
  it { is_expected.to match_array(tags_from_strings(%w[1 2])) }
185
+
186
+ context "with since tag set to the most recent tag" do
187
+ let(:generator) { GitHubChangelogGenerator::Generator.new(since_tag: "1") }
188
+ it { is_expected.to match_array(tags_from_strings(%w[1])) }
189
+ end
185
190
  end
186
191
 
187
192
  context "with invalid since tag" do
@@ -12,7 +12,7 @@ RSpec.describe GitHubChangelogGenerator::Options do
12
12
  it "raises an error" do
13
13
  expect do
14
14
  described_class.new(
15
- git_remote: "origin",
15
+ project: "rails",
16
16
  strength: "super-strength",
17
17
  wisdom: "deep"
18
18
  )
@@ -22,13 +22,13 @@ RSpec.describe GitHubChangelogGenerator::Options do
22
22
  end
23
23
 
24
24
  describe "#[]=(key, value)" do
25
- let(:options) { described_class.new(git_remote: "origin") }
25
+ let(:options) { described_class.new(project: "rails") }
26
26
 
27
27
  context "with known options" do
28
28
  it "sets the option value" do
29
29
  expect do
30
- options[:git_remote] = "in the cloud"
31
- end.to change { options[:git_remote] }.to "in the cloud"
30
+ options[:project] = "trails"
31
+ end.to change { options[:project] }.to "trails"
32
32
  end
33
33
  end
34
34
 
@@ -1,83 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  describe GitHubChangelogGenerator::Parser do
4
- describe ".user_project_from_remote" do
5
- context "when remote is type 1" do
6
- subject { GitHubChangelogGenerator::Parser.user_project_from_remote("origin https://github.com/skywinder/ActionSheetPicker-3.0 (fetch)") }
7
- it { is_expected.to be_a(Array) }
8
- it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
9
- end
10
- context "when remote is type 2" do
11
- subject { GitHubChangelogGenerator::Parser.user_project_from_remote("https://github.com/skywinder/ActionSheetPicker-3.0") }
12
- it { is_expected.to be_a(Array) }
13
- it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
14
- end
15
- context "when remote is type 3" do
16
- subject { GitHubChangelogGenerator::Parser.user_project_from_remote("https://github.com/skywinder/ActionSheetPicker-3.0") }
17
- it { is_expected.to be_a(Array) }
18
- it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
19
- end
20
- context "when remote is type 4" do
21
- subject { GitHubChangelogGenerator::Parser.user_project_from_remote("origin git@github.com:skywinder/ActionSheetPicker-3.0.git (fetch)") }
22
- it { is_expected.to be_a(Array) }
23
- it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
24
- end
25
- context "when remote is invalid" do
26
- subject { GitHubChangelogGenerator::Parser.user_project_from_remote("some invalid text") }
27
- it { is_expected.to be_a(Array) }
28
- it { is_expected.to match_array([nil, nil]) }
29
- end
30
- end
31
- describe ".user_project_from_option" do
32
- context "when option is invalid" do
33
- it("should return nil") { expect(GitHubChangelogGenerator::Parser.user_project_from_option("blah", nil, nil)).to be_nil }
34
- end
35
-
36
- context "when option is valid" do
37
- subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", nil, nil) }
38
- it { is_expected.to be_a(Array) }
39
- it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
40
- end
41
- context "when option nil" do
42
- subject { GitHubChangelogGenerator::Parser.user_project_from_option(nil, nil, nil) }
43
- it { is_expected.to be_a(Array) }
44
- it { is_expected.to match_array([nil, nil]) }
45
- end
46
- context "when site is nil" do
47
- subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", nil, nil) }
48
- it { is_expected.to be_a(Array) }
49
- it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
50
- end
51
- context "when site is valid" do
52
- subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", nil, "https://codeclimate.com") }
53
- it { is_expected.to be_a(Array) }
54
- it { is_expected.to match_array(["skywinder", "ActionSheetPicker-3.0"]) }
55
- end
56
- context "when second arg is not nil" do
57
- subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", "blah", nil) }
58
- it { is_expected.to be_a(Array) }
59
- it { is_expected.to match_array([nil, nil]) }
60
- end
61
- context "when all args is not nil" do
62
- subject { GitHubChangelogGenerator::Parser.user_project_from_option("skywinder/ActionSheetPicker-3.0", "blah", "https://codeclimate.com") }
63
- it { is_expected.to be_a(Array) }
64
- it { is_expected.to match_array([nil, nil]) }
65
- end
66
- end
67
- describe ".fetch_user_and_project" do
68
- before do
69
- stub_const("ARGV", ["https://github.com/skywinder/github-changelog-generator"])
70
- end
71
-
72
- context do
73
- let(:valid_user) { "initialized_user" }
74
- let(:options) { { user: valid_user } }
75
- let(:options_before_change) { options.dup }
76
- it "should leave user unchanged" do
77
- expect { GitHubChangelogGenerator::Parser.fetch_user_and_project(options) }.to change { options }
78
- .from(options_before_change)
79
- .to(options_before_change.merge(project: "github-changelog-generator"))
80
- end
81
- end
82
- end
83
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: github_changelog_generator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.15.0.pre.alpha
4
+ version: 1.15.0.pre.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Petr Korolev
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-10-01 00:00:00.000000000 Z
12
+ date: 2017-10-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake