qb 0.1.72 → 0.1.73

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: 56ee085b4ad133ca61ffc618b9e91886b22d623a
4
- data.tar.gz: e4a66a0fa5f86d261310562d93dc6896285d87c2
3
+ metadata.gz: baf1eddaaa71efa535ebd3f532c7c2d8e28a1b23
4
+ data.tar.gz: b250253f17d6f9e39e2acaea1c62b2355260dc6a
5
5
  SHA512:
6
- metadata.gz: 84cc40de910e66c346335b39e63d4d10bbe3f73bc6c53ce070a8e8e63f5265602869e42ff2cdfdc83d4bc2294346193e95954b76f3df86226b75d7e82438b289
7
- data.tar.gz: 756bf8f03c836c59ff05b17d074232673a63fb46f48d2a91a871ac636cb8ce16ad86fc79e3c4f44e75877e12354f51a1b8467ff782029b485c5c2c439b474ea0
6
+ metadata.gz: a4a205093b9e7b961fb1c3c4dbab7589228d58a954dcde6ee8bdcb637ef9e8b338e39da1b4333b03b5de43a0d5b12d73fae77a93f7d9fe06f29abb0de075b6ff
7
+ data.tar.gz: 78d9edc4152419d0f0af107355b2eb494f16c01694720b5605575fc597484f6ba3e7e988d3f97da9c8e9453eaaf0656566d3c6837edfbbff0360de2f7a48f497
@@ -3,27 +3,28 @@ require 'pp'
3
3
 
4
4
  module QB
5
5
  class AnsibleModule
6
+
7
+ # Class Variables
8
+ # =====================================================================
9
+
6
10
  @@arg_types = {}
7
11
 
12
+
13
+ # Class Methods
14
+ # =====================================================================
15
+
8
16
  def self.stringify_keys hash
9
17
  hash.map {|k, v| [k.to_s, v]}.to_h
10
18
  end
11
19
 
20
+
12
21
  def self.arg name, type
13
22
  @@arg_types[name.to_sym] = type
14
23
  end
15
24
 
16
- def debug *args
17
- if @qb_stdio_err
18
- header = "<QB::AnsibleModule #{ self.class.name }>"
19
-
20
- if args[0].is_a? String
21
- header += " " + args.shift
22
- end
23
-
24
- QB.debug header, *args
25
- end
26
- end
25
+
26
+ # Constructor
27
+ # =====================================================================
27
28
 
28
29
  def initialize
29
30
  @changed = false
@@ -31,13 +32,14 @@ module QB
31
32
  @input = File.read @input_file
32
33
  @args = JSON.load @input
33
34
  @facts = {}
35
+ @warnings = []
34
36
 
35
37
  @qb_stdio_out = nil
36
38
  @qb_stdio_err = nil
37
39
  @qb_stdio_in = nil
38
40
 
39
- debug "HERE!"
40
- debug ENV
41
+ # debug "HERE!"
42
+ # debug ENV
41
43
 
42
44
  # if QB_STDIO_ env vars are set send stdout and stderr
43
45
  # to those sockets to print in the parent process
@@ -75,6 +77,53 @@ module QB
75
77
  }
76
78
  end
77
79
 
80
+
81
+
82
+ # Instance Methods
83
+ # =====================================================================
84
+
85
+ # Logging
86
+ # ---------------------------------------------------------------------
87
+ #
88
+ # Logging is a little weird in Ansible modules... Ansible has facilities
89
+ # for notifying the user about warnings and depreciations, which we will
90
+ # make accessible, but it doesn't seem to have facilities for notices and
91
+ # debugging, which I find very useful.
92
+ #
93
+ # When run inside of QB (targeting localhost only at the moment, sadly)
94
+ # we expose additional IO channels for STDIN, STDOUT and STDERR through
95
+ # opening unix socket files that the main QB process spawns threads to
96
+ # listen to, and we provide those file paths via environment variables
97
+ # so modules can pick those up and interact with those streams, allowing
98
+ # them to act like regular scripts inside Ansible-world (see
99
+ # QB::Util::STDIO for details and implementation).
100
+ #
101
+ # We use those channels if present to provide logging mechanisms.
102
+ #
103
+
104
+ # Forward args to {QB.debug} if we are connected to a QB STDERR stream
105
+ # (write to STDERR).
106
+ #
107
+ # @param args see QB.debug
108
+ #
109
+ def debug *args
110
+ if @qb_stdio_err
111
+ header = "<QB::AnsibleModule #{ self.class.name }>"
112
+
113
+ if args[0].is_a? String
114
+ header += " " + args.shift
115
+ end
116
+
117
+ QB.debug header, *args
118
+ end
119
+ end
120
+
121
+ # Append a warning message to @warnings.
122
+ def warn msg
123
+ @warnings << msg
124
+ end
125
+
126
+
78
127
  def run
79
128
  result = main
80
129
 
@@ -98,7 +147,8 @@ module QB
98
147
 
99
148
  def done
100
149
  exit_json changed: @changed,
101
- ansible_facts: self.class.stringify_keys(@facts)
150
+ ansible_facts: self.class.stringify_keys(@facts),
151
+ warnings: @warnings
102
152
  end
103
153
 
104
154
  def exit_json hash
@@ -123,7 +173,7 @@ module QB
123
173
  end
124
174
 
125
175
  def fail msg
