qb 0.1.88 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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