qb 0.3.17 → 0.3.18

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: 9234ff3dddbac0d5c2b4c640f63873e53a18d9b2
4
- data.tar.gz: a9a33b610f3691b8873efef6fa9b7cce0f6a113c
3
+ metadata.gz: 36d941451916fb21a0e92a4c73520240b12a7b4a
4
+ data.tar.gz: 6e11fb934d559165fe98096924e3afe27c80b684
5
5
  SHA512:
6
- metadata.gz: cdee8091f290db8ccd416b1463d95cb1e7ce949c284a63b6941d5aac6d5ac7c603b78317968749026e77af09dc3573009f4cb20992e83d11f6d4929685041c01
7
- data.tar.gz: 40dfc00d62b08512ae3a02ddf863f4618bb7623440fb9a99600ccd1b12cd2534da30d76a5890f2ebb00640cef1e1722dc3357f1de0b891945cdc3380241b245b
6
+ metadata.gz: b43a422d14c11a56580fa543f3fa6679ee0c3187d7379b6bfe942fc6c3d194baa74a8da76dd8f6a4fe74acbbe8633f4129c2173e23ed59c7c383566e1ef59bec
7
+ data.tar.gz: 657b3bd019e0cc539a21dc072d5a6d498a68a1e4b73af6e70ce517299d5445512d78de6316942f31ed46c0905c08af51d9658aa1400a7c1f1d68d37244f5cb7f
data/exe/qb CHANGED
@@ -55,6 +55,8 @@ def main args
55
55
  [:run, args.rest]
56
56
  when 'setup'
57
57
  [:setup, args.rest]
58
+ when 'list', 'ls'
59
+ [:list, *args.rest]
58
60
  else
59
61
  # default to `run` on the full args
60
62
  [:run, args]
@@ -12,6 +12,7 @@ require_relative './cli/help'
12
12
  require_relative './cli/play'
13
13
  require_relative './cli/run'
14
14
  require_relative './cli/setup'
15
+ require_relative './cli/list'
15
16
 
16
17
 
17
18
  # Requirements
@@ -1,15 +1,3 @@
1
- # Requirements
2
- # =====================================================================
3
-
4
- # package
5
-
6
-
7
- # Declarations
8
- # =======================================================================
9
-
10
- module QB; end
11
-
12
-
13
1
  # Definitions
14
2
  # =======================================================================
15
3
 
@@ -37,11 +25,17 @@ module QB::CLI
37
25
  # Error exit status - we don't want `qb ... && ...` to move on to the
38
26
  # second command when we end up falling back to `help`.
39
27
  #
40
- def self.list args = []
41
- puts QB::Role.available
28
+ def self.list pattern = nil
29
+ roles = if pattern
30
+ QB::Role.matches pattern
31
+ else
32
+ QB::Role.available
33
+ end
34
+
35
+ puts roles
42
36
  puts
43
37
 
44
- return 1
38
+ return 0
45
39
  end # .help
46
40
 
47
41
  end # module QB::CLI
@@ -93,8 +93,8 @@ class QB::Package::Version < QB::Util::Resource
93
93
  prop :major, type: NUMBER_SEGMENT
94
94
  prop :minor, type: NUMBER_SEGMENT, default: 0
95
95
  prop :patch, type: NUMBER_SEGMENT, default: 0
96
- prop :prerelease, type: t.array(MIXED_SEGMENT), default: []
97
- prop :build, type: t.array(MIXED_SEGMENT), default: []
96
+ prop :prerelease, type: t.array(MIXED_SEGMENT), default: ->{ [] }
97
+ prop :build, type: t.array(MIXED_SEGMENT), default: ->{ [] }
98
98
 
99
99
  prop :release, type: t.str, source: :@release
100
100
  prop :is_release, type: t.bool, source: :release?
@@ -7,16 +7,16 @@
7
7
  require 'yaml'
8
8
  require 'cmds'
9
9
 
10
-
11
10
  # deps
12
11
  # ----------------------------------------------------------------------------
13
12
 
14
-
15
13
  # package
16
14
  # ----------------------------------------------------------------------------
17
15
 
18
16
  # Breakouts
19
17
  require 'qb/role/errors'
18
+ require 'qb/role/search_path'
19
+ require 'qb/role/matches'
20
20
  require 'qb/role/name'
21
21
  require 'qb/role/default_dir'
22
22
 
@@ -24,14 +24,8 @@ require 'qb/role/default_dir'
24
24
  # Refinements
25
25
  # =======================================================================
26
26
 
27
- require 'nrser/refinements'
28
27
  using NRSER
29
-
30
-
31
- # Declarations
32
- # =======================================================================
33
-
34
- module QB; end
28
+ using NRSER::Types
35
29
 
