qb 0.1.88 → 0.3.1

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/exe/qb +3 -53
  3. data/lib/qb.rb +3 -1
  4. data/lib/qb/ansible.rb +5 -0
  5. data/lib/qb/ansible/config_file.rb +147 -0
  6. data/lib/qb/ansible/env.rb +49 -0
  7. data/lib/qb/ansible/playbook.rb +4 -0
  8. data/lib/qb/cli.rb +4 -0
  9. data/lib/qb/cli/play.rb +33 -0
  10. data/lib/qb/options.rb +48 -7
  11. data/lib/qb/role.rb +228 -74
  12. data/lib/qb/util.rb +2 -2
  13. data/lib/qb/util/bundler.rb +56 -0
  14. data/lib/qb/version.rb +1 -1
  15. data/node_modules/.bin/semver +1 -1
  16. data/plugins/filter_plugins/{path.py → path_plugins.py} +35 -0
  17. data/plugins/filter_plugins/{ruby_interop.py → ruby_interop_plugins.py} +0 -0
  18. data/plugins/filter_plugins/{string.py → string_plugins.py} +19 -0
  19. data/plugins/filter_plugins/{version.py → version_plugins.py} +0 -0
  20. data/qb.gemspec +1 -0
  21. data/roles/nrser.blockinfile/tests/expected/test-follow/link2.txt +1 -1
  22. data/roles/nrser.blockinfile/tests/fixtures/test-follow/link0.txt +1 -1
  23. data/roles/nrser.blockinfile/tests/fixtures/test-follow/link1.txt +1 -1
  24. data/roles/nrser.blockinfile/tests/fixtures/test-follow/link2.txt +1 -1
  25. data/roles/nrser.blockinfile/tests/roles/yaegashi.blockinfile +1 -1
  26. data/roles/qb.gitignore/files/gitignore/Clojure.gitignore +1 -1
  27. data/roles/qb.gitignore/files/gitignore/Fortran.gitignore +1 -1
  28. data/roles/qb.qb_role/templates/qb.yml.j2 +1 -1
  29. data/roles/qb/dev/ref/repo/git/defaults/main.yml +32 -0
  30. data/roles/qb/dev/ref/repo/git/meta/main.yml +8 -0
  31. data/roles/qb/dev/ref/repo/git/meta/qb.yml +67 -0
  32. data/roles/qb/dev/ref/repo/git/tasks/main.yml +16 -0
  33. data/roles/qb/facts/defaults/main.yml +3 -0
  34. data/roles/{qb.facts → qb/facts}/meta/main.yml +1 -1
  35. data/roles/{qb.facts → qb/facts}/meta/qb.yml +1 -1
  36. data/roles/{qb.facts → qb/facts}/tasks/main.yml +1 -1
  37. data/roles/qb/osx/git/change_case/README.md +16 -0
  38. data/roles/qb/osx/git/change_case/defaults/main.yml +2 -0
  39. data/roles/qb/osx/git/change_case/meta/main.yml +8 -0
  40. data/roles/qb/osx/git/change_case/meta/qb.yml +80 -0
  41. data/roles/qb/osx/git/change_case/tasks/main.yml +6 -0
  42. data/roles/qb/osx/git/change_case/tasks/upcase.yml +26 -0
  43. metadata +42 -11
  44. data/roles/qb.facts/defaults/main.yml +0 -3
data/lib/qb/role.rb CHANGED
@@ -12,38 +12,136 @@ module QB
12
12
  #
13
13
  #
14
14
  class Role
15
- # attrs
16
- # =======================================================================
17
15
 
18
- # @!attribute [r] path
19
- # @return [Pathname]
20
- # location of the role directory.
21
- attr_reader :path
16
+ # Constants
17
+ # =====================================================================
22
18
 
23
- # @!attribute [r] name
24
- # @return [String]
25
- # the role's ansible "name", which is it's directory name.
26
- attr_reader :name
27
-
28
- # @!attribute [r] display_path
19
+ # Array of string paths to directories to search for roles or paths to
20
+ # `ansible.cfg` files to look for an extract role paths from.
29
21
  #
30
- # the path to the role that we display. we only show the directory name
31
- # for QB roles, and use {QB::Util.compact_path} to show `.` and `~` for
32
- # paths relative to the current directory and home directory, respectively.
22
+ # For the moment at least you can just mutate this value like you would
23
+ # `$LOAD_PATH`:
33
24
  #
