rockette 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +11 -1
- data/Gemfile.lock +39 -4
- data/exe/rockette +0 -17
- data/lib/requirements.rb +19 -0
- data/lib/rockette.rb +28 -84
- data/lib/rockette/cli.rb +22 -5
- data/lib/rockette/commands/deploy.rb +3 -2
- data/lib/rockette/commands/export.rb +3 -2
- data/lib/rockette/commands/interactive.rb +24 -0
- data/lib/rockette/controller.rb +82 -0
- data/lib/rockette/controller/configurator.rb +85 -0
- data/lib/rockette/controller/deployer.rb +106 -0
- data/lib/rockette/controller/exporter.rb +73 -0
- data/lib/rockette/controller/viewer.rb +82 -0
- data/lib/rockette/rester.rb +79 -0
- data/lib/rockette/text_helper.rb +6 -0
- data/lib/rockette/version.rb +1 -1
- data/rockette.gemspec +8 -3
- data/templates/config.yml.erb +3 -0
- metadata +108 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1685044bb3c16dc23e0d91b6a056c58b26828385fb015f3bb9798d33b9fb5c3f
|
4
|
+
data.tar.gz: 260ad666d3d0bebac6a4d50739184eaccc5d40b0141e67051bec26bcd7c4ef79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ccd3c264a613d1f283470b98940c31e052f27700bffcb5eaf735537343ad6232301b0187e89a6e465d6eb2e91dcf8aada0a4e6594aca6bd399da330e6e050159
|
7
|
+
data.tar.gz: 7cf907bcbe33ecf78cee5e2e641c0f72096142a18b4894fff42ea00fdd38f516d9ea33bf160ba2c152d2bca7209bc000af4b530d49ec76260a2ef9ee049afc74
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -16,4 +16,14 @@
|
|
16
16
|
|
17
17
|
- Add copy switch to deploy command
|
18
18
|
- Use file name passed via -f switch
|
19
|
-
- Try to find export file and let caller know if file not found
|
19
|
+
- Try to find export file and let caller know if file not found
|
20
|
+
|
21
|
+
## [0.0.3] - In progress
|
22
|
+
|
23
|
+
- Improved handling of copy and file switches
|
24
|
+
- Check /usr/app for Docker
|
25
|
+
- Add interactive mode
|
26
|
+
- Add configuration option to interactive mode
|
27
|
+
- Add resource viewer mode to interactive
|
28
|
+
- Add exporter option to interactive
|
29
|
+
- Add deployer mode to interactive
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rockette (0.0.
|
4
|
+
rockette (0.0.2)
|
5
|
+
pastel (~> 0.0)
|
5
6
|
rest-client (~> 2.0)
|
6
7
|
thor (~> 1.0)
|
8
|
+
tty-config (~> 0.0)
|
9
|
+
tty-editor (~> 0.0)
|
10
|
+
tty-font (~> 0.0)
|
11
|
+
tty-prompt (~> 0.0)
|
12
|
+
tty-spinner (~> 0.0)
|
13
|
+
tty-table (~> 0.0)
|
7
14
|
|
8
15
|
GEM
|
9
16
|
remote: https://rubygems.org/
|
@@ -18,12 +25,14 @@ GEM
|
|
18
25
|
method_source (1.0.0)
|
19
26
|
mime-types (3.3.1)
|
20
27
|
mime-types-data (~> 3.2015)
|
21
|
-
mime-types-data (3.2021.
|
28
|
+
mime-types-data (3.2021.0225)
|
22
29
|
minitest (5.14.4)
|
23
30
|
netrc (0.11.0)
|
24
31
|
parallel (1.20.1)
|
25
32
|
parser (3.0.0.0)
|
26
33
|
ast (~> 2.4.1)
|
34
|
+
pastel (0.8.0)
|
35
|
+
tty-color (~> 0.5)
|
27
36
|
pry (0.14.0)
|
28
37
|
coderay (~> 1.1)
|
29
38
|
method_source (~> 1.0)
|
@@ -36,7 +45,7 @@ GEM
|
|
36
45
|
mime-types (>= 1.16, < 4.0)
|
37
46
|
netrc (~> 0.8)
|
38
47
|
rexml (3.2.4)
|
39
|
-
rubocop (1.
|
48
|
+
rubocop (1.11.0)
|
40
49
|
parallel (~> 1.10)
|
41
50
|
parser (>= 3.0.0.0)
|
42
51
|
rainbow (>= 2.2.2, < 4.0)
|
@@ -48,14 +57,40 @@ GEM
|
|
48
57
|
rubocop-ast (1.4.1)
|
49
58
|
parser (>= 2.7.1.5)
|
50
59
|
ruby-progressbar (1.11.0)
|
60
|
+
strings (0.2.1)
|
61
|
+
strings-ansi (~> 0.2)
|
62
|
+
unicode-display_width (>= 1.5, < 3.0)
|
63
|
+
unicode_utils (~> 1.4)
|
64
|
+
strings-ansi (0.2.0)
|
51
65
|
thor (1.1.0)
|
66
|
+
tty-color (0.6.0)
|
67
|
+
tty-config (0.4.0)
|
68
|
+
tty-cursor (0.7.1)
|
69
|
+
tty-editor (0.6.0)
|
70
|
+
tty-prompt (~> 0.22)
|
71
|
+
tty-font (0.5.0)
|
72
|
+
tty-prompt (0.23.0)
|
73
|
+
pastel (~> 0.8)
|
74
|
+
tty-reader (~> 0.8)
|
75
|
+
tty-reader (0.9.0)
|
76
|
+
tty-cursor (~> 0.7)
|
77
|
+
tty-screen (~> 0.8)
|
78
|
+
wisper (~> 2.0)
|
79
|
+
tty-screen (0.8.1)
|
80
|
+
tty-spinner (0.9.3)
|
81
|
+
tty-cursor (~> 0.7)
|
82
|
+
tty-table (0.12.0)
|
83
|
+
pastel (~> 0.8)
|
84
|
+
strings (~> 0.2.0)
|
85
|
+
tty-screen (~> 0.8)
|
52
86
|
unf (0.1.4)
|
53
87
|
unf_ext
|
54
88
|
unf_ext (0.0.7.7)
|
55
89
|
unicode-display_width (2.0.0)
|
90
|
+
unicode_utils (1.4.0)
|
91
|
+
wisper (2.0.1)
|
56
92
|
|
57
93
|
PLATFORMS
|
58
|
-
ruby
|
59
94
|
x86_64-darwin-19
|
60
95
|
|
61
96
|
DEPENDENCIES
|
data/exe/rockette
CHANGED
@@ -3,23 +3,6 @@
|
|
3
3
|
|
4
4
|
require "rockette"
|
5
5
|
|
6
|
-
app_path = File.join(Dir.home, ".rockette")
|
7
|
-
lib_path = File.expand_path("../lib", __dir__)
|
8
|
-
tem_path = File.expand_path("../templates", __dir__)
|
9
|
-
$LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
|
10
|
-
|
11
|
-
Dir.mkdir(app_path) unless File.exist?(app_path)
|
12
|
-
unless File.exist?(File.join(app_path, "config.yml"))
|
13
|
-
template = File.read(File.join(tem_path, "config.yml.erb"))
|
14
|
-
data = ERB.new(template).result(binding)
|
15
|
-
File.write(File.join(app_path, "config.yml"), data)
|
16
|
-
end
|
17
|
-
Dir.mkdir(File.join(app_path, "exports")) unless File.exist?(File.join(app_path, "exports"))
|
18
|
-
|
19
|
-
CONF = Psych.load(File.read(File.join(app_path, "config.yml")))
|
20
|
-
ENV["THOR_SILENCE_DEPRECATION"] = "true"
|
21
|
-
EXPORT_DIR = File.join(app_path, "exports")
|
22
|
-
|
23
6
|
Signal.trap("INT") do
|
24
7
|
warn("\n#{caller.join("\n")}: interrupted")
|
25
8
|
exit(1)
|
data/lib/requirements.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
require "json"
|
5
|
+
require "pastel"
|
6
|
+
require "psych"
|
7
|
+
require "rest-client"
|
8
|
+
require "thor"
|
9
|
+
require "tty-config"
|
10
|
+
require "tty-editor"
|
11
|
+
require "tty-font"
|
12
|
+
require "tty-prompt"
|
13
|
+
require "tty-spinner"
|
14
|
+
require "tty-table"
|
15
|
+
require_relative "rockette/cli"
|
16
|
+
require_relative "rockette/controller"
|
17
|
+
require_relative "rockette/rester"
|
18
|
+
require_relative "rockette/text_helper"
|
19
|
+
require_relative "rockette/version"
|
data/lib/rockette.rb
CHANGED
@@ -1,91 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
require_relative "requirements"
|
4
|
+
|
5
|
+
# Set paths
|
6
|
+
APP_PATH = if File.exist?(File.join("usr", "app")) # Why Docker, why?
|
7
|
+
File.join("usr", "app", ".rockette")
|
8
|
+
elsif File.exist?(Dir.home)
|
9
|
+
File.join(Dir.home, ".rockette")
|
10
|
+
else
|
11
|
+
no_rockette_dir
|
12
|
+
end
|
13
|
+
lib_path = File.expand_path("../lib", __dir__)
|
14
|
+
tem_path = File.expand_path("../templates", __dir__)
|
15
|
+
$LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
|
16
|
+
|
17
|
+
# Create directories and config file if needed
|
18
|
+
Dir.mkdir(APP_PATH) unless File.exist?(APP_PATH)
|
19
|
+
unless File.exist?(File.join(APP_PATH, "config.yml"))
|
20
|
+
template = File.read(File.join(tem_path, "config.yml.erb"))
|
21
|
+
data = ERB.new(template).result(binding)
|
22
|
+
File.write(File.join(APP_PATH, "config.yml"), data)
|
23
|
+
end
|
24
|
+
Dir.mkdir(File.join(APP_PATH, "exports")) unless File.exist?(File.join(APP_PATH, "exports"))
|
25
|
+
|
26
|
+
# Set config and export directory paths
|
27
|
+
CONF = File.join(APP_PATH, "config.yml")
|
28
|
+
ENV["THOR_SILENCE_DEPRECATION"] = "true"
|
29
|
+
EXPORT_DIR = File.join(APP_PATH, "exports")
|
11
30
|
|
12
31
|
# APEX deployment
|
13
32
|
module Rockette
|
14
33
|
class Error < StandardError; end
|
15
|
-
|
16
|
-
# rest-client calls with error handling and auto retries
|
17
|
-
class Rester
|
18
|
-
VERBS = {
|
19
|
-
"delete" => :Delete,
|
20
|
-
"get" => :Get,
|
21
|
-
"post" => :Post,
|
22
|
-
"put" => :Put
|
23
|
-
}.freeze
|
24
|
-
|
25
|
-
def initialize(headers: {}, meth: "Get", params: {}, url: "https://array/", config: {})
|
26
|
-
@headers = headers
|
27
|
-
@meth = meth
|
28
|
-
@params = params
|
29
|
-
@url = url
|
30
|
-
@config = config
|
31
|
-
@config["timeout"] = @config["timeout"] ||= 30
|
32
|
-
end
|
33
|
-
|
34
|
-
def make_call
|
35
|
-
response = RestClient::Request.execute(headers: @headers,
|
36
|
-
method: VERBS[@meth.downcase], payload: @params,
|
37
|
-
timeout: @config["timeout"], url: @url, verify_ssl: false)
|
38
|
-
rescue SocketError, IOError => e
|
39
|
-
puts "#{e.class}: #{e.message}"
|
40
|
-
nil
|
41
|
-
rescue StandardError => e
|
42
|
-
e.response
|
43
|
-
else
|
44
|
-
response
|
45
|
-
end
|
46
|
-
|
47
|
-
def cookie
|
48
|
-
if @url =~ %r{auth/session}
|
49
|
-
response = make_call
|
50
|
-
raise "There was an issue getting a cookie!" unless response.code == 200
|
51
|
-
|
52
|
-
(response.cookies.map { |key, val| "#{key}=#{val}" })[0]
|
53
|
-
else
|
54
|
-
error_text("cookie", @url.to_s, "auth/session")
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# use rest-client with retry
|
59
|
-
def rest_try
|
60
|
-
3.times do |i|
|
61
|
-
response = make_call
|
62
|
-
unless response.nil?
|
63
|
-
break response if (200..299).include? response.code
|
64
|
-
break response if i > 1
|
65
|
-
end
|
66
|
-
puts "Failed #{@meth} on #{@url}, retry...#{i + 1}"
|
67
|
-
sleep 3 unless i > 1
|
68
|
-
return nil if i > 1 # Handles socket errors, etc. where there is no response.
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def error_text(method_name, url, wanted)
|
75
|
-
{
|
76
|
-
"response" =>
|
77
|
-
"ERROR: Wrong url for the #{method_name} method.\n"\
|
78
|
-
"Sent: #{url}\n"\
|
79
|
-
"Expected: \"#{wanted}\" as part of the url.",
|
80
|
-
"status" => 400
|
81
|
-
}
|
82
|
-
end
|
83
|
-
|
84
|
-
def responder(response)
|
85
|
-
{
|
86
|
-
"response" => JSON.parse(response.body),
|
87
|
-
"status" => response.code.to_i
|
88
|
-
}
|
89
|
-
end
|
90
|
-
end
|
34
|
+
# Code in lib/rockette
|
91
35
|
end
|
data/lib/rockette/cli.rb
CHANGED
@@ -18,10 +18,10 @@ module Rockette
|
|
18
18
|
desc "deploy", "Deploy chosen export file to target APEX instance"
|
19
19
|
method_option :help, aliases: "-h", type: :boolean,
|
20
20
|
desc: "Display usage information"
|
21
|
-
option :app_id, aliases: "-a",
|
22
|
-
desc: "
|
21
|
+
option :app_id, aliases: "-a", default: "0",
|
22
|
+
desc: "Update this App ID with export set by '-f' Leave off or pass '-c' to copy export to target"
|
23
23
|
option :file, aliases: "-f", required: true,
|
24
|
-
desc: "Provide an APEX application export file (
|
24
|
+
desc: "Provide an APEX application export file (sql)"
|
25
25
|
option :url, aliases: "-u", required: true,
|
26
26
|
desc: "Provide a valid APEX deployment url"
|
27
27
|
option :copy, aliases: "-c", required: false,
|
@@ -42,6 +42,9 @@ module Rockette
|
|
42
42
|
desc: "Provide an APEX application ID"
|
43
43
|
option :url, aliases: "-u", required: true,
|
44
44
|
desc: "Provide a valid url"
|
45
|
+
option :file, aliases: "-f", required: false,
|
46
|
+
desc: "Save export with this file name"
|
47
|
+
|
45
48
|
def export(*)
|
46
49
|
if options[:help]
|
47
50
|
invoke :help, ["export"]
|
@@ -51,9 +54,9 @@ module Rockette
|
|
51
54
|
end
|
52
55
|
end
|
53
56
|
|
54
|
-
desc "config", "
|
57
|
+
desc "config", "Set configuration options..."
|
55
58
|
method_option :help, aliases: "-h", type: :boolean,
|
56
|
-
desc: "
|
59
|
+
desc: "Not implemented yet..."
|
57
60
|
def config(*)
|
58
61
|
if options[:help]
|
59
62
|
invoke :help, ["config"]
|
@@ -62,5 +65,19 @@ module Rockette
|
|
62
65
|
Rockette::Commands::Config.new(options).execute
|
63
66
|
end
|
64
67
|
end
|
68
|
+
|
69
|
+
desc "interactive", "Start Rockette in interactive mode"
|
70
|
+
method_option :help, aliases: "-h", type: :boolean,
|
71
|
+
desc: "'rockette' by itself will start interactive mode"
|
72
|
+
def interactive(*)
|
73
|
+
if options[:help]
|
74
|
+
invoke :help, ["interactive"]
|
75
|
+
else
|
76
|
+
require_relative "commands/interactive"
|
77
|
+
Rockette::Commands::Interactive.new(options).execute
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
default_task :interactive
|
65
82
|
end
|
66
83
|
end
|
@@ -12,7 +12,8 @@ module Rockette
|
|
12
12
|
super()
|
13
13
|
@options = options
|
14
14
|
@filey = @options[:file]
|
15
|
-
@
|
15
|
+
@conf = Psych.load(File.read(CONF))
|
16
|
+
@body = @conf["deploy_body"]
|
16
17
|
@body["app_id_src"] = "1" # @options[:app_id]
|
17
18
|
@body["app_id_tgt"] = @options[:copy] ? 0 : @options[:app_id]
|
18
19
|
@body["blob_url"] = @filey
|
@@ -34,7 +35,7 @@ module Rockette
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def pusher
|
37
|
-
push_hdrs =
|
38
|
+
push_hdrs = @conf["push_hdrs"]
|
38
39
|
push_hdrs["file_name"] = @filey
|
39
40
|
push_url = "#{@options[:url]}data_loader/blob"
|
40
41
|
# Push the chosen export file to the target system
|
@@ -48,8 +48,9 @@ module Rockette
|
|
48
48
|
sleep 1
|
49
49
|
response = grabber
|
50
50
|
# Write file if export was grabbed.
|
51
|
-
|
52
|
-
|
51
|
+
save_file = @options[:file] || @filey
|
52
|
+
File.open(File.join(EXPORT_DIR, save_file), "wb") { |file| file.write(response.body) }
|
53
|
+
output.puts padder("Finished downloading #{save_file}. Have a good one!")
|
53
54
|
end
|
54
55
|
end
|
55
56
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../command"
|
4
|
+
|
5
|
+
module Rockette
|
6
|
+
module Commands
|
7
|
+
# Interactive Rockette
|
8
|
+
class Interactive < Rockette::Command
|
9
|
+
include TextHelper
|
10
|
+
|
11
|
+
def initialize(options)
|
12
|
+
super()
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute(input: $stdin, output: $stdout)
|
17
|
+
output.puts
|
18
|
+
check_input(input)
|
19
|
+
controller = Rockette::Controller.new
|
20
|
+
controller.launch!
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "controller/configurator"
|
4
|
+
require_relative "controller/deployer"
|
5
|
+
require_relative "controller/exporter"
|
6
|
+
require_relative "controller/viewer"
|
7
|
+
require_relative "text_helper"
|
8
|
+
|
9
|
+
module Rockette
|
10
|
+
MAIN_ACTIONS = { "🔭 View Resources" => 1, "🚀 Deploy" => 2, "📥 Export" => 3,
|
11
|
+
"🛠 Configure" => 4, "❌ Quit" => 5 }.freeze
|
12
|
+
# Manage Rockette in interactive mode
|
13
|
+
class Controller
|
14
|
+
def initialize
|
15
|
+
@conf = Psych.load(File.read(CONF))
|
16
|
+
@pastel = Pastel.new
|
17
|
+
@prompt = TTY::Prompt.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def launch!
|
21
|
+
introduction
|
22
|
+
|
23
|
+
if @conf["rockette"]["check_for_url"] && @conf["rockette"]["controller_url"].length < 10
|
24
|
+
configurer = Rockette::Configurator.new
|
25
|
+
configurer.first_run
|
26
|
+
end
|
27
|
+
|
28
|
+
# input/action loop
|
29
|
+
loop do
|
30
|
+
action = actions
|
31
|
+
break if action == 5
|
32
|
+
|
33
|
+
do_action(action)
|
34
|
+
end
|
35
|
+
conclusion
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def introduction
|
41
|
+
font = TTY::Font.new(:starwars)
|
42
|
+
puts "-" * 85
|
43
|
+
puts
|
44
|
+
puts @pastel.yellow(font.write("Rockette"))
|
45
|
+
puts
|
46
|
+
puts "-" * 85
|
47
|
+
puts "Rockette helps export and deploy APEX applications."
|
48
|
+
puts
|
49
|
+
end
|
50
|
+
|
51
|
+
def conclusion
|
52
|
+
puts
|
53
|
+
puts "-" * 85
|
54
|
+
puts @pastel.yellow("Have a good one!".upcase.center(85))
|
55
|
+
puts "-" * 85
|
56
|
+
puts
|
57
|
+
end
|
58
|
+
|
59
|
+
def actions
|
60
|
+
@prompt.select("What would you like to do?", MAIN_ACTIONS)
|
61
|
+
end
|
62
|
+
|
63
|
+
def do_action(action)
|
64
|
+
case action
|
65
|
+
when 1
|
66
|
+
viewer = Rockette::Viewer.new
|
67
|
+
viewer.launch!
|
68
|
+
when 2
|
69
|
+
deployer = Rockette::Deployer.new
|
70
|
+
deployer.launch!
|
71
|
+
when 3
|
72
|
+
exporter = Rockette::Exporter.new
|
73
|
+
exporter.launch!
|
74
|
+
when 4
|
75
|
+
configurer = Rockette::Configurator.new
|
76
|
+
configurer.launch!
|
77
|
+
else
|
78
|
+
puts "\nI don't understand that command.\n\n"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../text_helper"
|
4
|
+
|
5
|
+
module Rockette
|
6
|
+
# Configure Rockette application
|
7
|
+
class Configurator
|
8
|
+
include TextHelper
|
9
|
+
|
10
|
+
attr_reader :config
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@config = TTY::Config.new
|
14
|
+
@config.append_path APP_PATH
|
15
|
+
@config.read
|
16
|
+
|
17
|
+
@pastel = Pastel.new
|
18
|
+
@prompt = TTY::Prompt.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.config
|
22
|
+
@config ||= self.class.new.config
|
23
|
+
end
|
24
|
+
|
25
|
+
def launch!
|
26
|
+
puts
|
27
|
+
puts @pastel.yellow("Configure Rockette here. Choosing 'editor' will let you edit the config file directly.")
|
28
|
+
puts @pastel.yellow("Choose 'url' to enter a controller url, the api endpoint with your environments, etc.")
|
29
|
+
puts
|
30
|
+
# input/action loop
|
31
|
+
loop do
|
32
|
+
action = @prompt.select("What will it be?", %w[editor url back])
|
33
|
+
break if action == "back"
|
34
|
+
|
35
|
+
do_action(action)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def do_action(action)
|
40
|
+
case action
|
41
|
+
when "editor"
|
42
|
+
edit_config
|
43
|
+
when "url"
|
44
|
+
add_url
|
45
|
+
else
|
46
|
+
puts "\nI don't understand that command.\n\n"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_url
|
51
|
+
uri = @prompt.ask("Please enter APEX deployment URI (base path):") do |u|
|
52
|
+
u.validate(%r{^https://\w+.\w+})
|
53
|
+
end
|
54
|
+
@config.set(:rockette, :controller_url, value: uri)
|
55
|
+
@config.write(force: true)
|
56
|
+
refresh_conf
|
57
|
+
end
|
58
|
+
|
59
|
+
def edit_config
|
60
|
+
edit = TTY::Editor.open(CONF)
|
61
|
+
puts "There seems to have been an issue trying to open the file: #{CONF}" unless edit
|
62
|
+
refresh_conf if edit
|
63
|
+
end
|
64
|
+
|
65
|
+
def first_run
|
66
|
+
puts
|
67
|
+
puts "I see this is your first time running Rockette. Entering an APEX deployment"
|
68
|
+
puts "uri will allow me to discover more about your environments."
|
69
|
+
puts
|
70
|
+
response = @prompt.yes?("Would you like to enter a URI?")
|
71
|
+
if response == true
|
72
|
+
add_url
|
73
|
+
else
|
74
|
+
response = @prompt.yes?("Would you like to disable these checks in the future?")
|
75
|
+
@config.set(:rockette, :check_for_url, value: false) if response == true
|
76
|
+
end
|
77
|
+
@config.write(force: true) if response == true
|
78
|
+
refresh_conf
|
79
|
+
end
|
80
|
+
|
81
|
+
def refresh_conf
|
82
|
+
@config.read
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../text_helper"
|
4
|
+
require_relative "../commands/deploy"
|
5
|
+
|
6
|
+
module Rockette
|
7
|
+
# Push APEX application to target instance
|
8
|
+
class Deployer
|
9
|
+
include TextHelper
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@pastel = Pastel.new
|
13
|
+
@prompt = TTY::Prompt.new
|
14
|
+
@spinner = TTY::Spinner.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def launch!
|
18
|
+
@conf = Psych.load(File.read(CONF))
|
19
|
+
intro_text
|
20
|
+
|
21
|
+
# input/action loop
|
22
|
+
loop do
|
23
|
+
actions = { "🛸 Add" => 1, "📝 Update" => 2, "⬅️ Go Back" => 3 }
|
24
|
+
response = @prompt.select("Add application or update an existing application?", actions)
|
25
|
+
break if response == 3
|
26
|
+
|
27
|
+
add_app if response == 1
|
28
|
+
updater if response == 2
|
29
|
+
end
|
30
|
+
puts
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def intro_text
|
36
|
+
puts
|
37
|
+
puts @pastel.yellow("These are the two available options:")
|
38
|
+
print "1. Add application to an APEX instance. "
|
39
|
+
print "It's #{@pastel.green.bold("generally safe and creates a new application")} from the export\n"
|
40
|
+
print "2. Update an existing application with your chosen export. "
|
41
|
+
print "#{@pastel.red.bold("Tread carefully")} with this option!\n"
|
42
|
+
puts
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_app
|
46
|
+
puts padder("Let's choose an export to add")
|
47
|
+
file = choose_file
|
48
|
+
url = choose_env
|
49
|
+
puts padder("You chose to add #{file} to the environment at #{url}")
|
50
|
+
puts
|
51
|
+
return unless @prompt.yes?("Proceed with the deployment?")
|
52
|
+
|
53
|
+
options = Thor::CoreExt::HashWithIndifferentAccess.new "app_id" => "0", "url" => url, "file" => file,
|
54
|
+
"force" => true
|
55
|
+
Rockette::Commands::Deploy.new(options).execute
|
56
|
+
puts
|
57
|
+
end
|
58
|
+
|
59
|
+
def choose_app(apps_url)
|
60
|
+
apps = Rockette::Viewer.new.applications(apps_url)
|
61
|
+
list = list_builder(apps)
|
62
|
+
action = @prompt.slider("Application to update => ", list, default: 1)
|
63
|
+
|
64
|
+
apps[action - 1]
|
65
|
+
end
|
66
|
+
|
67
|
+
def choose_env
|
68
|
+
enviros = Rockette::Viewer.new.environments
|
69
|
+
list = list_builder(enviros)
|
70
|
+
action = @prompt.select("Which environment?", list)
|
71
|
+
enviros[action - 1]["deployment_api"]
|
72
|
+
end
|
73
|
+
|
74
|
+
def choose_file
|
75
|
+
list = Dir.children(EXPORT_DIR)
|
76
|
+
@prompt.select("Which export from #{EXPORT_DIR}?", list)
|
77
|
+
end
|
78
|
+
|
79
|
+
def list_builder(array)
|
80
|
+
names = [] # Start building selection list
|
81
|
+
if array[0].key?("name")
|
82
|
+
array.each { |n| names << n["name"] }
|
83
|
+
else
|
84
|
+
array.each { |n| names << "#{n["application_name"]} App ID: #{n["application_id"]}" }
|
85
|
+
end
|
86
|
+
names << "Go Back"
|
87
|
+
names.map.with_index { |n, x| [n, x + 1] }.to_h
|
88
|
+
end
|
89
|
+
|
90
|
+
def updater
|
91
|
+
puts padder("Please choose the export with your updated application code")
|
92
|
+
file = choose_file
|
93
|
+
url = choose_env
|
94
|
+
app = choose_app(url)
|
95
|
+
puts "Application: #{app["application_name"]} | App ID: #{app["application_id"]} | Env URI: #{url}"
|
96
|
+
puts "will be updated with the code from export: #{file}"
|
97
|
+
puts
|
98
|
+
return unless @prompt.yes?("Proceed with the deployment?")
|
99
|
+
|
100
|
+
options = Thor::CoreExt::HashWithIndifferentAccess.new "app_id" => app["application_id"], "url" => url,
|
101
|
+
"file" => file, "force" => true
|
102
|
+
Rockette::Commands::Deploy.new(options).execute
|
103
|
+
puts
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../text_helper"
|
4
|
+
require_relative "../commands/export"
|
5
|
+
|
6
|
+
module Rockette
|
7
|
+
# Export and download APEX application
|
8
|
+
class Exporter
|
9
|
+
include TextHelper
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@pastel = Pastel.new
|
13
|
+
@prompt = TTY::Prompt.new
|
14
|
+
@spinner = TTY::Spinner.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def launch!
|
18
|
+
@conf = Psych.load(File.read(CONF))
|
19
|
+
puts padder("Choose an application to download.")
|
20
|
+
puts
|
21
|
+
|
22
|
+
# input/action loop
|
23
|
+
loop do
|
24
|
+
enviros = Rockette::Viewer.new.environments
|
25
|
+
list = list_builder(enviros)
|
26
|
+
action = @prompt.select("Which environment is the app found?", list)
|
27
|
+
break if action == list.length
|
28
|
+
|
29
|
+
apps_url = enviros[action - 1]["deployment_api"]
|
30
|
+
choose_app(apps_url)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def do_export(app_id, apps_url)
|
35
|
+
response = @prompt.yes?("Would you like to enter a filename for the export?")
|
36
|
+
if response == true
|
37
|
+
file = @prompt.ask("Please enter your desired filename:")
|
38
|
+
options = Thor::CoreExt::HashWithIndifferentAccess.new "app_id" => app_id, "url" => apps_url, "file" => file,
|
39
|
+
"force" => true
|
40
|
+
else
|
41
|
+
puts padder("Saving under default file name: f#{app_id}.sql")
|
42
|
+
options = Thor::CoreExt::HashWithIndifferentAccess.new "app_id" => app_id, "url" => apps_url, "force" => true
|
43
|
+
end
|
44
|
+
Rockette::Commands::Export.new(options).execute
|
45
|
+
puts
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def choose_app(apps_url)
|
51
|
+
loop do
|
52
|
+
apps = Rockette::Viewer.new.applications(apps_url)
|
53
|
+
list = list_builder(apps)
|
54
|
+
action = @prompt.slider("Download application => ", list, default: 1)
|
55
|
+
break if action == list.length
|
56
|
+
|
57
|
+
app_id = apps[action - 1]["application_id"]
|
58
|
+
do_export(app_id, apps_url)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def list_builder(array)
|
63
|
+
names = [] # Start building selection list
|
64
|
+
if array[0].key?("name")
|
65
|
+
array.each { |n| names << n["name"] }
|
66
|
+
else
|
67
|
+
array.each { |n| names << "#{n["application_name"]} App ID: #{n["application_id"]}" }
|
68
|
+
end
|
69
|
+
names << "Go Back"
|
70
|
+
names.map.with_index { |n, x| [n, x + 1] }.to_h
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../text_helper"
|
4
|
+
|
5
|
+
module Rockette
|
6
|
+
# View resources
|
7
|
+
class Viewer
|
8
|
+
include TextHelper
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@conf = Psych.load(File.read(CONF))
|
12
|
+
@pastel = Pastel.new
|
13
|
+
@prompt = TTY::Prompt.new
|
14
|
+
@spinner = TTY::Spinner.new # ("[:spinner] Loading APEX environments ...", format: pulse_2)
|
15
|
+
@view_actions = { "🏔 APEX Environments" => 1, "🎭 Registered Applications" => 2, "⬅️ Go Back" => 3 }
|
16
|
+
end
|
17
|
+
|
18
|
+
def launch!
|
19
|
+
puts padder("You can view environments or registered applications")
|
20
|
+
puts
|
21
|
+
|
22
|
+
# input/action loop
|
23
|
+
loop do
|
24
|
+
action = @prompt.select("Which would you like to see?", @view_actions)
|
25
|
+
break if action == 3
|
26
|
+
|
27
|
+
do_action(action)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def do_action(action)
|
32
|
+
case action
|
33
|
+
when 1
|
34
|
+
puts
|
35
|
+
environments unless @table_env
|
36
|
+
@spinner.auto_spin
|
37
|
+
sleep(1)
|
38
|
+
@spinner.stop
|
39
|
+
puts @table_env.render(:ascii)
|
40
|
+
puts
|
41
|
+
when 2
|
42
|
+
puts
|
43
|
+
registered unless @table_reg
|
44
|
+
puts @table_reg.render(:ascii)
|
45
|
+
puts
|
46
|
+
else
|
47
|
+
puts "\nI don't understand that command.\n\n"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def ape_e_i(uri)
|
52
|
+
response = Rester.new(url: uri).rest_try
|
53
|
+
bail unless response
|
54
|
+
abort padder("#{uri} didn't work. Received: #{response.code}") unless response.code == 200
|
55
|
+
response
|
56
|
+
end
|
57
|
+
|
58
|
+
def applications(url)
|
59
|
+
uri = "#{url}deploy/apps"
|
60
|
+
response = ape_e_i(uri)
|
61
|
+
JSON.parse(response.body)["items"]
|
62
|
+
end
|
63
|
+
|
64
|
+
def environments
|
65
|
+
uri = "#{@conf["rockette"]["controller_url"]}deploy/environments/"
|
66
|
+
response = ape_e_i(uri)
|
67
|
+
@table_env = TTY::Table.new(header: ["Environment Name", "API", "Domain", "Owner", "Workspace"])
|
68
|
+
items = JSON.parse(response.body)["items"]
|
69
|
+
items.each { |h| @table_env << [h["name"], h["deployment_api"], h["domain"], h["owner"], h["workspace"]] }
|
70
|
+
end
|
71
|
+
|
72
|
+
def registered
|
73
|
+
uri = "#{@conf["rockette"]["controller_url"]}deploy/registered_apps/"
|
74
|
+
response = ape_e_i(uri)
|
75
|
+
@table_reg = TTY::Table.new(header: ["Registered Name", "Source App ID", "Source URI", "Target App ID",
|
76
|
+
"Target URI"])
|
77
|
+
JSON.parse(response.body)["items"].each do |h|
|
78
|
+
@table_reg << [h["registered_name"], h["src_app_id"], h["src_url"], h["tgt_app_id"], h["tgt_url"]]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rockette
|
4
|
+
# configurable rest-client calls with error handling and auto retries
|
5
|
+
class Rester
|
6
|
+
VERBS = {
|
7
|
+
"delete" => :Delete,
|
8
|
+
"get" => :Get,
|
9
|
+
"post" => :Post,
|
10
|
+
"put" => :Put
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
def initialize(headers: {}, meth: "Get", params: {}, url: "https://array/", config: {})
|
14
|
+
@headers = headers
|
15
|
+
@meth = meth
|
16
|
+
@params = params
|
17
|
+
@url = url
|
18
|
+
@config = config
|
19
|
+
@config["timeout"] = @config["timeout"] ||= 30
|
20
|
+
end
|
21
|
+
|
22
|
+
def make_call
|
23
|
+
response = RestClient::Request.execute(headers: @headers,
|
24
|
+
method: VERBS[@meth.downcase], payload: @params,
|
25
|
+
timeout: @config["timeout"], url: @url, verify_ssl: false)
|
26
|
+
rescue SocketError, IOError => e
|
27
|
+
puts "#{e.class}: #{e.message}"
|
28
|
+
nil
|
29
|
+
rescue StandardError => e
|
30
|
+
e.response
|
31
|
+
else
|
32
|
+
response
|
33
|
+
end
|
34
|
+
|
35
|
+
def cookie
|
36
|
+
if @url =~ %r{auth/session}
|
37
|
+
response = make_call
|
38
|
+
raise "There was an issue getting a cookie!" unless response.code == 200
|
39
|
+
|
40
|
+
(response.cookies.map { |key, val| "#{key}=#{val}" })[0]
|
41
|
+
else
|
42
|
+
error_text("cookie", @url.to_s, "auth/session")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# use rest-client with retry
|
47
|
+
def rest_try(tries = 3)
|
48
|
+
tries.times do |i|
|
49
|
+
response = make_call
|
50
|
+
unless response.nil?
|
51
|
+
break response if (200..299).include? response.code
|
52
|
+
break response if i > 1
|
53
|
+
end
|
54
|
+
puts "Failed #{@meth} on #{@url}, retry...#{i + 1}"
|
55
|
+
sleep 3 unless i > 1
|
56
|
+
return nil if i > 1 # Handles socket errors, etc. where there is no response.
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def error_text(method_name, url, wanted)
|
63
|
+
{
|
64
|
+
"response" =>
|
65
|
+
"ERROR: Wrong url for the #{method_name} method.\n"\
|
66
|
+
"Sent: #{url}\n"\
|
67
|
+
"Expected: \"#{wanted}\" as part of the url.",
|
68
|
+
"status" => 400
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def responder(response)
|
73
|
+
{
|
74
|
+
"response" => JSON.parse(response.body),
|
75
|
+
"status" => response.code.to_i
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/rockette/text_helper.rb
CHANGED
data/lib/rockette/version.rb
CHANGED
data/rockette.gemspec
CHANGED
@@ -32,10 +32,15 @@ Gem::Specification.new do |spec|
|
|
32
32
|
# Dev dependencies
|
33
33
|
spec.add_development_dependency "pry", "~> 0.0"
|
34
34
|
|
35
|
-
#
|
35
|
+
# Dependencies
|
36
|
+
spec.add_dependency "pastel", "~> 0.0"
|
36
37
|
spec.add_dependency "rest-client", "~> 2.0"
|
37
38
|
spec.add_dependency "thor", "~> 1.0"
|
39
|
+
spec.add_dependency "tty-config", "~> 0.0"
|
40
|
+
spec.add_dependency "tty-editor", "~> 0.0"
|
41
|
+
spec.add_dependency "tty-font", "~> 0.0"
|
42
|
+
spec.add_dependency "tty-prompt", "~> 0.0"
|
43
|
+
spec.add_dependency "tty-spinner", "~> 0.0"
|
44
|
+
spec.add_dependency "tty-table", "~> 0.0"
|
38
45
|
|
39
|
-
# For more information and examples about making a new gem, checkout our
|
40
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
41
46
|
end
|
data/templates/config.yml.erb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rockette
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kody Wilson
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-03-
|
11
|
+
date: 2021-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pastel
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rest-client
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +66,90 @@ dependencies:
|
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: tty-config
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: tty-editor
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: tty-font
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: tty-prompt
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: tty-spinner
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0.0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0.0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: tty-table
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0.0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0.0'
|
55
153
|
description: Rockette helps deploy and export APEX applications.
|
56
154
|
email:
|
57
155
|
- kodywilson@gmail.com
|
@@ -73,6 +171,7 @@ files:
|
|
73
171
|
- bin/console
|
74
172
|
- bin/setup
|
75
173
|
- exe/rockette
|
174
|
+
- lib/requirements.rb
|
76
175
|
- lib/rockette.rb
|
77
176
|
- lib/rockette/cli.rb
|
78
177
|
- lib/rockette/command.rb
|
@@ -80,6 +179,13 @@ files:
|
|
80
179
|
- lib/rockette/commands/config.rb
|
81
180
|
- lib/rockette/commands/deploy.rb
|
82
181
|
- lib/rockette/commands/export.rb
|
182
|
+
- lib/rockette/commands/interactive.rb
|
183
|
+
- lib/rockette/controller.rb
|
184
|
+
- lib/rockette/controller/configurator.rb
|
185
|
+
- lib/rockette/controller/deployer.rb
|
186
|
+
- lib/rockette/controller/exporter.rb
|
187
|
+
- lib/rockette/controller/viewer.rb
|
188
|
+
- lib/rockette/rester.rb
|
83
189
|
- lib/rockette/text_helper.rb
|
84
190
|
- lib/rockette/version.rb
|
85
191
|
- rockette.gemspec
|