zillabyte-cli 0.0.24 → 0.1.0

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.
Files changed (40) hide show
  1. checksums.yaml +6 -14
  2. data/lib/#zillabyte-cli.rb# +5 -0
  3. data/lib/zillabyte/api/apps.rb +16 -132
  4. data/lib/zillabyte/api/components.rb +115 -0
  5. data/lib/zillabyte/api/flows.rb +121 -0
  6. data/lib/zillabyte/api/keys.rb +70 -0
  7. data/lib/zillabyte/api.rb +15 -2
  8. data/lib/zillabyte/auth.rb +43 -16
  9. data/lib/zillabyte/cli/#logs.rb# +12 -0
  10. data/lib/zillabyte/cli/#repl.rb# +43 -0
  11. data/lib/zillabyte/cli/apps.rb +52 -893
  12. data/lib/zillabyte/cli/auth.rb +3 -8
  13. data/lib/zillabyte/cli/base.rb +28 -7
  14. data/lib/zillabyte/cli/components.rb +245 -0
  15. data/lib/zillabyte/cli/flows.rb +549 -0
  16. data/lib/zillabyte/cli/git.rb +38 -0
  17. data/lib/zillabyte/cli/help.rb +11 -4
  18. data/lib/zillabyte/cli/keys.rb +177 -0
  19. data/lib/zillabyte/cli/query.rb +0 -1
  20. data/lib/zillabyte/cli/relations.rb +2 -1
  21. data/lib/zillabyte/cli/templates/{js → apps/js}/simple_function.js +0 -0
  22. data/lib/zillabyte/cli/templates/{js → apps/js}/zillabyte.conf.yaml +0 -0
  23. data/lib/zillabyte/cli/templates/apps/python/app.py +17 -0
  24. data/lib/zillabyte/cli/templates/{python → apps/python}/requirements.txt +0 -0
  25. data/lib/zillabyte/cli/templates/{python → apps/python}/zillabyte.conf.yaml +1 -1
  26. data/lib/zillabyte/cli/templates/{ruby → apps/ruby}/Gemfile +0 -0
  27. data/lib/zillabyte/cli/templates/{ruby → apps/ruby}/app.rb +1 -1
  28. data/lib/zillabyte/cli/templates/{ruby → apps/ruby}/zillabyte.conf.yaml +0 -0
  29. data/lib/zillabyte/cli/templates/python/{simple_function.py → #simple_function.py#} +3 -6
  30. data/lib/zillabyte/common/session.rb +3 -1
  31. data/lib/zillabyte/helpers.rb +64 -1
  32. data/lib/zillabyte/runner/app_runner.rb +226 -0
  33. data/lib/zillabyte/runner/component_operation.rb +529 -0
  34. data/lib/zillabyte/runner/component_runner.rb +244 -0
  35. data/lib/zillabyte/runner/multilang_operation.rb +1133 -0
  36. data/lib/zillabyte/runner/operation.rb +11 -0
  37. data/lib/zillabyte/runner.rb +6 -0
  38. data/lib/zillabyte-cli/version.rb +1 -1
  39. data/zillabyte-cli.gemspec +1 -0
  40. metadata +83 -52
@@ -0,0 +1,549 @@
1
+ require "zillabyte/cli/base"
2
+ require "zillabyte/cli/config"
3
+ require "zillabyte/common"
4
+ require "pty"
5
+ require 'indentation'
6
+ require 'open3'
7
+ require 'securerandom'
8
+ require 'colorize'
9
+ require 'time_difference'
10
+ require 'net/http'
11
+ require "zillabyte/runner/component_runner"
12
+ require "zillabyte/runner/app_runner"
13
+
14
+
15
+ # HIDDEN: superclass for components and apps
16
+ #
17
+ class Zillabyte::Command::Flows < Zillabyte::Command::Base
18
+
19
+ # flows:status [DIR]
20
+ #
21
+ # fetches detailed status of the flow
22
+ #
23
+ # --output_type OUTPUT_TYPE # specify an output type i.e. json
24
+ # --directory DIR # Directory of the flow
25
+ #
26
+ def status
27
+
28
+ id = options[:id] || shift_argument
29
+ type = options[:output_type]
30
+
31
+ if id.nil?
32
+ id = read_name_from_conf(options)
33
+ options[:is_name] = true
34
+ elsif !(id =~ /^\d*$/)
35
+ options[:is_name] = true
36
+ end
37
+
38
+ res = api.request(
39
+ :expects => 200,
40
+ :method => :get,
41
+ :path => "/flows/#{id}"
42
+ )
43
+
44
+ res.body
45
+ if type == "json"
46
+ display res.body.to_json
47
+ else
48
+ # TODO
49
+ display res.body.to_json
50
+ end
51
+
52
+ end
53
+
54
+ # flows:pull ID DIR
55
+ #
56
+ # pulls a flow source to a directory.
57
+ #
58
+ # --force # pulls even if the directory exists
59
+ # --output_type OUTPUT_TYPE # specify an output type i.e. json
60
+ # --directory DIR # Directory of the flow
61
+ #
62
+ # Examples:
63
+ #
64
+ # $ zillabyte flows:pull .
65
+ #
66
+ def pull
67
+
68
+ flow_id = options[:id] || shift_argument
69
+ type = options[:output_type]
70
+
71
+ if !(flow_id =~ /^\d*$/)
72
+ options[:is_name] = true
73
+ end
74
+
75
+ dir = options[:directory] || shift_argument
76
+ error("no directory given", type) if dir.nil?
77
+ dir = File.expand_path(dir)
78
+
79
+ error("no id given", type) if flow_id.nil?
80
+
81
+ # Create if not exists..
82
+ if File.exists?(dir)
83
+ if Dir.entries(dir).size != 2 and options[:force].nil?
84
+ error("target directory not empty. use --force to override", type)
85
+ end
86
+ else
87
+ FileUtils.mkdir_p(dir)
88
+ end
89
+
90
+ res = api.flows.pull_to_directory flow_id, dir, session, options
91
+
92
+ if res['error']
93
+ error("error: #{res['error_message']}", type)
94
+ else
95
+ if type == "json"
96
+ display "{}"
97
+ else
98
+ display "flow ##{res['id']} pulled to #{dir}"
99
+ end
100
+ end
101
+
102
+ end
103
+ alias_command "pull", "flows:pull"
104
+
105
+ # flows:prep [DIR]
106
+ #
107
+ # performs any necessary initialization for the flow
108
+ #
109
+ # --directory DIR # flow directory
110
+ # --output_type OUTPUT_TYPE # specify an output type i.e. json
111
+ #
112
+ def prep
113
+
114
+ type = options[:output_type]
115
+ dir = options[:directory] || shift_argument
116
+ if dir.nil?
117
+ dir = Dir.pwd
118
+ else
119
+ dir = File.expand_path(dir)
120
+ end
121
+ options[:directory] = dir
122
+ meta = Zillabyte::CLI::Config.get_config_info(dir)
123
+
124
+ if meta.nil?
125
+ error("The specified directory (#{dir}) does not appear to contain a valid Zillabyte configuration file.", type)
126
+ end
127
+
128
+ case meta["language"]
129
+ when "ruby"
130
+
131
+ # Execute in the bundler context
132
+ full_script = File.join(dir, meta["script"])
133
+ cmd = "cd \"#{meta['home_dir']}\"; unset BUNDLE_GEMFILE; unset RUBYOPT; bundle install"
134
+ exec(cmd)
135
+
136
+ when "python"
137
+ vDir = "#{meta['home_dir']}/vEnv"
138
+ lock_file = meta['home_dir']+"/zillabyte_thread_lock_file"
139
+ if File.exists?(lock_file)
140
+ sleep(1) while File.exists?(lock_file)
141
+ else
142
+ begin
143
+ cmd = "touch #{lock_file}; virtualenv --clear --system-site-packages #{vDir}; PYTHONPATH=~/zb1/multilang/python/Zillabyte #{vDir}/bin/pip install -r #{meta['home_dir']}/requirements.txt"
144
+ system cmd, :out => :out
145
+ ensure
146
+ File.delete(lock_file)
147
+ end
148
+ end
149
+
150
+ when "js"
151
+ end
152
+
153
+ end
154
+ alias_command "prep", "flows:prep"
155
+
156
+ # flows:logs ID [OPERATION_NAME]
157
+ #
158
+ # streams logs from the distributed workers
159
+ #
160
+ # --output_type OUTPUT_TYPE # specify an output type i.e. json
161
+ # --operation OPERATION_NAME # specify the operation to show logs for
162
+ # -v, --verbose LEVEL # sets the verbosity (error, info, debug) (default: info)
163
+ #
164
+ def logs
165
+
166
+ flow_id = options[:id] || shift_argument
167
+ if flow_id.nil?
168
+ flow_id = read_name_from_conf(options)
169
+ options[:is_name] = true
170
+ elsif !(flow_id =~ /^\d*$/)
171
+ options[:is_name] = true
172
+ end
173
+
174
+ operation_id = options[:operation] || shift_argument || '_ALL_'
175
+ category = options[:verbose] || '_ALL_'
176
+ type = options[:output_type]
177
+
178
+ carry_settings = {
179
+ :category => category
180
+ }
181
+
182
+ display "Retrieving logs for flow ##{flow_id}...please wait..." if type.nil?
183
+ hash = self.api.logs.get(flow_id, operation_id, options)
184
+
185
+ fetch_logs(hash, operation_id)
186
+
187
+ end
188
+ alias_command "logs", "flows:logs"
189
+
190
+
191
+ # flows:errors ID
192
+ #
193
+ # show recent errors generated by the flow
194
+ # --output_type OUTPUT_TYPE # specify an output type i.e. json
195
+ #
196
+ def errors
197
+
198
+ # Init
199
+ flow_id = options[:id] || shift_argument
200
+
201
+ # No name?
202
+ if flow_id.nil?
203
+ flow_id = read_name_from_conf(options)
204
+ options[:is_name] = true
205
+ elsif !(flow_id =~ /^\d*$/)
206
+ options[:is_name] = true
207
+ end
208
+
209
+ type = options[:output_type]
210
+
211
+ # Make the request
212
+ res = api.request(
213
+ :expects => 200,
214
+ :method => :get,
215
+ :body => options.to_json,
216
+ :path => "/flows/#{CGI.escape(flow_id)}/errors"
217
+ )
218
+
219
+ # Render
220
+ display "Recent errors:" if type.nil?
221
+ headings = ["operation", "date", "error"]
222
+ rows = (res.body["recent_errors"] || []).map do |row|
223
+ if row['date']
224
+ d = Time.at(row['date']/1000)
225
+ else
226
+ d = nil
227
+ end
228
+ [row['name'], d, row['message']]
229
+ end
230
+ rows.sort! do |a,b|
231
+ a[1] <=> b[1]
232
+ end
233
+ color_map = {}
234
+ colors = LogFormatter::COLORS.clone
235
+ rows.each do |row|
236
+ name = row[0]
237
+ time = row[1]
238
+ message = row[2].strip
239
+ color_map[name] ||= colors.shift
240
+ if time
241
+ display "#{"* #{name} - #{time_ago_in_words(time)} ago".colorize(color_map[name])}:" if type.nil?
242
+ else
243
+ display "#{"* #{name}".colorize(color_map[name])}:" if type.nil?
244
+ end
245
+ message.split('\n').each do |sub_line|
246
+ display " #{sub_line}" if type.nil?
247
+ end
248
+ end
249
+
250
+ end
251
+ alias_command "errors", "flows:errors"
252
+
253
+
254
+ # flows:live_run [OPERATION_NAME] [PIPE_NAME] [DIR]
255
+ #
256
+ # runs a local flow with live data
257
+ #
258
+ # --config CONFIG_FILE # use the given config file
259
+ # --output_type OUTPUT_TYPE # specify an output type i.e. json
260
+ # --directory DIR # Directory of the flow
261
+ #
262
+ def live_run
263
+
264
+ name = options[:name] || shift_argument
265
+ type = options[:output_type]
266
+
267
+ thread_id = options[:thread] || shift_argument || ""
268
+ dir = options[:directory] || shift_argument
269
+ if dir.nil?
270
+ dir = Dir.pwd
271
+ else
272
+ dir = File.expand_path(dir)
273
+ end
274
+ options[:directory] = dir
275
+
276
+ meta = Zillabyte::CLI::Config.get_config_info(dir, options)
277
+
278
+ if meta.nil?
279
+ error("could not find meta information for: #{dir}", type)
280
+ end
281
+
282
+ if(thread_id == "")
283
+ exec(command("--execute_live --name #{name.to_s}",type, dir))
284
+ else
285
+ exec(command("--execute_live --name #{name.to_s} --pipe #{dir}/#{thread_id}",type, dir))
286
+ end
287
+ end
288
+ alias_command "live_run", "flows:live_run"
289
+
290
+ # flows:info [DIR]
291
+ #
292
+ # outputs the info for the flow in the dir.
293
+ #
294
+ # --pretty # Pretty prints the info output
295
+ # --output_type OUTPUT_TYPE # specify an output type i.e. json
296
+ # --directory DIR # Directory of the flow
297
+ #
298
+ def info
299
+
300
+ dir = options[:directory] || shift_argument
301
+ if dir.nil?
302
+ dir = Dir.pwd
303
+ else
304
+ dir = File.expand_path(dir)
305
+ end
306
+ options[:directory] = dir
307
+
308
+ info_file = "#{dir}/#{SecureRandom.uuid}"
309
+ type = options[:output_type]
310
+
311
+ cmd = command("--info --file #{info_file}", type, dir)
312
+ flow_info = Zillabyte::Command::Apps.get_info(cmd, info_file, dir, options)
313
+
314
+ if type == "json"
315
+ puts flow_info
316
+ else
317
+ if options[:pretty]
318
+ puts JSON.pretty_generate(JSON.parse(flow_info))
319
+ else
320
+ puts flow_info
321
+ end
322
+ end
323
+
324
+ exit
325
+ end
326
+ alias_command "info", "flows:info"
327
+
328
+ # flows:delete ID
329
+ #
330
+ # deletes a flow. if the flow is running, this command will kill it.
331
+ #
332
+ # -f, --force # don't ask for confirmation
333
+ # --output_type OUTPUT_TYPE # specify an output type i.e. json
334
+ #
335
+ def delete
336
+ flow_id = options[:id] || shift_argument
337
+
338
+ if flow_id.nil?
339
+ flow_id = read_name_from_conf(options)
340
+ options[:is_name] = true
341
+ elsif !(flow_id =~ /^\d*$/)
342
+ options[:is_name] = true
343
+ end
344
+ forced = options[:force]
345
+ type = options[:output_type]
346
+
347
+ if not forced
348
+
349
+ if !type.nil?
350
+ error("specify -f, --force to confirm deletion", type)
351
+ end
352
+
353
+ while true
354
+ display "This operation cannot be undone. Are you sure you want to delete this flow? (yes/no):", false
355
+ confirm = ask
356
+ break if confirm == "yes" || confirm == "no"
357
+ display "Please enter 'yes' to delete the flow or 'no' to exit"
358
+ end
359
+
360
+ end
361
+
362
+ confirmed = forced || confirm == "yes"
363
+
364
+ if confirmed
365
+ response = api.flows.delete(flow_id, options)
366
+ if type == "json"
367
+ display "{}"
368
+ else
369
+ display response["body"]
370
+ end
371
+ end
372
+
373
+ end
374
+
375
+
376
+ #
377
+ # --output_type OUTPUT_TYPE # specify an output type i.e. json
378
+ #
379
+ def self.get_info(cmd, info_file, dir, options = {})
380
+ type = options[:output_type]
381
+
382
+ response = `#{cmd}`
383
+ if($?.exitstatus == 1)
384
+
385
+ File.delete("#{info_file}") if File.exists?(info_file)
386
+
387
+ if options[:output_type].nil?
388
+ exit(1)
389
+ else
390
+ Zillabyte::Helpers.error("error: #{response}", type)
391
+ end
392
+ end
393
+
394
+ flow_info = {}
395
+ File.open("#{info_file}", "r+").each do |line|
396
+ line = JSON.parse(line)
397
+ if(line["class"] == "node")
398
+ flow_info["nodes"] << line["info"]
399
+ elsif(line["class"] == "arc")
400
+ flow_info["arcs"] << line["info"]
401
+ else
402
+ flow_info = line
403
+ flow_info["nodes"] = []
404
+ flow_info["arcs"] = []
405
+ end
406
+ end
407
+ File.delete("#{info_file}")
408
+
409
+ flow_info = flow_info.to_json
410
+ if(File.exists?("#{dir}/info_to_java.in"))
411
+ java_pipe = open("#{dir}/info_to_java.in","w+")
412
+ java_pipe.puts(flow_info+"\n")
413
+ java_pipe.flush
414
+ java_pipe.close()
415
+ end
416
+
417
+ flow_info
418
+ end
419
+
420
+
421
+
422
+
423
+ # flows:test
424
+ #
425
+ # tests a local flow with sample data
426
+ #
427
+ # --config CONFIG_FILE # use the given config file
428
+ # --input INPUT_FILE # writes sink output to a file
429
+ # --output OUTPUT_FILE # writes sink output to a file
430
+ # --cycles CYCLES # number of cycles to emit (default 1)
431
+ # --directory DIR # app directory
432
+ # -i, --interactive # allow user to control input and read output
433
+ #
434
+ def test
435
+ dir = options[:directory]
436
+ if dir.nil?
437
+ dir = Dir.pwd
438
+ else
439
+ dir = File.expand_path(dir)
440
+ end
441
+ options[:directory] = dir
442
+
443
+
444
+ # Get the class
445
+ flow_class = read_class_from_conf(options)
446
+
447
+ case flow_class
448
+ when "component"
449
+ runner = Zillabyte::Runner::ComponentRunner.new()
450
+ when "app"
451
+ runner = Zillabyte::Runner::AppRunner.new()
452
+ else
453
+ # Default to App
454
+ runner = Zillabyte::Runner::AppRunner.new()
455
+ end
456
+
457
+ # Start Runner
458
+ runner.run(dir, self, options)
459
+
460
+ end
461
+ alias_command "test", "flows:test"
462
+
463
+
464
+ private
465
+
466
+ #
467
+ # --output_type OUTPUT_TYPE # specify an output type i.e. json
468
+ #
469
+ def command(arg="--execute_live", type = nil, dir = Dir.pwd, ignore_stderr = false)
470
+ meta = Zillabyte::CLI::Config.get_config_info(dir, self, options)
471
+
472
+ #meta = Zillabyte::API::Functions.get_rich_meta_info_from_script(dir, self)
473
+ error("could not extract meta information. missing zillabyte.conf.yml?", type) if meta.nil?
474
+ error(meta["error_message"], type) if meta['status'] == "error"
475
+ full_script = File.join(dir, meta["script"])
476
+ stderr_opt = "2> /dev/null" if ignore_stderr
477
+
478
+ case meta["language"]
479
+ when "ruby"
480
+ # Execute in the bundler context
481
+ cmd = "cd \"#{dir}\"; unset BUNDLE_GEMFILE; ZILLABYTE_HARNESS=1 bundle exec ruby \"#{full_script}\" #{arg} #{stderr_opt}"
482
+
483
+ when "python"#{
484
+ if(File.directory?("#{dir}/vEnv"))
485
+ cmd = "cd \"#{dir}\"; PYTHONPATH=~/zb1/multilang/python/Zillabyte #{dir}/vEnv/bin/python \"#{full_script}\" #{arg} #{stderr_opt}"
486
+ else
487
+ cmd = "cd \"#{dir}\"; PYTHONPATH=~/zb1/multilang/python/Zillabyte python \"#{full_script}\" #{arg} #{stderr_opt}"
488
+ end
489
+
490
+ when "js"
491
+ # cmd = "#{Zillabyte::API::CASPERJS_BIN} #{Zillabyte::API::API_CLIENT_JS} \"#{full_script}\" #{arg}"
492
+ cmd = "cd \"#{dir}\"; NODE_PATH=~/zb1/multilang/js/src/lib #{Zillabyte::API::NODEJS_BIN} \"#{full_script}\" #{arg} #{stderr_opt}"
493
+
494
+ else
495
+ error("no language specified", type)
496
+ end
497
+
498
+ return cmd
499
+
500
+ end
501
+
502
+ def read_name_from_conf(options = {})
503
+ type = options[:output_type]
504
+ hash = Zillabyte::API::Apps.get_rich_meta_info_from_script Dir.pwd, options
505
+ error("No id given and current directory does not contain a valid Zillabyte configuration file. Please specify a flow id or run command from the directory containing the flow.",type) if hash["error"]
506
+ hash["name"]
507
+ end
508
+
509
+ def read_class_from_conf(options = {})
510
+ type = options[:output_type]
511
+ hash = Zillabyte::API::Apps.get_rich_meta_info_from_script Dir.pwd, options
512
+ error("No id given and current directory does not contain a valid Zillabyte configuration file. Please specify a flow id or run command from the directory containing the flow.",type) if hash["error"]
513
+ hash["class"]
514
+ end
515
+
516
+ def fetch_logs(hash, operation=nil, exit_on=nil)
517
+ def color_for(operation_name)
518
+ @color_map[operation_name] ||= @all_colors[ @color_map.size % @all_colors.size ]
519
+ @color_map[operation_name]
520
+ end
521
+
522
+ @color_map = {}
523
+ @all_colors = [:green, :yellow, :magenta, :cyan, :light_black, :light_green, :light_yellow, :light_blue, :light_magenta, :light_cyan]
524
+ if(hash["server"] == "localhost")
525
+ cmd = "tail -n 500 -f /tmp/flows/f#{hash["flow"]}/flow_logs/flow_#{hash["flow"]}.log"
526
+ else
527
+ cmd = "curl -G http://#{hash["flow"]}:#{hash["token"]}@#{hash["server"]}:#{hash["port"]}/getLogs?flow=#{hash["flow"]}"
528
+ end
529
+
530
+ begin
531
+ PTY.spawn( cmd ) do |r, w, pid|
532
+ r.each do |line|
533
+ begin
534
+ op = line.match(/\[(.*?)\]/)[0].to_s[1...-1]
535
+ op_name = op.split(".")[0]
536
+ next if op_name != operation and operation != "_ALL_"
537
+ line_split = line.split("[")
538
+ print line_split[0] + "[" + op.colorize(color_for(op)) + line_split[1..-1].join("[")[op.length..-1]
539
+ break if exit_on and line.include? exit_on
540
+ rescue Exception => e
541
+ next
542
+ end
543
+ end
544
+ end
545
+ rescue PTY::ChildExited => e
546
+ end
547
+ end
548
+
549
+ end
@@ -0,0 +1,38 @@
1
+ require "zillabyte/cli/base"
2
+
3
+ # manage git for apps
4
+ #
5
+ class Zillabyte::Command::Git < Zillabyte::Command::Base
6
+
7
+ # git:remote [OPTIONS]
8
+ #
9
+ # adds a git remote to an app repo
10
+ #
11
+ # if OPTIONS are specified they will be passed to git remote add
12
+ #
13
+ # -r, --remote REMOTE # the git remote to create, default "Zillabyte"
14
+ # -n, --name NAME # the name of the app, default current directory's name
15
+ #
16
+ #Examples:
17
+ #
18
+ # $ zillabyte git:remote -a example
19
+ # Git remote zillabyte added
20
+ #
21
+ # $ zillabyte git:remote -a example
22
+ # ! Git remote zillabyte already exists
23
+ #
24
+ def remote
25
+ git_options = args.join(" ")
26
+ remote = options[:remote] || 'zillabyte'
27
+ app_name = options[:name] || File.basename(Dir.pwd)
28
+
29
+ if git('remote').split("\n").include?(remote)
30
+ # already exists.. remove it.
31
+ git("remote remove #{remote}")
32
+ end
33
+
34
+ git_url = "git@#{Zillabyte::Auth.git_host}:#{Zillabyte::Auth.user}/#{app_name}"
35
+ create_git_remote(remote, git_url)
36
+ end
37
+
38
+ end
@@ -4,7 +4,7 @@ require "zillabyte/cli/base"
4
4
  #
5
5
  class Zillabyte::Command::Help < Zillabyte::Command::Base
6
6
 
7
- PRIMARY_NAMESPACES = %w( relations query apps logs )
7
+ PRIMARY_NAMESPACES = %w( relations query apps components logs )
8
8
 
9
9
  # help
10
10
  def index(*direct_args)
@@ -85,13 +85,20 @@ private
85
85
 
86
86
  def summary_for_aliases(command_aliases)
87
87
  used_aliases = command_aliases.select{|c,a| !c.include?("-") and !c.include?(":") and !skip_command?(commands[a])}
88
- a_size = longest(used_aliases.map { |al,al_to| al }) + 2
89
- a_to_size = longest(used_aliases.map { |al,al_to| al_to }) + 2
88
+ a_size = longest(used_aliases.map { |al,al_to| al }) + 4
89
+ a_to_size = longest(used_aliases.map { |al,al_to| al_to }) + 4
90
90
  used_aliases.sort_by{|k,v| v}.each do |command_alias, alias_to|
91
+
91
92
  if command_alias.include?("-") or command_alias.include?(":")
92
93
  next #skip -h, -help and live-run etc.
93
94
  end
94
- puts "#{command_alias.ljust(a_size)} --> #{alias_to.ljust(a_to_size)} # #{commands[alias_to][:summary]}"
95
+
96
+ if alias_to.include?("flows")
97
+ puts "#{command_alias.ljust(a_size)} --> #{alias_to.gsub("flows", "apps").ljust(a_to_size)} # #{commands[alias_to][:summary].gsub("flow", "app")}"
98
+ puts "#{" ".ljust(a_size)} --> #{alias_to.gsub("flows", "components").ljust(a_to_size)} # #{commands[alias_to][:summary].gsub("flow", "component")}"
99
+ else
100
+ puts "#{command_alias.ljust(a_size)} --> #{alias_to.ljust(a_to_size)} # #{commands[alias_to][:summary]}"
101
+ end
95
102
  end
96
103
  end
97
104