qb 0.3.14 → 0.3.15

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 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