36
30
 
37
31
  # Contains info on a QB role.
@@ -44,134 +38,9 @@ class QB::Role
44
38
  include SemanticLogger::Loggable
45
39
 
46
40
 
47
- # Constants
48
- # =====================================================================
49
-
50
- # Array of string paths to directories to search for roles or paths to
51
- # `ansible.cfg` files to look for an extract role paths from.
52
- #
53
- # For the moment at least you can just mutate this value like you would
54
- # `$LOAD_PATH`:
55
- #
56
- # QB::Role::PATH.unshift '~/where/some/roles/be'
57
- # QB::Role::PATH.unshift '~/my/ansible.cfg'
58
- #
59
- # The paths are searched from first to last.
60
- #
61
- # **WARNING**
62
- #
63
- # Search is **deep** - don't point this at large directory trees and
64
- # expect any sort of reasonable performance (any directory that
65
- # contains `node_modules` is usually a terrible idea for instance).
66
- #
67
- BUILTIN_PATH = [
68
-
69
- # Development Paths
70
- # =================
71
- #
72
- # These come first because:
73
- #
74
- # 1. They are working dir-local.
75
- #
76
- # 2. They should only be present in local development, and should be
77
- # capable of overriding roles in other local directories to allow
78
- # custom development behavior (the same way `./dev/bin` is put in
79
- # front or `./bin`).
80
- #
81
-
82
- # Role paths declared in ./dev/ansible.cfg, if it exists.
83
- File.join('.', 'dev', 'ansible.cfg'),
84
-
85
- # Roles in ./dev/roles
86
- File.join('.', 'dev', 'roles'),
87
-
88
-
89
- # Working Directory Paths
90
- # =======================
91
- #
92
- # Next up, `ansible.cfg` and `roles` directory in the working dir.
93
- # Makes sense, right?
94
- #
95
-
96
- # ./ansible.cfg
97
- File.join('.', 'ansible.cfg'),
98
-
99
- # ./roles
100
- File.join('.', 'roles'),
101
-
102
-
103
- # Working Directory-Local Ansible Directory
104
- # =========================================
105
- #
106
- # `ansible.cfg` and `roles` in a `./ansible` directory, making a common
107
- # place to put Ansible stuff in an project accessible when running from
108
- # the project root.
109
- #
110
-
111
- # ./ansible/ansible.cfg
112
- File.join('.', 'ansible', 'ansible.cfg'),
113
-
114
- # ./ansible/roles
115
- File.join('.', 'ansible', 'roles'),
116
-
117
- # TODO Git repo root relative?
118
- # Some sort of flag file for a find-up?
119
- # System Ansible locations?
120
-
121
-
122
- # QB Gem Role Directories
123
- # =======================
124
- #
125
- # Last, but far from least, paths provided by the QB Gem to the user's
126
- # QB role install location and the roles that come built-in to the gem.
127
-
128
- QB::USER_ROLES_DIR,
129
-
130
- QB::GEM_ROLES_DIR,
131
- ].freeze
132
-
133
-
134
- # Array of string paths to directories to search for roles or paths to
135
- # `ansible.cfg` files to look for an extract role paths from.
136
- #
137
- # Value is a duplicate of the frozen {QB::Role::BUILTIN_PATH}. You can
138
- # reset to those values at any time via {QB::Role.reset_path!}.
139
- #
140
- # For the moment at least you can just mutate this value like you would
141
- # `$LOAD_PATH`:
142
- #
143
- # QB::Role::PATH.unshift '~/where/some/roles/be'
144
- # QB::Role::PATH.unshift '~/my/ansible.cfg'
145
- #
146
- # The paths are searched from first to last.
147
- #
148
- # **WARNING**
149
- #
150
- # Search is **deep** - don't point this at large directory trees and
151
- # expect any sort of reasonable performance (any directory that
152
- # contains `node_modules` is usually a terrible idea for instance).
153
- #
154
- PATH = BUILTIN_PATH.dup
155
-
156
-
157
41
  # Class Methods
158
42
  # =======================================================================
159
43
 
160
- # Reset {QB::Role::PATH} to the original built-in values in
161
- # {QB::Role::BUILTIN_PATH}.
162
- #
163
- # Created for testing but might be useful elsewhere as well.
164
- #
165
- # @return [Array<String>]
166
- # The reset {QB::Role::PATH}.
167
- #
168
- def self.reset_path!
169
- PATH.clear
170
- BUILTIN_PATH.each { |path| PATH << path }
171
- PATH
172
- end # .reset_path!
173
-
174
-
175
44
  # true if pathname is a QB role directory.
176
45
  def self.role_dir? pathname
177
46
  # must be a directory
