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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9b701329529f078964cbe45228492216b6bb764
4
- data.tar.gz: be835698732b5193d85befda5a6ae70545a51d51
3
+ metadata.gz: eff9442acda19ad45e2a0fc85dde3fa21f34d33a
4
+ data.tar.gz: 0ca507c08588f891232f983b45e10b953281dce8
5
5
  SHA512:
6
- metadata.gz: 4b00d86a4b0c90a8d0e94b6489327aa90a141b00be216549660474f9360d7d75d92045b479f17579f6e91dd80f44f30426d54bf591cc4144e5587ac43edc0834
7
- data.tar.gz: ac43dde773b412798bc80e84c412a057e56bfdfd55ecc27d343225ad0533a189aff389e9edba3f908187636d30b8eecf4c80589e9b01bedfc3535d9488eb00f5
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.available_roles
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
- matches = QB.role_matches role_arg
230
- debug "role matches" => matches
231
-
232
- role = case matches.length
233
- when 0
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
- when 1
237
- matches[0]
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
- vars = qb_meta['vars'] || []
262
- var_prefix = qb_meta['var_prefix'] || role.namespaceless
107
+ options = QB::Options.parse! role, args
263
108
 
264
- options = parse! role, var_prefix, vars, defaults, args
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, qb_meta, cwd, options
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: 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: 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 saved_options_path.read
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
- options = saved_options[role.options_key].merge options
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
- playbook_role['dir'] = dir
318
- playbook_role['qb_dir'] = dir
319
- playbook_role['qb_cwd'] = cwd
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: 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
- unless (
344
- options.empty? ||
345
- (qb_meta.key?('save_options') && qb_meta['save_options'] == false)
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] = options
348
- FileUtils.mkdir_p saved_options_path.dirname unless saved_options_path.dirname.exist?
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.role_dirs
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
- msg, values = case args.length
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
- raise ArgumentError, "debug needs at least one argument"
31
+ header
23
32
  when 1
24
- if args[0].is_a? Hash
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
- raise ArgumentError, "debug needs at least one argument"
35
+ {header => args}
33
36
  end
34
37
 
35
- $stderr.puts("DEBUG " + format(msg, values))
36
- end
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, qb_meta, cwd, options
42
+ def self.get_default_dir role, cwd, options
79
43
  debug "get_default_dir", role: role,
80
- qb_meta: qb_meta,
44
+ meta: role.meta,
81
45
  cwd: cwd,
82
46
  options: options
83
47
 
84
48
  key = 'default_dir'
85
- case qb_meta[key]
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 qb_meta[key].key? 'exe'
102
- exe_path = qb_meta[key]['exe']
103
- exe_input_data = options
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'
@@ -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
@@ -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
@@ -1,7 +1,7 @@
1
1
  module QB
2
2
  GEM_NAME = 'qb'
3
3
 
4
- VERSION = "0.1.13"
4
+ VERSION = "0.1.14"
5
5
 
6
6
  def self.gemspec
7
7
  Gem.loaded_specs[GEM_NAME]
@@ -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
- with cd(path):
58
- subprocess.check_call(['git', 'add', '-f', '.gitkeep'])
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(
@@ -1,5 +1,7 @@
1
1
  ---
2
2
  # meta file for qb.git_repo
3
3
 
4
- dependencies: []
5
- # - role: role-name
4
+ dependencies:
5
+ - role: qb.gitignore
6
+ gitignore_name: Global/OSX
7
+ when: ansible_distribution == "MacOSX"
@@ -3,5 +3,4 @@
3
3
 
4
4
  allow_duplicates: yes
5
5
 
6
- dependencies:
7
- - role: qb.git_repo
6
+ dependencies: []
@@ -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,8 @@
1
+ ---
2
+ # tasks file for qb.npm_module
3
+
4
+ - name: create package.json
5
+ template:
6
+ src: package.json.j2
7
+ dest: "{{ qb_dir }}/package.json"
8
+ force: "{{ npm_force }}"
@@ -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
+ }
@@ -2,7 +2,4 @@
2
2
  # meta file for qb.project
3
3
 
4
4
  dependencies:
5
- - role: qb.gitignore
6
- gitignore_name: Global/OSX
7
- gitignore_force: "{{ project_force }}"
8
-
5
+ - role: qb.git_repo
@@ -21,6 +21,8 @@
21
21
  - name: create tmp directory
22
22
  git_mkdir:
23
23
  path: "{{ qb_dir }}/tmp"
24
+ # have to explicitly commit it since it's ignored
25
+ commit: true
24
26
  when: project_tmp
25
27
 
26
28
  - name: ignore tmp dir in git
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.13
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-02 00:00:00.000000000 Z
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