126
- exit_json failed: true, msg: msg
176
+ exit_json failed: true, msg: msg, warnings: @warnings
127
177
  end
128
178
  end
129
179
  end # QB
data/lib/qb/errors.rb CHANGED
@@ -16,4 +16,8 @@ module QB
16
16
  # Raised when the current QB version doesn't satisfy a role as defined
17
17
  # in `<role_dir>/meta/qb[.yml]:required_qb_version`).
18
18
  class QBVersionError < VersionError; end
19
+
20
+ # Raised when the file system is in a state that doesn't work for what we're
21
+ # trying to do.
22
+ class FSStateError < Error; end
19
23
  end # module QB
data/lib/qb/options.rb CHANGED
@@ -136,18 +136,31 @@ module QB
136
136
  Integer
137
137
  when 'version'
138
138
  QB::Package::Version
139
+ when 'hash', 'dict'
140
+ Class.new.tap { |klass|
141
+ opts.accept(klass) {|value|
142
+ value.split(',').map { |pair_str|
143
+ split = pair_str.split ':'
144
+ if split.length > 2
145
+ raise "Can only have a single ':' in hash options, " +
146
+ "found #{ pair_str.inspect } in #{ value.inspect }"
147
+ end
148
+ [split[0], split[1]]
149
+ }.to_h
150
+ }
151
+ }
139
152
  when Hash
140
153
  if option.meta['type'].key? 'one_of'