34
- # @return [Pathname]
35
- attr_reader :display_path
25
+ # QB::Role::PATH.unshift '~/where/some/roles/be'
26
+ # QB::Role::PATH.unshift '~/my/ansible.cfg'
27
+ #
28
+ # The paths are searched from first to last.
29
+ #
30
+ # **WARNING**
31
+ #
32
+ # Search is **deep** - don't point this at large directory trees and
33
+ # expect any sort of reasonable performance (any directory that
34
+ # contains `node_modules` is usually a terrible idea for instance).
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
36
101
 
37
- # @!attribute [r] meta_path
38
- # @return [String, nil]
39
- # the path qb metadata was load from. `nil` if it's never been loaded
40
- # or doesn't exist.
41
- attr_reader :meta_path
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
+ PATH = BUILTIN_PATH.dup
42
124
 
43
125
 
44
- # static role utils
126
+ # Class Methods
45
127
  # =======================================================================
46
128
 
129
+
130
+ # Reset {QB::Role::PATH} to the original built-in values in
131
+ # {QB::Role::BUILTIN_PATH}.
132
+ #
133
+ # Created for testing but might be useful elsewhere as well.
134
+ #
135
+ # @return [Array<String>]
136
+ # The reset {QB::Role::PATH}.
137
+ #
138
+ def self.reset_path!
139
+ PATH.clear
140
+ BUILTIN_PATH.each { |path| PATH << path }
141
+ PATH
142
+ end # .reset_path!
143
+
144
+
47
145
  # true if pathname is a QB role directory.
48
146
  def self.role_dir? pathname
49
147
  # must be a directory
@@ -52,14 +150,15 @@ module QB
52
150
  ['qb.yml', 'qb'].any? {|filename| pathname.join('meta', filename).file?}
53
151
  end
54
152
 
55
- # get role paths from ansible.cfg if it exists in a directory.
153
+ # Get role paths from ansible.cfg if it exists in a directory.
56
154
  #
57
155
  # @param dir [Pathname] directory to look for ansible.cfg in.
58
156
  #
59
- # @return [Array<String>] role paths
157
+ # @return [Array<String>]
158
+ # Absolute role paths.
60
159
  #
61
- def self.cfg_roles_path dir
62
- path = dir.join 'ansible.cfg'
160
+ def self.cfg_roles_paths path
161
+ path = File.join(path, 'ansible.cfg') unless File.basename(path) == 'ansible.cfg'
63
162
 
64
163
  if path.file?
65
164
  config = ParseConfig.new path.to_s
@@ -89,14 +188,8 @@ module QB
89
188
  # 2. paths specific to the current directory:
90
189
  # a. paths specified in ./ansible.cfg (if it exists)
91
190
  # b. ./roles
92
- # c. ./roles/tmp
93
- # - used for roles that are downloaded but shouldn't be included
94
- # in source control.
95
191
  # d. paths specified in ./ansible/ansible.cfg (if it exists)
96
192
  # e. ./ansible/roles
97
- # f. ./ansible/roles/tmp
98
- # - used for roles that are downloaded but shouldn't be included
99
- # in source control.
100
193
  # g. paths specified in ./dev/ansible.cfg (if it exists)
101
194
  # h. ./dev/roles
102
195
  # i. ./dev/roles/tmp
@@ -108,17 +201,18 @@ module QB
108
201
  # places to look for role dirs.
109
202
  #
110
203
  def self.search_path
111
- [
112
- QB::USER_ROLES_DIR,
113
- QB::GEM_ROLES_DIR
114
- ] + [
115
- QB::Util.resolve,
116
- QB::Util.resolve('ansible'),
117
- QB::Util.resolve('dev'),
118
- ].map {|dir|
119
- roles_paths dir
120
- }.
121
- flatten
204
+ QB::Role::PATH.
205
+ map { |path|
206
+ if QB::Ansible::ConfigFile.file_path?(path)
207
+ if File.file?(path)
208
+ QB::Ansible::ConfigFile.new(path).defaults.roles_path
209
+ end
210
+ else
211
+ QB::Util.resolve path
212
+ end
213
+ }.
214
+ flatten.
215
+ reject(&:nil?)
122
216
  end
123
217
 
124
218
  # array of QB::Role found in search path.
@@ -129,12 +223,14 @@ module QB
129
223
  search_dir.directory?
130
224
  }.
131
225
  map {|search_dir|
132
- # grab all the child directories that are role directories
133
- search_dir.children.select {|child| role_dir? child }
226
+ Pathname.glob(search_dir.join '**', 'meta', 'qb.yml').
227
+ map {|meta_path|
228
+ [meta_path.dirname.dirname, search_dir: search_dir]
229
+ }
134
230
  }.
