qb 0.1.72 → 0.1.73

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