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.
- checksums.yaml +4 -4
- data/exe/qb +3 -53
- data/lib/qb.rb +3 -1
- data/lib/qb/ansible.rb +5 -0
- data/lib/qb/ansible/config_file.rb +147 -0
- data/lib/qb/ansible/env.rb +49 -0
- data/lib/qb/ansible/playbook.rb +4 -0
- data/lib/qb/cli.rb +4 -0
- data/lib/qb/cli/play.rb +33 -0
- data/lib/qb/options.rb +48 -7
- data/lib/qb/role.rb +228 -74
- data/lib/qb/util.rb +2 -2
- data/lib/qb/util/bundler.rb +56 -0
- data/lib/qb/version.rb +1 -1
- data/node_modules/.bin/semver +1 -1
- data/plugins/filter_plugins/{path.py → path_plugins.py} +35 -0
- data/plugins/filter_plugins/{ruby_interop.py → ruby_interop_plugins.py} +0 -0
- data/plugins/filter_plugins/{string.py → string_plugins.py} +19 -0
- data/plugins/filter_plugins/{version.py → version_plugins.py} +0 -0
- data/qb.gemspec +1 -0
- data/roles/nrser.blockinfile/tests/expected/test-follow/link2.txt +1 -1
- data/roles/nrser.blockinfile/tests/fixtures/test-follow/link0.txt +1 -1
- data/roles/nrser.blockinfile/tests/fixtures/test-follow/link1.txt +1 -1
- data/roles/nrser.blockinfile/tests/fixtures/test-follow/link2.txt +1 -1
- data/roles/nrser.blockinfile/tests/roles/yaegashi.blockinfile +1 -1
- data/roles/qb.gitignore/files/gitignore/Clojure.gitignore +1 -1
- data/roles/qb.gitignore/files/gitignore/Fortran.gitignore +1 -1
- data/roles/qb.qb_role/templates/qb.yml.j2 +1 -1
- data/roles/qb/dev/ref/repo/git/defaults/main.yml +32 -0
- data/roles/qb/dev/ref/repo/git/meta/main.yml +8 -0
- data/roles/qb/dev/ref/repo/git/meta/qb.yml +67 -0
- data/roles/qb/dev/ref/repo/git/tasks/main.yml +16 -0
- data/roles/qb/facts/defaults/main.yml +3 -0
- data/roles/{qb.facts → qb/facts}/meta/main.yml +1 -1
- data/roles/{qb.facts → qb/facts}/meta/qb.yml +1 -1
- data/roles/{qb.facts → qb/facts}/tasks/main.yml +1 -1
- data/roles/qb/osx/git/change_case/README.md +16 -0
- data/roles/qb/osx/git/change_case/defaults/main.yml +2 -0
- data/roles/qb/osx/git/change_case/meta/main.yml +8 -0
- data/roles/qb/osx/git/change_case/meta/qb.yml +80 -0
- data/roles/qb/osx/git/change_case/tasks/main.yml +6 -0
- data/roles/qb/osx/git/change_case/tasks/upcase.yml +26 -0
- metadata +42 -11
- 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
|
-
#
|
19
|
-
#
|
20
|
-
# location of the role directory.
|
21
|
-
attr_reader :path
|
16
|
+
# Constants
|
17
|
+
# =====================================================================
|
22
18
|
|
23
|
-
#
|
24
|
-
#
|
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
|
31
|
-
#
|
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
|
-
#
|
35
|
-
|
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
|
-
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
|
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
|
-
#
|
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
|
-
#
|
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>]
|
157
|
+
# @return [Array<String>]
|
158
|
+
# Absolute role paths.
|
60
159
|
#
|
61
|
-
def self.
|
62
|
-
path =
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
-
|
133
|
-
|
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 {|
|
137
|
-
QB::Role.new
|
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
|
-
|
188
|
-
|
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
|
-
|
194
|
-
|
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
|
-
|
202
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
320
|
-
|
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
|
-
|
328
|
-
|
329
|
-
|
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_
|
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
|