semverify 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,475 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Semverify
6
+ # rubocop:disable Metrics/ClassLength
7
+
8
+ # The semverify cli
9
+ #
10
+ # @example
11
+ # require 'semverify'
12
+ #
13
+ # Semverify::CommandLine.start(ARGV)
14
+ #
15
+ # @api private
16
+ #
17
+ class CommandLine < Thor
18
+ # Tell Thor to exit with a non-zero status code if a command fails
19
+ def self.exit_on_failure? = true
20
+
21
+ desc 'current [-q]', 'Show the current gem version'
22
+
23
+ long_desc <<-LONG_DESC
24
+ Output the current gem version from the file that stores the gem version.
25
+
26
+ The command fails if the gem version could not be found or is invalid.
27
+
28
+ Use `--quiet` to ensure that a gem version could be found and is valid without producing any output.
29
+ LONG_DESC
30
+
31
+ option :quiet, type: :boolean, aliases: '-q', desc: 'Do not print the current version to stdout'
32
+
33
+ # Show the current gem version
34
+ # @return [void]
35
+ def current
36
+ version_file = Semverify::VersionFileFactory.find
37
+
38
+ if version_file.nil?
39
+ warn 'version file not found or is not valid' unless options[:quiet]
40
+ exit 1
41
+ end
42
+
43
+ puts version_file.version unless options[:quiet]
44
+ end
45
+
46
+ desc 'file [-q]', 'Show the path to the file containing the gem version'
47
+
48
+ long_desc <<-LONG_DESC
49
+ Output the relative path to the file that stores the gem version.
50
+
51
+ The command fails if the gem version could not be found.
52
+
53
+ Use `--quiet` to ensure that a gem version could be found without producing any output.
54
+ LONG_DESC
55
+
56
+ option :quiet, type: :boolean, aliases: '-q', desc: 'Do not print the version file path to stdout'
57
+
58
+ # Show the gem's version file
59
+ # @return [void]
60
+ def file
61
+ version_file = Semverify::VersionFileFactory.find
62
+
63
+ if version_file.nil?
64
+ warn 'version file not found or is not valid' unless options[:quiet]
65
+ exit 1
66
+ end
67
+
68
+ puts version_file.path unless options[:quiet]
69
+ end
70
+
71
+ desc 'next-major [VERSION] [-p [-t TYPE]] [-b BUILD] [-n] [-q]', "Increment the version's major part"
72
+
73
+ long_desc <<-LONG_DESC
74
+ Increase the current gem version to the next major version, update the
75
+ file that stores the version, and output the resulting version to stdout.
76
+
77
+ Increments 1.x.y to 2.0.0.
78
+
79
+ The command fails if the current gem version file could not be found or
80
+ if the version is not valid.
81
+
82
+ If VERSION is given, use that instead of the current gem version. Giving
83
+ VERSION implies --dry-run. The command fails if VERSION is not valid.
84
+
85
+ --pre can be used to specify that the version should be incremented AND
86
+ given a pre-release part. For instance:
87
+
88
+ /x5 $ semverify next-major --pre
89
+
90
+ increments '1.2.3' to '2.0.0-pre.1'.
91
+
92
+ By default, the pre-release type is 'pre'. --pre-type=TYPE can be used with
93
+ --pre to specify a different pre-release type such as alpha, beta, rc, etc.
94
+ For instance:
95
+
96
+ /x5 $ semverify next-major --pre --pre-type=alpha
97
+
98
+ increments '1.2.3' to '2.0.0-alpha.1'.
99
+
100
+ The command fails if the existing pre-release type is not lexically less than or
101
+ equal to TYPE. For example, it the current version is '1.2.3-beta.1' and TYPE
102
+ is 'alpha', the the command will fail since the new version would sort before the
103
+ existing version ('beta' is not less than or equal to 'alpha').
104
+
105
+ Use --build=BUILD to set the build metadata for the new version (See
106
+ [Build Metadata in the Semantic Versioning Specification](https://semver.org/spec/v2.0.0.html#spec-item-10)).
107
+ If --build is not given, the incremented version retain the previous build
108
+ metadata. Pass an empty string to remove the build metadata (--build="")
109
+
110
+ Use --dry-run to run this command without updating the version file.
111
+
112
+ Use --quiet to increment the version without producing any output.
113
+ LONG_DESC
114
+
115
+ option :pre, type: :boolean, aliases: '-p', desc: 'Create a pre-release version'
116
+ option :'pre-type', type: :string, aliases: '-t', default: 'pre', banner: 'TYPE',
117
+ desc: 'The type of pre-release version (alpha, beta, etc.)'
118
+ option :build, type: :string, aliases: '-b', desc: 'The build metadata to add to the version'
119
+ option :'dry-run', type: :boolean, aliases: '-n', desc: 'Do not write the new version to the version file'
120
+ option :quiet, type: :boolean, aliases: '-q', desc: 'Do not print the new version to stdout'
121
+
122
+ # Increment the gem version's major part
123
+ # @param version [String, nil] The version to increment or nil to use the version
124
+ # from the gem's version file
125
+ # @return [void]
126
+ def next_major(version = nil)
127
+ increment_core_version(:next_major, version)
128
+ end
129
+
130
+ desc 'next-minor [VERSION] [-p [-t TYPE]] [-b BUILD] [-n] [-q]', "Increment the version's minor part"
131
+
132
+ long_desc <<-LONG_DESC
133
+ Increase the current gem version to the next minor version, update the
134
+ file that stores the version, and output the resulting version to stdout.
135
+
136
+ Increments 1.2.y to 1.3.0.
137
+
138
+ The command fails if the current gem version file could not be found or
139
+ if the version is not valid.
140
+
141
+ If VERSION is given, use that instead of the current gem version. Giving
142
+ VERSION implies --dry-run. The command fails if VERSION is not valid.
143
+
144
+ --pre can be used to specify that the version should be incremented AND
145
+ given a pre-release part. For instance:
146
+
147
+ /x5 $ semverify next-minor --pre
148
+
149
+ increments '1.2.3' to '2.0.0-pre.1'.
150
+
151
+ By default, the pre-release type is 'pre'. --pre-type=TYPE can be used with
152
+ --pre to specify a different pre-release type such as alpha, beta, rc, etc.
153
+ For instance:
154
+
155
+ /x5 $ semverify next-patch --pre --pre-type=alpha
156
+
157
+ increments '1.2.3' to '1.2.4-alpha.1'.
158
+
159
+ The command fails if the existing pre-release type is not lexically less than or
160
+ equal to TYPE. For example, it the current version is '1.2.3-beta.1' and TYPE
161
+ is 'alpha', the the command will fail since the new version would sort before the
162
+ existing version ('beta' is not less than or equal to 'alpha').
163
+
164
+ Use --build=BUILD to set the build metadata for the new version (See
165
+ [Build Metadata in the Semantic Versioning Specification](https://semver.org/spec/v2.0.0.html#spec-item-10)).
166
+ If --build is not given, the incremented version retain the previous build
167
+ metadata. Pass an empty string to remove the build metadata (--build="")
168
+
169
+ Use --dry-run to run this command without updating the version file.
170
+
171
+ Use --quiet to increment the version without producing any output.
172
+ LONG_DESC
173
+
174
+ option :pre, type: :boolean, aliases: '-p', desc: 'Create a pre-release version'
175
+ option :'pre-type', type: :string, aliases: '-t', default: 'pre', banner: 'TYPE',
176
+ desc: 'The type of pre-release version (alpha, beta, etc.)'
177
+ option :build, type: :string, aliases: '-b', desc: 'The build metadata to add to the version'
178
+ option :'dry-run', type: :boolean, aliases: '-n', desc: 'Do not write the new version to the version file'
179
+ option :quiet, type: :boolean, aliases: '-q', desc: 'Do not print the new version to stdout'
180
+
181
+ # Increment the gem version's minor part
182
+ # @param version [String, nil] The version to increment or nil to use the version
183
+ # from the gem's version file
184
+ # @return [void]
185
+ def next_minor(version = nil)
186
+ increment_core_version(:next_minor, version)
187
+ end
188
+
189
+ desc 'next-patch [VERSION] [-p [-t TYPE]] [-b BUILD] [-n] [-q]', "Increment the version's patch part"
190
+
191
+ long_desc <<-LONG_DESC
192
+ Increase the current gem version to the next patch version, update the
193
+ file that stores the version, and output the resulting version to stdout.
194
+
195
+ Increments 1.2.3 to 1.2.4.
196
+
197
+ The command fails if the current gem version file could not be found or
198
+ if the version is not valid.
199
+
200
+ If VERSION is given, use that instead of the current gem version. Giving
201
+ VERSION implies --dry-run. The command fails if VERSION is not valid.
202
+
203
+ --pre can be used to specify that the version should be incremented AND
204
+ given a pre-release part. For instance:
205
+
206
+ /x5 $ semverify next-patch --pre
207
+
208
+ increments '1.2.3' to '1.2.4-pre.1'.
209
+
210
+ By default, the pre-release type is 'pre'. --pre-type=TYPE can be used with
211
+ --pre to specify a different pre-release type such as alpha, beta, rc, etc.
212
+ For instance:
213
+
214
+ /x5 $ semverify next-patch --pre --pre-type=alpha
215
+
216
+ increments '1.2.3' to '1.2.4-alpha.1'.
217
+
218
+ The command fails if the existing pre-release type is not lexically less than or
219
+ equal to TYPE. For example, it the current version is '1.2.3-beta.1' and TYPE
220
+ is 'alpha', the the command will fail since the new version would sort before the
221
+ existing version ('beta' is not less than or equal to 'alpha').
222
+
223
+ Use --build=BUILD to set the build metadata for the new version (See
224
+ [Build Metadata in the Semantic Versioning Specification](https://semver.org/spec/v2.0.0.html#spec-item-10)).
225
+ If --build is not given, the incremented version retain the previous build
226
+ metadata. Pass an empty string to remove the build metadata (--build="")
227
+
228
+ Use --dry-run to run this command without updating the version file.
229
+
230
+ Use --quiet to increment the version without producing any output.
231
+ LONG_DESC
232
+
233
+ option :pre, type: :boolean, aliases: '-p', desc: 'Create a pre-release version'
234
+ option :'pre-type', type: :string, aliases: '-t', default: 'pre', banner: 'TYPE',
235
+ desc: 'The type of pre-release version (alpha, beta, etc.)'
236
+ option :build, type: :string, aliases: '-b', desc: 'The build metadata to add to the version'
237
+ option :'dry-run', type: :boolean, aliases: '-n', desc: 'Do not write the new version to the version file'
238
+ option :quiet, type: :boolean, aliases: '-q', desc: 'Do not print the new version to stdout'
239
+
240
+ # Increment the gem version's patch part
241
+ # @param version [String, nil] The version to increment or nil to use the version
242
+ # from the gem's version file
243
+ # @return [void]
244
+ def next_patch(version = nil)
245
+ increment_core_version(:next_patch, version)
246
+ end
247
+
248
+ desc 'next-pre [VERSION] [-t TYPE] [-b BUILD] [-n] [-q]', "Increment the version's pre-release part"
249
+
250
+ long_desc <<-LONG_DESC
251
+ Increase the current gem version to the next pre-release version, update the
252
+ file that stores the version, and output the resulting version to stdout.
253
+
254
+ The command fails if the current gem version file could not be found or
255
+ the version is not a valid pre-release version.
256
+
257
+ If VERSION is given, use that instead of the current gem version. Giving
258
+ VERSION implies --dry-run. The command fails if VERSION is not a valid
259
+ pre-release version.
260
+
261
+ By default, the existing pre-release type is preserved. For instance:
262
+
263
+ /x5 $ semverify next-pre
264
+
265
+ Increments 1.2.3-alpha.1 to 1.2.3-alpha.2.
266
+
267
+ --pre-type=TYPE can be used to change the pre-release type to TYPE (typical
268
+ examples include alpha, beta, rc, etc.). If the pre-release type is changed,
269
+ the pre-release number is reset to 1.
270
+
271
+ For example, if the version starts as 1.2.3-alpha.4, then:
272
+
273
+ /x5 $ semverify current
274
+ /x5 1.2.3-alpha.4
275
+ /x5 $ semver next-pre --pre-type=beta
276
+ /x5 1.2.3-beta.1
277
+ /x5 $ semver next-pre --pre-type=beta
278
+ /x5 1.2.3-beta.2
279
+ /x5 $ semver next-pre --pre-type=rc
280
+ /x5 1.2.3-rc.1
281
+ /x5 $ semverify next-release
282
+ /x5 1.2.3
283
+
284
+ The command fails if the existing pre-release type is not lexically less than or
285
+ equal to TYPE. For example, it the current version is '1.2.3-beta.1' and the TYPE
286
+ given type is 'alpha', then the command will fail since the new version would sort
287
+ before the existing version (since 'beta' is not <= 'alpha').
288
+
289
+ Use --build=BUILD to set the build metadata for the new version (See
290
+ [Build Metadata in the Semantic Versioning Specification](https://semver.org/spec/v2.0.0.html#spec-item-10)).
291
+ If --build is not given, the incremented version retain the previous build
292
+ metadata. Pass an empty string to remove the build metadata (--build="")
293
+
294
+ Use --dry-run to run this command without updating the version file.
295
+
296
+ Use --quiet to increment the version without producing any output.
297
+ LONG_DESC
298
+
299
+ option :'pre-type', type: :string, aliases: '-t', banner: 'TYPE',
300
+ desc: 'The type of pre-release version (alpha, beta, etc.)'
301
+ option :build, type: :string, aliases: '-b', desc: 'The build metadata to add to the version'
302
+ option :'dry-run', type: :boolean, aliases: '-n', desc: 'Do not write the new version to the version file'
303
+ option :quiet, type: :boolean, aliases: '-q', desc: 'Do not print the new version to stdout'
304
+
305
+ # Increment the gem version's pre-release part
306
+ # @param version [String, nil] The version to increment or nil to use the version
307
+ # from the gem's version file
308
+ # @return [void]
309
+ def next_pre(version = nil)
310
+ args = {}
311
+ args[:pre_type] = options[:'pre-type'] if options[:'pre-type']
312
+ args[:build_metadata] = options[:build] if options[:build]
313
+
314
+ new_version = increment_version(:next_pre, args, version)
315
+
316
+ puts new_version unless options[:quiet]
317
+ end
318
+
319
+ desc 'next-release [VERSION] [-b BUILD] [-n] [-q]', 'Increment a pre-release version to the release version'
320
+
321
+ long_desc <<-LONG_DESC
322
+ Increase the current gem version to the next release version, update the
323
+ file that stores the version, and output the resulting version to stdout.
324
+
325
+ Increments 1.2.3-rc.4 to 1.2.3.
326
+
327
+ The command fails if the current gem version file could not be found or
328
+ the version is not a valid pre-release version.
329
+
330
+ If VERSION is given, use that instead of the current gem version. Giving
331
+ VERSION implies --dry-run. The command fails if VERSION is not a valid
332
+ pre-release version.
333
+
334
+ Use --build=BUILD to set the build metadata for the new version (See
335
+ [Build Metadata in the Semantic Versioning Specification](https://semver.org/spec/v2.0.0.html#spec-item-10)).
336
+ If --build is not given, the incremented version retain the previous build
337
+ metadata. Pass an empty string to remove the build metadata (--build="")
338
+
339
+ Use --dry-run to run this command without updating the version file.
340
+
341
+ Use --quiet to increment the version without producing any output.
342
+ LONG_DESC
343
+
344
+ option :build, type: :string, aliases: '-b', desc: 'The build metadata to add to the version'
345
+ option :'dry-run', type: :boolean, aliases: '-n', desc: 'Do not write the new version to the version file'
346
+ option :quiet, type: :boolean, aliases: '-q', desc: 'Do not print the new version to stdout'
347
+
348
+ # Increment the gem's pre-release version to a release version
349
+ # @param version [String, nil] The version to increment or nil to use the version
350
+ # from the gem's version file
351
+ # @return [void]
352
+ def next_release(version = nil)
353
+ args = {}
354
+ args[:build_metadata] = options[:build] if options[:build]
355
+
356
+ new_version = increment_version(:next_release, args, version)
357
+
358
+ puts new_version unless options[:quiet]
359
+ end
360
+
361
+ desc 'validate VERSION [-q]', 'Validate the given version'
362
+
363
+ long_desc <<-LONG_DESC
364
+ Validate and output the given gem version.
365
+
366
+ The command fails if the gem version is not valid.
367
+
368
+ Use `--quiet` to validate the gem version without producing any output.
369
+ LONG_DESC
370
+
371
+ option :quiet, type: :boolean, aliases: '-q', desc: 'Do not print the given version to stdout'
372
+
373
+ # Validate that the give version is a valid IncrementableSemver version
374
+ # @param version [String] The version to validate
375
+ # @return [void]
376
+ #
377
+ def validate(version)
378
+ Semverify::IncrementableSemver.new(version)
379
+ rescue Semverify::Error => e
380
+ warn e.message unless options[:quiet]
381
+ exit 1
382
+ else
383
+ puts version unless options[:quiet]
384
+ end
385
+
386
+ private
387
+
388
+ # Build a hash of arguments to pass to the IncrementableSemver methods
389
+ #
390
+ # These arguments are specificly for the #next_major, #next_minor, and
391
+ # #next_patch method.
392
+ #
393
+ # @return [Hash]
394
+ #
395
+ def core_args
396
+ {}.tap do |args|
397
+ args[:pre] = options[:pre] if options[:pre]
398
+ args[:pre_type] = options[:'pre-type'] if options[:'pre-type']
399
+ args[:build_metadata] = options[:build] if options[:build]
400
+ end
401
+ end
402
+
403
+ # Increment the gem's major, minor, or patch version
404
+ #
405
+ # @param method [Symbol] The method to call on the IncrementableSemver object
406
+ # @param version [String, nil] The version to increment or nil to use the version
407
+ # from the gem's version file
408
+ #
409
+ # @return [Void]
410
+ #
411
+ def increment_core_version(method, version)
412
+ new_version = increment_version(method, core_args, version)
413
+
414
+ puts new_version unless options[:quiet]
415
+ end
416
+
417
+ # Increment the gem's version
418
+ #
419
+ # @param method [Symbol] The method to call on the IncrementableSemver object
420
+ #
421
+ # The method can bee one of: :next_major, :next_minor, :next_patch, :next_pre, :next_release
422
+ #
423
+ # @param args [Hash] The arguments to pass to the method
424
+ #
425
+ # @param version [String, nil] The version to increment or nil to use the version
426
+ #
427
+ # @return [Void]
428
+ #
429
+ def increment_version(method, args, version)
430
+ if version
431
+ increment_literal_version(method, args, version)
432
+ else
433
+ increment_gem_version(method, args)
434
+ end
435
+ rescue Semverify::Error => e
436
+ warn e.message unless options[:quiet]
437
+ exit 1
438
+ end
439
+
440
+ # Increment a literal version from a string
441
+ #
442
+ # @param method [Symbol] The method to call on the IncrementableSemver object
443
+ # @param args [Hash] The arguments to pass to the method
444
+ # @param version [String] The version to increment
445
+ #
446
+ # @return [Semverify::IncrementableSemver] the incremented version
447
+ # @raise [Semverify::Error] if the version is not a valid IncrementableSemver version
448
+ #
449
+ def increment_literal_version(method, args, version)
450
+ Semverify::IncrementableSemver.new(version).send(method, **args)
451
+ end
452
+
453
+ # Increment the gem's version from the gem's version file
454
+ #
455
+ # @param method [Symbol] The method to call on the IncrementableSemver object
456
+ # @param args [Hash] The arguments to pass to the method
457
+ #
458
+ # @return [Semverify::IncrementableSemver] the incremented version
459
+ # @raise [Semverify::Error] if the version is not a valid IncrementableSemver version
460
+ #
461
+ def increment_gem_version(method, args)
462
+ version_file = Semverify::VersionFileFactory.find
463
+
464
+ if version_file.nil?
465
+ warn 'version file not found or is not valid' unless options[:quiet]
466
+ exit 1
467
+ end
468
+
469
+ version_file&.version.send(method, **args).tap do |new_version|
470
+ version_file.version = new_version unless options[:'dry-run']
471
+ end
472
+ end
473
+ end
474
+ # rubocop:enable Metrics/ClassLength
475
+ end
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'semverify/semver'
4
+
5
+ module Semverify
6
+ # A Semverify::Semver with additional constraints on the pre-release part of the version
7
+ #
8
+ # A IncrementableSemver is valid if one of the two following conditions is met:
9
+ # 1. The pre-release part is empty
10
+ # 2. The pre-release part is composed of two dot-separated identifiers:
11
+ # * the first being a String representing the pre-release type (e.g. 'alpha',
12
+ # 'beta', etc.). The default pre-release type is 'pre'
13
+ # * the second being an Integer representing the pre-release sequence number
14
+ # (starting at 1)
15
+ #
16
+ # Valid versions with pre-release parts: `1.2.3-alpha.1`, `1.2.3-beta.2`, `1.2.3-pre.3`
17
+ #
18
+ # @api public
19
+ #
20
+ class IncrementableSemver < Semver
21
+ # Create a new IncrementableSemver object
22
+ #
23
+ # @example
24
+ # Semverify::Semver.new('1.2.3').valid? # => true
25
+ # Semverify::Semver.new('1.2.3-alpha.1+build.001').valid? # => true
26
+ # Semverify::Semver.new('1.2.3-alpha').valid? # => raise Semverify::Error
27
+ # Semverify::Semver.new('1.2.3-alpha.1.2').valid? # => raise Semverify::Error
28
+ # Semverify::Semver.new('1.2.3-alpha.one').valid? # => raise Semverify::Error
29
+ #
30
+ # @return [Boolean] true if the version string is a valid semver and meets the conditions above
31
+ #
32
+ def valid?
33
+ super && (
34
+ pre_release.empty? ||
35
+ (
36
+ pre_release_identifiers.size == 2 &&
37
+ pre_type.is_a?(String) &&
38
+ pre_number.is_a?(Integer)
39
+ )
40
+ )
41
+ end
42
+
43
+ # The default pre-release identifier
44
+ DEFAULT_PRE_TYPE = 'pre'
45
+
46
+ # Increment the major version
47
+ #
48
+ # @example
49
+ # IncrementableSemverify::Semver.new('1.2.3').next_major # => IncrementableSemverify::Semver.new('2.0.0')
50
+ #
51
+ # @return [IncrementableSemver] a new IncrementableSemver object with the major version incremented
52
+ #
53
+ def next_major(pre: false, pre_type: DEFAULT_PRE_TYPE, build_metadata: nil)
54
+ version_string = "#{major.to_i + 1}.0.0"
55
+ version_string += "-#{pre_type}.1" if pre
56
+ build_metadata = self.build_metadata if build_metadata.nil?
57
+ version_string += "+#{build_metadata}" unless build_metadata.empty?
58
+ IncrementableSemver.new(version_string)
59
+ end
60
+
61
+ # Increment the minor version
62
+ #
63
+ # @example
64
+ # IncrementableSemverify::Semver.new('1.2.3').next_minor # => IncrementableSemverify::Semver.new('1.3.0')
65
+ #
66
+ # @return [IncrementableSemver] a new IncrementableSemver object with the major version incremented
67
+ #
68
+ def next_minor(pre: false, pre_type: DEFAULT_PRE_TYPE, build_metadata: nil)
69
+ version_string = "#{major}.#{minor.to_i + 1}.0"
70
+ version_string += "-#{pre_type}.1" if pre
71
+ build_metadata = self.build_metadata if build_metadata.nil?
72
+ version_string += "+#{build_metadata}" unless build_metadata.empty?
73
+ IncrementableSemver.new(version_string)
74
+ end
75
+
76
+ # Increment the patch version
77
+ #
78
+ # @example
79
+ # IncrementableSemverify::Semver.new('1.2.3').next_patch # => IncrementableSemverify::Semver.new('1.2.1')
80
+ #
81
+ # @return [IncrementableSemver] a new IncrementableSemver object with the patch part incremented
82
+ #
83
+ def next_patch(pre: false, pre_type: DEFAULT_PRE_TYPE, build_metadata: nil)
84
+ version_string = "#{major}.#{minor}.#{patch.to_i + 1}"
85
+ version_string += "-#{pre_type}.1" if pre
86
+ build_metadata = self.build_metadata if build_metadata.nil?
87
+ version_string += "+#{build_metadata}" unless build_metadata.empty?
88
+ IncrementableSemver.new(version_string)
89
+ end
90
+
91
+ # Increment the pre_release part of the version
92
+ #
93
+ # @example
94
+ # IncrementableSemverify::Semver.new('1.2.3-pre.1').next_patch.to_s # => '1.2.3-pre.2'
95
+ #
96
+ # @return [IncrementableSemver] a new IncrementableSemver object with the pre_release part incremented
97
+ #
98
+ def next_pre(pre_type: nil, build_metadata: nil)
99
+ assert_is_a_pre_release_version
100
+ version_string = "#{major}.#{minor}.#{patch}"
101
+ version_string += next_pre_part(pre_type)
102
+ build_metadata ||= self.build_metadata
103
+ version_string += "+#{build_metadata}" unless build_metadata.empty?
104
+ IncrementableSemver.new(version_string)
105
+ end
106
+
107
+ # Drop the pre-release part of the version
108
+ #
109
+ # @example
110
+ # IncrementableSemverify::Semver.new('1.2.3-pre.1').next_release.to_s # => '1.2.3'
111
+ #
112
+ # @return [IncrementableSemver] a new IncrementableSemver object with the pre_release part dropped
113
+ # @raise [Semverify::Error] if the version is not a pre-release version
114
+ #
115
+ def next_release(build_metadata: nil)
116
+ assert_is_a_pre_release_version
117
+ version_string = "#{major}.#{minor}.#{patch}"
118
+ build_metadata ||= self.build_metadata
119
+ version_string += "+#{build_metadata}" unless build_metadata.empty?
120
+ IncrementableSemver.new(version_string)
121
+ end
122
+
123
+ # The pre-release type (for example, 'alpha', 'beta', 'pre', etc.)
124
+ #
125
+ # @example
126
+ # IncrementableSemverify::Semver.new('1.2.3-pre.1').pre_type # => 'pre'
127
+ #
128
+ # @return [String]
129
+ #
130
+ def pre_type
131
+ pre_release_identifiers[0]
132
+ end
133
+
134
+ # The pre-release sequence number
135
+ #
136
+ # The pre-release sequence number starts at 1 for each pre-release type.
137
+ #
138
+ # @example
139
+ # IncrementableSemverify::Semver.new('1.2.3-pre.1').pre_number # => 1
140
+ #
141
+ # @return [Integer]
142
+ #
143
+ def pre_number
144
+ pre_release_identifiers[1]
145
+ end
146
+
147
+ private
148
+
149
+ # Raise an error if the version is not a pre-release version
150
+ #
151
+ # @return [void]
152
+ # @raise [Semverify::Error] if the version is not a pre-release version
153
+ # @api private
154
+ def assert_is_a_pre_release_version
155
+ return unless pre_release.empty?
156
+
157
+ raise Semverify::Error, 'Cannot increment the pre-release part of a release version'
158
+ end
159
+
160
+ # Raise an error if new pre-release type is less than the old pre-release type
161
+ #
162
+ # If the new pre-release type is lexically less than the old pre-release type,
163
+ # then raise an error because the resulting error would sort before the existing
164
+ # version.
165
+ #
166
+ # @return [void]
167
+ # @raise [Semverify::Error] if the pre-release type is not valid
168
+ # @api private
169
+ def assert_pre_type_is_valid(pre_type)
170
+ return if self.pre_type <= pre_type
171
+
172
+ message = 'Cannot increment the pre-release identifier ' \
173
+ "from '#{self.pre_type}' to '#{pre_type}' " \
174
+ "because '#{self.pre_type}' is lexically less than '#{pre_type}'"
175
+
176
+ raise Semverify::Error, message
177
+ end
178
+
179
+ # Return the next pre-release part
180
+ # @param pre_type [String, nil] the given pre-release type or nil to use the existing pre-release type
181
+ # @return [String]
182
+ # @api private
183
+ def next_pre_part(pre_type)
184
+ pre_type ||= self.pre_type
185
+ assert_pre_type_is_valid(pre_type)
186
+ next_pre_number = self.pre_type == pre_type ? (pre_number + 1) : 1
187
+ "-#{pre_type}.#{next_pre_number}"
188
+ end
189
+ end
190
+ end