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 +4 -4
- data/LICENSE +1 -1
- data/README.md +20 -10
- data/Rakefile +2 -3
- data/lib/github_changelog_generator/generator/generator.rb +49 -26
- data/lib/github_changelog_generator/generator/generator_tags.rb +1 -1
- data/lib/github_changelog_generator/octo_fetcher.rb +58 -64
- data/lib/github_changelog_generator/options.rb +2 -1
- data/lib/github_changelog_generator/parser.rb +8 -108
- data/lib/github_changelog_generator/parser_file.rb +2 -1
- data/lib/github_changelog_generator/task.rb +1 -3
- data/lib/github_changelog_generator/version.rb +1 -1
- data/spec/unit/generator/generator_generation_spec.rb +56 -0
- data/spec/unit/generator/generator_tags_spec.rb +5 -0
- data/spec/unit/options_spec.rb +4 -4
- data/spec/unit/parser_spec.rb +0 -79
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 55def2416dfef54779832b68f186965d4c62bbd0
|
4
|
+
data.tar.gz: c9f978a6b01aab0356b676e7749f1b25d4aacc93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
-
|
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
|
-
-
|
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
|
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
|
-
|
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
|
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
|
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
|
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
|
-
|
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
|
102
|
-
log += generate_sub_section(
|
103
|
-
log += generate_sub_section(
|
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 [
|
113
|
+
# @return [Hash] Mapping of filtered arrays: (Bugs, Enhancements, Breaking stuff, Issues)
|
113
114
|
def parse_by_sections(issues, pull_requests)
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
127
|
+
sections[:bugs] << dict
|
123
128
|
added = true
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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
|
-
|
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
|
-
|
140
|
-
added_pull_requests
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
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
|
@@ -31,21 +31,27 @@ module GitHubChangelogGenerator
|
|
31
31
|
@project = @options[:project]
|
32
32
|
@since = @options[:since]
|
33
33
|
@http_cache = @options[:http_cache]
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
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
|
-
|
58
|
-
serializer: Marshal,
|
59
|
-
|
60
|
-
|
61
|
-
|
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,
|
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"
|
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
|
122
|
-
|
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",
|
142
|
+
count_pages = calculate_pages(@client, "issues", closed_pr_options)
|
141
143
|
|
142
|
-
iterate_pages(@client, "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.
|
155
|
-
|
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
|
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"]
|
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 { |
|
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 |(
|
260
|
-
output[
|
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 [
|
276
|
+
# @return [void]
|
278
277
|
def iterate_pages(client, method, *args)
|
279
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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]
|
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
|
-
|
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
|
@@ -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
|
data/spec/unit/options_spec.rb
CHANGED
@@ -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
|
-
|
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(
|
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[:
|
31
|
-
end.to change { options[:
|
30
|
+
options[:project] = "trails"
|
31
|
+
end.to change { options[:project] }.to "trails"
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
data/spec/unit/parser_spec.rb
CHANGED
@@ -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.
|
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-
|
12
|
+
date: 2017-10-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|