141
- klass = Class.new
142
- opts.accept(klass) {|value|
143
- if option.meta['type']['one_of'].include? value
144
- value
145
- else
146
- raise QB::Role::MetadataError,
147
- "option '#{ option.cli_name }' must be one of: #{ option.meta['type']['one_of'].join(', ') }"
148
- end
154
+ Class.new.tap { |klass|
155
+ opts.accept(klass) {|value|
156
+ if option.meta['type']['one_of'].include? value
157
+ value
158
+ else
159
+ raise QB::Role::MetadataError,
160
+ "option '#{ option.cli_name }' must be one of: #{ option.meta['type']['one_of'].join(', ') }"
161
+ end
162
+ }
149
163
  }
150
- klass
151
164
  else
152
165
  raise QB::Role::MetadataError,
153
166
  "bad type for option #{ option.meta_name }: #{ option.meta['type'].inspect }"
@@ -1,20 +1,56 @@
1
+ require 'nrser/types'
2
+
3
+ T = NRSER::Types
4
+
1
5
  module QB
2
6
  module Package
3
7
  # An attempt to unify NPM and Gem version schemes to a reasonable extend,
4
8
  # and hopefully cover whatever else the cat may drag in.
9
+ #
10
+ # Intended to be immutable for practical purposes.
11
+ #
5
12
  class Version
13
+
14
+ # Class Methods
15
+ # =====================================================================
16
+
17
+ # Utilities
18
+ # ---------------------------------------------------------------------
19
+
20
+ # @return [String]
21
+ # Time formatted to be stuck in a version segment per Semver spec.
22
+ # We also strip out '-' to avoid possible parsing weirdness.
23
+ def self.to_time_segment time
24
+ time.utc.iso8601.gsub /[^0-9A-Za-z]/, ''
25
+ end
26
+
27
+ # Instance Builders
28
+ # ---------------------------------------------------------------------
29
+
6
30
  # Create a Version instance from a Gem::Version
7
31
  def self.from_gem_version version
8
- release_segments = version.segments.take_while {|seg| !seg.is_a?(String)}
32
+ # release segments are everything before a string
33
+ release_segments = version.segments.take_while { |seg|
34
+ !seg.is_a?(String)
35
+ }
36
+
37
+ # We don't support > 3 release segments to make life somewhat
38
+ # reasonable. Yeah, I think I've seen projects do it. We'll cross that
39
+ # bridge if and when we get to it.
40
+ if release_segments.length > 3
41
+ raise ArgumentError,
42
+ "We don't handle releases with more than 3 segments " +
43
+ "(found #{ release_segments.inspect } in #{ version })"
44
+ end
45
+
9
46
  prerelease_segments = version.segments[release_segments.length..-1]
10
47
 
11
48
  new raw: version.to_s,
12
- major: release_segments[0],
13
- minor: release_segments[1],
14
- patch: release_segments[2],
49
+ major: release_segments[0] || 0,
50
+ minor: release_segments[1] || 0,
51
+ patch: release_segments[2] || 0,
15
52
  prerelease: prerelease_segments,
16
- build: [],
17
- release: version.release.to_s
53
+ build: []
18
54
  end
19
55
 
20
56
  def self.from_npm_version version
@@ -37,8 +73,7 @@ module QB
37
73
  minor: parse['minor'],
38
74
  patch: parse['patch'],
39
75
  prerelease: parse['prerelease'],
40
- build: parse['build'],
41
- release: [parse['major'], parse['minor'], parse['patch']].join(".")
76
+ build: parse['build']
42
77
  end
43
78
 
44
79
  def self.from_string string
@@ -49,6 +84,44 @@ module QB
49
84
  end
50
85
  end
51
86
 
87
+
88
+ # Instantiate from a hash. Slices out
89
+ #
90
+ # - `raw`
91
+ # - `major`
92
+ # - `minor`
93
+ # - `patch`
94
+ # - `prerelease`
95
+ # - `build`
96
+ #
97
+ # And passes their values to the constructor. Keys may be strings or
98
+ # symbols. All other key/values are ignored, allowing you to pass in
99
+ # the JSON encoding of a version instance.
100
+ #
101
+ # @param [Hash] hash
102
+ # Values to be passed to constructor.
103
+ #
104
+ # @return [QB::Package::Version]
105
+ #
106
+ def self.from_h hash
107
+ self.new(
108
+ NRSER.slice_keys(
109
+ NRSER.symbolize_keys(hash),
110
+ :raw,
111
+ :major,
112
+ :minor,
113
+ :patch,
114
+ :prerelease,
115
+ :build,
116
+ )
117
+ )
118
+ end # #from_h
119
+
120
+
121
+
122
+ # Attributes
123
+ # =====================================================================
124
+
52
125
  attr_reader :raw,
53
126
  :major,
54
127
  :minor,
@@ -56,40 +129,219 @@ module QB
56
129
  :prerelease,
57
130
  :build,
58
131
  :release,
59
- :level,
60
- :is_release,
61
- :is_dev,
62
- :is_rc
132
+ :level
133
+
134
+
135
+ # Constructor
136
+ # =====================================================================
63
137
 
64
138
  # Construct a new Version
65
- def initialize raw:, major:, minor:, patch:, prerelease:, build:, release:
66
- @raw = raw
67
- @major = major
68
- @minor = minor
69
- @patch = patch
70
- @prerelease = prerelease
71
- @build = build
72
- @release = release
73
-
74
- @level = @prerelease[0] || 'release'
139
+ def initialize(
140
+ raw: nil,
141
+ major:,
142
+ minor: 0,
143
+ patch: 0,
144
+ prerelease: [],
145
+ build: []
146
+ )
147
+ @raw = T.maybe(T.str).check raw
148
+ @major = T.non_neg_int.check major
149
+ @minor = T.non_neg_int.check minor
150
+ @patch = T.non_neg_int.check patch
151
+ @prerelease = T.array(T.union(T.non_neg_int, T.str)).check prerelease
152
+ @build = T.array(T.union(T.non_neg_int, T.str)).check build
153
+ @release = [major, minor, patch].join '.'
75
154
 
76
- @is_release = @prerelease.empty?
77
- @is_dev = @prerelease[0] == 'dev'
78
- @is_rc = @prerelease[0] == 'rc'
155
+ @level = T.match @prerelease[0], {
156
+ T.is(nil) => ->(_) { nil },
157
+
158
+ T.str => ->(str) { str },
159
+
160
+ T.non_neg_int => ->(int) { nil },
161
+ }
79
162
  end
80
163
 
164
+
165
+ # Instance Methods
166
+ # =====================================================================
167
+
168
+ # Tests
169
+ # ---------------------------------------------------------------------
170
+
171
+ # @return [Boolean]
172
+ # True if this version is a release (no prerelease or build values).
173
+ #
81
174
  def release?
82
- @is_release
175
+ @prerelease.empty? && @build.empty?
83
176
  end
84
177
 
178
+
179
+ # @return [Boolean]
180
+ # True if any prerelease segments are present (stuff after '-' in
181
+ # SemVer / "NPM" format, or the first string segment and anything
182
+ # following it in "Gem" format). Tests if {@prerelease} is not
183
+ # empty.
184
+ #
185
+ def prerelease?
186
+ !@prerelease.empty?
187
+ end
188
+
189
+
190
+ # @return [Boolean]
191
+ # True if any build segments are present (stuff after '+' character
192
+ # in SemVer / "NPM" format). Tests if {@build} is empty.
193
+ #
194
+ # As of writing, we don't have a way to convey build segments in
195
+ # "Gem" version format, so this will always be false when loading a
196
+ # Gem version.
197
+ #
198
+ def build?
199
+ !@build.empty?
200
+ end
201
+
202
+
203
+ # @return [Boolean]
204
+ # True if self is a prerelease version that starts with a string that
205
+ # we consider the 'level'.
206
+ #
207
+ def level?
208
+ !@level.nil?
209
+ end
210
+
211
+
212
+ # @return [Boolean]
213
+ # True if this version is a dev prerelease (first prerelease element
214
+ # is 'dev').
215
+ #
85
216
  def dev?
86
- @is_dev
217
+ @level == 'dev'
87
218
  end
88
219
 
220
+
221
+ # @return [Boolean]
222
+ # True if this version is a release candidate (first prerelease element
223
+ # is 'rc').
224
+ #
89
225
  def rc?
90
- @is_rc
226
+ @level == 'rc'
227
+ end
228
+
229
+
230
+ # Transformations
231
+ # ---------------------------------------------------------------------
232
+
233
+ # @return [String]
234
+ # A normalized raw version string in
235
+ # `Major.minor.patch-prerelease+build` format.
236
+ def normalized
237
+ result = release
238
+
239
+ unless prerelease.empty?
240
+ result += "-#{ prerelease.join '.' }"
241
+ end
242
+
243
+ unless build.empty?
244
+ result += "+#{ build.join '.' }"
245
+ end
246
+
247
+ result
248
+ end # #normalized
249
+
250
+
251
+ # @return [QB::Package::Version]
252
+ # A new {QB::Package::Version} created from {#release}. Even if `self`
253
+ # *is* a release version already, still returns a new instance.
254
+ #
255
+ def release_version
256
+ self.class.from_string release
257
+ end # #release_version
258
+
259
+
260
+ def merge overrides = {}
261
+ self.class.new self.to_h.merge(overrides)
262
+ end
263
+
264
+
265
+ # Return a new {QB::Package::Version} with build information added.
266
+ #
267
+ # @return [QB::Package::Version]
268
+ #
269
+ def build_version branch: nil, ref: nil, time: nil
270
+ time = self.class.to_time_segment(time) unless time.nil?
271
+
272
+ segments = [branch, ref, time].reject &:nil?
273
+
274
+ if segments.empty?
275
+ raise ArgumentError,
276
+ "Need to provide at least one of branch, ref, time."
277
+ end
278
+
279
+ merge raw: nil,
280
+ build: segments.join('.')
91
281
  end
92
282
 
283
+
284
+ # Language Interface
285
+ # =====================================================================
286
+
287
+ # Test for equality.
288
+ #
289
+ # Compares classes then {QB::Package::Version#to_a} results.
290
+ #
291
+ # @param [Object] other
292
+ # Object to compare to self.
293
+ #
294
+ # @return [Boolean]
295
+ # True if self and other are considered equal.
296
+ #
297
+ def == other
298
+ other.class == self.class &&
299
+ other.to_a == self.to_a
300
+ end # #==
301
+
302
+
303
+ # Return array of the version elements in order from greatest to least
304
+ # precedence.
305
+ #
306
+ # This is considered the representative structure for the object's data,
307
+ # from which all other values are dependently derived, and is used in
308
+ # {#==}, {#hash} and {#eql?}.
309
+ #
310
+ # @example
311
+ #
312
+ # version = QB::Package::Version.from_string(
313
+ # "0.1.2-rc.10+master.0ab1c3d"
314
+ # )
315
+ #
316
+ # version.to_a
317
+ # # => [0, 1, 2, ['rc', 10], ['master', '0ab1c3d']]
318
+ #
319
+ # QB::Package::Version.from_string('1').to_a
320
+ # # => [1, nil, nil, [], []]
321
+ #
322
+ # @return [Array]
323
+ #
324
+ def to_a
325
+ [
326
+ major,
327
+ minor,
328
+ patch,
329
+ prerelease,
330
+ build,
331
+ ]
332
+ end # #to_a
333
+
334
+
335
+ def hash
336
+ to_a.hash
337
+ end
338
+
339
+
340
+ def eql? other
341
+ self == other && self.hash == other.hash
342
+ end
343
+
344
+
93
345
  # dump all instance variables into a hash
94
346
  def to_h
95
347
  instance_variables.map {|var|
@@ -97,14 +349,25 @@ module QB
97
349
  }.to_h
98
350
  end # #to_h
99
351
 
352
+
100
353
  # Dump all instance variables in JSON serialization
101
354
  def to_json *args
102
- to_h.to_json *args
355
+ to_h.merge(
356
+ is_release: release?,
357
+ is_prerelease: prerelease?,
358
+ is_build: build?,
359
+ is_dev: dev?,
360
+ is_rc: rc?,
361
+ has_level: level?,
362
+ normalized: normalized,
363
+ ).to_json *args
103
364
  end
104
365
 
366
+
105
367
  def to_s
106
368
  "#<QB::Package::Version #{ @raw }>"
107
369
  end
108
- end
370
+
371
+ end # class Version
109
372
  end # Package
110
373
  end # QB
data/lib/qb/role.rb CHANGED
@@ -139,7 +139,10 @@ module QB
139
139
  uniq
140
140
  end
141
141
 
142
- # get an array of QB::Role that match an input string
142
+ # Get an array of QB::Role that match an input string.
143
+ #
144
+ # @return [Array<QB::Role>]
145
+ #
143
146
  def self.matches input
144
147
  # keep this here to we don't re-gen every loop
145
148
  available = self.available
@@ -19,7 +19,16 @@ module QB
19
19
  @input = input
20
20
  @matches = matches
21
21
 
22
- super "mutiple roles match input #{ @input.inspect }:\n#{ @matches.join("\n") }"
22
+ super <<-END
23
+ multiple roles match input #{ @input.inspect }:\
24
+
25
+ #{
26
+ @matches.map { |role|
27
+ "- #{ role.to_s } (#{ role.path.to_s })"
28
+ }.join("\n")
29
+ }
30
+
31
+ END
23
32
  end
