qb 0.3.14 → 0.3.15

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b5043907ea8b17d2fd4f2a4d5ad146e4a3c13a3a
4
- data.tar.gz: ddec830eccaae82a55107bae595e81ad93948deb
3
+ metadata.gz: 003b446aa520075c83fc445695b0babad54437b3
4
+ data.tar.gz: c994400156228766aeec6ea26103e7ee6217ad3c
5
5
  SHA512:
6
- metadata.gz: a609942e5b5ef542c8a48d8911defea06a1b33b95d4d2bf1c84d89e8a8871b9416647f03bcef1363bc7d67aac0a22dc696acb2493de9abcbf7c2127c8ad985ae
7
- data.tar.gz: 5636e6d6a03a0410c2b8233165b39f934fd63b8ff1e86c7aa897aead9220dcaadd437aba7b682bec83cae73a20754a95900288bb669cdb7f7c74d62b5619988d
6
+ metadata.gz: 2e379e64f336af6be769dc44e14654e598c01aa21b03e2148f5e82c51c23f8ece9808c0943459eb39158515b5b6e3c0f4c8ce6e27785ee2b30846543c8f6ba58
7
+ data.tar.gz: '0813dfb291517540ac560d6dabf4aafb4bb328b1abf54cd61dab3b858aea28833e3060be8c6ac057a505f78371a970ebcbe1b7656abe5e678191147d6da346e2'
data/.yardopts CHANGED
@@ -2,6 +2,9 @@
2
2
  --markup=markdown
3
3
  lib/**/*.rb
4
4
  dev/packages/gems/nrser/lib/**/*.rb
5
+ dev/packages/gems/cmds/lib/**/*.rb
5
6
  -
6
7
  README.md
7
8
  doc/**/*.md
9
+ dev/packages/gems/nrser/README.md
10
+ dev/packages/gems/cmds/README.md
@@ -19,6 +19,10 @@ module QB
19
19
  # Constants
20
20
  # =======================================================================
21
21
 
22
+ # Default initial values for {#qb}.
23
+ #
24
+ # @return [Hash]
25
+ #
22
26
  QB_DEFAULTS = {
23
27
  'hosts' => ['localhost'].freeze,
24
28
  'facts' => true,
@@ -28,15 +32,19 @@ module QB
28
32
  'ask' => false,
29
33
  }.freeze
30
34
 
31
- # appended on the end of an `opts.on` call to create a newline after
35
+
36
+ # Appended on the end of an `opts.on` call to create a newline after
32
37
  # the option (making the help output a bit easier to read)
33
38
  #
34
- # you might think the empty string would be reasonable, but OptionParser
39
+ # You might think the empty string would be reasonable, but OptionParser
35
40
  # blows up if you do that.
36
41
  #
42
+ # @return [String]
43
+ #
37
44
  SPACER = ' '
38
45
 
39
- # attributes
46
+
47
+ # Attributes
40
48
  # =======================================================================
41
49
 
42
50
  # @!attribute [r] ansible
@@ -319,7 +327,7 @@ module QB
319
327
  def initialize role, argv
320
328
  @role = role
321
329
  @argv = argv
322
- @qb = QB_DEFAULTS.clone
330
+ @qb = QB_DEFAULTS.dup
323
331
 
324
332
  parse!
325
333
  end
@@ -356,7 +364,7 @@ module QB
356
364
 
357
365
  opt_parser = OptionParser.new do |opts|
358
366
  opts.accept(QB::Package::Version) do |string|
359
- QB::Package::Version.from_string(string).to_h
367
+ QB::Package::Version.from( string ).to_h
360
368
  end
361
369
 
362
370
  opts.banner = @role.banner
@@ -78,7 +78,7 @@ class QB::Package::Gem < QB::Package
78
78
  # Whatever we were passes is the reference path
79
79
  values[:ref_path] = path
80
80
 
81
- # Cast to {Pathname} if it's not already and expand it to create the
81
+ # Cast to {Pathname} if it's not already and expand it to create the
82
82
  # root path
83
83
  values[:root_path] = path.to_pn.expand_path
84
84
 
@@ -98,8 +98,7 @@ class QB::Package::Gem < QB::Package
98
98
  values[:name] = values[:spec].name
99
99
 
100
100
  # Get the version from the spec
101
- values[:version] = QB::Package::Version.from_gem_version \
102
- values[:spec].version
101
+ values[:version] = QB::Package::Version.from values[:spec].version
103
102
 
104
103
  # Construct the resource instance and return it.
105
104
  new **values
@@ -34,7 +34,7 @@ class QB::Package < QB::Util::Resource; end
34
34
  # Definitions
35
35
  # =======================================================================
36
36
 
37
- # An attempt to unify NPM and Gem version schemes to a reasonable extend,
37
+ # An attempt to unify NPM and Gem version schemes to a reasonable extend,
38
38
  # and hopefully cover whatever else the cat may drag in.
39
39
  #
40
40
  # Intended to be immutable for practical purposes.
@@ -56,7 +56,7 @@ class QB::Package::Version < QB::Util::Resource
56
56
  MIXED_SEGMENT = t.union NUMBER_SEGMENT, NAME_SEGMENT
57
57
 
58
58
 
59
- # Reasonably simple regular expression to extract things that might be
59
+ # Reasonably simple regular expression to extract things that might be
60
60
  # versions from strings.
61
61
  #
62
62
  # Intended for use on reasonably short strings like `git tag` output or
@@ -76,8 +76,8 @@ class QB::Package::Version < QB::Util::Resource
76
76
  # `0-9`.
