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 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