rockette 0.0.2 → 0.0.3
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.
- 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
|