24
33
  end
25
34
 
data/lib/qb/version.rb CHANGED
@@ -4,7 +4,7 @@ module QB
4
4
 
5
5
  GEM_NAME = 'qb'
6
6
 
7
- VERSION = "0.1.72"
7
+ VERSION = "0.1.73"
8
8
 
9
9
  MIN_ANSIBLE_VERSION = Gem::Version.new '2.1.2'
10
10
 
data/library/path_facts CHANGED
@@ -29,6 +29,7 @@ end
29
29
  GITHUB_SSH_URL_RE = /^git@github\.com\:(?<owner>.*)\/(?<name>.*)\.git$/
30
30
  GITHUB_HTTPS_URL_RE = /^https:\/\/github\.com\/(?<owner>.*)\/(?<name>.*)\.git$/
31
31
 
32
+
32
33
  class PathFacts < QB::AnsibleModule
33
34
  # Add a bunch of useful things to know about the path
34
35
  def add_path_facts
@@ -57,8 +58,9 @@ class PathFacts < QB::AnsibleModule
57
58
  @result.is_realpath = @result.realpath == @path
58
59
  end
59
60
 
60
- # If the path is a Git repo (has a .git file in it's root) add useful
61
- # Git facts.
61
+
62
+ # If the path is part of a Git repo, as well as useful general
63
+ # Git environment facts.
62
64
  def add_git_facts