@@ -181,54 +50,15 @@ class QB::Role
181
50
  end
182
51
 
183
52
 
184
- # @param dir [Pathname] dir to include.
185
- def self.roles_paths dir
186
- cfg_roles_path(dir) + [
187
- dir.join('roles'),
188
- dir.join('roles', 'tmp'),
189
- ]
190
- end
191
-
192
- # places to look for qb role directories. these paths are also included
193
- # when qb runs a playbook.
194
- #
195
- # TODO resolution order:
196
- #
197
- # 1. paths specific to this run:
198
- # a. TODO paths provided on the cli.
199
- # 2. paths specific to the current directory:
200
- # a. paths specified in ./ansible.cfg (if it exists)
201
- # b. ./roles
202
- # d. paths specified in ./ansible/ansible.cfg (if it exists)
203
- # e. ./ansible/roles
204
- # g. paths specified in ./dev/ansible.cfg (if it exists)
205
- # h. ./dev/roles
206
- # i. ./dev/roles/tmp
207
- # - used for roles that are downloaded but shouldn't be included
208
- # in source control.
209
- # 3.
210
- #
211
- # @return [Array<Pathname>]
212
- # places to look for role dirs.
213
- #
214
- def self.search_path
215
- QB::Role::PATH.
216
- map { |path|
217
- if QB::Ansible::ConfigFile.end_with_config_file?(path)
218
- if File.file?(path)
219
- QB::Ansible::ConfigFile.new(path).defaults.roles_path
220
- end
221
- else
222
- QB::Util.resolve path
223
- end
224
- }.
225
- flatten.
226
- reject(&:nil?)
227
- end
228
-
229
- # array of QB::Role found in search path.
53
+ # All {QB::Role} found in search path.
54
+ #
55
+ # Does it's best to remove duplicates that end up being reached though
56
+ # multiple search paths (happens most in development).
57
+ #
58
+ # @return [Array<QB::Role>]
59
+ #
230
60
  def self.available
231
- search_path.
61
+ self.search_path.
232
62
  select {|search_dir|
233
63
  # make sure it's there (and a directory)
234
64
  search_dir.directory?
@@ -241,93 +71,11 @@ class QB::Role
241
71
  }
242
72
  }
243
73
  }.
244
- flatten(1).
245
- map {|args|
246
- QB::Role.new *args
247
- }.
74
+ flatten( 1 ).
75
+ map { |args| QB::Role.new *args }.
248
76
  uniq
249
77
  end
250
78
 
251
- # Get an array of QB::Role that match an input string.
252
- #
253
- # @return [Array<QB::Role>]
254
- #
255
- def self.matches input
256
- # keep this here to we don't re-gen every loop
257
- available = self.available
258
-
259
- # first off, see if input matches any relative paths exactly
260
- available.each {|role|
261
- return [role] if role.display_path.to_s == input
262
- }
263
-
264
- # create an array of "separator" variations to try *exact* matching
265
- # against. in order of preference:
266
- #
267
- # 1. exact input
268
- # - this means if you ended up with roles that actually *are*
269
- # differnetiated by '_/-' differences (which, IMHO, is a
270
- # horrible fucking idea), you can get exactly what you ask for
271
- # as a first priority
272
- # 2. input with '-' changed to '_'
273
- # - prioritized because convetion is to underscore-separate
274
- # role names.
275
- # 3. input with '_' changed to '-'
276
- # - really just for convience's sake so you don't really have to
277
- # remember what separator is used.
278
- #
279
- separator_variations = [
280
- input,
281
- input.gsub('-', '_'),
282
- input.gsub('_', '-'),
283
- ]
284
-
285
- separator_variations.each { |variation|
286
- available.each { |role|
287
- # exact match to full name
288
- return [role] if role.name == variation
289
- }.each { |role|
290
- # exact match without the namespace prefix ('qb.' or similar)
291
- return [role] if role.namespaceless == variation
292
- }
293
- }
294
-
295
- # see if we prefix match any full names
296
- separator_variations.each { |variation|
297
- name_prefix_matches = available.select { |role|
298
- role.name.start_with? variation
299
- }
300
- return name_prefix_matches unless name_prefix_matches.empty?
301
- }
302
-
303
- # see if we prefix match any name
304
- separator_variations.each { |variation|
305
- namespaceless_prefix_matches = available.select { |role|
306
- role.namespaceless.start_with? variation
307
- }
308
- unless namespaceless_prefix_matches.empty?
309
- return namespaceless_prefix_matches
310
- end
311
- }
312
-
313
- # see if we prefix match any display paths
314
- separator_variations.each { |variation|
315
- path_prefix_matches = available.select { |role|
316
- role.display_path.start_with? variation
317
- }
318
- return path_prefix_matches unless path_prefix_matches.empty?
319
- }
320
-
321
- # see if we word match any display paths
322
- name_word_matches = available.select { |role|
323
- QB::Util.words_start_with? role.display_path.to_s, input
324
- }
325
- return name_word_matches unless name_word_matches.empty?
326
-
327
- # nada
328
- []
329
- end # .matches
330
-
331
79
 