135
- flatten.
136
- map {|role_dir|
137
- QB::Role.new role_dir
231
+ flatten(1).
232
+ map {|args|
233
+ QB::Role.new *args
138
234
  }.
139
235
  uniq
140
236
  end
@@ -170,41 +266,47 @@ module QB
170
266
  separator_variations = [
171
267
  input,
172
268
  input.gsub('-', '_'),
173
- input.gsub('_', '_'),
269
+ input.gsub('_', '-'),
174
270
  ]
175
271
 
176
- separator_variations.each {|variation|
177
- available.each {|role|
272
+ separator_variations.each { |variation|
273
+ available.each { |role|
178
274
  # exact match to full name
179
275
  return [role] if role.name == variation
180
- }.each {|role|
276
+ }.each { |role|
181
277
  # exact match without the namespace prefix ('qb.' or similar)
182
278
  return [role] if role.namespaceless == variation
183
279
  }
184
280
  }
185
281
 
186
282
  # see if we prefix match any full names
187
- name_prefix_matches = available.select {|role|
188
- role.name.start_with? input
283
+ separator_variations.each { |variation|
284
+ name_prefix_matches = available.select { |role|
285
+ role.name.start_with? variation
286
+ }
287
+ return name_prefix_matches unless name_prefix_matches.empty?
189
288
  }
190
- return name_prefix_matches unless name_prefix_matches.empty?
191
289
 
192
290
  # see if we prefix match any name
193
- namespaceless_prefix_matches = available.select {|role|
194
- role.namespaceless.start_with? input
291
+ separator_variations.each { |variation|
292
+ namespaceless_prefix_matches = available.select { |role|
293
+ role.namespaceless.start_with? variation
294
+ }
295
+ unless namespaceless_prefix_matches.empty?
296
+ return namespaceless_prefix_matches
297
+ end
195
298
  }
196
- unless namespaceless_prefix_matches.empty?
197
- return namespaceless_prefix_matches
198
- end
199
299
 
200
300
  # see if we prefix match any display paths
201
- path_prefix_matches = available.select {|role|
202
- role.display_path.start_with? input
301
+ separator_variations.each { |variation|
302
+ path_prefix_matches = available.select { |role|
303
+ role.display_path.start_with? variation
304
+ }
305
+ return path_prefix_matches unless path_prefix_matches.empty?
203
306
  }
204
- return path_prefix_matches unless path_prefix_matches.empty?
205
307
 
206
- # see if we word match any display` paths
207
- name_word_matches = available.select {|role|
308
+ # see if we word match any display paths
309
+ name_word_matches = available.select { |role|
208
310
  QB::Util.words_start_with? role.display_path.to_s, input
209
311
  }
210
312
  return name_word_matches unless name_word_matches.empty?
@@ -287,14 +389,53 @@ module QB
287
389
  end
288
390
  end
289
391
 
290
- # instance methods
392
+
393
+ # Attributes
394
+ # =======================================================================
395
+
396
+ # @!attribute [r] path
397
+ # @return [Pathname]
398
+ # location of the role directory.
399
+ attr_reader :path
400
+
401
+
402
+ # @!attribute [r] name
403
+ # @return [String]
404
+ # the role's ansible "name", which is it's directory name.
405
+ attr_reader :name
406
+
407
+
408
+ # @!attribute [r] display_path
409
+ #
410
+ # the path to the role that we display. we only show the directory name
411
+ # for QB roles, and use {QB::Util.compact_path} to show `.` and `~` for
412
+ # paths relative to the current directory and home directory, respectively.
413
+ #
414
+ # @return [Pathname]
415
+ attr_reader :display_path
416
+
417
+
418
+ # @!attribute [r] meta_path
419
+ # @return [String, nil]
420
+ # the path qb metadata was load from. `nil` if it's never been loaded
421
+ # or doesn't exist.
422
+ attr_reader :meta_path
423
+
424
+
425
+ # Constructor
291
426
  # =======================================================================
292
427
 
428
+ # Instantiate a Role.
293
429
  #
294
430
  # @param [String|Pathname] path
295
431
  # location of the role directory
296
432
  #
297
- def initialize path
433
+ # @param [nil, Pathname] search_dir
434
+ # Directory in {QB::Role.search_path} that the role was found in.
435
+ # Used to figure out it's name correctly when using directory-structure
436
+ # namespacing.
437
+ #
438
+ def initialize path, search_dir: nil
298
439
  @path = if path.is_a?(Pathname) then path else Pathname.new(path) end
299
440
 
300
441
  # check it...
@@ -316,23 +457,36 @@ module QB
316
457
  raise Errno::ENOENT.new "#{ @path.join('meta').to_s }/[qb|qb.yml]"
317
458
  end
318
459
 
319
- @name = @path.to_s.split(File::SEPARATOR).last
320
- end
460
+
461
+ if search_dir.nil?
462
+ @name = @path.to_s.split(File::SEPARATOR).last
463
+ else
464
+ @name = @path.relative_path_from(search_dir).to_s
465
+ end
466
+ end # #initialize
467
+
468
+
469
+ # Instance Methods
470
+ # =====================================================================
321
471
 
322
472
  def to_s
323
473
  @display_path.to_s
324
474
  end
325
475
 
326
476
  def namespace
327
- if @name.include? '.'
328
- @name.split('.').first
329
- else
477
+ *namespace_segments, last = @name.split File::Separator
478
+
479
+ namespace_segments << last.split('.').first if last.include?('.')
480
+
481
+ if namespace_segments.empty?
330
482
  nil
483
+ else
484
+ File.join *namespace_segments
331
485
  end
332
486
  end
333
487
 
334
488
  def namespaceless
335
- @name.split('.', 2).last
489
+ File.basename(@name).split('.', 2).last
336
490
  end
337
491
 
338
492
  def options_key
data/lib/qb/util.rb CHANGED
@@ -1,15 +1,15 @@
1
1
  require_relative './util/stdio'
2
2
  require_relative './util/interop'
3
+ require_relative './util/bundler'
3
4
 
4
5
  require 'nrser'
5
-
6
6
  using NRSER
7
7
 
8
8
  module QB
9
9
  module Util
10
10
  # split a string into 'words' for word-based matching
11
11
  def self.words string
12
- string.split(/[\W_-]+/).reject {|w| w.empty?}
12
+ string.split(/[\W_\-\/]+/).reject {|w| w.empty?}
13
13
  end # .words
14
14
 
15
15
  # see if words from an input match words
@@ -0,0 +1,56 @@
1
+ module QB; end
2
+ module QB::Util; end
3
+
4
+ module QB::Util::Bundler
5
+ # needed for to clean the env if using bundler (like in dev).
6
+ #
7
+ # this is because the ansible gem module doesn't work right with the bundler
8
+ # env vars set, so we need to remove them, but when running in dev we want
9
+ # modules written in ruby like nrser.state_mate's `state` script to have
10
+ # access to them so it can fire up bundler and get the right libraries.
11
+ #
12
+ # to accomplish this, we detect Bundler, and when it's present we copy the
13
+ # bundler-related env vars (which i found by looking at
14
+ # https://github.com/bundler/bundler/blob/master/lib/bundler.rb#L257)
15
+ # into a hash to pass around the env sanitization, then copy them into
16
+ # corresponding 'QB_DEV_ENV_<NAME>' vars that modules can restore.
17
+ #
18
+ # we also set a 'QB_DEV_ENV=true' env var for modules to easily detect that
19
+ # we're running in dev and restore the variables.
20
+ #
21
+ def self.with_clean_env &block
22
+ if defined? ::Bundler
23
+ # copy the Bundler env vars into a hash
24
+ dev_env = ENV.select {|k, v|
25
+ k.start_with?("BUNDLE_") ||
26
+ [
27
+ 'GEM_HOME',
28
+ 'GEM_PATH',
29
+ 'MANPATH',
30
+ 'RUBYOPT',
31
+ 'RUBYLIB',
32
+ ].include?(k)
33
+ }
34
+
35
+ qb_env = ENV.select {|k, v| k.start_with? 'QB_'}
36
+
37
+ ::Bundler.with_clean_env do
38
+ # now that we're in a clean env, copy the Bundler env vars into
39
+ # 'QB_DEV_ENV_<NAME>' vars.
40
+ dev_env.each {|k, v| ENV["QB_DEV_ENV_#{ k }"] = v}
41
+
42
+ # set the flag that will be used by modules to know to restore the
43
+ # variables
44
+ ENV['QB_DEV_ENV'] = 'true'
45
+
46
+ qb_env.each {|k, v| ENV[k] = v}
47
+
48
+ # invoke the block
49
+ block.call
50
+ end
51
+ else
52
+ # bundler isn't loaded, so no env var silliness to deal with
53
+ block.call
54
+ end
55
+ end # .with_clean_env
56
+ end # module QB::Util::Bundler