63
65
  # see if we're in a git repo. first, we need a directory that exists
64
66
  dir = @path.expand_path.ascend.find {|p| p.directory? }
@@ -143,6 +145,7 @@ class PathFacts < QB::AnsibleModule
143
145
  end
144
146
  end
145
147
 
148
+
146
149
  # Find the only *.gemspec path in the `@path` directory. Warns and returns
147
150
  # `nil` if there is more than one match.
148
151
  def gemspec_path
@@ -159,6 +162,7 @@ class PathFacts < QB::AnsibleModule
159
162
  end
160
163
  end
161
164
 
165
+
162
166
  # If `path` is a directory containing the source for a Ruby Gem, add
163
167
  # useful information about it.
164
168
  def add_gem_facts
@@ -186,6 +190,8 @@ class PathFacts < QB::AnsibleModule
186
190
  gem.version = QB::Package::Version.from_gem_version spec.version
187
191
  end
188
192
 
193
+
194
+
189
195
  # Add facts about an NPM package based in `@path`, if any.
190
196
  def add_npm_facts
191
197
  package_json_path = @path.join('package.json')
@@ -212,6 +218,57 @@ class PathFacts < QB::AnsibleModule
212
218
  end
213
219
  end
214
220
 
221
+
222
+ # Add version/package facts derives from @path/VERSION file if one exists.
223
+ #
224
+ # Added values:
225
+ #
226
+ # - Always:
227
+ # - `has_version_file` - Boolean
228
+ # - True if `@path/VERSION` exists and was successfully parsed.
229
+ #
230
+ # - If `has_version_file` is `true`:
231
+ # -
232
+ #
233
+ def add_version_file_facts
234
+ unless @path.directory?
235
+ @result.has_version_file = false
236
+ return
237
+ end
238
+
239
+ version_file_path = @path.join 'VERSION'
240
+
241
+ unless version_file_path.file?
242
+ @result.has_version_file = false
243
+ return
244
+ end
245
+
246
+ version = begin
247
+ QB::Package::Version.from_string version_file_path.read
248
+ rescue Exception => e
249
+ warn "Unable to parse version from #{ version_file_path.to_s }; #{ e }"
250
+ @result.has_version_file = false
251
+ return
252
+ end
253
+
254
+ @result.has_version_file = true
255
+
256
+ version_file = @result.version_file = Result.new
257
+ @result.package.types << 'version_file'
258
+
259
+ # get the name from git if we have it
260
+ if @result.is_git && @result.git.name
261
+ version_file.name = @result.git.name
262
+ else
263
+ # otherwise use the directory name
264
+ version_file.name = version_file_path.dirname.basename.to_s
265
+ end
266
+
267
+ version_file.version = version
268
+ version_file.path = version_file_path
269
+ end
270
+
271
+
215
272
  # Run the module.
216
273
  def main
217
274
  # check the 'path' arg
@@ -225,13 +282,14 @@ class PathFacts < QB::AnsibleModule
225
282
 
226
283
  # Default to signaling no change (we're not gonna change anything in this
227
284
  # module either)
228
- @result.changed = false
285
+ @result.changed = @changed
229
286
 
230
287
  # String warnings that will be shown to the user
231
- @result.warnings = []
288
+ @result.warnings = @warnings
232
289
 
233
- @result.package = Result.new
234
- @result.package.types = []
290
+ # Set this up here so `add_*_facts` can append to `package.types`
291
+ package = @result.package = Result.new
292
+ package.types = []
235
293
 
236
294
  # Return the input as 'raw'
237
295
  @result.raw = @args['path']
@@ -239,10 +297,19 @@ class PathFacts < QB::AnsibleModule
239
297
  @path = Pathname.new @result.raw
240
298
 
241
299
  add_path_facts
300
+
301
+ # Add git facts if @path is in a git repo
242
302
  add_git_facts
303
+
304
+ # Add Ruby Gem version/package facts if @path is the root of a Ruby Gem
243
305
  add_gem_facts
306
+
307
+ # Add version/package facts from a @path/package.json file if present
244
308
  add_npm_facts
245
309
 
310
+ # Add version/package facts from a @path/VERSION file if present
311
+ add_version_file_facts
312
+
246
313
  # If we only have one type of package present, we set it's type and
247
314
  # version as `package.type` and `package.version`, which makes it easy for
248
315
  # code to 'auto-detect' the info.
@@ -250,10 +317,48 @@ class PathFacts < QB::AnsibleModule
250
317
  # If there's more than one then we obviously can't guess what to do; the
251
318
  # user needs to specify.
252
319
  #