332
80
  # Find exactly one matching role for the input string or raise.
333
81
  #
@@ -499,8 +247,15 @@ class QB::Role
499
247
  # Instance Methods
500
248
  # =====================================================================
501
249
 
250
+ # Just a string version of {#display_path}
251
+ #
252
+ def display_name
253
+ display_path.to_s
254
+ end
255
+
256
+
502
257
  def options_key
503
- @display_path.to_s
258
+ display_name
504
259
  end
505
260
 
506
261
  # load qb metadata from meta/qb.yml or from executing meta/qb and parsing
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # {QB::Role.matches} method, which is the (messy) bulk of figuring out what
5
+ # role to run based on user input.
6
+ #
7
+ # Broken out from the main `//lib/qb/role.rb` file because it was starting to
8
+ # get long and unwieldy.
9
+ #
10
+ ##
11
+
12
+ # Refinements
13
+ # =======================================================================
14
+
15
+ using NRSER
16
+ using NRSER::Types
17
+
18
+
19
+ # Definitions
20
+ # =======================================================================
21
+
22
+ class QB::Role
23
+
24
+ # Get an array of {QB::Role} that match an input string.
25
+ #
26
+ # This is the meat of whats needed to support {QB::Role.require}.
27
+ #
28
+ # How it works is... tricky. Read the comments and play around with it is
29
+ # the bast I can offer right now.
30
+ #
31
+ # @param [String] input
32
+ # The input string to match against role paths and names. Primarily what
33
+ # the user typed after `qb run` on the CLI.
34
+ #
35
+ # @return [Array<QB::Role>]
36
+ #
37
+ def self.matches input
38
+ # keep this here to we don't re-gen every loop
39
+ available = self.available
40
+
41
+ # first off, see if input matches any relative paths exactly
42
+ available.each {|role|
43
+ return [role] if role.display_path.to_s == input
44
+ }
45
+
46
+ # create an array of "separator" variations to try *exact* matching
47
+ # against. in order of preference:
48
+ #
49
+ # 1. exact input
50
+ # - this means if you ended up with roles that actually *are*
51
+ # differentiated by '_/-' differences (which, IMHO, is a
52
+ # horrible fucking idea), you can get exactly what you ask for
53
+ # as a first priority
54
+ # 2. input with '-' changed to '_'
55
+ # - prioritized because convention is to underscore-separate
56
+ # role names.
57
+ # 3. input with '_' changed to '-'
58
+ # - really just for convenience's sake so you don't really have to
59
+ # remember what separator is used.
60
+ #
61
+ separator_variations = [
62
+ input,
63
+ input.gsub('-', '_'),
64
+ input.gsub('_', '-'),
65
+ ]
66
+
67
+ # {QB::Role} method names to check against, from highest to lowest
68
+ # precedence
69
+ method_names = [
70
+ # 1. The path we display to the user. This comes first because typing
71
+ # in exactly what they see should always work.
72
+ :display_name,
73
+
74
+ # 2. The role's full name (with namespace) as it is likely to be used
75
+ # in Ansible
76
+ :name,
77
+
78
+ # 3. The part of the role after the namespace, which is far less
79
+ # specific, but nice short-hand if it's unique
80
+ :namespaceless
81
+ ]
82
+
83
+ # 1. Exact matches (allowing `-`/`_` substitution)
84
+ #
85
+ # Highest precedence, guaranteeing that exact verbatim matches will
86
+ # always work (or that's the intent).
87
+ #
88
+ method_names.each { |method_name|
89
+ separator_variations.each { |variation|
90
+ matches = available.select { |role|
91
+ role.public_send( method_name ) == variation
92
+ }
93
+ return matches unless matches.empty?
94
+ }
95
+ }
96
+
97
+ # 2. Prefix matches
98
+ #
99
+ # Do any of {#display_path}, {#name} or {#namespaceless} or start with
100
+ # the input pattern?
101
+ #
102
+ method_names.each { |method_name|
103
+ separator_variations.each { |variation|
104
+ matches = available.select { |role|
105
+ role.public_send( method_name ).start_with? variation
106
+ }
107
+ return matches unless matches.empty?
108
+ }
109
+ }
110
+
111
+ # 3. Word slice full matches
112
+ #
113
+ # Split the {#display_name} and input first by `/` and `.` segments,
114
+ # then {String#downcase} each segments and split it into words (using
115
+ # {NRSER.words}).
116
+ #
117
+ # Then see if the input appears in the role name.
118
+ #
119
+ # We test only {#display_name} because it should always contain
120
+ # {#name} and {#namesaceless}, so it's pointless to test the other
121
+ # two after it).
122
+ #
123
+
124
+ word_parse = ->( string ) {
125
+ string.split( /[\/\.]/ ).map { |seg| seg.downcase.words }
126
+ }
127
+
128
+ input_parse = word_parse.call input
129
+
130
+ exact_word_slice_matches = available.select { |role|
131
+ word_parse.call( role.display_name ).slice? input_parse
132
+ }
133
+
134
+ return exact_word_slice_matches unless exact_word_slice_matches.empty?
135
+
136
+ # 4. Word slice prefix matches
137
+ #
138
+ # Same thing as (3), but do a prefix match instead of the entire
139
+ # words.
140
+ #
141
+ name_word_matches = available.select { |role|
142
+ word_parse.call( role.display_name ).
143
+ slice?( input_parse ) { |role_words, input_words|
144
+ # Use a custom match block to implement prefix matching
145
+ #
146
+ # We want to match if each input word is the start of the
147
+ # corresponding role name word
148
+ #
149
+ if role_words.length >= input_words.length
150
+ input_words.each_with_index.all? { |input_word, index|
151
+ role_words[index].start_with? input_word
152
+ }
153
+ else
154
+ false
155
+ end
156
+ }
157
+ QB::Util.words_start_with? role.display_path.to_s, input
158
+ }
159
+ return name_word_matches unless name_word_matches.empty?
160
+
161
+ # nada
162
+ []
163
+ end # .matches
164
+
165
+ end # class QB::Role
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  ##
2
4
  # {QB::Role} methods for dealing with role names.
