semverify 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +26 -0
- data/.yardopts +6 -0
- data/CHANGELOG.md +36 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +116 -0
- data/Rakefile +85 -0
- data/exe/semverify +6 -0
- data/lib/semverify/command_line.rb +475 -0
- data/lib/semverify/incrementable_semver.rb +190 -0
- data/lib/semverify/regexp.rb +38 -0
- data/lib/semverify/semver.rb +372 -0
- data/lib/semverify/version.rb +6 -0
- data/lib/semverify/version_file.rb +101 -0
- data/lib/semverify/version_file_factory.rb +38 -0
- data/lib/semverify/version_file_sources/base.rb +54 -0
- data/lib/semverify/version_file_sources/gemspec.rb +37 -0
- data/lib/semverify/version_file_sources/version.rb +34 -0
- data/lib/semverify/version_file_sources/version_rb.rb +37 -0
- data/lib/semverify/version_file_sources.rb +11 -0
- data/lib/semverify.rb +18 -0
- data/semverify.gemspec +51 -0
- data/sig/semversion.rbs +4 -0
- metadata +235 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Semverify
|
4
|
+
# Match a semver within a string
|
5
|
+
SEMVER_REGEXP = /
|
6
|
+
(?<semver>
|
7
|
+
(?<major>0|[1-9]\d*)
|
8
|
+
\.
|
9
|
+
(?<minor>0|[1-9]\d*)
|
10
|
+
\.
|
11
|
+
(?<patch>0|[1-9]\d*)
|
12
|
+
(?:-
|
13
|
+
(?<pre_release>
|
14
|
+
(?:
|
15
|
+
0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*
|
16
|
+
)
|
17
|
+
(?:
|
18
|
+
\.
|
19
|
+
(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
|
20
|
+
)*
|
21
|
+
)
|
22
|
+
)?
|
23
|
+
(?:
|
24
|
+
\+
|
25
|
+
(?<build_metadata>
|
26
|
+
[0-9a-zA-Z-]+
|
27
|
+
(?:
|
28
|
+
\.
|
29
|
+
[0-9a-zA-Z-]+
|
30
|
+
)*
|
31
|
+
)
|
32
|
+
)?
|
33
|
+
)
|
34
|
+
/x
|
35
|
+
|
36
|
+
# Match a semver to the full string
|
37
|
+
SEMVER_REGEXP_FULL = /\A#{SEMVER_REGEXP.source}\z/x
|
38
|
+
end
|
@@ -0,0 +1,372 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'semverify/regexp'
|
4
|
+
|
5
|
+
module Semverify
|
6
|
+
# Parse and compare semver version strings
|
7
|
+
#
|
8
|
+
# This class will parse a semver version string that complies to Semantic
|
9
|
+
# Versioning 2.0.0.
|
10
|
+
#
|
11
|
+
# Two Semver objects can be compared using the spaceship operator (<=>)
|
12
|
+
# according to the rules of Semantic Versioning 2.0.0.
|
13
|
+
#
|
14
|
+
# @example Basic version parsing
|
15
|
+
# semver = Semverify::Semver.new('1.2.3')
|
16
|
+
# semver.major # => '1'
|
17
|
+
# semver.minor # => '2'
|
18
|
+
# semver.patch # => '3'
|
19
|
+
#
|
20
|
+
# @example Parsing a version with a pre-release identifier
|
21
|
+
# semver = Semverify::Semver.new('1.2.3-alpha.1')
|
22
|
+
# semver.pre_release # => 'alpha.1'
|
23
|
+
# semver.pre_release_identifiers # => ['alpha', '1']
|
24
|
+
#
|
25
|
+
# @example A version with build metadata
|
26
|
+
# semver = Semverify::Semver.new('1.2.3+build.1')
|
27
|
+
# semver.build_metadata # => 'build.1'
|
28
|
+
#
|
29
|
+
# @example Comparing versions
|
30
|
+
# semver1 = Semverify::Semver.new('1.2.3')
|
31
|
+
# semver2 = Semverify::Semver.new('1.2.4')
|
32
|
+
# semver1 <=> semver2 # => true
|
33
|
+
#
|
34
|
+
# See the Semantic Versioning 2.0.0 specification for more details.
|
35
|
+
#
|
36
|
+
# @see https://semver.org/spec/v2.0.0.html Semantic Versioning 2.0.0
|
37
|
+
#
|
38
|
+
# @api public
|
39
|
+
#
|
40
|
+
class Semver
|
41
|
+
include Comparable
|
42
|
+
|
43
|
+
# Create a new Semver object
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# version = Semverify::Semver.new('1.2.3-alpha.1')
|
47
|
+
#
|
48
|
+
# @param version [String] The version string to parse
|
49
|
+
#
|
50
|
+
# @raise [Semverify::Error] version is not a string or not a valid semver version
|
51
|
+
#
|
52
|
+
def initialize(version)
|
53
|
+
assert_version_must_be_a_string(version)
|
54
|
+
@version = version
|
55
|
+
parse
|
56
|
+
assert_valid_version
|
57
|
+
end
|
58
|
+
|
59
|
+
# @!attribute version [r]
|
60
|
+
#
|
61
|
+
# The complete version string
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
# semver = Semverify::Semver.new('1.2.3-alpha.1+build.001')
|
65
|
+
# semver.version #=> '1.2.3-alpha.1+build.001'
|
66
|
+
#
|
67
|
+
# @return [String]
|
68
|
+
#
|
69
|
+
# @api public
|
70
|
+
#
|
71
|
+
attr_reader :version
|
72
|
+
|
73
|
+
# @attribute major [r]
|
74
|
+
#
|
75
|
+
# The major part of the version
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# semver = Semverify::Semver.new('1.2.3-alpha.1+build.001')
|
79
|
+
# semver.major #=> '1'
|
80
|
+
#
|
81
|
+
# @return [String]
|
82
|
+
#
|
83
|
+
# @api public
|
84
|
+
#
|
85
|
+
attr_reader :major
|
86
|
+
|
87
|
+
# @attribute minor [r]
|
88
|
+
#
|
89
|
+
# The minor part of the version
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# semver = Semverify::Semver.new('1.2.3-alpha.1+build.001')
|
93
|
+
# semver.minor #=> '2'
|
94
|
+
#
|
95
|
+
# @return [String]
|
96
|
+
#
|
97
|
+
# @api public
|
98
|
+
#
|
99
|
+
attr_reader :minor
|
100
|
+
|
101
|
+
# @attribute patch [r]
|
102
|
+
#
|
103
|
+
# The patch part of the version
|
104
|
+
#
|
105
|
+
# @example
|
106
|
+
# semver = Semverify::Semver.new('1.2.3-alpha.1+build.001')
|
107
|
+
# semver.patch #=> '3'
|
108
|
+
#
|
109
|
+
# @return [String]
|
110
|
+
#
|
111
|
+
# @api public
|
112
|
+
#
|
113
|
+
attr_reader :patch
|
114
|
+
|
115
|
+
# @attribute pre_release [r]
|
116
|
+
#
|
117
|
+
# The pre_release part of the version
|
118
|
+
#
|
119
|
+
# Will be an empty string if the version has no pre_release part.
|
120
|
+
#
|
121
|
+
# @example
|
122
|
+
# semver = Semverify::Semver.new('1.2.3-alpha.1+build.001')
|
123
|
+
# semver.pre_release #=> 'alpha.1'
|
124
|
+
#
|
125
|
+
# @example When the version has no pre_release part
|
126
|
+
# semver = Semverify::Semver.new('1.2.3')
|
127
|
+
# semver.pre_release #=> ''
|
128
|
+
#
|
129
|
+
# @return [String]
|
130
|
+
#
|
131
|
+
# @api public
|
132
|
+
#
|
133
|
+
attr_reader :pre_release
|
134
|
+
|
135
|
+
# @attribute pre_release_identifiers [r]
|
136
|
+
#
|
137
|
+
# The pre_release identifiers of the version
|
138
|
+
#
|
139
|
+
# @example
|
140
|
+
# semver = Semverify::Semver.new('1.2.3-alpha.1+build.001')
|
141
|
+
# semver.pre_release_identifiers #=> ['alpha', '1']
|
142
|
+
#
|
143
|
+
# @example When the version has no pre_release part
|
144
|
+
# semver = Semverify::Semver.new('1.2.3')
|
145
|
+
# semver.pre_release_identifiers #=> []
|
146
|
+
#
|
147
|
+
# @return [Array<String>]
|
148
|
+
#
|
149
|
+
# @api public
|
150
|
+
#
|
151
|
+
attr_reader :pre_release_identifiers
|
152
|
+
|
153
|
+
# @attribute build_metadata [r]
|
154
|
+
#
|
155
|
+
# The build_metadata part of the version
|
156
|
+
#
|
157
|
+
# Will be an empty string if the version has no build_metadata part.
|
158
|
+
#
|
159
|
+
# @example
|
160
|
+
# semver = Semverify::Semver.new('1.2.3-alpha.1+build.001')
|
161
|
+
# semver.build_metadata #=> 'build.001'
|
162
|
+
#
|
163
|
+
# @example When the version has no build_metadata part
|
164
|
+
# semver = Semverify::Semver.new('1.2.3')
|
165
|
+
# semver.build_metadata #=> ''
|
166
|
+
#
|
167
|
+
# @return [String]
|
168
|
+
#
|
169
|
+
# @api public
|
170
|
+
#
|
171
|
+
attr_reader :build_metadata
|
172
|
+
|
173
|
+
# Compare two Semver objects
|
174
|
+
#
|
175
|
+
# See the [Precedence Rules](https://semver.org/spec/v2.0.0.html#spec-item-11)
|
176
|
+
# in the Semantic Versioning 2.0.0 Specification for more details.
|
177
|
+
#
|
178
|
+
# @example
|
179
|
+
# semver1 = Semverify::Semver.new('1.2.3')
|
180
|
+
# semver2 = Semverify::Semver.new('1.2.4')
|
181
|
+
# semver1 <=> semver2 # => -1
|
182
|
+
# semver2 <=> semver1 # => 1
|
183
|
+
#
|
184
|
+
# @example A Semver is equal to itself
|
185
|
+
# semver1 = Semverify::Semver.new('1.2.3')
|
186
|
+
# semver1 <=> semver1 # => 0
|
187
|
+
#
|
188
|
+
# @example A pre-release version is always older than a normal version
|
189
|
+
# semver1 = Semverify::Semver.new('1.2.3-alpha.1')
|
190
|
+
# semver2 = Semverify::Semver.new('1.2.3')
|
191
|
+
# semver1 <=> semver2 # => -1
|
192
|
+
#
|
193
|
+
# @example Pre-releases are compared by the parts of the pre-release version
|
194
|
+
# semver1 = Semverify::Semver.new('1.2.3-alpha.1')
|
195
|
+
# semver2 = Semverify::Semver.new('1.2.3-alpha.2')
|
196
|
+
# semver1 <=> semver2 # => -1
|
197
|
+
#
|
198
|
+
# @example Build metadata is ignored when comparing versions
|
199
|
+
# semver1 = Semverify::Semver.new('1.2.3+build.100')
|
200
|
+
# semver2 = Semverify::Semver.new('1.2.3+build.101')
|
201
|
+
# semver1 <=> semver2 # => 0
|
202
|
+
#
|
203
|
+
# @param other [Semver] the other Semver to compare to
|
204
|
+
#
|
205
|
+
# @return [Integer] -1 if self < other, 0 if self == other, or 1 if self > other
|
206
|
+
#
|
207
|
+
# @raise [Semverify::Error] other is not a semver
|
208
|
+
#
|
209
|
+
def <=>(other)
|
210
|
+
assert_other_is_a_semver(other)
|
211
|
+
|
212
|
+
result = compare_core_parts(other)
|
213
|
+
|
214
|
+
return result unless result.zero? && pre_release != other.pre_release
|
215
|
+
return 1 if pre_release.empty?
|
216
|
+
return -1 if other.pre_release.empty?
|
217
|
+
|
218
|
+
compare_pre_release_part(other)
|
219
|
+
end
|
220
|
+
|
221
|
+
# Determine if the version string is a valid semver
|
222
|
+
#
|
223
|
+
# Override this method in a subclass to provide extra or custom validation.
|
224
|
+
#
|
225
|
+
# @example
|
226
|
+
# Semverify::Semver.new('1.2.3').valid? # => true
|
227
|
+
# Semverify::Semver.new('1.2.3-alpha.1+build.001').valid? # => true
|
228
|
+
# Semverify::Semver.new('bogus').valid? # => raises Semverify::Error
|
229
|
+
#
|
230
|
+
# @return [Boolean] true if the version string is a valid semver
|
231
|
+
#
|
232
|
+
def valid?
|
233
|
+
# If major is set, then so is everything else
|
234
|
+
!major.nil?
|
235
|
+
end
|
236
|
+
|
237
|
+
# Two versions are equal if their version strings are equal
|
238
|
+
#
|
239
|
+
# @example
|
240
|
+
# Semverify::Semver.new('1.2.3') == '1.2.3' # => true
|
241
|
+
#
|
242
|
+
# @param other [Semver] the other Semver to compare to
|
243
|
+
#
|
244
|
+
# @return [Boolean] true if the version strings are equal
|
245
|
+
#
|
246
|
+
def ==(other)
|
247
|
+
version == other.to_s
|
248
|
+
end
|
249
|
+
|
250
|
+
# The string representation of a Semver is its version string
|
251
|
+
#
|
252
|
+
# @example
|
253
|
+
# Semverify::Semver.new('1.2.3').to_s # => '1.2.3'
|
254
|
+
#
|
255
|
+
# @return [String] the version string
|
256
|
+
#
|
257
|
+
def to_s
|
258
|
+
version
|
259
|
+
end
|
260
|
+
|
261
|
+
private
|
262
|
+
|
263
|
+
# Parse @version into its parts
|
264
|
+
# @return [void]
|
265
|
+
# @api private
|
266
|
+
def parse
|
267
|
+
return unless (match_data = version.match(Semverify::SEMVER_REGEXP_FULL))
|
268
|
+
|
269
|
+
core_parts(match_data)
|
270
|
+
pre_release_part(match_data)
|
271
|
+
build_metadata_part(match_data)
|
272
|
+
end
|
273
|
+
|
274
|
+
# Compare the major, minor, and patch parts of this Semver to other
|
275
|
+
# @param other [Semver] the other Semver to compare to
|
276
|
+
# @return [Integer] -1 if self < other, 0 if self == other, or 1 if self > other
|
277
|
+
# @api private
|
278
|
+
def compare_core_parts(other)
|
279
|
+
identifiers = [major.to_i, minor.to_i, patch.to_i]
|
280
|
+
other_identifiers = [other.major.to_i, other.minor.to_i, other.patch.to_i]
|
281
|
+
|
282
|
+
identifiers <=> other_identifiers
|
283
|
+
end
|
284
|
+
|
285
|
+
# Compare two pre-release identifiers
|
286
|
+
#
|
287
|
+
# Implements the rules for precedence for comparing two pre-release identifiers
|
288
|
+
# from the Semantic Versioning 2.0.0 Specification.
|
289
|
+
#
|
290
|
+
# @param identifier [String, Integer] the identifier to compare
|
291
|
+
# @param other_identifier [String, Integer] the other identifier to compare
|
292
|
+
# @return [Integer] -1, 0, or 1
|
293
|
+
# @api private
|
294
|
+
def compare_identifiers(identifier, other_identifier)
|
295
|
+
return 1 if other_identifier.nil?
|
296
|
+
return -1 if identifier.is_a?(Integer) && other_identifier.is_a?(String)
|
297
|
+
return 1 if other_identifier.is_a?(Integer) && identifier.is_a?(String)
|
298
|
+
|
299
|
+
identifier <=> other_identifier
|
300
|
+
end
|
301
|
+
|
302
|
+
# Compare two pre-release version parts
|
303
|
+
#
|
304
|
+
# Implements the rules for precedence for comparing the pre-release part of
|
305
|
+
# one version with the pre-release part of another version from the Semantic
|
306
|
+
# Versioning 2.0.0 Specification.
|
307
|
+
#
|
308
|
+
# @param other [Semver] the other Semver to compare to
|
309
|
+
# @return [Integer] -1, 0, or 1
|
310
|
+
# @api private
|
311
|
+
def compare_pre_release_part(other)
|
312
|
+
pre_release_identifiers.zip(other.pre_release_identifiers).each do |field, other_field|
|
313
|
+
result = compare_identifiers(field, other_field)
|
314
|
+
return result unless result.zero?
|
315
|
+
end
|
316
|
+
pre_release_identifiers.size < other.pre_release_identifiers.size ? -1 : 0
|
317
|
+
end
|
318
|
+
|
319
|
+
# Raise a error if other is not a valid Semver
|
320
|
+
# @param other [Semver] the other to check
|
321
|
+
# @return [void]
|
322
|
+
# @raise [Semverify::Error] if other is not a valid Semver
|
323
|
+
# @api private
|
324
|
+
def assert_other_is_a_semver(other)
|
325
|
+
raise Semverify::Error, 'other must be a Semver' unless other.is_a?(Semver)
|
326
|
+
end
|
327
|
+
|
328
|
+
# Raise a error if the given version is not a string
|
329
|
+
# @param version [Semver] the version to check
|
330
|
+
# @return [void]
|
331
|
+
# @raise [Semverify::Error] if the given version is not a string
|
332
|
+
# @api private
|
333
|
+
def assert_version_must_be_a_string(version)
|
334
|
+
raise Semverify::Error, 'Version must be a string' unless version.is_a?(String)
|
335
|
+
end
|
336
|
+
|
337
|
+
# Raise a error if this version object is not a valid Semver
|
338
|
+
# @return [void]
|
339
|
+
# @raise [Semverify::Error] if other is not a valid Semver
|
340
|
+
# @api private
|
341
|
+
def assert_valid_version
|
342
|
+
raise Semverify::Error, "Not a valid version string: #{version}" unless valid?
|
343
|
+
end
|
344
|
+
|
345
|
+
# Set the major, minor, and patch parts of this Semver
|
346
|
+
# @param match_data [MatchData] the match data from the version string
|
347
|
+
# @return [void]
|
348
|
+
# @api private
|
349
|
+
def core_parts(match_data)
|
350
|
+
@major = match_data[:major]
|
351
|
+
@minor = match_data[:minor]
|
352
|
+
@patch = match_data[:patch]
|
353
|
+
end
|
354
|
+
|
355
|
+
# Set the pre-release of this Semver
|
356
|
+
# @param match_data [MatchData] the match data from the version string
|
357
|
+
# @return [void]
|
358
|
+
# @api private
|
359
|
+
def pre_release_part(match_data)
|
360
|
+
@pre_release = match_data[:pre_release] || ''
|
361
|
+
@pre_release_identifiers = @pre_release.split('.').map { |f| f =~ /\A\d+\z/ ? f.to_i : f }
|
362
|
+
end
|
363
|
+
|
364
|
+
# Set the build_metadata of this Semver
|
365
|
+
# @param match_data [MatchData] the match data from the version string
|
366
|
+
# @return [void]
|
367
|
+
# @api private
|
368
|
+
def build_metadata_part(match_data)
|
369
|
+
@build_metadata = match_data[:build_metadata] || ''
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Semverify
|
4
|
+
# Represents a file that contains the gem's version and can update the version
|
5
|
+
#
|
6
|
+
# Use VersionFileFactory.find create a VersionFile instance.
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
#
|
10
|
+
class VersionFile
|
11
|
+
# Create an VersionFile instance
|
12
|
+
#
|
13
|
+
# Use VersionFileFactory.find create a VersionFile instance.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# version_file = Semverify::VersionFile.new('VERSION', 'VERSION = "', '1.2.3', '"')
|
17
|
+
#
|
18
|
+
# @param path [String] the path to the file relative to the current directory
|
19
|
+
# @param content_before [String] the content before the version
|
20
|
+
# @param version [String] the version
|
21
|
+
# @param content_after [String] the content after the version
|
22
|
+
#
|
23
|
+
# @raise [Semverify::Error] if the version is not an IncrementableSemver
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
#
|
27
|
+
def initialize(path, content_before, version, content_after)
|
28
|
+
raise Semverify::Error, 'version must be an IncrementableSemver' unless
|
29
|
+
version.is_a?(Semverify::IncrementableSemver)
|
30
|
+
|
31
|
+
@path = path
|
32
|
+
@content_before = content_before
|
33
|
+
@version = version
|
34
|
+
@content_after = content_after
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!attribute [r]
|
38
|
+
#
|
39
|
+
# The path to the file relative to the current directory
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# version_file = Semverify::VersionFile.new('lib/semverify/version.rb', 'VERSION = "', '1.2.3', '"')
|
43
|
+
# version_file.path # => 'lib/semverify/version.rb'
|
44
|
+
# @return [String]
|
45
|
+
# @api public
|
46
|
+
attr_reader :path
|
47
|
+
|
48
|
+
# @!attribute [r]
|
49
|
+
#
|
50
|
+
# The content in the version file before the version
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# version_file = Semverify::VersionFile.new('lib/semverify/version.rb', 'VERSION = "', '1.2.3', '"')
|
54
|
+
# version_file.content_before # => 'VERSION = "'
|
55
|
+
# @return [String]
|
56
|
+
# @api public
|
57
|
+
attr_reader :content_before
|
58
|
+
|
59
|
+
# @!attribute [r]
|
60
|
+
#
|
61
|
+
# The version from the version file
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
# version = Semverify::IncrementableSemver.new('1.2.3')
|
65
|
+
# version_file = Semverify::VersionFile.new('lib/semverify/version.rb', 'VERSION = "', version, '"')
|
66
|
+
# version_file.version.to_s # => '1.2.3'
|
67
|
+
# @return [Semverify::IncrementableSemver]
|
68
|
+
# @raise [Semverify::Error] if the version is not an IncrementableSemver
|
69
|
+
# @api public
|
70
|
+
attr_reader :version
|
71
|
+
|
72
|
+
# @!attribute [r]
|
73
|
+
#
|
74
|
+
# The content in the version file before the version
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# version_file = Semverify::VersionFile.new('lib/semverify/version.rb', 'VERSION = "', '1.2.3', '"')
|
78
|
+
# version_file.content_after # => '"'
|
79
|
+
# @return [String]
|
80
|
+
# @api public
|
81
|
+
attr_reader :content_after
|
82
|
+
|
83
|
+
# Update the version in the version file
|
84
|
+
#
|
85
|
+
# @param new_version [Semverify::IncrementableSemver] the new version
|
86
|
+
# @example
|
87
|
+
# version_file = Semverify::VersionFile.new('lib/semverify/version.rb', 'VERSION = "', '1.2.3', '"')
|
88
|
+
# version_file.version = '1.2.4'
|
89
|
+
# @return [Void]
|
90
|
+
# @raise [Semverify::Error] if new_version is not an IncrementableSemver
|
91
|
+
# @api public
|
92
|
+
#
|
93
|
+
def version=(new_version)
|
94
|
+
raise Semverify::Error, 'new_version must be an IncrementableSemver' unless
|
95
|
+
new_version.is_a?(Semverify::IncrementableSemver)
|
96
|
+
|
97
|
+
@version = version
|
98
|
+
File.write(path, content_before + new_version.to_s + content_after)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'semverify/version_file_sources'
|
4
|
+
|
5
|
+
module Semverify
|
6
|
+
# Finds the file that contains the gem's version and returns a VersionFile instance
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
#
|
10
|
+
class VersionFileFactory
|
11
|
+
# The list of VersionFileSources to check for the version file
|
12
|
+
#
|
13
|
+
# The order of the list is important. The first VersionFileSource that finds a version file will be used.
|
14
|
+
#
|
15
|
+
VERSION_FILE_SOURCES = [
|
16
|
+
Semverify::VersionFileSources::Version,
|
17
|
+
Semverify::VersionFileSources::VersionRb,
|
18
|
+
Semverify::VersionFileSources::Gemspec
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
# Finds the version file for the gem
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# version_file = Semverify::VersionFileFactory.find
|
25
|
+
# version_file.path # => 'lib/semverify/version.rb'
|
26
|
+
# version_file.version # => '1.2.3'
|
27
|
+
#
|
28
|
+
# @return [Semverify::VersionFile, nil] the version file or nil if no version file was found
|
29
|
+
#
|
30
|
+
def self.find
|
31
|
+
VERSION_FILE_SOURCES.each do |version_file_source|
|
32
|
+
version_file = version_file_source.find
|
33
|
+
return version_file if version_file
|
34
|
+
end
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'semverify/version_file'
|
4
|
+
|
5
|
+
module Semverify
|
6
|
+
module VersionFileSources
|
7
|
+
# Base class for a version file source which implements #find
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
#
|
11
|
+
class Base
|
12
|
+
# The first file from #glob whose content matches #content_regexp
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# version_file = Semverify::VersionFileSources::Gemspec.find
|
16
|
+
#
|
17
|
+
# @return [Semverify::VersionFile, nil] the version file or nil if no version file was found
|
18
|
+
#
|
19
|
+
def self.find
|
20
|
+
Dir[glob].filter_map do |path|
|
21
|
+
if (match = File.read(path).match(content_regexp))
|
22
|
+
version = Semverify::IncrementableSemver.new(match[:version])
|
23
|
+
Semverify::VersionFile.new(path, match[:content_before], version, match[:content_after])
|
24
|
+
end
|
25
|
+
end.first
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# The version file regexp
|
31
|
+
#
|
32
|
+
# A regular expression that matches the version file and has three named captures:
|
33
|
+
# - content_before: the content before the version
|
34
|
+
# - version: the version
|
35
|
+
# - content_after: the content after the version
|
36
|
+
#
|
37
|
+
# @return [Regexp]
|
38
|
+
# @api private
|
39
|
+
private_class_method def self.content_regexp
|
40
|
+
raise NotImplementedError, 'You must implement #content_regexp in a subclass'
|
41
|
+
end
|
42
|
+
|
43
|
+
# A glob that matches potential version files
|
44
|
+
#
|
45
|
+
# Files matching this glob will be checked to see if they match #version_file_regexp
|
46
|
+
#
|
47
|
+
# @return [String]
|
48
|
+
# @api private
|
49
|
+
private_class_method def self.glob
|
50
|
+
raise NotImplementedError, 'You must implement #glob in a subclass'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'semverify/version_file_sources/base'
|
4
|
+
|
5
|
+
module Semverify
|
6
|
+
module VersionFileSources
|
7
|
+
# Checks for the gem's version in a file named *.gemspec
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
#
|
11
|
+
class Gemspec < Base
|
12
|
+
# The regexp to find the version and surrounding content within the gemspec
|
13
|
+
VERSION_REGEXP = /
|
14
|
+
\A
|
15
|
+
(?<content_before>
|
16
|
+
.*
|
17
|
+
\.version\s*=\s*(?<quote>['"])
|
18
|
+
)
|
19
|
+
(?<version>#{Semverify::SEMVER_REGEXP.source})
|
20
|
+
(?<content_after>\k<quote>.*)
|
21
|
+
\z
|
22
|
+
/xm
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# The version file regexp
|
27
|
+
# @return [Regexp]
|
28
|
+
# @api private
|
29
|
+
private_class_method def self.content_regexp = VERSION_REGEXP
|
30
|
+
|
31
|
+
# A glob that matches potential version files
|
32
|
+
# @return [String]
|
33
|
+
# @api private
|
34
|
+
private_class_method def self.glob = '*.gemspec'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'semverify/version_file_sources/base'
|
4
|
+
|
5
|
+
module Semverify
|
6
|
+
module VersionFileSources
|
7
|
+
# Checks for the gem's version in a file named VERSION
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
#
|
11
|
+
class Version < Base
|
12
|
+
# The regexp to find the version and surrounding content within the version file
|
13
|
+
VERSION_REGEXP = /
|
14
|
+
\A
|
15
|
+
(?<content_before>\s*)
|
16
|
+
(?<version>#{Semverify::SEMVER_REGEXP.source})
|
17
|
+
(?<content_after>\s*)
|
18
|
+
\z
|
19
|
+
/x
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# The version file regexp
|
24
|
+
# @return [Regexp]
|
25
|
+
# @api private
|
26
|
+
private_class_method def self.content_regexp = VERSION_REGEXP
|
27
|
+
|
28
|
+
# A glob that matches potential version files
|
29
|
+
# @return [String]
|
30
|
+
# @api private
|
31
|
+
private_class_method def self.glob = 'VERSION'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|