sct 0.1.14 → 0.1.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/sct +3 -4
- data/{.DS_Store → cluster/lib/.DS_Store} +0 -0
- data/cluster/lib/cluster.rb +6 -0
- data/cluster/lib/cluster/commands_generator.rb +95 -0
- data/cluster/lib/cluster/module.rb +7 -0
- data/cluster/lib/cluster/runner.rb +239 -0
- data/{lib → sct/lib}/.DS_Store +0 -0
- data/sct/lib/sct.rb +17 -0
- data/sct/lib/sct/.DS_Store +0 -0
- data/sct/lib/sct/cli_tools_distributor.rb +46 -0
- data/{lib → sct/lib}/sct/command.rb +0 -0
- data/sct/lib/sct/commands/hostfile.rb +68 -0
- data/sct/lib/sct/commands/init.rb +37 -0
- data/sct/lib/sct/commands/mysqlproxy.rb +20 -0
- data/sct/lib/sct/commands_generator.rb +56 -0
- data/sct/lib/sct/tools.rb +12 -0
- data/sct/lib/sct/version.rb +3 -0
- data/sct_core/lib/.DS_Store +0 -0
- data/sct_core/lib/sct_core.rb +13 -0
- data/{lib/sct → sct_core/lib/sct_core}/.DS_Store +0 -0
- data/sct_core/lib/sct_core/command_executor.rb +104 -0
- data/{lib/sct → sct_core/lib/sct_core}/config.rb +3 -3
- data/sct_core/lib/sct_core/core_ext/string.rb +9 -0
- data/{lib/sct/setup/helpers.rb → sct_core/lib/sct_core/helper.rb} +2 -2
- data/sct_core/lib/sct_core/module.rb +0 -0
- data/sct_core/lib/sct_core/sct_pty.rb +53 -0
- data/sct_core/lib/sct_core/ui/implementations/shell.rb +129 -0
- data/sct_core/lib/sct_core/ui/interface.rb +120 -0
- data/sct_core/lib/sct_core/ui/ui.rb +26 -0
- data/shell/README.md +0 -0
- data/shell/lib/shell.rb +3 -0
- data/{lib/sct → shell/lib/shell}/ClassLevelInheritableAttributes.rb +0 -0
- data/shell/lib/shell/commands_generator.rb +14 -0
- data/{lib/sct → shell/lib/shell}/docker/composer.rb +4 -3
- data/{lib/sct → shell/lib/shell}/docker/docker.rb +7 -10
- data/{lib/sct → shell/lib/shell}/docker/php.rb +3 -2
- data/{lib/sct → shell/lib/shell}/docker/yarn.rb +4 -3
- data/shell/lib/shell/module.rb +9 -0
- data/shell/lib/shell/runner.rb +34 -0
- data/shell/lib/shell/tools.rb +7 -0
- metadata +92 -54
- data/.gitignore +0 -12
- data/.rspec +0 -3
- data/.travis.yml +0 -7
- data/CODE_OF_CONDUCT.md +0 -74
- data/Gemfile +0 -4
- data/Gemfile.lock +0 -48
- data/LICENSE.txt +0 -21
- data/README.md +0 -134
- data/Rakefile +0 -6
- data/lib/sct.rb +0 -61
- data/lib/sct/command_interface.rb +0 -18
- data/lib/sct/command_option.rb +0 -14
- data/lib/sct/commands/cluster.rb +0 -121
- data/lib/sct/commands/composer.rb +0 -29
- data/lib/sct/commands/hostfile.rb +0 -125
- data/lib/sct/commands/init.rb +0 -51
- data/lib/sct/commands/mysqlproxy.rb +0 -38
- data/lib/sct/commands/php.rb +0 -37
- data/lib/sct/commands/yarn.rb +0 -26
- data/lib/sct/version.rb +0 -3
- data/sct.gemspec +0 -40
File without changes
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Sct
|
2
|
+
class HostfileCommand
|
3
|
+
|
4
|
+
def execute(args, options)
|
5
|
+
return UI.error("SCT has not been initialized. Run 'sct init' first.") unless SctCore::Config.exists
|
6
|
+
|
7
|
+
return UI.error("This command needs to be run with sudo.") unless SctCore::Helper.isSudo
|
8
|
+
|
9
|
+
return unless SctCore::Helper.ingressAddress
|
10
|
+
|
11
|
+
ingressAddress = SctCore::Helper.ingressAddress
|
12
|
+
|
13
|
+
entries = [
|
14
|
+
{
|
15
|
+
host: "spend.cloud.local",
|
16
|
+
comment: "The spend cloud ingress url"
|
17
|
+
},
|
18
|
+
{
|
19
|
+
host: "mail.spend.cloud.local",
|
20
|
+
comment: "The spend cloud mail url"
|
21
|
+
},
|
22
|
+
{
|
23
|
+
host: "config.spend.cloud.local",
|
24
|
+
comment: "The spend cloud config url"
|
25
|
+
},
|
26
|
+
{
|
27
|
+
host: "spend-cloud.spend.cloud.local",
|
28
|
+
comment: "The spend cloud web app url"
|
29
|
+
},
|
30
|
+
{
|
31
|
+
host: "docs.spend.cloud.local",
|
32
|
+
comment: "The spend cloud documentation url"
|
33
|
+
}
|
34
|
+
]
|
35
|
+
|
36
|
+
if options.path
|
37
|
+
hosts_paths = [options.path]
|
38
|
+
else
|
39
|
+
hosts_paths = ["/etc/hosts"]
|
40
|
+
|
41
|
+
if SctCore::Helper.operatingSystem == SctCore::Helper::WINDOWS
|
42
|
+
hosts_paths << "/mnt/c/Windows/System32/drivers/etc/hosts"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
hosts_paths.each do |hosts_path|
|
47
|
+
line_ending = hosts_path == "/mnt/c/Windows/System32/drivers/etc/hosts" ? "\r\n" : "\n"
|
48
|
+
|
49
|
+
lines = File.readlines hosts_path
|
50
|
+
|
51
|
+
# select the lines that do not include any entry
|
52
|
+
lines = lines.select { |line| ! entries.any? { |entry| line.include? entry[:host] } }
|
53
|
+
|
54
|
+
# add entries
|
55
|
+
entries.each do |entry|
|
56
|
+
lines << "#{ingressAddress} #{entry[:host]} # #{entry[:comment]}#{line_ending}"
|
57
|
+
end
|
58
|
+
|
59
|
+
File.write hosts_path, lines.join
|
60
|
+
|
61
|
+
UI.success("Patched #{hosts_path} with #{ingressAddress}")
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'sct_core/ui/ui'
|
2
|
+
require 'sct_core/config'
|
3
|
+
|
4
|
+
module Sct
|
5
|
+
|
6
|
+
class InitCommand
|
7
|
+
|
8
|
+
def execute(args, options)
|
9
|
+
|
10
|
+
dir = SctCore::Config.dir
|
11
|
+
|
12
|
+
cli = HighLine.new
|
13
|
+
|
14
|
+
email = cli.ask("What is your email address?") { |q|
|
15
|
+
q.validate = URI::MailTo::EMAIL_REGEXP
|
16
|
+
}
|
17
|
+
|
18
|
+
cloud_proxy_path = cli.ask("What is the path of your cloud proxy json credentials?") { |q|
|
19
|
+
q.default = "~/.config/gcloud/application_default_credentials.json"
|
20
|
+
}
|
21
|
+
|
22
|
+
contents = ""
|
23
|
+
contents << "email=#{email}\n"
|
24
|
+
contents << "cloud-proxy-path=#{File.expand_path(cloud_proxy_path)}\n"
|
25
|
+
|
26
|
+
if !File.directory?(dir)
|
27
|
+
FileUtils.mkdir_p(dir)
|
28
|
+
end
|
29
|
+
|
30
|
+
File.write(SctCore::Config.path, contents)
|
31
|
+
|
32
|
+
puts "Generated config file at #{SctCore::Config.path}"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sct
|
2
|
+
class MysqlproxyCommand
|
3
|
+
|
4
|
+
DEFAULT_SECRET_NAME = "gcloud-credentials"
|
5
|
+
|
6
|
+
def execute(args, options)
|
7
|
+
|
8
|
+
return UI.error("SCT has not been initialized. Run 'sct init' first.") unless SctCore::Config.exists
|
9
|
+
|
10
|
+
path = SctCore::Config.get('cloud-proxy-path')
|
11
|
+
|
12
|
+
system("kubectl delete secret gcloud-credentials")
|
13
|
+
system("kubectl create secret generic gcloud-credentials --from-file=#{path}")
|
14
|
+
|
15
|
+
UI.success("Authenticated with secret-name: '#{DEFAULT_SECRET_NAME}'")
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'commander'
|
2
|
+
require 'sct/command'
|
3
|
+
|
4
|
+
module Sct
|
5
|
+
class CommandsGenerator
|
6
|
+
include Commander::Methods
|
7
|
+
|
8
|
+
def self.start
|
9
|
+
self.new.run
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
program :name, 'sct'
|
14
|
+
program :version, Sct::VERSION
|
15
|
+
program :summary, 'CLI helper tool for local SCT development'
|
16
|
+
program :description, 'SCT is a CLI tool for developers using the Visma Continuous Deployment Model in conjunction with the Google Cloud Platform (GCP). It provides multiple command to set up and maintain a kubernetes cluster on a machine for local development'
|
17
|
+
|
18
|
+
global_option('--verbose') { $verbose = true }
|
19
|
+
|
20
|
+
command :init do |c|
|
21
|
+
c.syntax = 'sct init'
|
22
|
+
c.description = 'setup sct'
|
23
|
+
|
24
|
+
c.action do |args, options|
|
25
|
+
UI.important("setting up sct")
|
26
|
+
Sct::InitCommand.new.execute(args, options)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
command :hostfile do |c|
|
31
|
+
|
32
|
+
c.syntax = 'sct hostfile'
|
33
|
+
c.description = 'patch hostfile with kubernetes ip'
|
34
|
+
|
35
|
+
c.action do |args, options|
|
36
|
+
UI.important("Trying to patch hostfile")
|
37
|
+
Sct::HostfileCommand.new.execute(args, options)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
command :'mysql proxy' do |c|
|
42
|
+
|
43
|
+
c.syntax = 'sct mysql proxy'
|
44
|
+
c.description = 'setup google mysql proxy'
|
45
|
+
|
46
|
+
c.action do |args, options|
|
47
|
+
UI.important("Trying to setup mysql proxy")
|
48
|
+
Sct::MysqlproxyCommand.new.execute(args, options)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
run!
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
Binary file
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'sct_core/core_ext/string'
|
2
|
+
require_relative 'sct_core/config'
|
3
|
+
require_relative 'sct_core/helper'
|
4
|
+
require_relative 'sct_core/command_executor'
|
5
|
+
require_relative 'sct_core/ui/ui'
|
6
|
+
require_relative 'sct_core/sct_pty'
|
7
|
+
|
8
|
+
# third party code
|
9
|
+
require 'colored'
|
10
|
+
require 'commander'
|
11
|
+
|
12
|
+
# these need to be imported after commander
|
13
|
+
require_relative 'sct_core/module'
|
Binary file
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require_relative 'ui/ui'
|
2
|
+
require_relative 'sct_pty'
|
3
|
+
|
4
|
+
module SctCore
|
5
|
+
# Executes commands and takes care of error handling and more
|
6
|
+
class CommandExecutor
|
7
|
+
class << self
|
8
|
+
# Cross-platform way of finding an executable in the $PATH. Respects the $PATHEXT, which lists
|
9
|
+
# valid file extensions for executables on Windows.
|
10
|
+
#
|
11
|
+
# which('ruby') #=> /usr/bin/ruby
|
12
|
+
#
|
13
|
+
# Derived from https://stackoverflow.com/a/5471032/3005
|
14
|
+
def which(cmd)
|
15
|
+
# PATHEXT contains the list of file extensions that Windows considers executable, semicolon separated.
|
16
|
+
# e.g. ".COM;.EXE;.BAT;.CMD"
|
17
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : []
|
18
|
+
exts << '' # Always have an empty string (= no file extension)
|
19
|
+
|
20
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
21
|
+
exts.each do |ext|
|
22
|
+
cmd_path = File.join(path, "#{cmd}#{ext}")
|
23
|
+
return cmd_path if Helper.executable?(cmd_path)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
return nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param command [String] The command to be executed
|
31
|
+
# @param print_all [Boolean] Do we want to print out the command output while running?
|
32
|
+
# @param print_command [Boolean] Should we print the command that's being executed
|
33
|
+
# @param error [Block] A block that's called if an error occurs
|
34
|
+
# @param prefix [Array] An array containing a prefix + block which might get applied to the output
|
35
|
+
# @param loading [String] A loading string that is shown before the first output
|
36
|
+
# @param suppress_output [Boolean] Should we print the command's output?
|
37
|
+
# @return [String] All the output as string
|
38
|
+
def execute(command: nil, print_all: false, print_command: true, error: nil, prefix: nil, loading: nil, suppress_output: false)
|
39
|
+
print_all = true if $verbose
|
40
|
+
prefix ||= {}
|
41
|
+
|
42
|
+
output = []
|
43
|
+
command = command.join(" ") if command.kind_of?(Array)
|
44
|
+
command = self.escape_characters_in_string(command)
|
45
|
+
UI.command(command) if print_command
|
46
|
+
|
47
|
+
if print_all && loading # this is only used to show the "Loading text"...
|
48
|
+
UI.command_output(loading)
|
49
|
+
end
|
50
|
+
|
51
|
+
begin
|
52
|
+
status = SctCore::SctPty.spawn(command) do |command_stdout, command_stdin, pid|
|
53
|
+
command_stdout.each do |l|
|
54
|
+
line = l.chomp
|
55
|
+
output << line
|
56
|
+
|
57
|
+
next unless print_all
|
58
|
+
|
59
|
+
# Prefix the current line with a string
|
60
|
+
prefix.each do |element|
|
61
|
+
line = element[:prefix] + line if element[:block] && element[:block].call(line)
|
62
|
+
end
|
63
|
+
|
64
|
+
UI.command_output(line) unless suppress_output
|
65
|
+
end
|
66
|
+
end
|
67
|
+
rescue => ex
|
68
|
+
# SctPty adds exit_status on to StandardError so every error will have a status code
|
69
|
+
status = ex.exit_status
|
70
|
+
|
71
|
+
# This could happen when the environment is wrong:
|
72
|
+
# > invalid byte sequence in US-ASCII (ArgumentError)
|
73
|
+
output << ex.to_s
|
74
|
+
o = output.join("\n")
|
75
|
+
puts(o)
|
76
|
+
if error
|
77
|
+
error.call(o, nil)
|
78
|
+
else
|
79
|
+
raise ex
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Exit status for build command, should be 0 if build succeeded
|
84
|
+
if status != 0
|
85
|
+
o = output.join("\n")
|
86
|
+
puts(o) unless suppress_output # the user has the right to see the raw output
|
87
|
+
UI.error("Exit status: #{status}")
|
88
|
+
if error
|
89
|
+
error.call(o, status)
|
90
|
+
else
|
91
|
+
UI.user_error!("Exit status: #{status}")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
return output.join("\n")
|
96
|
+
end
|
97
|
+
|
98
|
+
def escape_characters_in_string(string)
|
99
|
+
pattern = /(\'|\"|\.|\*|\/|\-|\\|\)|\$|\+|\(|\^|\?|\!|\~|\`)/
|
100
|
+
string.gsub(pattern){|match|"\\" + match}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
module
|
1
|
+
module SctCore
|
2
2
|
class Config
|
3
3
|
|
4
4
|
def self.dir
|
5
|
-
return "#{
|
5
|
+
return "#{SctCore::Helper.homePath}/.config/sct"
|
6
6
|
end
|
7
7
|
|
8
8
|
def self.path
|
@@ -30,7 +30,7 @@ module Sct
|
|
30
30
|
|
31
31
|
contents = File.read(self.path)
|
32
32
|
|
33
|
-
return
|
33
|
+
return SctCore::Helper.to_hash(contents)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
File without changes
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class StandardError
|
2
|
+
def exit_status
|
3
|
+
return -1
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
module SctCore
|
8
|
+
class SctPtyError < StandardError
|
9
|
+
attr_reader :exit_status
|
10
|
+
def initialize(e, exit_status)
|
11
|
+
super(e)
|
12
|
+
set_backtrace(e.backtrace) if e
|
13
|
+
@exit_status = exit_status
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class SctPty
|
18
|
+
def self.spawn(command)
|
19
|
+
require 'pty'
|
20
|
+
PTY.spawn(command) do |command_stdout, command_stdin, pid|
|
21
|
+
begin
|
22
|
+
yield(command_stdout, command_stdin, pid)
|
23
|
+
rescue Errno::EIO
|
24
|
+
# Exception ignored intentionally.
|
25
|
+
# https://stackoverflow.com/questions/10238298/ruby-on-linux-pty-goes-away-without-eof-raises-errnoeio
|
26
|
+
# This is expected on some linux systems, that indicates that the subcommand finished
|
27
|
+
# and we kept trying to read, ignore it
|
28
|
+
ensure
|
29
|
+
begin
|
30
|
+
Process.wait(pid)
|
31
|
+
rescue Errno::ECHILD, PTY::ChildExited
|
32
|
+
# The process might have exited.
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
$?.exitstatus
|
37
|
+
rescue LoadError
|
38
|
+
require 'open3'
|
39
|
+
Open3.popen2e(command) do |command_stdin, command_stdout, p| # note the inversion
|
40
|
+
yield(command_stdout, command_stdin, p.value.pid)
|
41
|
+
|
42
|
+
command_stdin.close
|
43
|
+
command_stdout.close
|
44
|
+
p.value.exitstatus
|
45
|
+
end
|
46
|
+
rescue StandardError => e
|
47
|
+
# Wrapping any error in SctPtyError to allow
|
48
|
+
# callers to see and use $?.exitstatus that
|
49
|
+
# would usually get returned
|
50
|
+
raise SctPtyError.new(e, $?.exitstatus)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require_relative '../interface'
|
2
|
+
|
3
|
+
module SctCore
|
4
|
+
# Shell is the terminal output of things
|
5
|
+
# For documentation for each of the methods open `interface.rb`
|
6
|
+
class Shell < Interface
|
7
|
+
require 'tty-screen'
|
8
|
+
|
9
|
+
def log
|
10
|
+
$stdout.sync = true
|
11
|
+
end
|
12
|
+
|
13
|
+
def writeToUser(message)
|
14
|
+
puts message
|
15
|
+
end
|
16
|
+
|
17
|
+
def error(message)
|
18
|
+
writeToUser(message.to_s.red)
|
19
|
+
end
|
20
|
+
|
21
|
+
def important(message)
|
22
|
+
writeToUser(message.to_s.yellow)
|
23
|
+
end
|
24
|
+
|
25
|
+
def success(message)
|
26
|
+
writeToUser(message.to_s.green)
|
27
|
+
end
|
28
|
+
|
29
|
+
def message(message)
|
30
|
+
writeToUser(message.to_s)
|
31
|
+
end
|
32
|
+
|
33
|
+
def deprecated(message)
|
34
|
+
writeToUser(message.to_s.deprecated)
|
35
|
+
end
|
36
|
+
|
37
|
+
def command(message)
|
38
|
+
writeToUser("$ #{message}".cyan)
|
39
|
+
end
|
40
|
+
|
41
|
+
def command_output(message)
|
42
|
+
actual = (message.split("\r").last || "") # as clearing the line will remove the `>` and the time stamp
|
43
|
+
actual.split("\n").each do |msg|
|
44
|
+
# prefix = msg.include?("▸") ? "" : "▸ "
|
45
|
+
prefix = ""
|
46
|
+
writeToUser(prefix + "" + msg.magenta)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def verbose(message)
|
51
|
+
message.to_s if $verbose
|
52
|
+
end
|
53
|
+
|
54
|
+
def header(message)
|
55
|
+
format = format_string
|
56
|
+
if message.length + 8 < TTY::Screen.width - format.length
|
57
|
+
message = "--- #{message} ---"
|
58
|
+
i = message.length
|
59
|
+
else
|
60
|
+
i = TTY::Screen.width - format.length
|
61
|
+
end
|
62
|
+
success("-" * i)
|
63
|
+
success(message)
|
64
|
+
success("-" * i)
|
65
|
+
end
|
66
|
+
|
67
|
+
def content_error(content, error_line)
|
68
|
+
error_line = error_line.to_i
|
69
|
+
return unless error_line > 0
|
70
|
+
|
71
|
+
contents = content.split(/\r?\n/).map(&:chomp)
|
72
|
+
|
73
|
+
start_line = error_line - 2 < 1 ? 1 : error_line - 2
|
74
|
+
end_line = error_line + 2 < contents.length ? error_line + 2 : contents.length
|
75
|
+
|
76
|
+
Range.new(start_line, end_line).each do |line|
|
77
|
+
str = line == error_line ? " => " : " "
|
78
|
+
str << line.to_s.rjust(Math.log10(end_line) + 1)
|
79
|
+
str << ":\t#{contents[line - 1]}"
|
80
|
+
error(str)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
#####################################################
|
85
|
+
# @!group Errors: Inputs
|
86
|
+
#####################################################
|
87
|
+
|
88
|
+
def interactive?
|
89
|
+
interactive = true
|
90
|
+
interactive = false if $stdout.isatty == false
|
91
|
+
return interactive
|
92
|
+
end
|
93
|
+
|
94
|
+
def input(message)
|
95
|
+
verify_interactive!(message)
|
96
|
+
ask("#{format_string}#{message.to_s.yellow}").to_s.strip
|
97
|
+
end
|
98
|
+
|
99
|
+
def confirm(message)
|
100
|
+
verify_interactive!(message)
|
101
|
+
agree("#{format_string}#{message.to_s.yellow} (y/n)", true)
|
102
|
+
end
|
103
|
+
|
104
|
+
def select(message, options)
|
105
|
+
verify_interactive!(message)
|
106
|
+
|
107
|
+
important(message)
|
108
|
+
choose(*options)
|
109
|
+
end
|
110
|
+
|
111
|
+
def password(message)
|
112
|
+
verify_interactive!(message)
|
113
|
+
|
114
|
+
ask("#{format_string}#{message.to_s.yellow}") { |q| q.echo = "*" }
|
115
|
+
end
|
116
|
+
|
117
|
+
def user_error!(error_message, options = {})
|
118
|
+
writeToUser(error_message.yellow)
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def verify_interactive!(message)
|
124
|
+
return if interactive?
|
125
|
+
important(message)
|
126
|
+
crash!("Could not retrieve response as sct runs in non-interactive mode")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|