253
- if @result.package.types.length == 1
254
- @result.package.type = @result.package.types[0]
255
- @result.package.name = @result[@result.package.type].name
256
- @result.package.version = @result[@result.package.type].version
320
+ if package.types.length == 1
321
+ package.type = package.types[0]
322
+ package.name = @result[package.type].name
323
+ package.version = @result[package.type].version
324
+ else
325
+ # Otherwise, if all the present info matches, we can use that.
326
+ #
327
+ # We won't have a `type` though, because that doesn't make any sense.
328
+ #
329
+
330
+ # See if we have a unique common version that we can use for the overall
331
+ # version of the package.
332
+ #
333
+ versions = package.types.
334
+ # Map the types to their version object
335
+ map { |type| @result[type].version }.
336
+ # Omit any that don't have a version
337
+ reject(&:nil?).
338
+ # Reduce to unique versions
339
+ uniq
340
+
341
+ # Do we have a single unique version?
342
+ if versions.length == 1
343
+ # Yes!
344
+ package.version = versions.first
345
+ end
346
+
347
+ # See if we have a unique common name that we can use use for the overall
348
+ # name of the package
349
+ #
350
+ # TODO since version_file kinda fudges this using the git repo name or
351
+ # directory name, this might be problematic since those may not
352
+ # stay consistent... we'll have to see how it goes.
353
+ #
354
+ names = package.types.
355
+ map { |type| @result[type].name }.
356
+ reject(&:nil?).
357
+ uniq
358
+
359
+ if names.length == 1
360
+ package.name = names.first
361
+ end
257
362
  end
258
363
 
259
364
  nil
data/qb.gemspec CHANGED
@@ -94,7 +94,7 @@ Gem::Specification.new do |spec|
94
94
  spec.add_development_dependency "yard"
95
95
 
96
96
  spec.add_dependency "cmds",'~> 0.0', ">= 0.2.1"
97
- spec.add_dependency "nrser",'~> 0.0', ">= 0.0.17"
97
+ spec.add_dependency "nrser",'~> 0.0', ">= 0.0.18"
98
98
  spec.add_dependency "nrser-extras", '~> 0.0', ">= 0.0.3"
99
99
  spec.add_dependency "state_mate", '~> 0.0', ">= 0.0.9"
100
100
  spec.add_dependency 'parseconfig', '~> 1.0', '>= 1.0.8'
@@ -32,8 +32,8 @@
32
32
 
33
33
  {'rubies_list' => list}
34
34
 
35
- - debug:
36
- msg: "{{ rbenv_gem_rubies_list }}"
35
+ # - debug:
36
+ # msg: "{{ rbenv_gem_rubies_list }}"
37
37
 
38
38
  # this will clone the repo, build the .gem and point rbenv_gem_source to it
39
39
  - include: clone-repo.yml
@@ -30,7 +30,7 @@
30
30
  version: "{{ rbenv_gem_version }}"
31
31
  executable: /usr/bin/gem
32
32
  become: true
33
- when: "{{ rbenv_gem_version and (item == 'system') }}"
33
+ when: rbenv_gem_version and (item == 'system')
34
34
  with_items: "{{ rbenv_gem_rubies_list }}"
35
35
 
36
36
  - name: "manage {{ rbenv_gem_name }} gem in system ruby"
@@ -41,5 +41,5 @@
41
41
  version: "{{ rbenv_gem_version }}"
42
42
  executable: /usr/bin/gem
43
43
  become: true
44
- when: "{{ (not rbenv_gem_version) and (item == 'system') }}"
44
+ when: (not rbenv_gem_version) and (item == 'system')
45
45
  with_items: "{{ rbenv_gem_rubies_list }}"
@@ -113,7 +113,7 @@
113
113
  # shitty ansible.
114
114
  #
115
115
  # - name: kickoff frontend build...
116
- # when: "{{ bump_build }}"
116
+ # when: bump_build
117
117
  # include_role:
118
118
  # name: build
119
119
  # vars:
@@ -1,34 +1,51 @@
1
1
  ---
2
2
  # tasks file for qb.git_repo
3
3
 
4
- - name: initialize git
4
+ - name: >-
5
+ Create {{ git_repo_dest }} directory
6
+ file:
7
+ dest: >-
8
+ {{ git_repo_dest }}
9
+ state: directory
10
+
11
+ - name: >-
12
+ Initialize git
5
13
  command: git init
6
14
  args:
7
- chdir: "{{ git_repo_dest }}"
8
- creates: "{{ git_repo_dest }}/.git"
15
+ chdir: >-
16
+ {{ git_repo_dest }}
17
+ creates: >-
18
+ {{ git_repo_dest }}/.git
9
19
 
10
- - name: Tell Git to ignore OSX artifacts
20
+ - name: >-
21
+ Tell Git to ignore OSX artifacts
11
22
  when: ansible_distribution == "MacOSX"
12
23
  include_role:
13
24
  name: qb.gitignore
14
25
  vars:
15
26
  gitignore_name: Global/macOS
16
- gitignore_dest: "{{ git_repo_dest }}"
27
+ gitignore_dest: >-
28
+ {{ git_repo_dest }}
17
29
 
18
- - name: Tell Git to ignore QB artifacts
30
+ - name: >-
31
+ Tell Git to ignore QB artifacts
19
32
  include_role:
20
33
  name: qb.gitignore
21
34
  vars:
22
35
  gitignore_name: QB
23
- gitignore_dest: "{{ git_repo_dest }}"
36
+ gitignore_dest: >-
37
+ {{ git_repo_dest }}
24
38
 
25
39
  - name: >-
26
40
  Tell Git to ignore each of <git_repo_gitignores>
27
- with_items: "{{ git_repo_gitignores }}"
41
+ with_items: >-
42
+ {{ git_repo_gitignores }}
28
43
  loop_control:
29
44
  loop_var: name
30
45
  include_role:
31
46
  name: qb.gitignore
32
47
  vars:
33
- gitignore_name: "{{ name }}"
34
- gitignore_dest: "{{ git_repo_dest }}"
48
+ gitignore_name: >-
49
+ {{ name }}
50
+ gitignore_dest: >-
51
+ {{ git_repo_dest }}
@@ -1,13 +1,20 @@
1
1
  ---
2
2
  # defaults file for qb.install
3
3
 
4
- install_path: "{{ qb_user_roles_dir }}"
4
+ install_path: >-
5
+ {{ qb_user_roles_dir }}
5
6
 
6
- install_src: "{% if install_create -%}
7
- {{ ansible_env.GITHUB_HOME }}/{{ install_name.split('.')[0] }}/ansible-{{ install_name }}
8
- {%- else -%}
9
- {{ install_name.split('.')[0] }}/ansible-{{ install_name }}
10
- {%- endif %}"
7
+ install_src: >-
8
+ {{
9
+ (
10
+ ansible_env.GITHUB_HOME | path_join(
11
+ install_name.split('.')[0],
12
+ ('ansible-' + install_name)
13
+ )
14
+ ) if install_create else (
15
+ install_name.split('.')[0] | path_join('ansible-' + install_name)
16
+ )
17
+ }}
11
18
 
12
19
  install_version: master
13
20
 
@@ -15,10 +22,13 @@ install_force: false
15
22
 
16
23
  install_update: false
17
24
 
18
- install_link: "{% if install_create -%}
19
- true
20
- {%- else -%}
21
- false
22
- {%- endif %}"
25
+ install_link: >-
26
+ {{ install_create }}
23
27
 
24
28
  install_create: false
29
+
30
+ install_edit: >-
31
+ {{ install_create }}
32
+
33
+ install_add_to_project: false
34
+
@@ -6,7 +6,7 @@ allow_duplicates: yes
6
6
  dependencies: # []
7
7
  # - role: role-name
8
8
  - role: qb.qb_role
9
- when: "{{ install_create }}"
9
+ when: install_create
10
10
  role_dest: "{{ install_src }}"
11
11
  role_role_name: "{{ install_name }}"
12
12
  role_project: true
@@ -39,11 +39,17 @@ default_user: null
39
39
  save_options: false
40
40
 
41
41
  examples:
42
- create a new role backed by a GitHub repo: >
42
+ Create a new role backed by a GitHub repo: >
43
43
 
44
44
  qb install --create --name=nrser.blah
45
45
 
46
46
  (`name` needs to have the namespace attached if one is desired)