77
77
  #
78
78
  # This will match *many* strings that are not versions, but it should not
79
- # miss any that are. It cold obviously be refined and improve to reduce
80
- # false positives at the cost of additional complexity, but I wanted to
79
+ # miss any that are. It cold obviously be refined and improve to reduce
80
+ # false positives at the cost of additional complexity, but I wanted to
81
81
  # start simple and complicate it as needed.
82
82
  #
83
83
  # @return [Regexp]
@@ -97,13 +97,9 @@ class QB::Package::Version < QB::Util::Resource
97
97
  prop :build, type: t.array(MIXED_SEGMENT), default: []
98
98
 
99
99
  prop :release, type: t.str, source: :@release
100
- prop :level, type: t.str, source: :@level
101
100
  prop :is_release, type: t.bool, source: :release?
102
101
  prop :is_prerelease, type: t.bool, source: :prerelease?
103
102
  prop :is_build, type: t.bool, source: :build?
104
- prop :is_dev, type: t.bool, source: :dev?
105
- prop :is_rc, type: t.bool, source: :rc?
106
- prop :has_level, type: t.bool, source: :level?
107
103
  prop :semver, type: t.str, source: :semver
108
104
  prop :docker_tag, type: t.str, source: :docker_tag
109
105
  prop :build_commit, type: t.maybe(t.str), source: :build_commit
@@ -113,8 +109,7 @@ class QB::Package::Version < QB::Util::Resource
113
109
  # Attributes
114
110
  # =====================================================================
115
111
 
116
- attr_reader :release,
117
- :level
112
+ attr_reader :release
118
113
 
119
114
 
120
115
  # Class Methods
@@ -123,110 +118,31 @@ class QB::Package::Version < QB::Util::Resource
123
118
  # Utilities
124
119
  # ---------------------------------------------------------------------
125
120
 
126
- # Time formatted to be stuck in a version segment per [Semver][] spec.
127
- # We also strip out '-' to avoid possible parsing weirdness.
128
- #
129
- # [Semver]: https://semver.org/
130
- #
131
- # @return [String]
132
- #
133
- def self.to_time_segment time
134
- time.utc.iso8601.gsub /[^0-9A-Za-z]/, ''
121
+ def self.from object
122
+ QB::Package::Version::From.object object
135
123
  end
136
124
 
137
125
 
138
- # Instance Builders
139
- # ---------------------------------------------------------------------
140
-
141
- # Create a Version instance from a {Gem::Version}.
142
- #
143
- # @param [Gem::Version] version
126
+ # @depreciated Use {.from} instead.
144
127
  #