3
5
  #
4
- # Broken out from the main `//lib/qb/role.rb` file because it was starting to
6
+ # Broken out from the main `//lib/qb/role.rb` file because it was starting to
5
7
  # get long and unwieldy.
6
8
  #
7
9
  ##
@@ -23,12 +25,8 @@ require 'qb/util'
23
25
  # Refinements
24
26
  # =======================================================================
25
27
 
26
- require 'nrser/refinements'
27
28
  using NRSER
28
-
29
-
30
- # Declarations
31
- # =======================================================================
29
+ using NRSER::Types
32
30
 
33
31
 
34
32
  # Definitions
@@ -66,8 +64,8 @@ class QB::Role
66
64
  # Find the first directory in the search path that contains the path,
67
65
  # if any do.
68
66
  #
69
- # It *could* be in more than one in funky situations like overlapping
70
- # search paths or link silliness, but that doesn't matter - we consider
67
+ # It *could* be in more than one in funky situations like overlapping
68
+ # search paths or link silliness, but that doesn't matter - we consider
71
69
  # the first place we find it to be the relevant once, since the search
72
70
  # path is most-important-first.
73
71
  #
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # {QB::Role} methods for dealing with role search path.
5
+ #
6
+ # Broken out from the main `//lib/qb/role.rb` file because it was starting to
7
+ # get long and unwieldy.
8
+ #
9
+ ##
10
+
11
+ # Refinements
12
+ # =======================================================================
13
+
14
+ using NRSER
15
+ using NRSER::Types
16
+
17
+
18
+ # Definitions
19
+ # =======================================================================
20
+
21
+ class QB::Role
22
+
23
+ # Constants
24
+ # =====================================================================
25
+
26
+ # "Factory defaults" that {QB::Role::PATH} is initialized to, and what it
27
+ # gets reset to when {QB::Role.reset_path!} is called.
28
+ #
29
+ # Read the {QB::Role::PATH} docs for details on how QB role paths work.
30
+ #
31
+ # This value is deeply frozen, and you should not attempt to change it -
32
+ # mess with {QB::Role::PATH} instead.
33
+ #
34
+ # @return [Array<String>]
35
+ #
36
+ BUILTIN_PATH = [
37
+
38
+ # Development Paths
39
+ # =================
40
+ #
41
+ # These come first because:
42
+ #
43
+ # 1. They are working dir-local.
44
+ #
45
+ # 2. They should only be present in local development, and should be
46
+ # capable of overriding roles in other local directories to allow
47
+ # custom development behavior (the same way `./dev/bin` is put in
48
+ # front or `./bin`).
49
+ #
50
+
51
+ # Role paths declared in ./dev/ansible.cfg, if it exists.
52
+ File.join('.', 'dev', 'ansible.cfg'),
53
+
54
+ # Roles in ./dev/roles
55
+ File.join('.', 'dev', 'roles'),
56
+
57
+
58
+ # Working Directory Paths
59
+ # =======================
60
+ #
61
+ # Next up, `ansible.cfg` and `roles` directory in the working dir.
62
+ # Makes sense, right?
63
+ #
64
+
65
+ # ./ansible.cfg
66
+ File.join('.', 'ansible.cfg'),
67
+
68
+ # ./roles
69
+ File.join('.', 'roles'),
70
+
71
+
72
+ # Working Directory-Local Ansible Directory
73
+ # =========================================
74
+ #
75
+ # `ansible.cfg` and `roles` in a `./ansible` directory, making a common
76
+ # place to put Ansible stuff in an project accessible when running from
77
+ # the project root.
78
+ #
79
+
80
+ # ./ansible/ansible.cfg
81
+ File.join('.', 'ansible', 'ansible.cfg'),
82
+
83
+ # ./ansible/roles
84
+ File.join('.', 'ansible', 'roles'),
85
+
86
+ # TODO Git repo root relative?
87
+ # Some sort of flag file for a find-up?
88
+ # System Ansible locations?
89
+
90
+
91
+ # QB Gem Role Directories
92
+ # =======================
93
+ #
94
+ # Last, but far from least, paths provided by the QB Gem to the user's
95
+ # QB role install location and the roles that come built-in to the gem.
96
+
97
+ QB::USER_ROLES_DIR,
98
+
99
+ QB::GEM_ROLES_DIR,
100
+ ].freeze
101
+
102
+
103
+ # Array of string paths to directories to search for roles or paths to
104
+ # `ansible.cfg` files to look for an extract role paths from.
105
+ #
106
+ # Value is a duplicate of the frozen {QB::Role::BUILTIN_PATH}. You can
107
+ # reset to those values at any time via {QB::Role.reset_path!}.
108
+ #
109
+ # For the moment at least you can just mutate this value like you would
110
+ # `$LOAD_PATH`:
111
+ #
112
+ # QB::Role::PATH.unshift '~/where/some/roles/be'
113
+ # QB::Role::PATH.unshift '~/my/ansible.cfg'
114
+ #
115
+ # The paths are searched from first to last.
116
+ #
117
+ # **WARNING**
118
+ #
119
+ # Search is **deep** - don't point this at large directory trees and
120
+ # expect any sort of reasonable performance (any directory that
121
+ # contains `node_modules` is usually a terrible idea for instance).
122
+ #
123
+ # @return [Array<String>]
124
+ #
125
+ PATH = BUILTIN_PATH.dup
126
+
127
+
128
+ # Class Methods
129
+ # ======================================================================
130
+
131
+ # Reset {QB::Role::PATH} to the original built-in values in
132
+ # {QB::Role::BUILTIN_PATH}.
133
+ #
134
+ # Created for testing but might be useful elsewhere as well.
135
+ #
136
+ # @return [Array<String>]
137
+ # The reset {QB::Role::PATH}.
138
+ #
139
+ def self.reset_path!
140
+ PATH.clear
141
+ BUILTIN_PATH.each { |path| PATH << path }
142
+ PATH
143
+ end # .reset_path!
144
+
145
+
146
+ # Gets the array of paths to search for QB roles based on {QB::Role::PATH}
147
+ # and the working directory at the time it's called.
148
+ #
149
+ # QB then uses the returned value to figure out what roles are available.
150
+ #
151
+ # The process:
152
+ #
153
+ # 1. Resolve relative paths against the working directory.
154
+ #
155
+ # 2. Load up any `ansible.cfg` files on the path and add any `roles_path`
156
+ # they define where the `ansible.cfg` entry was in {QB::Role::PATH}.
157
+ #
158
+ # @return [Array<Pathname>]
159
+ # Directories to search for QB roles.
160
+ #
161
+ def self.search_path
162
+ QB::Role::PATH.
163
+ map { |path|
164
+ if QB::Ansible::ConfigFile.end_with_config_file?(path)
165
+ if File.file?(path)
166
+ QB::Ansible::ConfigFile.new(path).defaults.roles_path
167
+ end
168
+ else
169
+ QB::Util.resolve path
170
+ end
171
+ }.
172
+ flatten.
173
+ reject(&:nil?)
174
+ end
175
+
176
+ end # class QB::Role
@@ -8,47 +8,29 @@ using NRSER
8
8
 
