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