kontena-plugin-shell 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.travis.yml +16 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +191 -0
- data/README.md +105 -0
- data/bin/kosh +37 -0
- data/kontena-plugin-shell.gemspec +26 -0
- data/kosh.gif +0 -0
- data/lib/kontena/plugin/shell.rb +16 -0
- data/lib/kontena/plugin/shell/callbacks/stack_file.rb +31 -0
- data/lib/kontena/plugin/shell/command.rb +85 -0
- data/lib/kontena/plugin/shell/commands/batch.rb +29 -0
- data/lib/kontena/plugin/shell/commands/batch_do.rb +30 -0
- data/lib/kontena/plugin/shell/commands/context_top.rb +22 -0
- data/lib/kontena/plugin/shell/commands/context_up.rb +15 -0
- data/lib/kontena/plugin/shell/commands/debug.rb +29 -0
- data/lib/kontena/plugin/shell/commands/exit.rb +20 -0
- data/lib/kontena/plugin/shell/commands/help.rb +43 -0
- data/lib/kontena/plugin/shell/commands/kontena.rb +57 -0
- data/lib/kontena/plugin/shell/completer.rb +216 -0
- data/lib/kontena/plugin/shell/context.rb +59 -0
- data/lib/kontena/plugin/shell/session.rb +115 -0
- data/lib/kontena/plugin/shell/shell_command.rb +10 -0
- data/lib/kontena/plugin/shell/version.rb +7 -0
- data/lib/kontena_cli_plugin.rb +11 -0
- metadata +127 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
module Kontena::Plugin
|
2
|
+
module Shell
|
3
|
+
class Command
|
4
|
+
|
5
|
+
attr_reader :context, :args, :session
|
6
|
+
|
7
|
+
def self.command(name = nil)
|
8
|
+
return @command if instance_variable_defined?(:@command)
|
9
|
+
disabled_commands = ENV['KOSH_DISABLED_COMMANDS'].to_s.split(/,/)
|
10
|
+
Array(name).each { |name| Shell.commands[name] = self unless disabled_commands.include?(name) }
|
11
|
+
@command = name
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.subcommands(subcommands = nil)
|
15
|
+
return @subcommands if instance_variable_defined?(:@subcommands)
|
16
|
+
@subcommands = {}
|
17
|
+
Array(subcommands).each do |sc|
|
18
|
+
Array(sc.command).each do |name|
|
19
|
+
@subcommands[name] = sc
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.has_subcommands?
|
25
|
+
!subcommands.nil? && !subcommands.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_subcommands?
|
29
|
+
self.class.has_subcommands?
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.description(text = nil)
|
33
|
+
@description ||= text
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.help(text = nil)
|
37
|
+
@help ||= text
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.completions(*completions)
|
41
|
+
@completions ||= completions
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(context = nil, args = nil, session = nil)
|
45
|
+
@args = Array(args)
|
46
|
+
@context = context
|
47
|
+
@session = session
|
48
|
+
end
|
49
|
+
|
50
|
+
def run
|
51
|
+
if has_subcommands?
|
52
|
+
if args[1]
|
53
|
+
subcommand = self.class.subcommands[args[1]]
|
54
|
+
if subcommand
|
55
|
+
subcommand.new(context, args[1..-1], session).run
|
56
|
+
else
|
57
|
+
puts Kontena.pastel.red("Unknown subcommand")
|
58
|
+
end
|
59
|
+
else
|
60
|
+
context << args[0]
|
61
|
+
end
|
62
|
+
else
|
63
|
+
execute
|
64
|
+
end
|
65
|
+
rescue Kontena::Plugin::Shell::ExitCommand::CleanExit
|
66
|
+
puts Kontena.pastel.green("Bye!")
|
67
|
+
exit 0
|
68
|
+
rescue => ex
|
69
|
+
puts Kontena.pastel.red("ERROR: " + ex.message)
|
70
|
+
puts Kontena.pastel.green(ex.backtrace.join("\n ")) if ENV["DEBUG"]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# SubCommand is just like a command, except it doesn't register the
|
75
|
+
# class to Shell.commands.
|
76
|
+
class SubCommand < Command
|
77
|
+
def self.command(name = nil)
|
78
|
+
@command ||= name
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
Dir[File.expand_path('../commands/**/*.rb', __FILE__)].each { |file| require file }
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'kontena/plugin/shell/command'
|
2
|
+
require 'kontena/plugin/shell/commands/batch_do'
|
3
|
+
|
4
|
+
module Kontena::Plugin
|
5
|
+
module Shell
|
6
|
+
class BatchCommand < Command
|
7
|
+
|
8
|
+
command 'batch'
|
9
|
+
description 'Run/create command batches'
|
10
|
+
help -> (context, tokens) {
|
11
|
+
if tokens && tokens[2] && subcommands[tokens[2]]
|
12
|
+
subcommands[tokens[2]].help
|
13
|
+
else
|
14
|
+
<<-EOB.gsub(/^\s+/, '')
|
15
|
+
To run a series of commands in a batch, use:
|
16
|
+
|
17
|
+
> batch do
|
18
|
+
> command
|
19
|
+
> command 2
|
20
|
+
> end
|
21
|
+
EOB
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
subcommands [Kontena::Plugin::Shell::BatchDoCommand]
|
26
|
+
completions *subcommands.keys
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'kontena/plugin/shell/command'
|
2
|
+
|
3
|
+
module Kontena::Plugin
|
4
|
+
module Shell
|
5
|
+
class BatchDoCommand < SubCommand
|
6
|
+
|
7
|
+
command 'do'
|
8
|
+
description 'Run a batch of commands'
|
9
|
+
help "Example:\n> batch do\n> master users ls\n> master ls\n> end"
|
10
|
+
completions nil
|
11
|
+
|
12
|
+
def execute
|
13
|
+
if args.size > 1
|
14
|
+
lines = args[1..-1].join(' ').split(/(?<!\\);/).map(&:strip)
|
15
|
+
else
|
16
|
+
lines = []
|
17
|
+
while buf = Readline.readline("#{Kontena.pastel.green('..')}#{Kontena.pastel.red('>')} ", true)
|
18
|
+
buf.strip!
|
19
|
+
break if buf == 'end'
|
20
|
+
lines << buf unless buf.empty?
|
21
|
+
end
|
22
|
+
(lines.size + 1).times { Readline::HISTORY.pop }
|
23
|
+
Readline::HISTORY.push "batch do #{lines.join('; ')}"
|
24
|
+
end
|
25
|
+
|
26
|
+
lines.each { |line| session.run_command(line) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'kontena/plugin/shell/command'
|
2
|
+
|
3
|
+
module Kontena::Plugin
|
4
|
+
module Shell
|
5
|
+
class ContextTopCommand < Command
|
6
|
+
command '/'
|
7
|
+
description 'Clear context'
|
8
|
+
help "Go to top in context.\n\nYou can also directly call other commands without switching context first by using for example #{Kontena.pastel.yellow('/ master ls')}"
|
9
|
+
|
10
|
+
def execute
|
11
|
+
if args[1] && session
|
12
|
+
old_context = context.to_a.clone
|
13
|
+
context.top
|
14
|
+
session.run_command(args[1..-1].join(' '))
|
15
|
+
context.concat(old_context)
|
16
|
+
else
|
17
|
+
context.top
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'kontena/plugin/shell/command'
|
2
|
+
|
3
|
+
module Kontena::Plugin
|
4
|
+
module Shell
|
5
|
+
class DebugCommand < Command
|
6
|
+
command 'debug'
|
7
|
+
description 'Toggle debug output'
|
8
|
+
help 'Use debug on/off to toggle debug output.'
|
9
|
+
|
10
|
+
completions 'on', 'off', 'api'
|
11
|
+
|
12
|
+
def execute
|
13
|
+
case args[1]
|
14
|
+
when 'true', 'on', '1'
|
15
|
+
ENV['DEBUG'] = 'true'
|
16
|
+
when 'api'
|
17
|
+
ENV['DEBUG'] = 'api'
|
18
|
+
when 'off', 'false', '0'
|
19
|
+
ENV.delete('DEBUG')
|
20
|
+
when NilClass
|
21
|
+
# do nothing
|
22
|
+
else
|
23
|
+
puts Kontena.pastel.red("Unknown argument '#{args[1]}'")
|
24
|
+
end
|
25
|
+
puts "Debug #{Kontena.pastel.send(*ENV['DEBUG'] ? [:green, 'on'] : [:red, 'off'])}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'kontena/plugin/shell/command'
|
2
|
+
|
3
|
+
module Kontena::Plugin
|
4
|
+
module Shell
|
5
|
+
class ExitCommand < Command
|
6
|
+
command 'exit'
|
7
|
+
description 'Quit shell'
|
8
|
+
help 'Enter "exit" to quit.'
|
9
|
+
|
10
|
+
completions nil
|
11
|
+
|
12
|
+
CleanExit = Class.new(StandardError)
|
13
|
+
|
14
|
+
def execute
|
15
|
+
raise CleanExit
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'kontena/plugin/shell/command'
|
2
|
+
|
3
|
+
module Kontena::Plugin
|
4
|
+
module Shell
|
5
|
+
class HelpCommand < Command
|
6
|
+
command 'help'
|
7
|
+
description 'Show help'
|
8
|
+
help 'Use "help <command>" to see help for a specific command'
|
9
|
+
#completions -> (context, tokens, word) { Kontena::Completer.complete(context.to_a + tokens) }
|
10
|
+
|
11
|
+
def cmd
|
12
|
+
full_line = context + args[1..-1]
|
13
|
+
cmd = Shell.command(full_line.first) || Shell.command('kontena')
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute
|
17
|
+
if cmd.help.respond_to?(:call)
|
18
|
+
help_text = cmd.help.call(context, args[1..-1])
|
19
|
+
else
|
20
|
+
help_text = cmd.help
|
21
|
+
end
|
22
|
+
puts help_text
|
23
|
+
|
24
|
+
if cmd.has_subcommands?
|
25
|
+
puts
|
26
|
+
puts Kontena.pastel.green("Subcommands:")
|
27
|
+
cmd.subcommands.each do |name, sc|
|
28
|
+
puts sprintf(' %-29s %s', name, sc.description)
|
29
|
+
end
|
30
|
+
puts
|
31
|
+
end
|
32
|
+
|
33
|
+
if args.empty? || (args.size == 1 && args.first == 'help')
|
34
|
+
puts Kontena.pastel.green('KOSH commands:')
|
35
|
+
Shell.commands.each do |name, cmd|
|
36
|
+
next if cmd == Kontena::Plugin::Shell::KontenaCommand
|
37
|
+
puts sprintf(' %-29s %s', name, cmd.description)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'kontena/main_command' unless Kontena.const_defined?(:MainCommand)
|
2
|
+
require 'kontena/plugin/shell/command'
|
3
|
+
require 'kontena/plugin/shell/completer'
|
4
|
+
require 'kontena/plugin/shell/context'
|
5
|
+
|
6
|
+
module Kontena::Plugin
|
7
|
+
module Shell
|
8
|
+
class KontenaCommand < Command
|
9
|
+
|
10
|
+
command ['kontena'] + Kontena::MainCommand.recognised_subcommands.flat_map(&:names)
|
11
|
+
description 'Run Kontena-cli command'
|
12
|
+
help -> (context, tokens) { new(context, tokens).subcommand_class.help('').gsub(/^(\s+)\[OPTIONS\] SUB/, "\\1 SUB") }
|
13
|
+
completions -> (context, tokens, word) { Kontena::Plugin::Shell::Completer.complete(context.to_a + tokens) }
|
14
|
+
|
15
|
+
def cmd
|
16
|
+
cmd = Kontena::MainCommand.new('')
|
17
|
+
cmdline = context.to_a + args
|
18
|
+
cmdline.shift if cmdline.first == 'kontena'
|
19
|
+
cmd.parse(cmdline)
|
20
|
+
cmd
|
21
|
+
end
|
22
|
+
|
23
|
+
def execute
|
24
|
+
cmd.run([])
|
25
|
+
rescue Clamp::HelpWanted => ex
|
26
|
+
unless args.include?('--help') || args.include?('-h')
|
27
|
+
context.concat(args)
|
28
|
+
end
|
29
|
+
rescue SystemExit => ex
|
30
|
+
puts Kontena.pastel.red('[Command exited with error]') unless ex.status.zero?
|
31
|
+
rescue => ex
|
32
|
+
puts Kontena.pastel.red("ERROR: #{ex.message}")
|
33
|
+
end
|
34
|
+
|
35
|
+
def subcommand_class
|
36
|
+
(context + args).reject { |t| t.start_with?('-') }.inject(Kontena::MainCommand) do |base, token|
|
37
|
+
if base.has_subcommands?
|
38
|
+
sc = base.recognised_subcommands.find { |sc| sc.names.include?(token) }
|
39
|
+
sc ? sc.subcommand_class : base
|
40
|
+
else
|
41
|
+
base
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if ENV["KOSH_DISABLED_COMMANDS"]
|
50
|
+
ENV["KOSH_DISABLED_COMMANDS"].split(',').each do |command|
|
51
|
+
tokens = command.split(' ')
|
52
|
+
subcommand = tokens.pop
|
53
|
+
kontena = Kontena::Plugin::Shell::KontenaCommand.new(Kontena::Plugin::Shell::Context.new(tokens))
|
54
|
+
found_subcommand = kontena.subcommand_class
|
55
|
+
found_subcommand.recognised_subcommands.delete_if { |sc| sc.names.include?(subcommand) }
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
# this is mostly just a wrapped version of the completer in cli
|
2
|
+
require 'kontena/cli/common' unless Kontena.const_defined?(:Cli) && Kontena::Cli.const_defined?(:Common)
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Kontena::Plugin::Shell
|
6
|
+
module Completer
|
7
|
+
class Helper
|
8
|
+
include Kontena::Cli::Common
|
9
|
+
|
10
|
+
def grids
|
11
|
+
client.get("grids")['grids'].map{|grid| grid['id']}
|
12
|
+
rescue
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
|
16
|
+
def nodes
|
17
|
+
client.get("grids/#{current_grid}/nodes")['nodes'].map{|node| node['name']}
|
18
|
+
rescue
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
|
22
|
+
def stacks
|
23
|
+
stacks = client.get("grids/#{current_grid}/stacks")['stacks']
|
24
|
+
results = []
|
25
|
+
results.push stacks.map{|s| s['name']}
|
26
|
+
results.delete('null')
|
27
|
+
results
|
28
|
+
rescue
|
29
|
+
[]
|
30
|
+
end
|
31
|
+
|
32
|
+
def services
|
33
|
+
services = client.get("grids/#{current_grid}/services")['services']
|
34
|
+
results = []
|
35
|
+
results.push services.map{ |s|
|
36
|
+
stack = s['stack']['id'].split('/').last
|
37
|
+
if stack != 'null'
|
38
|
+
"#{stack}/#{s['name']}"
|
39
|
+
else
|
40
|
+
s['name']
|
41
|
+
end
|
42
|
+
}
|
43
|
+
results
|
44
|
+
rescue
|
45
|
+
[]
|
46
|
+
end
|
47
|
+
|
48
|
+
def containers
|
49
|
+
results = []
|
50
|
+
client.get("grids/#{current_grid}/services")['services'].each do |service|
|
51
|
+
containers = client.get("services/#{service['id']}/containers")['containers']
|
52
|
+
results.push(containers.map{|c| c['name'] })
|
53
|
+
results.push(containers.map{|c| c['id'] })
|
54
|
+
end
|
55
|
+
results
|
56
|
+
rescue
|
57
|
+
[]
|
58
|
+
end
|
59
|
+
|
60
|
+
def yml_services
|
61
|
+
if File.exist?('kontena.yml')
|
62
|
+
yaml = YAML.safe_load(File.read('kontena.yml'))
|
63
|
+
services = yaml['services']
|
64
|
+
services.keys
|
65
|
+
end
|
66
|
+
rescue
|
67
|
+
[]
|
68
|
+
end
|
69
|
+
|
70
|
+
def yml_files
|
71
|
+
Dir["./*.yml"].map{|file| file.sub('./', '')}
|
72
|
+
rescue
|
73
|
+
[]
|
74
|
+
end
|
75
|
+
|
76
|
+
def master_names
|
77
|
+
config_file = File.expand_path('~/.kontena_client.json')
|
78
|
+
if(File.exist?(config_file))
|
79
|
+
config = JSON.parse(File.read(config_file))
|
80
|
+
return config['servers'].map{|s| s['name']}
|
81
|
+
end
|
82
|
+
rescue
|
83
|
+
[]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.complete(words)
|
88
|
+
while words.first == 'kontena' || words.first == 'complete'
|
89
|
+
words.shift
|
90
|
+
end
|
91
|
+
helper = Helper.new
|
92
|
+
completion = []
|
93
|
+
|
94
|
+
case words[0]
|
95
|
+
when NilClass, ''
|
96
|
+
completion.concat %w(cloud logout grid app service stack vault certificate node master vpn registry container etcd external-registry whoami plugin version)
|
97
|
+
when 'plugin'
|
98
|
+
sub_commands = %w(list ls search install uninstall)
|
99
|
+
if words[1]
|
100
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
101
|
+
else
|
102
|
+
completion.push sub_commands
|
103
|
+
end
|
104
|
+
when 'etcd'
|
105
|
+
sub_commands = %w(get set mkdir mk list ls rm)
|
106
|
+
if words[1]
|
107
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
108
|
+
else
|
109
|
+
completion.push sub_commands
|
110
|
+
end
|
111
|
+
when 'registry'
|
112
|
+
sub_commands = %w(create remove rm)
|
113
|
+
if words[1]
|
114
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
115
|
+
else
|
116
|
+
completion.push sub_commands
|
117
|
+
end
|
118
|
+
when 'grid'
|
119
|
+
sub_commands = %w(add-user audit-log create current list user remove show use)
|
120
|
+
if words[1]
|
121
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
122
|
+
completion.push helper.grids
|
123
|
+
else
|
124
|
+
completion.push sub_commands
|
125
|
+
end
|
126
|
+
when 'node'
|
127
|
+
sub_commands = %w(list show remove)
|
128
|
+
if words[1]
|
129
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
130
|
+
completion.push helper.nodes
|
131
|
+
else
|
132
|
+
completion.push sub_commands
|
133
|
+
end
|
134
|
+
when 'master'
|
135
|
+
sub_commands = %w(list use users current remove rm config cfg login logout token join audit-log init-cloud)
|
136
|
+
if words[1] && words[1] == 'use'
|
137
|
+
completion.push helper.master_names
|
138
|
+
elsif words[1] && words[1] == 'users'
|
139
|
+
users_sub_commands = %(invite list role)
|
140
|
+
completion.push users_sub_commands
|
141
|
+
elsif words[1] && ['config', 'cfg'].include?(words[1])
|
142
|
+
config_sub_commands = %(set get dump load import export unset)
|
143
|
+
completion.push config_sub_commands
|
144
|
+
elsif words[1] && words[1] == 'token'
|
145
|
+
token_sub_commands = %(list ls rm remove show current create)
|
146
|
+
completion.push token_sub_commands
|
147
|
+
elsif words[1]
|
148
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
149
|
+
else
|
150
|
+
completion.push sub_commands
|
151
|
+
end
|
152
|
+
when 'cloud'
|
153
|
+
sub_commands = %w(login logout master)
|
154
|
+
if words[1] && words[1] == 'master'
|
155
|
+
cloud_master_sub_commands = %(list ls remove rm add show update)
|
156
|
+
completion.push cloud_master_sub_commands
|
157
|
+
elsif words[1]
|
158
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
159
|
+
else
|
160
|
+
completion.push sub_commands
|
161
|
+
end
|
162
|
+
when 'service'
|
163
|
+
sub_commands = %w(containers create delete deploy list logs restart
|
164
|
+
scale show start stats stop update monitor env
|
165
|
+
secret link unlink)
|
166
|
+
if words[1]
|
167
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
168
|
+
completion.push helper.services
|
169
|
+
else
|
170
|
+
completion.push sub_commands
|
171
|
+
end
|
172
|
+
when 'container'
|
173
|
+
sub_commands = %w(exec inspect logs)
|
174
|
+
if words[1]
|
175
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
176
|
+
completion.push helper.containers
|
177
|
+
else
|
178
|
+
completion.push sub_commands
|
179
|
+
end
|
180
|
+
when 'vpn'
|
181
|
+
completion.push %w(config create delete)
|
182
|
+
when 'external-registry'
|
183
|
+
completion.push %w(add list delete)
|
184
|
+
when 'app'
|
185
|
+
sub_commands = %w(init build config deploy start stop remove rm ps list
|
186
|
+
logs monitor show)
|
187
|
+
if words[1]
|
188
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
189
|
+
completion.push helper.yml_services
|
190
|
+
else
|
191
|
+
completion.push sub_commands
|
192
|
+
end
|
193
|
+
when 'stack'
|
194
|
+
sub_commands = %w(build install upgrade deploy start stop remove rm ls list
|
195
|
+
logs monitor show registry)
|
196
|
+
if words[1]
|
197
|
+
if words[1] == 'registry'
|
198
|
+
registry_sub_commands = %(push pull search show rm)
|
199
|
+
completion.push registry_sub_commands
|
200
|
+
elsif %w(install).include?(words[1])
|
201
|
+
completion.push helper.yml_files
|
202
|
+
elsif words[1] == 'upgrade' && words[3]
|
203
|
+
completion.push helper.yml_files
|
204
|
+
else
|
205
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
206
|
+
completion.push helper.stacks
|
207
|
+
end
|
208
|
+
else
|
209
|
+
completion.push sub_commands
|
210
|
+
end
|
211
|
+
else
|
212
|
+
end
|
213
|
+
completion.flatten.flat_map { |item| item.split(/\s+/) }
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|