9
9
  module QB
10
10
  module Util
11
- # split a string into 'words' for word-based matching
11
+
12
+ # Split a string into 'words' for word-based matching
13
+ #
14
+ # @return [Array<String>]
15
+ # Array of non-empty words in `string`.
16
+ #
12
17
  def self.words string
13
- string.split(/[\W_\-\/]+/).reject {|w| w.empty?}
18
+ string.words
14
19
  end # .words
15
20
 
21
+
22
+ def self.words_slice? full_string, input, &is_match
23
+ full_string.words.slice? input.words, &is_match
24
+ end # .words_include?
25
+
26
+
16
27
  # see if words from an input match words
17
28
  def self.words_start_with? full_string, input
18
- # QB.debug "does #{ input } match #{ full_string }?"
19
-
20
- input_words = words input
21
-
22
- # short-circuit if there are no input words ('./' for example)
23
- return false if input_words.empty?
24
-
25
- full_string_words = words full_string
26
-
27
- full_string_words.each_with_index {|word, start_index|
28
- # compute the end index in full_string_words
29
- end_index = start_index + input_words.length - 1
30
-
31
- # short-circuit if can't match (more input words than full words left)
32
- if end_index >= full_string_words.length
33
- return false
34
- end
35
-
36
- # create the slice to test against
37
- slice = full_string_words[start_index..end_index]
38
-
39
- # see if every word in the slice starts with the corresponding word
40
- # in the input
41
- if slice.zip(input_words).all? {|full_word, input_word|
42
- full_word.start_with? input_word
43
- }
44
- # got a match!
45
- return true
46
- end
47
- }
48
-
49
- # no match
50
- false
51
- end # .match_words?
29
+ words_slice? full_string, input do |full_string_word, input_word|
30
+ full_string_word.start_with? input_word
31
+ end
32
+ end # .words_start_with?
33
+
52
34
 
