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