qb 0.1.13 → 0.1.14
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 +69 -185
- data/lib/qb.rb +28 -59
- data/lib/qb/options.rb +189 -0
- data/lib/qb/role.rb +181 -0
- data/lib/qb/version.rb +1 -1
- data/library/git_mkdir.py +6 -3
- data/roles/qb.git_repo/meta/main.yml +4 -2
- data/roles/qb.gitignore/meta/main.yml +1 -2
- data/roles/qb.npm/defaults/main.yml +16 -0
- data/roles/qb.npm/meta/main.yml +14 -0
- data/roles/qb.npm/meta/qb.yml +71 -0
- data/roles/qb.npm/tasks/main.yml +8 -0
- data/roles/qb.npm/templates/package.json.j2 +13 -0
- data/roles/qb.project/meta/main.yml +1 -4
- data/roles/qb.project/tasks/main.yml +2 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eff9442acda19ad45e2a0fc85dde3fa21f34d33a
|
4
|
+
data.tar.gz: 0ca507c08588f891232f983b45e10b953281dce8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c05d0cf7fad902ca2a4988170b614a1c21444bf51d0c16ce0e6c63b5a17a826a93014bf6ef65adefb5507cd9c4ecc04ef79f51725464e7612fb269eeb014393d
|
7
|
+
data.tar.gz: 22d00588741f03b6044dddb945388e12e08f07428ed8168ce6faa5aeb6494f3e01afc64096e2947cde146dbb6c90a4b8233a19bcbcb48a0776dd884b76aa6df0
|
data/exe/qb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
require 'pathname'
|
4
4
|
require 'pp'
|
5
5
|
require 'yaml'
|
6
|
-
require 'optparse'
|
7
6
|
require 'json'
|
8
7
|
require 'fileutils'
|
9
8
|
|
@@ -37,15 +36,6 @@ def format msg, dump = {}
|
|
37
36
|
msg
|
38
37
|
end
|
39
38
|
|
40
|
-
def role? pathname
|
41
|
-
pathname.directory? &&
|
42
|
-
['qb.yml', 'qb'].any? {|fn| pathname.join('meta', fn).file?}
|
43
|
-
end
|
44
|
-
|
45
|
-
def role_matches
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
39
|
def debug *args
|
50
40
|
QB.debug *args
|
51
41
|
end
|
@@ -58,129 +48,6 @@ def set_debug! args
|
|
58
48
|
end
|
59
49
|
end
|
60
50
|
|
61
|
-
def parse! role, var_prefix, vars, defaults, args
|
62
|
-
positional = vars.select do |var|
|
63
|
-
var['positional'] == true
|
64
|
-
end
|
65
|
-
|
66
|
-
positional_banner = if positional.empty?
|
67
|
-
''
|
68
|
-
else
|
69
|
-
' ' + positional.map {|var|
|
70
|
-
var['name'].upcase
|
71
|
-
}.join(' ')
|
72
|
-
end
|
73
|
-
|
74
|
-
options = {}
|
75
|
-
|
76
|
-
opt_parser = OptionParser.new do |opts|
|
77
|
-
# opts.banner = "qb #{ role.name } [OPTIONS]#{ positional_banner }"
|
78
|
-
opts.banner = "qb #{ role.name } [OPTIONS] DIRECTORY"
|
79
|
-
|
80
|
-
vars.each do |var|
|
81
|
-
arg_name = var.fetch 'name'
|
82
|
-
var_name = "#{ var_prefix }_#{ arg_name }"
|
83
|
-
required = var['required'] || false
|
84
|
-
arg_style = required ? :REQUIRED : :OPTIONAL
|
85
|
-
|
86
|
-
# on_args = [arg_style]
|
87
|
-
on_args = []
|
88
|
-
|
89
|
-
if var['type'] == 'boolean'
|
90
|
-
if var['short']
|
91
|
-
on_args << "-#{ var['short'] }"
|
92
|
-
end
|
93
|
-
|
94
|
-
on_args << "--[no-]#{ var['name'] }"
|
95
|
-
|
96
|
-
else
|
97
|
-
ruby_type = case var['type']
|
98
|
-
when 'string'
|
99
|
-
String
|
100
|
-
when Hash
|
101
|
-
if var['type'].key? 'one_of'
|
102
|
-
klass = Class.new
|
103
|
-
opts.accept(klass) {|value|
|
104
|
-
if var['type']['one_of'].include? value
|
105
|
-
value
|
106
|
-
else
|
107
|
-
raise ArgumentError, "argument '#{ var['name'] }' must be " +
|
108
|
-
"one of: #{ var['type']['one_of'].join(', ') }"
|
109
|
-
end
|
110
|
-
}
|
111
|
-
klass
|
112
|
-
else
|
113
|
-
raise ArgumentError, "bad type: #{ var['type'].inspect }"
|
114
|
-
end
|
115
|
-
else
|
116
|
-
raise ArgumentError, "bad type: #{ var['type'].inspect }"
|
117
|
-
end
|
118
|
-
|
119
|
-
if var['short']
|
120
|
-
on_args << "-#{ var['short'] } #{ arg_name.upcase }"
|
121
|
-
end
|
122
|
-
|
123
|
-
on_args << "--#{ var['name'] }=#{ arg_name.upcase }"
|
124
|
-
|
125
|
-
on_args << ruby_type
|
126
|
-
end
|
127
|
-
|
128
|
-
# description
|
129
|
-
description = if var.key? 'description'
|
130
|
-
var['description']
|
131
|
-
else
|
132
|
-
"set the #{ var_name } variable"
|
133
|
-
end
|
134
|
-
|
135
|
-
if var['type'].is_a?(Hash) && var['type'].key?('one_of')
|
136
|
-
lb = "\n" + "\t" * 5
|
137
|
-
description += " options:" +
|
138
|
-
"#{ lb }#{ var['type']['one_of'].join(lb) }"
|
139
|
-
end
|
140
|
-
|
141
|
-
on_args << description
|
142
|
-
|
143
|
-
if defaults.key? var_name
|
144
|
-
on_args << if var['type'] == 'boolean'
|
145
|
-
if defaults[var_name]
|
146
|
-
"default --#{ var['name'] }"
|
147
|
-
else
|
148
|
-
"default --no-#{ var['name'] }"
|
149
|
-
end
|
150
|
-
else
|
151
|
-
"default = #{ defaults[var_name] }"
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
debug "adding option", name: arg_name, on_args: on_args
|
156
|
-
|
157
|
-
opts.on(*on_args) do |value|
|
158
|
-
options[var['name']] = value
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
# No argument, shows at tail. This will print an options summary.
|
163
|
-
# Try it and see!
|
164
|
-
opts.on_tail("-h", "--help", "Show this message") do
|
165
|
-
puts opts
|
166
|
-
exit
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
opt_parser.parse! args
|
171
|
-
|
172
|
-
# args.each_with_index do |value, index|
|
173
|
-
# var_name = positional[index]['name']
|
174
|
-
# if options.key? var_name
|
175
|
-
# raise ArgumentError, "don't supply #{ var_name } as option and positionaly"
|
176
|
-
# else
|
177
|
-
# options[var_name] = value
|
178
|
-
# end
|
179
|
-
# end
|
180
|
-
|
181
|
-
options
|
182
|
-
end
|
183
|
-
|
184
51
|
# needed for to clean the env if using bundler (like in dev)
|
185
52
|
def with_clean_env &block
|
186
53
|
if defined? Bundler
|
@@ -212,7 +79,7 @@ use `qb ROLE -h` for role options.
|
|
212
79
|
available roles:
|
213
80
|
|
214
81
|
END
|
215
|
-
puts QB.
|
82
|
+
puts QB::Role.available
|
216
83
|
puts
|
217
84
|
exit 1
|
218
85
|
end
|
@@ -226,44 +93,20 @@ def main args
|
|
226
93
|
|
227
94
|
help if role_arg.nil? || ['-h', '--help', 'help'].include?(role_arg)
|
228
95
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
puts "ERROR - no roles match arg #{ role_arg.inspect }\n\n"
|
96
|
+
begin
|
97
|
+
role = QB::Role.require role_arg
|
98
|
+
rescue QB::Role::NoMatchesError => e
|
99
|
+
puts "ERROR - #{ e.message }\n\n"
|
100
|
+
# exits with status code 1
|
235
101
|
help
|
236
|
-
|
237
|
-
|
238
|
-
else
|
239
|
-
puts "ERROR - multiple role matches:\n\n"
|
240
|
-
puts matches
|
241
|
-
puts
|
102
|
+
rescue QB::Role::MultipleMatchesError => e
|
103
|
+
puts "ERROR - #{ e.message }\n\n"
|
242
104
|
exit 1
|
243
105
|
end
|
244
|
-
debug role: role
|
245
|
-
|
246
|
-
defaults_path = role.path + 'defaults' + 'main.yml'
|
247
|
-
defaults = if defaults_path.file?
|
248
|
-
YAML.load(defaults_path.read) || {}
|
249
|
-
else
|
250
|
-
{}
|
251
|
-
end
|
252
|
-
|
253
|
-
qb_meta = if (role.path + 'meta' + 'qb').exist?
|
254
|
-
YAML.load Cmds.out! (role.path + 'meta' + 'qb').realpath.to_s
|
255
|
-
elsif (role.path + 'meta' + 'qb.yml').exist?
|
256
|
-
YAML.load (role.path + 'meta' + 'qb.yml').read
|
257
|
-
else
|
258
|
-
{}
|
259
|
-
end
|
260
106
|
|
261
|
-
|
262
|
-
var_prefix = qb_meta['var_prefix'] || role.namespaceless
|
107
|
+
options = QB::Options.parse! role, args
|
263
108
|
|
264
|
-
options
|
265
|
-
|
266
|
-
debug options: options
|
109
|
+
debug "options set on cli", options.select {|k, o| !o.value.nil?}
|
267
110
|
|
268
111
|
cwd = Dir.getwd
|
269
112
|
|
@@ -274,7 +117,7 @@ def main args
|
|
274
117
|
#
|
275
118
|
# in some cases (like projects) the dir can be figured out in other ways:
|
276
119
|
#
|
277
|
-
QB.get_default_dir role,
|
120
|
+
QB.get_default_dir role, cwd, options
|
278
121
|
|
279
122
|
when 1
|
280
123
|
# there is a single positional arg, which is used as dir
|
@@ -286,12 +129,12 @@ def main args
|
|
286
129
|
|
287
130
|
end
|
288
131
|
|
289
|
-
debug input_dir
|
132
|
+
debug "input_dir", dir
|
290
133
|
|
291
134
|
# normalize to expanded path (has no trailing slash)
|
292
135
|
dir = File.expand_path dir
|
293
136
|
|
294
|
-
debug normalized_dir
|
137
|
+
debug "normalized_dir", dir
|
295
138
|
|
296
139
|
# create the dir if it doesn't exist (so don't have to cover this in
|
297
140
|
# every role)
|
@@ -300,28 +143,58 @@ def main args
|
|
300
143
|
saved_options_path = Pathname.new(dir) + '.qb-options.yml'
|
301
144
|
|
302
145
|
saved_options = if saved_options_path.exist?
|
303
|
-
YAML.load
|
146
|
+
YAML.load(saved_options_path.read).tap {|saved_options|
|
147
|
+
debug "found saved options", saved_options
|
148
|
+
}
|
304
149
|
else
|
150
|
+
debug "no saved options"
|
305
151
|
{}
|
306
152
|
end
|
307
153
|
|
308
154
|
if saved_options.key? role.options_key
|
309
|
-
|
155
|
+
role_saved_options = saved_options[role.options_key]
|
156
|
+
|
157
|
+
debug "found saved options for role", role_saved_options
|
158
|
+
|
159
|
+
role_saved_options.each do |cli_option_name, value|
|
160
|
+
option = options[cli_option_name]
|
161
|
+
|
162
|
+
if option.value.nil?
|
163
|
+
debug "setting from saved options", option: option, value: value
|
164
|
+
|
165
|
+
option.value = value
|
166
|
+
end
|
167
|
+
end
|
310
168
|
end
|
311
169
|
|
170
|
+
set_options = options.select {|k, o| !o.value.nil?}
|
171
|
+
|
172
|
+
debug "set options", set_options
|
173
|
+
|
312
174
|
playbook_role = {'role' => role.name}
|
313
|
-
options.each do |arg_name, arg_value|
|
314
|
-
playbook_role["#{ var_prefix }_#{ arg_name }"] = arg_value
|
315
|
-
end
|
316
175
|
|
317
|
-
|
318
|
-
|
319
|
-
|
176
|
+
playbook_vars = {
|
177
|
+
'qb_dir' => dir,
|
178
|
+
# depreciated due to mass potential for conflict
|
179
|
+
'dir' => dir,
|
180
|
+
'qb_cwd' => cwd,
|
181
|
+
}
|
182
|
+
|
183
|
+
set_options.values.each do |option|
|
184
|
+
playbook_role[option.ansible_var_name] = option.value
|
185
|
+
end
|
320
186
|
|
321
187
|
playbook = [
|
322
188
|
{
|
323
189
|
'hosts' => 'localhost',
|
190
|
+
'vars' => playbook_vars,
|
324
191
|
'pre_tasks' => [
|
192
|
+
# need ansible 2.1.2.0 at least to run
|
193
|
+
{
|
194
|
+
'assert' => {
|
195
|
+
'that' => "'ansible 2.1.2' in lookup('pipe', 'ansible --version')",
|
196
|
+
},
|
197
|
+
},
|
325
198
|
{'qb_facts' => nil},
|
326
199
|
],
|
327
200
|
'roles' => [
|
@@ -331,7 +204,7 @@ def main args
|
|
331
204
|
}
|
332
205
|
]
|
333
206
|
|
334
|
-
debug playbook
|
207
|
+
debug "playbook", playbook
|
335
208
|
|
336
209
|
playbook_path = ROOT + '.qb-playbook.yml'
|
337
210
|
debug playbook_path: playbook_path.to_s
|
@@ -340,12 +213,23 @@ def main args
|
|
340
213
|
f.write YAML.dump(playbook)
|
341
214
|
end
|
342
215
|
|
343
|
-
|
344
|
-
|
345
|
-
|
216
|
+
# save the options back
|
217
|
+
if (
|
218
|
+
# we set some options that we can save
|
219
|
+
set_options.values.select {|o| o.save }.length > 0 &&
|
220
|
+
# the role says to save options
|
221
|
+
role.save_options
|
346
222
|
)
|
347
|
-
saved_options[role.options_key] =
|
348
|
-
|
223
|
+
saved_options[role.options_key] = set_options.select{|key, option|
|
224
|
+
option.save
|
225
|
+
}.map {|key, option|
|
226
|
+
[key, option.value]
|
227
|
+
}.to_h
|
228
|
+
|
229
|
+
unless saved_options_path.dirname.exist?
|
230
|
+
FileUtils.mkdir_p saved_options_path.dirname
|
231
|
+
end
|
232
|
+
|
349
233
|
saved_options_path.open('w') do |f|
|
350
234
|
f.write YAML.dump(saved_options)
|
351
235
|
end
|
@@ -355,7 +239,7 @@ def main args
|
|
355
239
|
|
356
240
|
ansible_roles_path = (
|
357
241
|
[tmp_roles_path] +
|
358
|
-
QB.
|
242
|
+
QB::Role.search_path
|
359
243
|
).join(':')
|
360
244
|
|
361
245
|
Dir.chdir ROOT do
|
data/lib/qb.rb
CHANGED
@@ -6,6 +6,9 @@ module QB
|
|
6
6
|
ROOT = (Pathname.new(__FILE__).dirname + '..').expand_path
|
7
7
|
ROLES_DIR = ROOT + 'roles'
|
8
8
|
|
9
|
+
class Error < StandardError
|
10
|
+
end
|
11
|
+
|
9
12
|
# TODO this should be in an instance that is run instead of module global
|
10
13
|
# hack for now
|
11
14
|
@@debug = false
|
@@ -15,74 +18,35 @@ module QB
|
|
15
18
|
end
|
16
19
|
|
17
20
|
def self.debug *args
|
18
|
-
return unless @@debug
|
21
|
+
return unless @@debug && args.length > 0
|
19
22
|
|
20
|
-
|
23
|
+
header = 'DEBUG'
|
24
|
+
|
25
|
+
if args[0].is_a? String
|
26
|
+
header += " " + args.shift
|
27
|
+
end
|
28
|
+
|
29
|
+
dumpObj = case args.length
|
21
30
|
when 0
|
22
|
-
|
31
|
+
header
|
23
32
|
when 1
|
24
|
-
|
25
|
-
['', args[0]]
|
26
|
-
else
|
27
|
-
[args[0], {}]
|
28
|
-
end
|
29
|
-
when 2
|
30
|
-
[args[0], args[1]]
|
33
|
+
{header => args[0]}
|
31
34
|
else
|
32
|
-
|
35
|
+
{header => args}
|
33
36
|
end
|
34
37
|
|
35
|
-
$stderr.puts("DEBUG " + format(msg, values))
|
36
|
-
|
37
|
-
|
38
|
-
def self.role_dirs
|
39
|
-
[
|
40
|
-
ROLES_DIR,
|
41
|
-
Pathname.new(Dir.getwd).join('roles'),
|
42
|
-
Pathname.new(Dir.getwd).join('dev', 'roles'),
|
43
|
-
Pathname.new(Dir.getwd).join('dev', 'roles', 'tmp'),
|
44
|
-
]
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.available_roles
|
48
|
-
role_dirs.
|
49
|
-
select {|role_dir|
|
50
|
-
role_dir.directory?
|
51
|
-
}.
|
52
|
-
map {|role_dir|
|
53
|
-
role_dir.children.select {|child| role? child }
|
54
|
-
}.
|
55
|
-
flatten.
|
56
|
-
uniq.
|
57
|
-
map {|role_dir|
|
58
|
-
QB::Role.new role_dir
|
59
|
-
}
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.role_matches input
|
63
|
-
available_roles.each {|role|
|
64
|
-
# exact match to relitive path
|
65
|
-
return [role] if role.rel_path.to_s == input
|
66
|
-
}.each {|role|
|
67
|
-
# exact match to full name
|
68
|
-
return [role] if role.name == input
|
69
|
-
}.each {|role|
|
70
|
-
# exact match without the namespace prefix ('qb.' or similar)
|
71
|
-
return [role] if role.namespaceless == input
|
72
|
-
}.select {|role|
|
73
|
-
# select any that have that string in them
|
74
|
-
role.rel_path.to_s.include? input
|
75
|
-
}
|
38
|
+
# $stderr.puts("DEBUG " + format(msg, values))
|
39
|
+
$stderr.puts YAML.dump(dumpObj)
|
76
40
|
end
|
77
41
|
|
78
|
-
def self.get_default_dir role,
|
42
|
+
def self.get_default_dir role, cwd, options
|
79
43
|
debug "get_default_dir", role: role,
|
80
|
-
|
44
|
+
meta: role.meta,
|
81
45
|
cwd: cwd,
|
82
46
|
options: options
|
83
47
|
|
84
48
|
key = 'default_dir'
|
85
|
-
case
|
49
|
+
case role.meta[key]
|
86
50
|
when nil, false
|
87
51
|
# there is no get_dir info in meta/qb.yml, can't get the dir
|
88
52
|
raise "unable to infer default directory: no '#{ key }' key in meta/qb.yml"
|
@@ -98,9 +62,13 @@ module QB
|
|
98
62
|
when Hash
|
99
63
|
debug "qb meta option is a Hash"
|
100
64
|
|
101
|
-
if
|
102
|
-
exe_path =
|
103
|
-
exe_input_data =
|
65
|
+
if role.meta[key].key? 'exe'
|
66
|
+
exe_path = role.meta[key]['exe']
|
67
|
+
exe_input_data = Hash[
|
68
|
+
options.map {|option|
|
69
|
+
[option.cli_option_name, option.value]
|
70
|
+
}
|
71
|
+
]
|
104
72
|
|
105
73
|
unless exe_path.start_with?('~') || exe_path.start_with?('/')
|
106
74
|
exe_path = File.join(role.path, exe_path)
|
@@ -121,4 +89,5 @@ module QB
|
|
121
89
|
end
|
122
90
|
|
123
91
|
# needs QB::ROLES_DIR
|
124
|
-
require 'qb/role'
|
92
|
+
require 'qb/role'
|
93
|
+
require 'qb/options'
|
data/lib/qb/options.rb
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module QB
|
4
|
+
module Options
|
5
|
+
# small struct to hold the differnt names of the option now that including
|
6
|
+
# makes it more complicated
|
7
|
+
Option = Struct.new :qb_meta_name,
|
8
|
+
:cli_option_name,
|
9
|
+
:ansible_var_name,
|
10
|
+
:required,
|
11
|
+
:save,
|
12
|
+
:value
|
13
|
+
|
14
|
+
# errors
|
15
|
+
# ======
|
16
|
+
|
17
|
+
# base for errors in the module, extends QB:Error
|
18
|
+
class Error < QB::Error
|
19
|
+
end
|
20
|
+
|
21
|
+
# raised when an included role includes another, which we don't support
|
22
|
+
# (for now)
|
23
|
+
class NestedIncludeError < Error
|
24
|
+
def initialize
|
25
|
+
super "can't nest role includes"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.include_role opts, options, include_var
|
30
|
+
role_name = include_var['include']
|
31
|
+
role = QB::Role.require role_name
|
32
|
+
include_as = include_var['as'] || role.namespaceless
|
33
|
+
|
34
|
+
QB.debug "including #{ role.name } as #{ include_as }"
|
35
|
+
|
36
|
+
add opts, options, role, include_as
|
37
|
+
end
|
38
|
+
|
39
|
+
# add the options from a role to the OptionParser
|
40
|
+
def self.add opts, options, role, include_as = nil
|
41
|
+
QB.debug "adding options", "role" => role
|
42
|
+
|
43
|
+
role.vars.each do |var|
|
44
|
+
if var.key? 'include'
|
45
|
+
# we don't support nested includes
|
46
|
+
unless include_as.nil?
|
47
|
+
raise NestedIncludeError.new
|
48
|
+
end
|
49
|
+
|
50
|
+
include_role opts, options, var
|
51
|
+
|
52
|
+
else
|
53
|
+
# create an option
|
54
|
+
|
55
|
+
# variable's name in meta
|
56
|
+
qb_meta_name = var.fetch 'name'
|
57
|
+
|
58
|
+
# name that will be used in QB cli
|
59
|
+
cli_option_name = if include_as
|
60
|
+
"#{ include_as }_#{ qb_meta_name }"
|
61
|
+
else
|
62
|
+
qb_meta_name
|
63
|
+
end
|
64
|
+
|
65
|
+
# name that is passed to ansible role
|
66
|
+
ansible_var_name = "#{ role.var_prefix }_#{ qb_meta_name }"
|
67
|
+
|
68
|
+
required = var['required'] || false
|
69
|
+
save = if var.key? 'save'
|
70
|
+
!!var['save']
|
71
|
+
else
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
option = options[cli_option_name] = Option.new qb_meta_name,
|
76
|
+
cli_option_name,
|
77
|
+
ansible_var_name,
|
78
|
+
required,
|
79
|
+
save,
|
80
|
+
nil
|
81
|
+
|
82
|
+
arg_style = required ? :REQUIRED : :OPTIONAL
|
83
|
+
|
84
|
+
# on_args = [arg_style]
|
85
|
+
on_args = []
|
86
|
+
|
87
|
+
if var['type'] == 'boolean'
|
88
|
+
# don't use short names when included (for now)
|
89
|
+
if include_as.nil? && var['short']
|
90
|
+
on_args << "-#{ var['short'] }"
|
91
|
+
end
|
92
|
+
|
93
|
+
on_args << "--[no-]#{ cli_option_name }"
|
94
|
+
|
95
|
+
else
|
96
|
+
ruby_type = case var['type']
|
97
|
+
when 'string'
|
98
|
+
String
|
99
|
+
when Hash
|
100
|
+
if var['type'].key? 'one_of'
|
101
|
+
klass = Class.new
|
102
|
+
opts.accept(klass) {|value|
|
103
|
+
if var['type']['one_of'].include? value
|
104
|
+
value
|
105
|
+
else
|
106
|
+
raise ArgumentError, "argument '#{ cli_option_name }' must be " +
|
107
|
+
"one of: #{ var['type']['one_of'].join(', ') }"
|
108
|
+
end
|
109
|
+
}
|
110
|
+
klass
|
111
|
+
else
|
112
|
+
raise ArgumentError, "bad type: #{ var['type'].inspect }"
|
113
|
+
end
|
114
|
+
else
|
115
|
+
raise ArgumentError, "bad type: #{ var['type'].inspect }"
|
116
|
+
end
|
117
|
+
|
118
|
+
# don't use short names when included (for now)
|
119
|
+
if include_as.nil? && var['short']
|
120
|
+
on_args << "-#{ var['short'] } #{ cli_option_name.upcase }"
|
121
|
+
end
|
122
|
+
|
123
|
+
on_args << "--#{ cli_option_name }=#{ cli_option_name.upcase }"
|
124
|
+
|
125
|
+
on_args << ruby_type
|
126
|
+
end
|
127
|
+
|
128
|
+
# description
|
129
|
+
description = if var.key? 'description'
|
130
|
+
var['description']
|
131
|
+
else
|
132
|
+
"set the #{ ansible_var_name } variable"
|
133
|
+
end
|
134
|
+
|
135
|
+
if var['type'].is_a?(Hash) && var['type'].key?('one_of')
|
136
|
+
lb = "\n" + "\t" * 5
|
137
|
+
description += " options:" +
|
138
|
+
"#{ lb }#{ var['type']['one_of'].join(lb) }"
|
139
|
+
end
|
140
|
+
|
141
|
+
on_args << description
|
142
|
+
|
143
|
+
if role.defaults.key? ansible_var_name
|
144
|
+
on_args << if var['type'] == 'boolean'
|
145
|
+
if role.defaults[ansible_var_name]
|
146
|
+
"default --#{ cli_option_name }"
|
147
|
+
else
|
148
|
+
"default --no-#{ cli_option_name }"
|
149
|
+
end
|
150
|
+
else
|
151
|
+
"default = #{ role.defaults[ansible_var_name] }"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
QB.debug "adding option", option: option, on_args: on_args
|
156
|
+
|
157
|
+
opts.on(*on_args) do |value|
|
158
|
+
QB.debug "setting option",
|
159
|
+
option: option,
|
160
|
+
value: value
|
161
|
+
|
162
|
+
option.value = value
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end # each var
|
166
|
+
end # add
|
167
|
+
|
168
|
+
def self.parse! role, args
|
169
|
+
options = {}
|
170
|
+
|
171
|
+
opt_parser = OptionParser.new do |opts|
|
172
|
+
opts.banner = "qb #{ role.name } [OPTIONS] DIRECTORY"
|
173
|
+
|
174
|
+
add opts, options, role
|
175
|
+
|
176
|
+
# No argument, shows at tail. This will print an options summary.
|
177
|
+
# Try it and see!
|
178
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
179
|
+
puts opts
|
180
|
+
exit
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
opt_parser.parse! args
|
185
|
+
|
186
|
+
options
|
187
|
+
end # parse!
|
188
|
+
end # Options
|
189
|
+
end # QB
|
data/lib/qb/role.rb
CHANGED
@@ -1,7 +1,121 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'cmds'
|
3
|
+
|
1
4
|
module QB
|
2
5
|
class Role
|
3
6
|
attr_accessor :path, :name, :rel_path
|
4
7
|
|
8
|
+
# errors
|
9
|
+
# ======
|
10
|
+
|
11
|
+
# base for errors in the module, extends QB:Error
|
12
|
+
class Error < QB::Error
|
13
|
+
end
|
14
|
+
|
15
|
+
# raised by `.require` when no roles match input
|
16
|
+
class NoMatchesError < Error
|
17
|
+
attr_accessor :input
|
18
|
+
|
19
|
+
def initialize input
|
20
|
+
@input = input
|
21
|
+
|
22
|
+
super "no roles match input #{ @input.inspect }"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# rasied by `.require` when multiple roles match
|
27
|
+
class MultipleMatchesError < Error
|
28
|
+
attr_accessor :input, :matches
|
29
|
+
|
30
|
+
def initialize input, matches
|
31
|
+
@input = input
|
32
|
+
@matches = matches
|
33
|
+
|
34
|
+
super "mutiple roles match input #{ @input.inspect }:\n#{ @matches.join("\n") }"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# static role utils
|
39
|
+
# =================
|
40
|
+
|
41
|
+
# true if pathname is a QB role directory.
|
42
|
+
def self.role_dir? pathname
|
43
|
+
# must be a directory
|
44
|
+
pathname.directory? &&
|
45
|
+
# and must have meta/qb.yml or meta/qb file
|
46
|
+
['qb.yml', 'qb'].any? {|filename| pathname.join('meta', filename).file?}
|
47
|
+
end
|
48
|
+
|
49
|
+
# array of Pathname places to look for role dirs.
|
50
|
+
def self.search_path
|
51
|
+
[
|
52
|
+
QB::ROLES_DIR,
|
53
|
+
Pathname.new(Dir.getwd).join('roles'),
|
54
|
+
Pathname.new(Dir.getwd).join('dev', 'roles'),
|
55
|
+
Pathname.new(Dir.getwd).join('dev', 'roles', 'tmp'),
|
56
|
+
]
|
57
|
+
end
|
58
|
+
|
59
|
+
# array of QB::Role found in search path.
|
60
|
+
def self.available
|
61
|
+
search_path.
|
62
|
+
select {|search_dir|
|
63
|
+
# make sure it's there (and a direcotry)
|
64
|
+
search_dir.directory?
|
65
|
+
}.
|
66
|
+
map {|search_dir|
|
67
|
+
# grab all the child directories that are role directories
|
68
|
+
search_dir.children.select {|child| role_dir? child }
|
69
|
+
}.
|
70
|
+
flatten.
|
71
|
+
# needed when qb is run from the qb repo since QB::ROLES_DIR and
|
72
|
+
# ./roles are the same dir
|
73
|
+
uniq.
|
74
|
+
map {|role_dir|
|
75
|
+
QB::Role.new role_dir
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
# get an array of QB::Role that match an input string
|
80
|
+
def self.matches input
|
81
|
+
available.each {|role|
|
82
|
+
# exact match to relitive path
|
83
|
+
return [role] if role.rel_path.to_s == input
|
84
|
+
}.each {|role|
|
85
|
+
# exact match to full name
|
86
|
+
return [role] if role.name == input
|
87
|
+
}.each {|role|
|
88
|
+
# exact match without the namespace prefix ('qb.' or similar)
|
89
|
+
return [role] if role.namespaceless == input
|
90
|
+
}.select {|role|
|
91
|
+
# select any that have that string in them
|
92
|
+
role.rel_path.to_s.include? input
|
93
|
+
}.tap {|matches|
|
94
|
+
QB.debug "role matches" => matches
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
# find exactly one matching role for the input string or raise.
|
99
|
+
def self.require input
|
100
|
+
matches = self.matches input
|
101
|
+
|
102
|
+
role = case matches.length
|
103
|
+
when 0
|
104
|
+
raise NoMatchesError.new input
|
105
|
+
when 1
|
106
|
+
matches[0]
|
107
|
+
else
|
108
|
+
raise MultipleMatchesError.new input, matches
|
109
|
+
end
|
110
|
+
|
111
|
+
QB.debug "role match" => role
|
112
|
+
|
113
|
+
role
|
114
|
+
end
|
115
|
+
|
116
|
+
# instance methods
|
117
|
+
# ================
|
118
|
+
|
5
119
|
def initialize path
|
6
120
|
@path = path
|
7
121
|
|
@@ -35,5 +149,72 @@ module QB
|
|
35
149
|
def options_key
|
36
150
|
@rel_path.to_s
|
37
151
|
end
|
152
|
+
|
153
|
+
# load qb metadata from meta/qb.yml or from executing meta/qb and parsing
|
154
|
+
# the YAML written to stdout.
|
155
|
+
#
|
156
|
+
# if `cache` is true caches it as `@meta`
|
157
|
+
#
|
158
|
+
def load_meta cache = true
|
159
|
+
meta = if (@path + 'meta' + 'qb').exist?
|
160
|
+
YAML.load(Cmds.out!((@path + 'meta' + 'qb').realpath.to_s)) || {}
|
161
|
+
elsif (@path + 'meta' + 'qb.yml').exist?
|
162
|
+
YAML.load((@path + 'meta' + 'qb.yml').read) || {}
|
163
|
+
else
|
164
|
+
{}
|
165
|
+
end
|
166
|
+
|
167
|
+
if cache
|
168
|
+
@meta = meta
|
169
|
+
end
|
170
|
+
|
171
|
+
meta
|
172
|
+
end
|
173
|
+
|
174
|
+
# gets the qb metadata for the role
|
175
|
+
def meta
|
176
|
+
@meta || load_meta
|
177
|
+
end
|
178
|
+
|
179
|
+
# gets the variable prefix that will be appended to cli options before
|
180
|
+
# passing them to the role. defaults to `#namespaceless` unless specified
|
181
|
+
# in meta.
|
182
|
+
def var_prefix
|
183
|
+
meta['var_prefix'] || namespaceless
|
184
|
+
end
|
185
|
+
|
186
|
+
# gets the vars from the metadata, defaulting to [] if noe defined.
|
187
|
+
def vars
|
188
|
+
meta['vars'] || []
|
189
|
+
end
|
190
|
+
|
191
|
+
# loads the defaults from defaults/main.yml, caching by default
|
192
|
+
def load_defaults cache = true
|
193
|
+
defaults_path = @path + 'defaults' + 'main.yml'
|
194
|
+
defaults = if defaults_path.file?
|
195
|
+
YAML.load(defaults_path.read) || {}
|
196
|
+
else
|
197
|
+
{}
|
198
|
+
end
|
199
|
+
|
200
|
+
if cache
|
201
|
+
@defaults = defaults
|
202
|
+
end
|
203
|
+
|
204
|
+
defaults
|
205
|
+
end
|
206
|
+
|
207
|
+
# gets the role variable defaults from defaults/main.yml, or {}
|
208
|
+
def defaults
|
209
|
+
@defaults || load_defaults
|
210
|
+
end
|
211
|
+
|
212
|
+
def save_options
|
213
|
+
if meta.key? 'save_options'
|
214
|
+
!!meta['save_options']
|
215
|
+
else
|
216
|
+
true
|
217
|
+
end
|
218
|
+
end
|
38
219
|
end
|
39
220
|
end
|
data/lib/qb/version.rb
CHANGED
data/library/git_mkdir.py
CHANGED
@@ -38,13 +38,15 @@ def cd(newdir):
|
|
38
38
|
def main():
|
39
39
|
module = AnsibleModule(
|
40
40
|
argument_spec = dict(
|
41
|
-
path=dict(required=True),
|
41
|
+
path=dict(required = True),
|
42
|
+
commit=dict(required = False, default = False, type = 'bool'),
|
42
43
|
),
|
43
44
|
supports_check_mode = False,
|
44
45
|
)
|
45
46
|
|
46
47
|
changed = False
|
47
48
|
path = module.params['path']
|
49
|
+
commit = module.params['commit']
|
48
50
|
|
49
51
|
if os.path.isdir(path) is False:
|
50
52
|
mkdir_p(path)
|
@@ -54,8 +56,9 @@ def main():
|
|
54
56
|
|
55
57
|
if (not os.path.exists(keep_path)) and (not contians_files(path)):
|
56
58
|
open(os.path.join(path, '.gitkeep'), 'a').close()
|
57
|
-
|
58
|
-
|
59
|
+
if commit is True:
|
60
|
+
with cd(path):
|
61
|
+
subprocess.check_call(['git', 'add', '-f', '.gitkeep'])
|
59
62
|
changed = True
|
60
63
|
|
61
64
|
module.exit_json(
|
@@ -0,0 +1,16 @@
|
|
1
|
+
---
|
2
|
+
# defaults file for qb.npm
|
3
|
+
npm_force: false
|
4
|
+
|
5
|
+
# package.json stuff
|
6
|
+
npm_name: "{{ qb_dir | basename }}"
|
7
|
+
npm_scoped_name: "{{ ('@' + npm_scope + '/' + npm_name) if (npm_scope is defined) else npm_name }}"
|
8
|
+
npm_private: true
|
9
|
+
npm_version: "0.1.0"
|
10
|
+
npm_description: "{{ npm_scoped_name }} package"
|
11
|
+
npm_main: "index.js"
|
12
|
+
npm_keywords: []
|
13
|
+
npm_license: "{{ 'UNLICENSED' if npm_private else 'ISC' }}"
|
14
|
+
npm_author: "{{ npm_scope if (npm_scope is defined) else ansible_env.GITHUB_USER }}"
|
15
|
+
|
16
|
+
npm_project: false
|
@@ -0,0 +1,14 @@
|
|
1
|
+
---
|
2
|
+
# meta file for qb.npm
|
3
|
+
|
4
|
+
allow_duplicates: yes
|
5
|
+
|
6
|
+
dependencies:
|
7
|
+
- role: qb.gitignore
|
8
|
+
gitignore_name: Node
|
9
|
+
gitignore_force: "{{ npm_force }}"
|
10
|
+
|
11
|
+
- role: qb.project
|
12
|
+
project_private: "{{ npm_private }}"
|
13
|
+
project_force: "{{ npm_force }}"
|
14
|
+
when: npm_project
|
@@ -0,0 +1,71 @@
|
|
1
|
+
---
|
2
|
+
# meta/qb.yml file for qb.npm
|
3
|
+
#
|
4
|
+
# qb settings for this role. see README.md for more info.
|
5
|
+
#
|
6
|
+
|
7
|
+
# prefix for role variables
|
8
|
+
var_prefix: null
|
9
|
+
|
10
|
+
# how to get a default for `dir` if it's not provided as the
|
11
|
+
default_dir: null
|
12
|
+
|
13
|
+
# set to false to not save options in /Users/nrser/dev/gh/nrser/qb/roles/qb.npm/.qb-options.yml
|
14
|
+
save_options: true
|
15
|
+
|
16
|
+
vars:
|
17
|
+
# - name: example
|
18
|
+
# description: an example of a variable.
|
19
|
+
# required: false
|
20
|
+
# type: boolean # boolean (default) | string
|
21
|
+
# short: e
|
22
|
+
- name: force
|
23
|
+
description: force stuff (npm init, qb.gitigore, qb.project).
|
24
|
+
type: boolean
|
25
|
+
short: f
|
26
|
+
save: false
|
27
|
+
|
28
|
+
- name: scope
|
29
|
+
description: npm scope to create the module under.
|
30
|
+
type: string
|
31
|
+
short: s
|
32
|
+
|
33
|
+
- name: name
|
34
|
+
description: name of npm module.
|
35
|
+
type: string
|
36
|
+
|
37
|
+
- name: private
|
38
|
+
description: mark the npm module private in package.json
|
39
|
+
type: boolean
|
40
|
+
|
41
|
+
- name: version
|
42
|
+
description: package.json version.
|
43
|
+
type: string
|
44
|
+
|
45
|
+
- name: description
|
46
|
+
description: package.json description.
|
47
|
+
type: string
|
48
|
+
|
49
|
+
- name: main
|
50
|
+
description: pacakge.json main (entry point).
|
51
|
+
type: string
|
52
|
+
|
53
|
+
# TODO lists don't work
|
54
|
+
# - name: keywords
|
55
|
+
# description: package.json keywords
|
56
|
+
# type: Array<string>
|
57
|
+
|
58
|
+
- name: license
|
59
|
+
description: package.json license.
|
60
|
+
type: string
|
61
|
+
|
62
|
+
- name: author
|
63
|
+
description: pacakge.json author.
|
64
|
+
type: string
|
65
|
+
|
66
|
+
- name: project
|
67
|
+
description: include qb.project role.
|
68
|
+
type: boolean
|
69
|
+
short: p
|
70
|
+
|
71
|
+
- include: qb.project
|
@@ -0,0 +1,13 @@
|
|
1
|
+
{
|
2
|
+
"name": "{{ npm_scoped_name }}",
|
3
|
+
"private": {{ 'true' if npm_private else 'false' }},
|
4
|
+
"version": "{{ npm_version }}",
|
5
|
+
"description": "{{ npm_description }}",
|
6
|
+
"main": "{{ npm_main }}",
|
7
|
+
"scripts": {
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
9
|
+
},
|
10
|
+
"keywords": {{ npm_keywords | to_json }},
|
11
|
+
"author": "{{ npm_author }}",
|
12
|
+
"license": "{{ npm_license }}"
|
13
|
+
}
|
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.1.
|
4
|
+
version: 0.1.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- nrser
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-10-
|
11
|
+
date: 2016-10-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -109,6 +109,7 @@ files:
|
|
109
109
|
- dev/setup.yml
|
110
110
|
- exe/qb
|
111
111
|
- lib/qb.rb
|
112
|
+
- lib/qb/options.rb
|
112
113
|
- lib/qb/role.rb
|
113
114
|
- lib/qb/version.rb
|
114
115
|
- library/git_mkdir.py
|
@@ -419,6 +420,11 @@ files:
|
|
419
420
|
- roles/qb.install_gem/meta/main.yml
|
420
421
|
- roles/qb.install_gem/meta/qb.yml
|
421
422
|
- roles/qb.install_gem/tasks/main.yml
|
423
|
+
- roles/qb.npm/defaults/main.yml
|
424
|
+
- roles/qb.npm/meta/main.yml
|
425
|
+
- roles/qb.npm/meta/qb.yml
|
426
|
+
- roles/qb.npm/tasks/main.yml
|
427
|
+
- roles/qb.npm/templates/package.json.j2
|
422
428
|
- roles/qb.project/.qb-options.yml
|
423
429
|
- roles/qb.project/defaults/main.yml
|
424
430
|
- roles/qb.project/files/ansible.cfg
|