53
35
  # @return [Pathname] absolute resolved path.
54
36
  def self.resolve *segments
@@ -4,7 +4,7 @@ module QB
4
4
 
5
5
  GEM_NAME = 'qb'
6
6
 
7
- VERSION = "0.3.17"
7
+ VERSION = "0.3.18"
8
8
 
9
9
  MIN_ANSIBLE_VERSION = Gem::Version.new '2.1.2'
10
10
 
data/qb.gemspec CHANGED
@@ -195,7 +195,7 @@ Gem::Specification.new do |spec|
195
195
  # ----------------------------------------------------------------------------
196
196
 
197
197
  spec.add_dependency "cmds", '~> 0.0', ">= 0.2.7"
198
- spec.add_dependency "nrser", '~> 0.1', ">= 0.1.3"
198
+ spec.add_dependency "nrser", '~> 0.1', ">= 0.1.4"
199
199
  spec.add_dependency "state_mate", '~> 0.0', ">= 0.1.0"
200
200
 
201
201
  # Used to parse `ansible.cfg` files
@@ -1,3 +1,3 @@
1
1
  ---
2
- qb.qb_role:
2
+ qb/role/qb:
3
3
  defaults: false
@@ -1,8 +1,7 @@
1
1
  ---
2
- # meta file for qb.vars
2
+ # meta file for qb/dump/vars
3
3
 
4
4
  allow_duplicates: yes
5
5
 
6
6
  dependencies: []
7
7
  # - role: role-name
8
-
@@ -1,5 +1,5 @@
1
1
  ---
2
- # meta/qb.yml file for qb.vars
2
+ # meta/qb.yml file for qb/dump/vars
3
3
  #
4
4
  # qb settings for this role. see README.md for more info.
5
5
  #
@@ -7,7 +7,7 @@
7
7
  # prefix for role variables
8
8
  var_prefix: null
9
9
 
10
- # how to get a default for `dir` if it's not provided as the
10
+ # how to get a default for `dir` if it's not provided as the
11
11
  default_dir: null
12
12
 
13
13
  # set to false to not save options in .qb-options.yml files
@@ -1,5 +1,5 @@
1
1
  ---
2
- # tasks file for qb.vars
2
+ # tasks file for qb/dump/vars
3
3
 
4
4
  - when: vars_output is defined
5
5
  local_action:
@@ -10,7 +10,7 @@
10
10
 
11
11
  # Shown in help output, etc.
12
12
  description: >-
13
- TODO describe qb/git/check/clean role
13
+ Fail if Git repo working directory isn't clean
14
14
 
15
15
  # Gemspec-style requirements. Right now only `.gems.qb` is used.
16
16
  requirements:
@@ -1,4 +1,4 @@
1
1
  ---
2
- # defaults file for qb.git_repo
2
+ # defaults file for qb/git/repo
3
3
  git_repo_dest: "{{ qb_dir }}"
4
4
  git_repo_gitignores: []
@@ -0,0 +1,4 @@
1
+ ---
2
+ # meta file for qb/git/repo
3
+
4
+ dependencies: []
@@ -0,0 +1,6 @@
1
+ # Shown in help output, etc.
2
+ description: >-
3
+ Ensure directory is a Git repo - init and add basic ignore sections
4
+
5
+ # Prefix for role variables
6
+ var_prefix: git_repo
@@ -1,5 +1,5 @@
1
1
  ---