47
+
48
+ Add a newly created role to the current Atom project:
49
+
50
+ (Assumes $EDITOR is set to Atom's path)
51
+
52
+ qb install --create --name=nrser.blah --add-to-project
47
53
 
48
54
  options:
49
55
  # - name: example
@@ -101,3 +107,18 @@ options:
101
107
  required: false
102
108
  type: boolean
103
109
  short: c
110
+
111
+ - name: edit
112
+ description: >-
113
+ Open the role direcotry in EDITOR when done.
114
+ type: boolean
115
+ short: e
116
+
117
+ - name: add_to_project
118
+ description: >-
119
+ Include an `--add` option when opening role directory in EDITOR.
120
+ Added for Atom to add to current project, but who knows, maybe
121
+ works elsewhere too.
122
+ type: boolean
123
+ short: a
124
+
@@ -0,0 +1,9 @@
1
+ ---
2
+
3
+ - name: >-
4
+ Open new qb role repo in Atom
5
+ shell: >-
6
+ {{ ansible_env.EDITOR.replace('--wait', '') }}
7
+ {% if install_add_to_project %}--add{% endif %}
8
+ {{ install_src }}
9
+
@@ -1,16 +1,17 @@
1
1
  ---
2
2
  # symlink to src directory
3
3
 
4
- - name: |
4
+ - when: install_force
5
+ name: |
5
6
  remove destination
6
7
 
7
8
  {{ install_path }}/{{ install_name }}
8
9
 
9
10
  prior to linking.
10
11
  file:
11
- path: "{{ install_path }}/{{ install_name }}"
12
+ path: >-
13
+ {{ install_path }}/{{ install_name }}
12
14
  state: absent
13
- when: "{{ install_force }}"
14
15
 
15
16
  - name: |
16
17
  symlink
@@ -21,7 +22,9 @@
21
22
 
22
23
  {{ install_src | realpath }}
23
24
  file:
24
- src: "{{ install_src | realpath }}"
25
- dest: "{{ install_path }}/{{ install_name }}"
25
+ src: >-
26
+ {{ install_src | realpath }}
27
+ dest: >-
28
+ {{ install_path }}/{{ install_name }}
26
29
  state: link
27
30
 
@@ -7,5 +7,5 @@
7
7
  - include: link.yml
8
8
  when: install_link
9
9
 
10
- - include: create.yml
11
- when: install_create
10
+ - include: edit.yml
11
+ when: install_edit
@@ -7,10 +7,37 @@
7
7
  bind:
8
8
  qb_dir: "{{ qb_dir }}"
9
9
  src: |
10
- spec = Gem::Specification::load(Dir.glob("#{ qb_dir }/*.gemspec")[0])
11
- name = spec.name
10
+ spec_pattern = "#{ qb_dir }/*.gemspec"
11
+ spec_path = Dir.glob(spec_pattern)[0]
12
12
 
13
- segments = spec.version.segments.dup
13
+ if spec_path.nil?
14
+ raise "No gemspec found for pattern #{ spec_pattern }"
15
+ end
16
+
17
+ spec = Gem::Specification.load spec_path
18
+
19
+ # The gem *may already be loaded*, which would break the standard gemspec
20
+ # approach because the `require` will be a no-op, resulting in the
21
+ # already loaded version number being used instead of the one in the
22
+ # file.
23
+ #
24
+ # This is only a problem for NRSER, which is loaded in vars.rb.rb, but
25
+ # this fix should work for any module without worrying about what is
26
+ # currently loaded... grab the info we need in a clean child process.
27
+ #
28
+ code = <<-END
29
+ require 'json'
30
+ spec = Gem::Specification.load(#{ JSON.dump spec_path })
31
+ puts JSON.dump({
32
+ 'version' => spec.version.version,
33
+ 'name' => spec.name,
34
+ })
35
+ END
36
+ obj = JSON.load `ruby -e #{ code.shellescape }`
37
+ version = Gem::Version.new obj['version']
38
+ name = obj['name']
39
+
40
+ segments = version.segments.dup
14
41
  segments.pop while segments.any? {|s| s.is_a? String}
15
42
 
16
43
  segments[-1] = segments[-1].succ
@@ -18,24 +45,27 @@
18
45
 
19
46
  next_version = segments.join('.')
20
47
 
21
- # match = /^(\d+)\.(\d+)\.(\d+)(\.dev)?$/.match(current_version)
22
-
23
- # if current_version.ends_with? '.dev'
24
- # end
25
-
26
48
  {
27
49
  'name' => name,
28
- 'current_version' => spec.version.version,
29
- 'release_version' => spec.version.release,
50
+ 'current_version' => version.version,
51
+ 'release_version' => version.release,
30
52
  'next_version' => next_version,
31
53
  'version_path' => "#{ qb_dir }/lib/#{ name }/version.rb",
54
+ 'spec_path' => spec_path,
32
55
  }
33
56
 
34
57
  - debug:
35
- msg: >
36
- current: {{ release_gem_current_version }} |
37
- release: {{ release_gem_release_version }} |
38
- next: {{ release_gem_next_version }}
58
+ msg:
59
+ # - spec_path: >-
60
+ # {{ release_gem_spec_path }}
61
+ # - version_path: >-
62
+ # {{ release_gem_version_path }}
63
+ - current: >-
64
+ {{ release_gem_current_version }}
65
+ - release: >-
66
+ {{ release_gem_release_version }}
67
+ - next: >-
68
+ {{ release_gem_next_version }}
39
69
 
40
70
  - name: "change version to release version {{ release_gem_release_version }}"
41
71
  replace:
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.1.72
4
+ version: 0.1.73
5
5
  platform: ruby
6
6
  authors:
7
7
  - nrser
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-08-09 00:00:00.000000000 Z
11
+ date: 2017-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -95,7 +95,7 @@ dependencies:
95
95
  version: '0.0'
96
96
  - - ">="
97
97
  - !ruby/object:Gem::Version
98
- version: 0.0.17
98
+ version: 0.0.18
99
99
  type: :runtime
100
100
  prerelease: false
101
101
  version_requirements: !ruby/object:Gem::Requirement
@@ -105,7 +105,7 @@ dependencies:
105
105
  version: '0.0'
106
106
  - - ">="
107
107
  - !ruby/object:Gem::Version
108
- version: 0.0.17
108
+ version: 0.0.18
109
109
  - !ruby/object:Gem::Dependency
110
110
  name: nrser-extras
111
111
  requirement: !ruby/object:Gem::Requirement
@@ -288,13 +288,11 @@ files:
288
288
  - roles/nrser.rbenv_gem/README.md
289
289
  - roles/nrser.rbenv_gem/VERSION
290
290
  - roles/nrser.rbenv_gem/defaults/main.yml
291
- - roles/nrser.rbenv_gem/handlers/main.yml
292
291
  - roles/nrser.rbenv_gem/meta/main.yml
293
292
  - roles/nrser.rbenv_gem/tasks/clone-repo.yml
294
293
  - roles/nrser.rbenv_gem/tasks/main.yml
295
294
  - roles/nrser.rbenv_gem/tasks/manage-source.yml
296
295
  - roles/nrser.rbenv_gem/tasks/manage-version.yml
297
- - roles/nrser.rbenv_gem/vars/main.yml
298
296
  - roles/nrser.state_mate/.gitignore
299
297
  - roles/nrser.state_mate/.rspec
300
298
  - roles/nrser.state_mate/README.md
@@ -562,7 +560,7 @@ files:
562
560
  - roles/qb.install/defaults/main.yml
563
561
  - roles/qb.install/meta/main.yml
564
562
  - roles/qb.install/meta/qb.yml
565
- - roles/qb.install/tasks/create.yml
563
+ - roles/qb.install/tasks/edit.yml
566
564
  - roles/qb.install/tasks/git.yml
567
565
  - roles/qb.install/tasks/link.yml
568
566
  - roles/qb.install/tasks/main.yml
@@ -1,2 +0,0 @@
1
- ---
2
- # handlers file for nrser.rbenv_gem
@@ -1,2 +0,0 @@
1
- ---
2
- # vars file for nrser.rbenv_gem
@@ -1,4 +0,0 @@
1
- ---
2
- - name: open new qb role repo in atom
3
- shell: "atom {{ install_src }}"
4
-