145
- # @return [QB::Package::Version]
146
- #
147
- def self.from_gem_version version
148
- # release segments are everything before a string
149
- release_segments = version.segments.take_while { |seg|
150
- !seg.is_a?(String)
151
- }
152
-
153
- # We don't support > 3 release segments to make life somewhat
154
- # reasonable. Yeah, I think I've seen projects do it. We'll cross that
155
- # bridge if and when we get to it.
156
- if release_segments.length > 3
157
- raise ArgumentError,
158
- "We don't handle releases with more than 3 segments " +
159
- "(found #{ release_segments.inspect } in #{ version })"
160
- end
161
-
162
- prerelease_segments = version.segments[release_segments.length..-1]
163
-
164
- new raw: version.to_s,
165
- major: release_segments[0] || 0,
166
- minor: release_segments[1] || 0,
167
- patch: release_segments[2] || 0,
168
- prerelease: prerelease_segments,
169
- build: []
170
- end
171
-
172
-
173
- def self.from_npm_version version
174
- stmt = NRSER.squish <<-END
175
- var Semver = require('semver');
176
-
177
- console.log(
178
- JSON.stringify(
179
- Semver(#{ JSON.dump version })
180
- )
181
- );
182
- END
183
-
184
- parse = JSON.load Cmds.new(
185
- "node --eval %s", args: [stmt], chdir: QB::ROOT
186
- ).out!
187
-
188
- new raw: version,
189
- major: parse['major'],
190
- minor: parse['minor'],
191
- patch: parse['patch'],
192
- prerelease: parse['prerelease'],
193
- build: parse['build']
128
+ def self.from_string string
129
+ QB::Package::Version::From.string string
194
130
  end
195
131
 
196
-
197
- # Parse Docker image tag version into a string. Reverse of
198
- # {QB::Package::Version#docker_tag}.
199
- #
200
- # @param [String] version
201
- # String version to parse.
202
- #
203
- # @return [QB::Package::Version]
204
- #
205
- def self.from_docker_tag version
206
- from_string(version.gsub('_', '+')).merge raw: version
207
- end # .from_docker_tag
132
+ singleton_class.send :alias_method, :from_s, :from_string
208
133
 
209
134
 
210
- # Parse string version into an instance. Accept Semver, Ruby Gem and
211
- # Docker image tag formats.
135
+ # Time formatted to be stuck in a version segment per [Semver][] spec.
136
+ # We also strip out '-' to avoid possible parsing weirdness.
212
137
  #
213
- # @param [String]
214
- # String version to parse.
138
+ # [Semver]: https://semver.org/
215
139
  #
216
- # @return [QB::Package::Version]
140
+ # @return [String]
217
141
  #
218
- def self.from_string string
219
- if string.include? '_'
220
- self.from_docker_tag string
221
- elsif string.include?( '-' ) || string.include?( '+' )
222
- self.from_npm_version string
223
- else
224
- self.from_gem_version Gem::Version.new(string)
225
- end
142
+ def self.to_time_segment time
143
+ time.utc.iso8601.gsub /[^0-9A-Za-z]/, ''
226
144
  end
227
145
 
228
- singleton_class.send :alias_method, :from_s, :from_string
229
-
230
146
 
231
147
  # Extract version number from a string.
232
148
  #
@@ -239,7 +155,7 @@ class QB::Package::Version < QB::Util::Resource
239
155
  def self.extract string
240
156
  string.scan( POSSIBLE_VERSION_RE ).map { |possible_version_string|
241
157
  begin
242
- from_string possible_version_string
158
+ from possible_version_string
243
159
  rescue
244
160
  nil
245
161
  end
@@ -247,29 +163,6 @@ class QB::Package::Version < QB::Util::Resource
247
163
  end # .extract
248
164
 
249
165
 
250
- # Constructor
251
- # =====================================================================
252
-
253
- # Construct a new Version
254
- def initialize **values
255
- super **values
256
-
257
- @release = [major, minor, patch].join '.'
258
-
259
- @level = t.match prerelease[0], {
260
- t.is(nil) => ->(_) {
261
- if build.empty?
262
- 'release'
263
- end
264
- },
265
-
266
- NAME_SEGMENT => ->(str) { str },
267
-
268
- NUMBER_SEGMENT => ->(int) { nil },
269
- }
270
- end
271
-
272
-
273
166
  # Instance Methods
274
167
  # =====================================================================
275
168
 
@@ -285,7 +178,7 @@ class QB::Package::Version < QB::Util::Resource
285
178
 
286
179
 
287
180
  # @return [Boolean]
288
- # True if any prerelease segments are present (stuff after '-' in
181
+ # True if any prerelease segments are present (stuff after '-' in
289
182
  # SemVer / "NPM" format, or the first string segment and anything
290
183
  # following it in "Gem" format). Tests if {@prerelease} is not
291
184
  # empty.
@@ -299,8 +192,8 @@ class QB::Package::Version < QB::Util::Resource
299
192
  # True if any build segments are present (stuff after '+' character
300
193
  # in SemVer / "NPM" format). Tests if {@build} is empty.
301
194
  #
302
- # As of writing, we don't have a way to convey build segments in
303
- # "Gem" version format, so this will always be false when loading a
195
+ # As of writing, we don't have a way to convey build segments in
196
+ # "Gem" version format, so this will always be false when loading a
304
197
  # Gem version.
305
198
  #
306
199
  def build?
@@ -323,36 +216,14 @@ class QB::Package::Version < QB::Util::Resource
323
216
  end # #build_dirty?
324
217
 
325
218
 
326
- # @return [Boolean]
327
- # True if self is a prerelease version that starts with a string that
328
- # we consider the 'level'.
329
- #
330
- def level?
331
- !level.nil?
332
- end
333
-
334
-
335
- # @return [Boolean]
336
- # True if this version is a dev prerelease (first prerelease element
337
- # is 'dev').
338
- #
339
- def dev?
340
- level == 'dev'
341
- end
342
-
219
+ # Derived Properties
220
+ # ---------------------------------------------------------------------
343
221
 
344
- # @return [Boolean]
345
- # True if this version is a release candidate (first prerelease element
346
- # is 'rc').
347
- #
348
- def rc?
349
- level == 'rc'
222
+ def release
223
+ [major, minor, patch].join '.'
350
224
  end
351
225
 
352
226
 
353
- # Derived Properties
354
- # ---------------------------------------------------------------------
355
-
356
227
  # @return [String]
357
228
  # The Semver version string
358
229
  # (`Major.minor.patch-prerelease+build` format).
@@ -403,7 +274,7 @@ class QB::Package::Version < QB::Util::Resource
403
274
  # Related Versions
404
275
  # ---------------------------------------------------------------------
405
276
  #
406
- # Functions that construct new version instances based on the current
277
+ # Functions that construct new version instances based on the current
407
278
  # one as well as additional information provided.
408
279
  #
409
280
 
@@ -412,7 +283,7 @@ class QB::Package::Version < QB::Util::Resource
412
283
  # *is* a release version already, still returns a new instance.
413
284
  #
414
285
  def release_version
415
- self.class.from_string release
286
+ self.class.from release
416
287
  end # #release_version
417
288
 
418
289
 
@@ -448,115 +319,6 @@ class QB::Package::Version < QB::Util::Resource
448
319
  end # #prerelease_version
449
320
 
450
321
 
451
- # Bumping
452
- # ---------------------------------------------------------------------
453
-
454
-
455
- # @todo Document bump_dev method.
456
- #
457
- # @param [type] arg_name
458
- # @todo Add name param description.
459
- #
460
- # @return [return_type]
461
- # @todo Document return value.
462
- #
463
- def bump_to_dev
464
- props = { prerelease: ['dev'] }
465
-
466
- case self.level
467
- when 'release'
468
- merge patch: patch.succ, **props
469
- when 'rc'
470
- merge **props
471
- when 'dev'
472
- self
473
- end
474
- end # #bump_dev
475
-
476
-
477
- # Bump to next release-candidate version.
478
- #
479
- # This is a little tricky because we need to know what the *last* rc
480
- # version was, which is not in the version in most cases.
481
- #
482
- # @param [type] arg_name
483
- # @todo Add name param description.
484
- #
485
- # @return [return_type]
486
- # @todo Document return value.
487
- #
488
- def bump_to_rc existing_versions: nil
489
- case self.level
490
- when 'release'
491
- merge patch: patch.succ, prerelease: ['rc', 0]
492
- when 'rc'
493
- merge prerelease: ['rc', prerelease[1].succ]
494
- when 'dev'
495
- if existing_versions.nil?
496
- raise ArgumentError.squished <<-END
497
- Can't bump to next rc version without knowing what rc versions have
498
- already been used.
499
- END
500
- elsif existing_versions.is_a? String
501
- existing_versions = self.class.extract existing_versions
502
- end
503
-
504
- last_existing_rc = existing_versions.
505
- select { |version|
506
- version.rc? && version.release == release
507
- }.
508
- sort.
509
- last
510
-
511
- rc_number = if last_existing_rc.nil?
512
- 0
513
- else
514
- last_existing_rc.prerelease[1].succ
515
- end
516
-
517
- merge prerelease: ['rc', rc_number]
518
- end
519
- end # #bump_rc
520
-
521
-
522
- # @todo Document bump_to_release method.
523
- #
524
- # @param [type] arg_name
525
- # @todo Add name param description.
526
- #
527
- # @return [return_type]
528
- # @todo Document return value.
529
- #
530
- def bump_to_release
531
- case self.level
532
- when 'release'
533
- # bump forward to next release, M.m.p -> M.m.(p+1)
534
- merge patch: patch.succ
535
- when 'rc', 'dev'
536
- # bump forward to release version for rc or dev
537
- release
538
- end
539
- end # #bump_to_release
540
-
541
-
542
- # @todo Document bump method.
543
- #
544
- # @param [type] arg_name
545
- # @todo Add name param description.
546
- #
547
- # @return [return_type]
548
- # @todo Document return value.
549
- #
550
- def bump level:, **options
551
- method_name = "bump_to_#{ level }"
552
- if options.empty?
553
- public_send method_name
554
- else
555
- public_send method_name, **options
556
- end
557
- end # #bump
558
-
559
-
560
322
  # Language Interface
561
323
  # =====================================================================
562
324
 
@@ -572,7 +334,7 @@ class QB::Package::Version < QB::Util::Resource
572
334
  #
573
335
  def == other
574
336
  other.class == self.class &&
575
- other.to_a == self.to_a
337
+ other.to_a == self.to_a
576
338
  end # #==
577
339
 
578
340
 
@@ -585,19 +347,17 @@ class QB::Package::Version < QB::Util::Resource
585
347
  # precedence.
586
348
  #
587
349
  # This is considered the representative structure for the object's data,
588
- # from which all other values are dependently derived, and is used in
350
+ # from which all other values are dependently derived, and is used in
589
351
  # {#==}, {#hash} and {#eql?}.
590
352
  #
591
353
  # @example
592
354
  #
593
- # version = QB::Package::Version.from_string(
594
- # "0.1.2-rc.10+master.0ab1c3d"
595
- # )
355
+ # version = QB::Package::Version.from "0.1.2-rc.10+master.0ab1c3d"
596
356
  #
597
357
  # version.to_a
598
358
  # # => [0, 1, 2, ['rc', 10], ['master', '0ab1c3d']]
599
359
  #
600
- # QB::Package::Version.from_string('1').to_a
360
+ # QB::Package::Version.from( '1' ).to_a
601
361
  # # => [1, nil, nil, [], []]
602
362
  #
603
363
  # @return [Array]
@@ -628,3 +388,10 @@ class QB::Package::Version < QB::Util::Resource
628
388
  end
629
389
 
630
390
  end # class QB::Package::Version
391
+
392
+
393
+ # Post-Processing
394
+ # =======================================================================
395
+
396
+ require 'qb/package/version/leveled'
397
+ require 'qb/package/version/from'
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Refinements
4
+ # =======================================================================
5
+
6
+ using NRSER
7
+ using NRSER::Types
8
+
9
+
10
+ # Definitions
11
+ # =======================================================================
12
+
13
+ # Module of factory methods to create {QB::Package::Version} instances from
14
+ # other objects (strings, {Gem::Version}, etc.)
15
+ #
16
+ module QB::Package::Version::From
17
+
18
+ # Get class to instantiate for prop values - either {QB::Package::Version}
19
+ # or a specialized subclass like {QB::Package::Version::Leveled}.
20
+ #
21
+ # @param [Hash<Symbol, Object>] **values
22
+ # Prop values.
23
+ #
24
+ # @return [Class<QB::Package::Version>]
25
+ #
26
+ def self.class_for **values
27
+ if QB::Package::Version::Leveled.level_for **values
28
+ QB::Package::Version::Leveled
29
+ else
30
+ QB::Package::Version
31
+ end
32
+ end # .class_for
33
+
34
+
35
+ # Instantiate an instance from prop values, using {.class_for} to choose
36
+ # the possible specialized class.
37
+ #
38
+ # @param [Hash<Symbol, Object>] **values
39
+ # Prop values.
40
+ #
41
+ # @return [QB::Package::Version]
42
+ #
43
+ def self.prop_values **values
44
+ class_for( **values ).new **values
45
+ end # .values
46
+
47
+
48
+ # Create an instance from a Gem-style version.
49
+ #
50
+ # @param [String | Gem::Version] version
51
+ #
52
+ # @return [QB::Package::Version]
53
+ #
54
+ def self.gemver version
55
+ version = Gem::Version.new( version ) if version.is_a?( String )
56
+
57
+ # release segments are everything before a string
58
+ release_segments = version.segments.take_while { |seg|
59
+ !seg.is_a?(String)
60
+ }
61
+
62
+ # We don't support > 3 release segments to make life somewhat
63
+ # reasonable. Yeah, I think I've seen projects do it. We'll cross that
64
+ # bridge if and when we get to it.
65
+ if release_segments.length > 3
66
+ raise ArgumentError,
67
+ "We don't handle releases with more than 3 segments " +
68
+ "(found #{ release_segments.inspect } in #{ version })"
69
+ end
70
+
71
+ prerelease_segments = version.segments[release_segments.length..-1]
72
+
73
+ prop_values \
74
+ raw: version.to_s,
75
+ major: release_segments[0] || 0,
76
+ minor: release_segments[1] || 0,
77
+ patch: release_segments[2] || 0,
78
+ prerelease: prerelease_segments,
79
+ build: []
80
+ end
81
+
82
+ singleton_class.send :alias_method, :gem_version, :gemver
83
+
84
+
85
+ def self.semver version
86
+ stmt = NRSER.squish <<-END
87
+ var Semver = require('semver');
88
+
89
+ console.log(
90
+ JSON.stringify(
91
+ Semver(#{ JSON.dump version })
92
+ )
93
+ );
94
+ END
95
+
96
+ parse = JSON.load Cmds.new(
97
+ "node --eval %s", args: [stmt], chdir: QB::ROOT
98
+ ).out!
99
+
100
+ prop_values \
101
+ raw: version,
102
+ major: parse['major'],
103
+ minor: parse['minor'],
104
+ patch: parse['patch'],
105
+ prerelease: parse['prerelease'],
106
+ build: parse['build']
107
+ end
108
+
109
+ singleton_class.send :alias_method, :npm_version, :semver
110
+
111
+
112
+ # Parse Docker image tag version and create an instance.
113
+ #
114
+ # @param [String] version
115
+ # String version to parse.
116
+ #
117
+ # @return [QB::Package::Version]
118
+ #
119
+ def self.docker_tag version
120
+ string( version.gsub( '_', '+' ) ).merge raw: version
121
+ end # .docker_tag
122
+
123
+
124
+ # Parse string version into an instance. Accept Semver, Ruby Gem and
125
+ # Docker image tag formats.
126
+ #
127
+ # @param [String]
128
+ # String version to parse.
129
+ #
130
+ # @return [QB::Package::Version]
131
+ #
132
+ def self.string string
133
+ if string.include? '_'
134
+ docker_tag string
135
+ elsif string.include?( '-' ) || string.include?( '+' )
136
+ semver string
137
+ else
138
+ gem_version string
139
+ end
140
+ end
141
+
142
+ singleton_class.send :alias_method, :s, :string
143
+
144
+
145
+ def self.object object
146
+ case object
147
+ when String
148
+ string object
149
+ when Hash
150
+ prop_values **object
151
+ when Gem::Version
152
+ gem_version object
153
+ else
154
+ raise TypeError.new binding.erb <<-END
155
+ `object` must be String, Hash or Gem::Version
156
+
157
+ Found:
158
+
159
+ <%= object.pretty_inspect %>
160
+
161
+ END
162
+ end
163
+ end
164
+
165
+ end # module QB::Package::Version::From
@@ -0,0 +1,295 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Requirements
4
+ # =======================================================================
5
+
6
+ # Stdlib
7
+ # -----------------------------------------------------------------------
8
+
9
+ # Deps
10
+ # -----------------------------------------------------------------------
11
+
12
+ # Project / Package
13
+ # -----------------------------------------------------------------------
14
+ require 'qb/package/version'
15
+
16
+
17
+ # Refinements
18
+ # =======================================================================
19
+
20
+ using NRSER
21
+ using NRSER::Types
22
+
23
+
24
+ # Definitions
25
+ # =======================================================================
26
+
27
+ # An attempt to unify NPM and Gem version schemes to a reasonable extend,
28
+ # and hopefully cover whatever else the cat may drag in.
29
+ #
30
+ # Intended to be immutable for practical purposes.
31
+ #
32
+ class QB::Package::Version::Leveled < QB::Package::Version
33
+ DEV = 'dev'
34
+ RC = 'rc'
35
+ RELEASE = 'release'
36
+
37
+ LEVELS = Set[ DEV, RC, RELEASE ].freeze
38
+
39
+ module Types
40
+ LEVEL = t.in LEVELS, name: 'LevelType'
41
+
42
+ def self.level
43
+ LEVEL
44
+ end
45
+ end
46
+
47
+
48
+ # Props
49
+ # ==========================================================================
50
+
51
+ prop :level, type: Types.level, source: :level
52
+ prop :is_dev, type: t.bool, source: :dev?
53
+ prop :is_rc, type: t.bool, source: :rc?
54
+
55
+
56
+ # Class Methods
57
+ # ==========================================================================
58
+
59
+ # Get the level for version prop values. Returns `nil` if they are not
60
+ # "leveled".
61
+ #
62
+ # @param [Array<String | Integer>] prerelease:
63
+ # The prerelease segments of the version.
64
+ #
65
+ # @param [Array<String | Integer>] build:
66
+ # The build segments of the version.
67
+ #
68
+ # @param [Hash<Symbol, Object>] **etc
69
+ # Really, anything, but meant to allow you to just pass all
70
+ # {QB::Package::Version} prop values to the method.
71
+ #
72
+ # @return [nil]
73
+ # If the prop values don't have a level.
74
+ #
75
+ # @return ['dev' | 'rc' | 'release']
76
+ # If the values do have a level.
77
+ #
78
+ def self.level_for prerelease: [], build: [], **etc
79
+ return RELEASE if prerelease.empty? && build.empty?
80
+
81
+ return DEV if prerelease[0] == DEV
82
+
83
+ if prerelease[0] == RC &&
84
+ prerelease.length == 2 &&
85
+ t.non_neg_int.test( prerelease[1] )
86
+ return RC
87
+ end
88
+
89
+ nil
90
+ end # .level_for
91
+
92
+
93
+ # Just like {.level_for} but raises if the props don't represent a valid
94
+ # level.
95
+ #
96
+ # @param (see .level_for)
97
+ # @return (see .level_for)
98
+ #
99
+ # @raise [ArgumentError]
100
+ # If the prop values don't represent a version level.
101
+ #
102
+ def self.level_for! **values
103
+ level_for( **values ).tap { |response|
104
+ if response.nil?
105
+ raise ArgumentError.new binding.erb <<-END
106
+ Prop values not valid for a leveled version:
107
+
108
+ <%= values.pretty_inspect %>
109
+
110
+ END
111
+ end
112
+ }
113
+ end
114
+
115
+
116
+ # Constructor
117
+ # ==========================================================================
118
+
119
+ # Construct a new Version
120
+ def initialize **values
121
+ # Just to do the type check...
122
+ self.class.level_for! **values
123
+ super **values
124
+ end
125
+
126
+
127
+ # Instance Methods
128
+ # ============================================================================
129
+
130
+ def level
131
+ self.class.level_for \
132
+ prerelease: prerelease,
133
+ build: build
134
+ end
135
+
136
+
137
+ # @return [Boolean]
138
+ # True if this version is a dev prerelease (first prerelease element
139
+ # is 'dev').
140
+ #
141
+ def dev?
142
+ level == DEV
143
+ end
144
+
145
+
146
+ # @return [Boolean]
147
+ # True if this version is a release candidate (first prerelease element
148
+ # is 'rc').
149
+ #
150
+ def rc?
151
+ level == RC
152
+ end
153
+
154
+
155
+ # Transitions
156
+ # ---------------------------------------------------------------------
157
+ #
158
+ # Methods for transitioning from one level to another that abide by rules
159
+ # for cycling between them.
160
+ #
161
+
162
+ # @todo Document bump_dev method.
163
+ #
164
+ # @param [type] arg_name
165
+ # @todo Add name param description.
166
+ #
167
+ # @return [return_type]
168
+ # @todo Document return value.
169
+ #
170
+ def transition_to_dev inc: :patch
171
+ props = { prerelease: ['dev'] }
172
+
173
+ t.match level,
174
+ 'release', ->(_) {
175
+ succ = public_send( inc ).succ
176
+
177
+ merge inc => succ, **props
178
+ },
179
+
180
+ 'rc', ->(_) {
181
+ merge **props
182
+ },
183
+
184
+ 'dev', ->(_) {
185
+ raise QB::VersionError,
186
+ "Version #{ self } is already at `dev` level"
187
+ }
188
+ end # #transition_to_dev
189
+
190
+
191
+ # Transition to next release-candidate version.
192
+ #
193
+ # This is a little tricky because we need to know what the *last* rc
194
+ # version was, which is not in the version in most cases.
195
+ #
196
+ # @param [nil | String | Array] existing_versions:
197
+ # Required when transitioning *from* `dev` level; ignored from `rc` and
198
+ # `release` levels.
199
+ #
200
+ # > When transitioning from `dev` to `rc` we need to know what `rc.X`
201
+ # versions have already been used in order to figure out the correct
202
+ # next one.
203
+ #
204
+ # Value details:
205
+ #
206
+ # - `nil` - Default value; fine when {#level} is `release` or `rc`. An
207
+ # {ArgumentError} will be raised if {#level} is `dev`.
208
+ #
209
+ # - `String` -
210
+ #
211
+ # @return [return_type]
212
+ # @todo Document return value.
213
+ #
214
+ def transition_to_rc existing_versions: nil
215
+ t.match level,
216
+ 'release', ->(_) {
217
+ raise QB::VersionError,
218
+ "Can not transition from `release` to `rc` levels (for #{ self })"
219
+ },
220
+
221
+ 'rc', ->(_) {
222
+ merge prerelease: ['rc', prerelease[1].succ]
223
+ },
224
+
225
+ 'dev', ->(_) {
226
+ if existing_versions.nil?
227
+ raise ArgumentError.squished <<-END
228
+ Can't bump to next rc version without knowing what rc versions have
229
+ already been used.
230
+ END
231
+ elsif existing_versions.is_a? String
232
+ existing_versions = self.class.extract existing_versions
233
+ end
234
+
235
+ last_existing_rc = existing_versions.
236
+ select { |version|
237
+ version.rc? && version.release == release
238
+ }.
239
+ sort.
240
+ last
241
+
242
+ rc_number = if last_existing_rc.nil?
243
+ 0
244
+ else
245
+ last_existing_rc.prerelease[1].succ
246
+ end
247
+
248
+ merge prerelease: ['rc', rc_number]
249
+ }
250
+ end # #transition_to_rc
251
+
252
+
253
+ # @todo Document transition_to_release method.
254
+ #
255
+ # @return [QB::Package::Version]
256
+ #
257
+ def transition_to_release
258
+ t.match level,
259
+ 'release', ->(_) {
260
+ raise QB::VersionError,
261
+ "Version #{ self } is already at `release` level"
262
+ },
263
+
264
+ 'dev', ->(_) {
265
+ raise QB::VersionError,
266
+ "Can not transition from `dev` to `release` levels (for #{ self })"
267
+ },
268
+
269
+ 'rc', ->(_) {
270
+ release_version
271
+ }
272
+ end # #transition_to_release
273
+
274
+
275
+ # @todo Document bump method.
276
+ #
277
+ # @param [type] arg_name
278
+ # @todo Add name param description.
279
+ #
280
+ # @return [return_type]
281
+ # @todo Document return value.
282
+ #
283
+ def transition_to level, **options
284
+ Types.level.check level.to_s
285
+
286
+ method_name = "transition_to_#{ level }"
287
+ if options.empty?
288
+ public_send method_name
289
+ else
290
+ public_send method_name, **options
291
+ end
292
+ end # #transition
293
+
294
+
295
+ end
@@ -4,7 +4,7 @@ module QB
4
4
 
5
5
  GEM_NAME = 'qb'
6
6
 
7
- VERSION = "0.3.14"
7
+ VERSION = "0.3.15"
8
8
 
9
9
  MIN_ANSIBLE_VERSION = Gem::Version.new '2.1.2'
10
10
 
@@ -169,7 +169,7 @@ class PathFacts < QB::Ansible::Module
169
169
  end
170
170
 
171
171
 
172
- # If `path` is a directory containing the source for a Ruby Gem, add
172
+ # If `path` is a directory containing the source for a Ruby Gem, add
173
173
  # useful information about it.
174
174
  def add_gem_facts
175
175
  unless @path.directory?
@@ -193,7 +193,7 @@ class PathFacts < QB::Ansible::Module
193
193
 
194
194
  spec = Gem::Specification::load(gemspec_path.to_s)
195
195
  gem.name = spec.name
196
- gem.version = QB::Package::Version.from_gem_version spec.version
196
+ gem.version = QB::Package::Version.from spec.version
197
197
  end
198
198
 
199
199
 
@@ -218,9 +218,7 @@ class PathFacts < QB::Ansible::Module
218
218
  npm.name = npm.package_json['name']
219
219
 
220
220
  if npm.package_json['version']
221
- npm.version = QB::Package::Version.from_npm_version(
222
- npm.package_json['version']
223
- )
221
+ npm.version = QB::Package::Version.from npm.package_json['version']
224
222
  end
225
223
  end
226
224
 
@@ -234,7 +232,7 @@ class PathFacts < QB::Ansible::Module
234
232
  # - True if `@path/VERSION` exists and was successfully parsed.
235
233
  #
236
234
  # - If `has_version_file` is `true`:
237
- # -
235
+ # -
238
236
  #
239
237
  def add_version_file_facts
240
238
  unless @path.directory?
@@ -250,7 +248,7 @@ class PathFacts < QB::Ansible::Module
250
248
  end
251
249
 
252
250
  version = begin
253
- QB::Package::Version.from_string version_file_path.read
251
+ QB::Package::Version.from version_file_path.read
254
252
  rescue Exception => e
255
253
  warn "Unable to parse version from #{ version_file_path.to_s }; #{ e }"
256
254
  @result.has_version_file = false
@@ -316,7 +314,7 @@ class PathFacts < QB::Ansible::Module
316
314
  # Add version/package facts from a @path/VERSION file if present
317
315
  add_version_file_facts
318
316
 
319
- # If we only have one type of package present, we set it's type and
317
+ # If we only have one type of package present, we set it's type and
320
318
  # version as `package.type` and `package.version`, which makes it easy for
321
319
  # code to 'auto-detect' the info.
322
320
  #
@@ -2,10 +2,18 @@
2
2
  # WANT_JSON
3
3
 
4
4
  # init bundler in dev env
5
+
6
+ # HACK Keep track of the ENV vars we overwrite so we can swap them back in
7
+ # later when we need to run the command.
8
+ #
9
+ $replaced_env_vars = {}
10
+
5
11
  if ENV['QB_DEV_ENV']
6
12
  ENV.each {|k, v|
7
13
  if k.start_with? 'QB_DEV_ENV_'
8
- ENV[k.sub('QB_DEV_ENV_', '')] = v
14
+ key = k.sub('QB_DEV_ENV_', '')
15
+ $replaced_env_vars[key] = [ENV[key], v]
16
+ ENV[key] = v
9
17
  end
10
18
  }
11
19
  require 'bundler/setup'
@@ -14,6 +22,8 @@ end
14
22
  require 'qb'
15
23
  require 'cmds'
16
24
 
25
+ using NRSER
26
+
17
27
  class Stream < QB::Ansible::Module
18
28
  def main
19
29
  template = @args['template'] || @args['cmd']
@@ -41,9 +51,31 @@ class Stream < QB::Ansible::Module
41
51
  cmd = Cmds.new(template, **opts)
42
52
 
43
53
  if @args['log']
44
- info "STREAMING\n\n#{ cmd.prepare }\n\n"
54
+ info binding.erb <<-END
55
+
56
+ STREAMING
57
+ ========================================================================
58
+
59
+ Template:
60
+
61
+ <%= template %>
62
+
63
+ Options:
64
+
65
+ <%= opts.pretty_inspect %>
66
+
67
+ Prepared command:
68
+
69
+ <%= cmd.prepare %>
70
+
71
+ END
45
72
  end
46
73
 
74
+ # HACK Swap any ENV vars we replaced at the top back in
75
+ $replaced_env_vars.each do |key, (original, replacement)|
76
+ ENV[key] = original
77
+ end
78
+
47
79
  cmd.stream!
48
80
 
49
81
  changed!
@@ -4,26 +4,36 @@ __metaclass__ = type
4
4
  import subprocess
5
5
  import os
6
6
  import json
7
+ import sys
7
8
 
8
9
  from ansible.errors import AnsibleError
9
10
 
10
11
 
11
- QB_ROOT = os.path.realpath(
12
+ HERE = os.path.dirname(os.path.realpath(__file__))
13
+
14
+ PROJECT_ROOT = os.path.realpath(
12
15
  os.path.join(
13
- os.path.dirname(os.path.realpath(__file__)), # /plugins/filter_plugins
14
- '..', # /plugins
15
- '..', # /
16
+ HERE, # //plugins/filter_plugins
17
+ '..', # //plugins
18
+ '..', # //
16
19
  )
17
20
  )
18
21
 
22
+ LIB_PYTHON_DIR = os.path.join( PROJECT_ROOT, 'lib', 'python' )
23
+
24
+ if not (LIB_PYTHON_DIR in sys.path):
25
+ sys.path.insert(0, LIB_PYTHON_DIR)
26
+
27
+ import qb.interop
28
+
19
29
 
20
30
  def get_semver_path():
21
- bin_path = os.path.join(QB_ROOT, 'node_modules', 'semver', 'bin', 'semver')
31
+ bin_path = os.path.join(PROJECT_ROOT, 'node_modules', 'semver', 'bin', 'semver')
22
32
 
23
33
  if not os.path.isfile(bin_path):
24
34
  raise Exception("can't find semver at %s" % bin_path)
25
35
 
26
- return bin_path
36
+ return bin_path
27
37
  # get_semver_path()
28
38
 
29
39
 
@@ -83,7 +93,7 @@ def semver_parse(version):
83
93
 
84
94
  out = subprocess.check_output(
85
95
  cmd,
86
- cwd = QB_ROOT
96
+ cwd = PROJECT_ROOT
87
97
  )
88
98
 
89
99
  version = json.loads(out)
@@ -118,23 +128,9 @@ def qb_version_parse(version_string):
118
128
  '''Parse version into QB::Package::Version
119
129
  '''
120
130
 
121
- ruby_code = (
122
- '''
123
- require 'qb'
124
-
125
- puts JSON.dump(
126
- QB::Package::Version.from_string %s
127
- )
128
- ''' % json.dumps(version_string)
131
+ return qb.interop.send_const(
132
+ 'QB::Package::Version', 'from', version_string
129
133
  )
130
-
131
- cmd = ['/usr/bin/env', 'ruby', '-e', ruby_code]
132
-
133
- out = subprocess.check_output(cmd)
134
-
135
- version = json.loads(out)
136
-
137
- return version
138
134
 
139
135
 
140
136
  def qb_read_version(file_path):
@@ -163,4 +159,4 @@ class FilterModule(object):
163
159
  if __name__ == '__main__':
164
160
  import doctest
165
161
  doctest.testmod()
166
-
162
+
data/qb.gemspec CHANGED
@@ -194,8 +194,8 @@ Gem::Specification.new do |spec|
194
194
  # Runtime Dependencies
195
195
  # ----------------------------------------------------------------------------
196
196
 
197
- spec.add_dependency "cmds", '~> 0.0', ">= 0.2.4"
198
- spec.add_dependency "nrser", '~> 0.1', ">= 0.1.0"
197
+ spec.add_dependency "cmds", '~> 0.0', ">= 0.2.5"
198
+ spec.add_dependency "nrser", '~> 0.1', ">= 0.1.1"
199
199
  spec.add_dependency "nrser-extras", '~> 0.0', ">= 0.0.3"
200
200
  spec.add_dependency "state_mate", '~> 0.0', ">= 0.1.0"
201
201
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.14
4
+ version: 0.3.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - nrser
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-01-10 00:00:00.000000000 Z
11
+ date: 2018-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -123,7 +123,7 @@ dependencies:
123
123
  version: '0.0'
124
124
  - - ">="
125
125
  - !ruby/object:Gem::Version
126
- version: 0.2.4
126
+ version: 0.2.5
127
127
  type: :runtime
128
128
  prerelease: false
129
129
  version_requirements: !ruby/object:Gem::Requirement
@@ -133,7 +133,7 @@ dependencies:
133
133
  version: '0.0'
134
134
  - - ">="
135
135
  - !ruby/object:Gem::Version
136
- version: 0.2.4
136
+ version: 0.2.5
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: nrser
139
139
  requirement: !ruby/object:Gem::Requirement
@@ -143,7 +143,7 @@ dependencies:
143
143
  version: '0.1'
144
144
  - - ">="
145
145
  - !ruby/object:Gem::Version
146
- version: 0.1.0
146
+ version: 0.1.1
147
147
  type: :runtime
148
148
  prerelease: false
149
149
  version_requirements: !ruby/object:Gem::Requirement
@@ -153,7 +153,7 @@ dependencies:
153
153
  version: '0.1'
154
154
  - - ">="
155
155
  - !ruby/object:Gem::Version
156
- version: 0.1.0
156
+ version: 0.1.1
157
157
  - !ruby/object:Gem::Dependency
158
158
  name: nrser-extras
159
159
  requirement: !ruby/object:Gem::Requirement
@@ -320,6 +320,8 @@ files:
320
320
  - lib/qb/package.rb
321
321
  - lib/qb/package/gem.rb
322
322
  - lib/qb/package/version.rb
323
+ - lib/qb/package/version/from.rb
324
+ - lib/qb/package/version/leveled.rb
323
325
  - lib/qb/path.rb
324
326
  - lib/qb/repo.rb
325
327
  - lib/qb/repo/git.rb