2
- # tasks file for qb.git_repo
2
+ # tasks file for qb/git/repo
3
3
 
4
4
  - name: >-
5
5
  Create {{ git_repo_dest }} directory
@@ -2,5 +2,6 @@
2
2
  # meta file for qb/project
3
3
 
4
4
  dependencies:
5
- - role: qb.git_repo
6
- git_repo_dest: "{{ project_dest }}"
5
+ - role: qb/git/repo
6
+ git_repo_dest: >-
7
+ {{ project_dest }}
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.17
4
+ version: 0.3.18
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-31 00:00:00.000000000 Z
11
+ date: 2018-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -143,7 +143,7 @@ dependencies:
143
143
  version: '0.1'
144
144
  - - ">="
145
145
  - !ruby/object:Gem::Version
146
- version: 0.1.3
146
+ version: 0.1.4
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.3
156
+ version: 0.1.4
157
157
  - !ruby/object:Gem::Dependency
158
158
  name: state_mate
159
159
  requirement: !ruby/object:Gem::Requirement
@@ -310,7 +310,9 @@ files:
310
310
  - lib/qb/role.rb
311
311
  - lib/qb/role/default_dir.rb
312
312
  - lib/qb/role/errors.rb
313
+ - lib/qb/role/matches.rb
313
314
  - lib/qb/role/name.rb
315
+ - lib/qb/role/search_path.rb
314
316
  - lib/qb/util.rb
315
317
  - lib/qb/util/bundler.rb
316
318
  - lib/qb/util/docker_mixin.rb
@@ -370,10 +372,6 @@ files:
370
372
  - roles/nrser.state_mate/test/ansible/ansible.cfg
371
373
  - roles/nrser.state_mate/test/ansible/hosts
372
374
  - roles/nrser.state_mate/test/ansible/template.yml
373
- - roles/qb.git_repo/defaults/main.yml
374
- - roles/qb.git_repo/meta/main.yml
375
- - roles/qb.git_repo/meta/qb.yml
376
- - roles/qb.git_repo/tasks/main.yml
377
375
  - roles/qb.git_submodule_update/.qb-options.yml
378
376
  - roles/qb.git_submodule_update/README.md
379
377
  - roles/qb.git_submodule_update/defaults/main.yml
@@ -416,11 +414,6 @@ files:
416
414
  - roles/qb.unhack_gem/meta/main.yml
417
415
  - roles/qb.unhack_gem/meta/qb.yml
418
416
  - roles/qb.unhack_gem/tasks/main.yml
419
- - roles/qb.vars/.qb-options.yml
420
- - roles/qb.vars/README.md
421
- - roles/qb.vars/meta/main.yml
422
- - roles/qb.vars/meta/qb.yml
423
- - roles/qb.vars/tasks/main.yml
424
417
  - roles/qb.yarn_release/defaults/main.yml
425
418
  - roles/qb.yarn_release/meta/main.yml
426
419
  - roles/qb.yarn_release/meta/qb.yml
@@ -433,6 +426,11 @@ files:
433
426
  - roles/qb/dev/ref/repo/git/meta/main.yml
434
427
  - roles/qb/dev/ref/repo/git/meta/qb.yml
435
428
  - roles/qb/dev/ref/repo/git/tasks/main.yml
429
+ - roles/qb/dump/vars/.qb-options.yml
430
+ - roles/qb/dump/vars/README.md
431
+ - roles/qb/dump/vars/meta/main.yml
432
+ - roles/qb/dump/vars/meta/qb.yml
433
+ - roles/qb/dump/vars/tasks/main.yml
436
434
  - roles/qb/facts/defaults/main.yml
437
435
  - roles/qb/facts/meta/main.yml
438
436
  - roles/qb/facts/meta/qb.yml
@@ -637,6 +635,10 @@ files:
637
635
  - roles/qb/git/ignore/meta/main.yml
638
636
  - roles/qb/git/ignore/meta/qb
639
637
  - roles/qb/git/ignore/tasks/main.yml
638
+ - roles/qb/git/repo/defaults/main.yml
639
+ - roles/qb/git/repo/meta/main.yml
640
+ - roles/qb/git/repo/meta/qb.yml
641
+ - roles/qb/git/repo/tasks/main.yml
640
642
  - roles/qb/github/pages/setup/defaults/main.yml
641
643
  - roles/qb/github/pages/setup/meta/main.yml
642
644
  - roles/qb/github/pages/setup/meta/qb.yml
@@ -1,4 +0,0 @@
1
- ---
2
- # meta file for qb.git_repo
3
-
4
- dependencies: []
File without changes