keepachangelog_manager 0.0.1
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.
- checksums.yaml +7 -0
- data/.yardopts +1 -0
- data/README.md +96 -0
- data/bin/keepachangelog_manager.rb +54 -0
- data/lib/keepachangelog_manager.rb +9 -0
- data/lib/keepachangelog_manager/changelog.rb +352 -0
- data/lib/keepachangelog_manager/exceptions.rb +10 -0
- data/lib/keepachangelog_manager/repo.rb +75 -0
- data/lib/keepachangelog_manager/version.rb +3 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 77a5bbc658a38412711067bc833cbd9d46b2bef2
|
4
|
+
data.tar.gz: c4ab817a80b97bb93938e1b20f53ea5cc4a5870c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 60d59c8034fa8ae6e546575859bfcd625a40977839eb9c71aa18f9c7e576a6ac83e9a1e45de7ae95ff092d108c35e796ba9ba47ed39c54f1d8a8c20d0c1d1c15
|
7
|
+
data.tar.gz: 04e47a37894d997698388616629f05640e01f2da4e5a1575f2523eb060d2c9e4d8de628ab4f555f635c04e7da0ecc9a8225e5cd4cbeda164e651c95a7bb5373d
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
lib/keepachangelog.rb lib/**/*.rb
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
|
2
|
+
# `keepachangelog_manager` Ruby gem for your CHANGELOG.md
|
3
|
+
[](https://rubygems.org/gems/keepachangelog_manager)
|
4
|
+
[](http://www.rubydoc.info/gems/keepachangelog_manager/0.0.1)
|
5
|
+
[](https://travis-ci.org/ianfixes/keepachangelog_manager_gem)
|
6
|
+
|
7
|
+
If you follow the [Keep A Changelog](http://keepachangelog.com) `CHANGELOG.md` style, this gem automates the process of updating the file for a release.
|
8
|
+
|
9
|
+
Before:
|
10
|
+
```markdown
|
11
|
+
# Change Log
|
12
|
+
All notable changes to this project will be documented in this file.
|
13
|
+
|
14
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
15
|
+
and this project adheres to [Semantic Versioning](http://semver.org/).
|
16
|
+
|
17
|
+
## [Unreleased]
|
18
|
+
### Added
|
19
|
+
|
20
|
+
### Changed
|
21
|
+
* Everything
|
22
|
+
|
23
|
+
### Deprecated
|
24
|
+
|
25
|
+
### Removed
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
|
29
|
+
### Security
|
30
|
+
|
31
|
+
|
32
|
+
## [0.0.1] - 2018-12-19
|
33
|
+
### Added
|
34
|
+
* Initial stuff
|
35
|
+
|
36
|
+
|
37
|
+
[Unreleased]: https://github.com/ianfixes/keepachangelog_manager_gem/compare/v0.0.1...HEAD
|
38
|
+
[0.0.1]: https://github.com/ianfixes/keepachangelog_manager_gem/compare/v0.0.0...v0.0.1
|
39
|
+
```
|
40
|
+
|
41
|
+
After running `bundle exec keepachangelog_release.md --increment-minor`:
|
42
|
+
|
43
|
+
```markdown
|
44
|
+
# Change Log
|
45
|
+
All notable changes to this project will be documented in this file.
|
46
|
+
|
47
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
48
|
+
and this project adheres to [Semantic Versioning](http://semver.org/).
|
49
|
+
|
50
|
+
## [Unreleased]
|
51
|
+
### Added
|
52
|
+
|
53
|
+
### Changed
|
54
|
+
|
55
|
+
### Deprecated
|
56
|
+
|
57
|
+
### Removed
|
58
|
+
|
59
|
+
### Fixed
|
60
|
+
|
61
|
+
### Security
|
62
|
+
|
63
|
+
|
64
|
+
## [0.1.0] - 2019-01-23
|
65
|
+
### Changed
|
66
|
+
* Everything
|
67
|
+
|
68
|
+
|
69
|
+
## [0.0.1] - 2018-12-19
|
70
|
+
### Added
|
71
|
+
* Initial stuff
|
72
|
+
|
73
|
+
|
74
|
+
[Unreleased]: https://github.com/ianfixes/keepachangelog_manager_gem/compare/v0.1.0...HEAD
|
75
|
+
[0.1.0]: https://github.com/ianfixes/keepachangelog_manager_gem/compare/v0.0.1...v0.1.0
|
76
|
+
[0.0.1]: https://github.com/ianfixes/keepachangelog_manager_gem/compare/v0.0.0...v0.0.1
|
77
|
+
```
|
78
|
+
|
79
|
+
|
80
|
+
## Installation In Your GitHub Project
|
81
|
+
|
82
|
+
Add the following to your `Gemfile`:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
source 'https://rubygems.org'
|
86
|
+
gem 'keepachangelog'
|
87
|
+
```
|
88
|
+
|
89
|
+
## Author
|
90
|
+
|
91
|
+
This gem was written by Ian Katz (ianfixes@gmail.com) in 2019. It's released under the Apache 2.0 license.
|
92
|
+
|
93
|
+
|
94
|
+
## See Also
|
95
|
+
|
96
|
+
* [Contributing](CONTRIBUTING.md)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'keepachangelog_manager'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
# Use some basic parsing to allow command-line overrides of config
|
6
|
+
class Parser
|
7
|
+
def self.parse(options)
|
8
|
+
parsed_config = {}
|
9
|
+
|
10
|
+
opt_parser = OptionParser.new do |opts|
|
11
|
+
opts.banner = "Usage: #{File.basename(__FILE__)} <ONLY ONE of the following options>"
|
12
|
+
|
13
|
+
opts.on("--major=VERSION", "Upgrade major version to VERSION") do |p|
|
14
|
+
parsed_config[:abs_major] = p
|
15
|
+
end
|
16
|
+
|
17
|
+
opts.on("--minor=VERSION", "Upgrade minor version to VERSION") do |p|
|
18
|
+
parsed_config[:abs_minor] = p
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on("--patch=VERSION", "Upgrade patch version to VERSION") do |p|
|
22
|
+
parsed_config[:abs_patch] = p
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("--increment-major", "Increment major version") do |v|
|
26
|
+
parsed_config[:inc_major] = v
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("--increment-minor", "Increment minor version") do |v|
|
30
|
+
parsed_config[:inc_minor] = v
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("--increment-patch", "Increment patch version") do |v|
|
34
|
+
parsed_config[:inc_patch] = v
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("-h", "--help", "Prints this help") do
|
38
|
+
puts opts
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
opt_parser.parse!(options)
|
44
|
+
parsed_config
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Read in command line options and make them read-only
|
49
|
+
@cli_options = (Parser.parse ARGV).freeze
|
50
|
+
Parser.parse %w[--help] if @cli_options.empty?
|
51
|
+
|
52
|
+
repo = KeepAChangelogManager::Repo.new(`git rev-parse --show-toplevel`.strip)
|
53
|
+
new_version = repo.changelog.update(@cli_options)
|
54
|
+
puts new_version
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require "keepachangelog_manager/version"
|
2
|
+
require "keepachangelog_manager/repo"
|
3
|
+
require "keepachangelog_manager/exceptions"
|
4
|
+
|
5
|
+
# KeepAChangelogManager updates the sections of CHANGELOG.md according to keepachangelog.org rules
|
6
|
+
# @author Ian Katz <ianfixes@gmail.com>
|
7
|
+
module KeepAChangelogManager
|
8
|
+
|
9
|
+
end
|
@@ -0,0 +1,352 @@
|
|
1
|
+
require 'semver'
|
2
|
+
require 'git/remote/parser'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
UNRELEASED = "Unreleased".freeze
|
6
|
+
DEFAULT_VERSION = "0.0.0".freeze
|
7
|
+
SECTION_ORDER = [:added, :changed, :deprecated, :removed, :fixed, :security].freeze
|
8
|
+
SECTION_NAME = {
|
9
|
+
added: "Added",
|
10
|
+
changed: "Changed",
|
11
|
+
deprecated: "Deprecated",
|
12
|
+
removed: "Removed",
|
13
|
+
fixed: "Fixed",
|
14
|
+
security: "Security",
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
module KeepAChangelogManager
|
18
|
+
|
19
|
+
# Handles all things related to a CHANGELOG.md
|
20
|
+
class Changelog
|
21
|
+
|
22
|
+
# Data Structure of a Changelog document:
|
23
|
+
# * Header (array of lines -- just the text, the title is predefined)
|
24
|
+
# * Release hash: key = semver (or :unreleased)
|
25
|
+
# * Date string (optional)
|
26
|
+
# * Section hash (order = )
|
27
|
+
# * array of lines
|
28
|
+
class ChangeData
|
29
|
+
# @return Array<String>
|
30
|
+
attr_accessor :header
|
31
|
+
|
32
|
+
# @return Hash
|
33
|
+
attr_accessor :releases
|
34
|
+
|
35
|
+
def initialize(header, releases)
|
36
|
+
@header = header
|
37
|
+
@releases = releases
|
38
|
+
end
|
39
|
+
|
40
|
+
# validate an attempted change to a version -- make sure no versions decrease
|
41
|
+
#
|
42
|
+
# @param version SemVer
|
43
|
+
# @param dimension Symbol
|
44
|
+
# @param newval Int
|
45
|
+
def validate(version, dimension, newval)
|
46
|
+
oldval = version.send(dimension)
|
47
|
+
raise ArgumentError, "Tried to set #{dimension} to #{newval}, which isn't greater than #{oldval}" unless newval > oldval
|
48
|
+
end
|
49
|
+
|
50
|
+
# Default "unreleased" structure
|
51
|
+
#
|
52
|
+
# @return Hash
|
53
|
+
def self.bare_unreleased_data
|
54
|
+
{
|
55
|
+
sections: Hash[SECTION_ORDER.map { |i| [i, []] }]
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
# content of a fresh (empty) changelog
|
60
|
+
#
|
61
|
+
# @return String
|
62
|
+
def self.bare
|
63
|
+
header_lines = [
|
64
|
+
"All notable changes to this project will be documented in this file.",
|
65
|
+
"",
|
66
|
+
"The format is based on [Keep a Changelog](http://keepachangelog.com/)",
|
67
|
+
"and this project adheres to [Semantic Versioning](http://semver.org/).",
|
68
|
+
]
|
69
|
+
ChangeData.new(header_lines, UNRELEASED => self.bare_unreleased_data)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Update a changelog by transforming Unreleased into a release
|
73
|
+
#
|
74
|
+
# Only one argument at a time should be supplied, all others nil
|
75
|
+
#
|
76
|
+
# @param inc_patch boolean whether to increment the patch version
|
77
|
+
# @param abs_patch int an absolute value for the patch version
|
78
|
+
# @param inc_minor boolean whether to increment the minor version
|
79
|
+
# @param abs_minor int an absolute value for the minor version
|
80
|
+
# @param inc_major boolean whether to increment the major version
|
81
|
+
# @param abs_major int an absolute value for the major version
|
82
|
+
# @return String the new version
|
83
|
+
def update(inc_patch: nil, abs_patch: nil,
|
84
|
+
inc_minor: nil, abs_minor: nil,
|
85
|
+
inc_major: nil, abs_major: nil)
|
86
|
+
num_args_supplied = binding.local_variables.count { |p| !binding.local_variable_get(p).nil? }
|
87
|
+
raise ArgumentError, "Only one update option should be specified" unless 1 == num_args_supplied
|
88
|
+
|
89
|
+
version = @releases.keys.reject { |k| k == UNRELEASED }.map { |k| SemVer.parse(k) }.max
|
90
|
+
version = SemVer.parse("0.0.0") if version.nil?
|
91
|
+
|
92
|
+
if !inc_patch.nil?
|
93
|
+
version.patch += 1
|
94
|
+
elsif !abs_patch.nil?
|
95
|
+
validate(version, :patch, abs_patch)
|
96
|
+
version.patch = abs_patch
|
97
|
+
elsif !inc_minor.nil?
|
98
|
+
version.minor += 1
|
99
|
+
version.patch = 0
|
100
|
+
elsif !abs_minor.nil?
|
101
|
+
validate(version, :minor, abs_minor)
|
102
|
+
version.minor = abs_minor
|
103
|
+
version.patch = 0
|
104
|
+
elsif !inc_major.nil?
|
105
|
+
version.major += 1
|
106
|
+
version.minor = 0
|
107
|
+
version.patch = 0
|
108
|
+
elsif !abs_major.nil?
|
109
|
+
validate(version, :major, abs_major)
|
110
|
+
version.major = abs_major
|
111
|
+
version.minor = 0
|
112
|
+
version.patch = 0
|
113
|
+
end
|
114
|
+
|
115
|
+
new_version = version.format("%M.%m.%p")
|
116
|
+
@releases[new_version] = @releases[UNRELEASED]
|
117
|
+
@releases[new_version][:date] = Date.today.strftime("%Y-%m-%d")
|
118
|
+
@releases[UNRELEASED] = self.class.bare_unreleased_data
|
119
|
+
|
120
|
+
new_version
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# @return KeepAChangelog::Repo the repository
|
125
|
+
attr_accessor :repo
|
126
|
+
|
127
|
+
# @param path String path to CHANGELOG.md (or where it should be)
|
128
|
+
def initialize(repo)
|
129
|
+
@repo = repo
|
130
|
+
end
|
131
|
+
|
132
|
+
# Removes empty entries from the end of an array
|
133
|
+
#
|
134
|
+
# Works very much like string chomp
|
135
|
+
#
|
136
|
+
# @param lines Array<String>
|
137
|
+
# @return Array<String>
|
138
|
+
def array_chomp(lines)
|
139
|
+
return [] if lines.empty?
|
140
|
+
return [] if lines.all?(&:empty?)
|
141
|
+
|
142
|
+
last_entry = lines.rindex { |l| !l.strip.empty? }
|
143
|
+
lines[0..last_entry]
|
144
|
+
end
|
145
|
+
|
146
|
+
# whether the changelog exists in its supposed path
|
147
|
+
#
|
148
|
+
# @return boolean
|
149
|
+
def exist?
|
150
|
+
File.exist? @repo.changelog_path
|
151
|
+
end
|
152
|
+
|
153
|
+
# Create an empty CHANGELOG.md for this repo
|
154
|
+
#
|
155
|
+
# keepachangelog.com CHANGELOG.md syntax assumes git _and_ github.com
|
156
|
+
# so use that to our advantage: assume git repo exists.
|
157
|
+
#
|
158
|
+
# @return String
|
159
|
+
def create(force = false)
|
160
|
+
return if File.exist(@repo.changelog_path) && !force
|
161
|
+
|
162
|
+
File.open(@repo.changelog_path, 'w') { |file| file.write(bare_changelog) }
|
163
|
+
end
|
164
|
+
|
165
|
+
# Update a changelog in-place by transforming Unreleased into a release
|
166
|
+
#
|
167
|
+
# @see ChangeData.update
|
168
|
+
# @return String the new version
|
169
|
+
def update(**kwargs)
|
170
|
+
content = File.open(@repo.changelog_path, "r").read
|
171
|
+
data = parse(content)
|
172
|
+
new_version = data.update(**kwargs)
|
173
|
+
File.open(@repo.changelog_path, 'w') { |file| file.write(render(data)) }
|
174
|
+
new_version
|
175
|
+
end
|
176
|
+
|
177
|
+
# content of a fresh (empty) changelog
|
178
|
+
#
|
179
|
+
# @return String
|
180
|
+
def bare
|
181
|
+
render(ChangeData.bare)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Sort section versions into a reverse-chronological array, unreleased first
|
185
|
+
#
|
186
|
+
# @param sections Hash the input
|
187
|
+
# @return Array<String> the sorted version strings
|
188
|
+
def version_order(releases)
|
189
|
+
# order sections in reverse chronological, unreleased on top
|
190
|
+
releases.keys.sort do |a, b|
|
191
|
+
next 0 if a == b
|
192
|
+
next -1 if a == UNRELEASED
|
193
|
+
next 1 if b == UNRELEASED
|
194
|
+
|
195
|
+
SemVer.parse(b) <=> SemVer.parse(a)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Render the data structure to text
|
200
|
+
#
|
201
|
+
# @param data ChangeData
|
202
|
+
# @return String
|
203
|
+
def render(data)
|
204
|
+
render_lines(data).join("\n") + "\n"
|
205
|
+
end
|
206
|
+
|
207
|
+
# Render the data structure to an array of strings
|
208
|
+
#
|
209
|
+
# Output Structure of a Changelog document:
|
210
|
+
# * Header ("Change Log" and the text under it, up to the first '## ')
|
211
|
+
# * Release versions, reverse chronological, starting with 'unreleased'
|
212
|
+
# * Version (or unreleased) as a link to the diff
|
213
|
+
# * Date (optional)
|
214
|
+
# * Sections
|
215
|
+
# * Added
|
216
|
+
# * Changed
|
217
|
+
# * Deprecated
|
218
|
+
# * Removed
|
219
|
+
# * Fixed
|
220
|
+
# * Security
|
221
|
+
# * Diff URLs
|
222
|
+
#
|
223
|
+
# Assumes the "Unreleased" section is fleshed out, even if blank
|
224
|
+
#
|
225
|
+
# @param data ChangeData
|
226
|
+
# @return Array<String>
|
227
|
+
def render_lines(data)
|
228
|
+
# header
|
229
|
+
out_lines = ["# Change Log"] + data.header
|
230
|
+
out_lines << ""
|
231
|
+
out_lines << ""
|
232
|
+
|
233
|
+
# releases
|
234
|
+
versions = version_order(data.releases)
|
235
|
+
versions.each do |v|
|
236
|
+
release = data.releases[v]
|
237
|
+
out_lines << "## [#{v}]" + (v == UNRELEASED || release[:date].nil? ? "" : " - #{release[:date]}")
|
238
|
+
|
239
|
+
SECTION_ORDER.each do |s|
|
240
|
+
next unless release[:sections].key? s
|
241
|
+
next if release[:sections][s].empty? && v != UNRELEASED
|
242
|
+
|
243
|
+
section = release[:sections][s]
|
244
|
+
out_lines << "### #{SECTION_NAME[s]}"
|
245
|
+
out_lines += section
|
246
|
+
out_lines << ""
|
247
|
+
end
|
248
|
+
out_lines << ""
|
249
|
+
end
|
250
|
+
|
251
|
+
# links. unreleased will come first and may be the only one
|
252
|
+
versions.each_with_index do |v, i|
|
253
|
+
next_index = i + 1
|
254
|
+
this_version = v == UNRELEASED ? "HEAD" : "v#{v}"
|
255
|
+
prev_version = next_index < versions.length ? versions[next_index] : DEFAULT_VERSION
|
256
|
+
out_lines << "[#{v}]: https://github.com/#{@repo.owner}/#{@repo.name}/compare/v#{prev_version}...#{this_version}"
|
257
|
+
end
|
258
|
+
|
259
|
+
out_lines
|
260
|
+
end
|
261
|
+
|
262
|
+
# Parse an existing changelog (by path)
|
263
|
+
#
|
264
|
+
# @param changelog_text String path
|
265
|
+
# @return ChangeData
|
266
|
+
def parse_file(path)
|
267
|
+
parse(File.open(path, "r").read)
|
268
|
+
end
|
269
|
+
|
270
|
+
# Parse an existing changelog (delivered as a string)
|
271
|
+
#
|
272
|
+
# @param changelog_text String input
|
273
|
+
# @return ChangeData
|
274
|
+
def parse(changelog_text)
|
275
|
+
# allowable transitions
|
276
|
+
next_states = {
|
277
|
+
initial: [:header],
|
278
|
+
header: [:header, :release],
|
279
|
+
release: [:section],
|
280
|
+
section: [:section, :section_body, :release, :links],
|
281
|
+
section_body: [:section_body, :release, :links]
|
282
|
+
}
|
283
|
+
state = :initial
|
284
|
+
|
285
|
+
# signals for transitions, plus capture groups for params
|
286
|
+
transitions = {
|
287
|
+
header: /^# Change/,
|
288
|
+
release: /^## \[([^\]]+)\]( - (.*))?/,
|
289
|
+
section: /^### ([A-Z][a-z]+)/,
|
290
|
+
links: /^\[Unreleased\]: https:\/\/github\.com\//,
|
291
|
+
}
|
292
|
+
|
293
|
+
# parser state data
|
294
|
+
header_lines = []
|
295
|
+
releases = {}
|
296
|
+
last_version = nil
|
297
|
+
last_section = nil
|
298
|
+
|
299
|
+
changelog_text.lines.each do |l|
|
300
|
+
# find the regex that matches, no transition if no match
|
301
|
+
want_state, regex = transitions.find(proc { [nil, nil] }) { |_s, re| re.match(l) }
|
302
|
+
good_transition = want_state.nil? || next_states[state].include?(want_state)
|
303
|
+
raise ChangelogParseFail, "Changing to #{want_state} from #{state}" unless good_transition
|
304
|
+
|
305
|
+
want_param = regex.match(l) unless regex.nil?
|
306
|
+
|
307
|
+
# do any pre-transition bookkeeping
|
308
|
+
case want_state
|
309
|
+
when :initial
|
310
|
+
raise ChangelogParseFail, "Tried to transition back to initial state"
|
311
|
+
when :header
|
312
|
+
# nothing to do
|
313
|
+
when :release
|
314
|
+
version = want_param[1]
|
315
|
+
date = want_param[3]
|
316
|
+
releases[version] = { sections: {}, date: date }
|
317
|
+
last_version = version
|
318
|
+
last_section = nil
|
319
|
+
when :section
|
320
|
+
section_name = want_param[1]
|
321
|
+
section = SECTION_NAME.key(section_name)
|
322
|
+
raise ChangelogParseFail, "Unknown section name: '#{section_name}'" if section.nil?
|
323
|
+
|
324
|
+
releases[last_version][:sections][section] = []
|
325
|
+
last_section = section
|
326
|
+
when :links
|
327
|
+
break
|
328
|
+
else
|
329
|
+
# line is just a normal line. decide where we are and start appending
|
330
|
+
|
331
|
+
case state
|
332
|
+
when :header
|
333
|
+
header_lines << l.chomp
|
334
|
+
when :section
|
335
|
+
releases[last_version][:sections][last_section] << l.chomp
|
336
|
+
end
|
337
|
+
end
|
338
|
+
state = want_state unless want_state.nil?
|
339
|
+
end
|
340
|
+
|
341
|
+
releases.each do |version, release|
|
342
|
+
release[:sections].each do |section, lines|
|
343
|
+
releases[version][:sections][section] = array_chomp(lines)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
ChangeData.new(array_chomp(header_lines), releases)
|
348
|
+
end
|
349
|
+
|
350
|
+
end # class
|
351
|
+
|
352
|
+
end # module
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module KeepAChangelogManager
|
2
|
+
# If the code in question is not actually a git repository
|
3
|
+
class NoGitRepo < RuntimeError; end
|
4
|
+
|
5
|
+
# If the remote doesn't seem sane
|
6
|
+
class BadGitRepoUrl < RuntimeError; end
|
7
|
+
|
8
|
+
# If an existing changelog doesn't seem sane
|
9
|
+
class ChangelogParseFail < RuntimeError; end
|
10
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "keepachangelog_manager/exceptions"
|
2
|
+
require 'keepachangelog_manager/changelog'
|
3
|
+
|
4
|
+
module KeepAChangelogManager
|
5
|
+
|
6
|
+
# Handles all things related to a repository and its filesystem
|
7
|
+
class Repo
|
8
|
+
|
9
|
+
# @return String the repository root
|
10
|
+
attr_accessor :root
|
11
|
+
|
12
|
+
# Create a new Repo representing git repository, given its path
|
13
|
+
#
|
14
|
+
# @param root String path to root directory of repository
|
15
|
+
def initialize(root)
|
16
|
+
@root = root
|
17
|
+
end
|
18
|
+
|
19
|
+
# Get the repository name. It is assumed to be the name of the repo root directory
|
20
|
+
#
|
21
|
+
# keepachangelog.com CHANGELOG.md syntax assumes git _and_ github.com
|
22
|
+
# so use that to our advantage: assume git repo exists.
|
23
|
+
#
|
24
|
+
# @return String
|
25
|
+
def name
|
26
|
+
File.basename(@root)
|
27
|
+
end
|
28
|
+
|
29
|
+
# The git remote origin url
|
30
|
+
#
|
31
|
+
# @return String
|
32
|
+
def origin_url
|
33
|
+
`git remote get-url origin`
|
34
|
+
end
|
35
|
+
|
36
|
+
# Extract the owner from a git URL
|
37
|
+
#
|
38
|
+
# @param url String the URL (git:// or https://)
|
39
|
+
# @return String
|
40
|
+
def _owner_from_git_url(url)
|
41
|
+
parsed = Git::Remote::Parser.new.parse(url)
|
42
|
+
raise BadGitRepoUrl, "Could not parse '#{url}' as a git url" if parsed.nil?
|
43
|
+
|
44
|
+
parsed.owner
|
45
|
+
end
|
46
|
+
|
47
|
+
# Get the repo owner
|
48
|
+
#
|
49
|
+
# Assumes an "origin" url!
|
50
|
+
#
|
51
|
+
# @return String
|
52
|
+
def owner
|
53
|
+
Dir.chdir(@root) do
|
54
|
+
url = origin_url
|
55
|
+
raise NoGitRepo, "Could not find a git repo in '#{@root}'" if url.empty?
|
56
|
+
|
57
|
+
_owner_from_git_url(url)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# the path to the CHANGELOG.md file
|
62
|
+
#
|
63
|
+
# @return String
|
64
|
+
def changelog_path
|
65
|
+
File.join(@root, "CHANGELOG.md")
|
66
|
+
end
|
67
|
+
|
68
|
+
# A changelog object
|
69
|
+
#
|
70
|
+
# @return KeepAChangelog::Changelog
|
71
|
+
def changelog
|
72
|
+
Changelog.new(self)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: keepachangelog_manager
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ian Katz
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-01-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: git-remote-parser
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: semver2
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.15'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.15'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: fakefs
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.18.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.18.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.59.2
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.59.2
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.9.11
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.9.11
|
111
|
+
description: ''
|
112
|
+
email:
|
113
|
+
- ianfixes@gmail.com
|
114
|
+
executables:
|
115
|
+
- keepachangelog_manager.rb
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".yardopts"
|
120
|
+
- README.md
|
121
|
+
- bin/keepachangelog_manager.rb
|
122
|
+
- lib/keepachangelog_manager.rb
|
123
|
+
- lib/keepachangelog_manager/changelog.rb
|
124
|
+
- lib/keepachangelog_manager/exceptions.rb
|
125
|
+
- lib/keepachangelog_manager/repo.rb
|
126
|
+
- lib/keepachangelog_manager/version.rb
|
127
|
+
homepage: http://github.com/ianfixes/keepachangelog_manager_gem
|
128
|
+
licenses:
|
129
|
+
- Apache-2.0
|
130
|
+
metadata: {}
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
requirements: []
|
146
|
+
rubyforge_project:
|
147
|
+
rubygems_version: 2.5.2.3
|
148
|
+
signing_key:
|
149
|
+
specification_version: 4
|
150
|
+
summary: CHANGELOG.md (keepachangelog.com style) section updater for automated releasing
|
151
|
+
test_files: []
|