neetodeploy 1.1.24 → 1.1.25
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/Gemfile.lock +12 -3
- data/lib/neeto_deploy/cli/addon/constants.rb +4 -0
- data/lib/neeto_deploy/cli/addon/info.rb +1 -1
- data/lib/neeto_deploy/cli/addon/scheduled_exports_settings.rb +7 -9
- data/lib/neeto_deploy/cli/autoscaling_config/commands.rb +1 -0
- data/lib/neeto_deploy/cli/autoscaling_config/constants.rb +13 -0
- data/lib/neeto_deploy/cli/autoscaling_config/list.rb +19 -7
- data/lib/neeto_deploy/cli/certificates/commands.rb +1 -0
- data/lib/neeto_deploy/cli/certificates/constants.rb +13 -0
- data/lib/neeto_deploy/cli/certificates/list.rb +24 -7
- data/lib/neeto_deploy/cli/dyno_console_manager.rb +26 -7
- data/lib/neeto_deploy/cli/env/list.rb +21 -9
- data/lib/neeto_deploy/cli/env/set.rb +5 -18
- data/lib/neeto_deploy/cli/env/unset.rb +4 -9
- data/lib/neeto_deploy/cli/exec/base.rb +25 -8
- data/lib/neeto_deploy/cli/login/base.rb +7 -5
- data/lib/neeto_deploy/cli/logs/base.rb +67 -58
- data/lib/neeto_deploy/cli/logs/constants.rb +4 -3
- data/lib/neeto_deploy/cli/redis/get.rb +1 -1
- data/lib/neeto_deploy/cli/redis/reset_stats.rb +2 -2
- data/lib/neeto_deploy/cli/redis/set.rb +2 -2
- data/lib/neeto_deploy/cli/session.rb +1 -1
- data/lib/neeto_deploy/cli.rb +1 -1
- data/lib/neeto_deploy/version.rb +1 -1
- data/neetodeploy.gemspec +6 -2
- metadata +49 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a0396bbc473e87998286bdc95905c601721c24787da18a64e7ab67a9cd42ddfa
|
|
4
|
+
data.tar.gz: 8d002597268fcca2205aa42a5fcdeb371f2a9edcbdfeb14ff80bff449407b7e0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0f07f591022176db4bd74ab0b6a5c3839634c728f5db28cef1cd504d2ff728fcec857fe84d85a9c02ba9b01a99a56d1086d41c023a318d789b5f3b26ddcd32b0
|
|
7
|
+
data.tar.gz: d7bc9e789cf2174cca1beb3fc37b816ceeb7d554fab45be4d50e46d24d0f4cec7d4f1d094b78f5b22847f46c59bf65c125317fa7180c307cfb202a07d8e5286b
|
data/Gemfile.lock
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
neetodeploy (1.1.
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
neetodeploy (1.1.25)
|
|
5
|
+
base64
|
|
6
|
+
bigdecimal
|
|
7
7
|
colorize
|
|
8
|
+
csv
|
|
8
9
|
dotenv (~> 2.8.1)
|
|
9
10
|
httparty (~> 0.21.0)
|
|
10
11
|
launchy (~> 2.5.0)
|
|
12
|
+
logger
|
|
13
|
+
ostruct
|
|
11
14
|
rexml
|
|
12
15
|
terminal-table (~> 3.0.2)
|
|
13
16
|
thor (~> 1.5.0)
|
|
@@ -34,8 +37,11 @@ GEM
|
|
|
34
37
|
jmespath (~> 1, >= 1.6.1)
|
|
35
38
|
aws-sigv4 (1.5.2)
|
|
36
39
|
aws-eventstream (~> 1, >= 1.0.2)
|
|
40
|
+
base64 (0.3.0)
|
|
41
|
+
bigdecimal (4.0.1)
|
|
37
42
|
byebug (11.1.3)
|
|
38
43
|
colorize (1.1.0)
|
|
44
|
+
csv (3.3.5)
|
|
39
45
|
dotenv (2.8.1)
|
|
40
46
|
eventmachine (1.2.7)
|
|
41
47
|
httparty (0.21.0)
|
|
@@ -44,8 +50,10 @@ GEM
|
|
|
44
50
|
jmespath (1.6.2)
|
|
45
51
|
launchy (2.5.2)
|
|
46
52
|
addressable (~> 2.8)
|
|
53
|
+
logger (1.7.0)
|
|
47
54
|
mini_mime (1.1.2)
|
|
48
55
|
multi_xml (0.6.0)
|
|
56
|
+
ostruct (0.6.3)
|
|
49
57
|
public_suffix (5.0.1)
|
|
50
58
|
rexml (3.4.1)
|
|
51
59
|
terminal-table (3.0.2)
|
|
@@ -69,6 +77,7 @@ PLATFORMS
|
|
|
69
77
|
arm64-darwin-21
|
|
70
78
|
arm64-darwin-23
|
|
71
79
|
arm64-darwin-24
|
|
80
|
+
arm64-darwin-25
|
|
72
81
|
x86_64-darwin-21
|
|
73
82
|
x86_64-darwin-23
|
|
74
83
|
x86_64-linux
|
|
@@ -42,7 +42,7 @@ module NeetoDeploy
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def print_output
|
|
45
|
-
ui.error(@response["error"]) and return unless @response.success?
|
|
45
|
+
ui.error(@response["error"] || @response.message) and return unless @response.success?
|
|
46
46
|
|
|
47
47
|
flatten_hash_from(JSON[@response.body]).each do |k, v|
|
|
48
48
|
ui.info("#{k}: #{v}")
|
|
@@ -12,34 +12,32 @@ module NeetoDeploy
|
|
|
12
12
|
include Constants
|
|
13
13
|
include Session
|
|
14
14
|
|
|
15
|
-
attr_reader :
|
|
15
|
+
attr_reader :app_slug
|
|
16
16
|
|
|
17
17
|
def initialize(options)
|
|
18
18
|
super()
|
|
19
|
-
@
|
|
19
|
+
@app_slug = options[:app]
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def run
|
|
23
23
|
ui.execute_with_loading("Fetching info...") do
|
|
24
|
-
|
|
24
|
+
@response = send_get_request(scheduled_exports_url(app_slug), { app_slug: })
|
|
25
25
|
end
|
|
26
26
|
print_output
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
private
|
|
30
30
|
|
|
31
|
-
def send_request
|
|
32
|
-
@response = send_get_request("#{NEETO_DEPLOY_CLI_API_BASE_URL}/scheduled_exports/#{app_name}", {app_slug: app_name})
|
|
33
|
-
end
|
|
34
|
-
|
|
35
31
|
def print_output
|
|
36
|
-
ui.error(@response["error"]) and return unless @response.success?
|
|
32
|
+
ui.error(@response["error"] || @response.message) and return unless @response.success?
|
|
37
33
|
|
|
38
34
|
scheduled_exports = JSON.parse(@response.body)["scheduled_exports_enabled"]
|
|
39
35
|
if scheduled_exports.nil?
|
|
40
36
|
ui.error("App doesn't seem to have a primary database addon")
|
|
37
|
+
elsif scheduled_exports
|
|
38
|
+
ui.success("Scheduled exports is turned on for #{app_slug}'s primary database")
|
|
41
39
|
else
|
|
42
|
-
ui.info("Scheduled exports is turned
|
|
40
|
+
ui.info("Scheduled exports is turned off for #{app_slug}'s primary database")
|
|
43
41
|
end
|
|
44
42
|
end
|
|
45
43
|
end
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "thor"
|
|
4
|
+
require "terminal-table"
|
|
5
|
+
|
|
6
|
+
require_relative "../session"
|
|
7
|
+
require_relative "./constants"
|
|
4
8
|
|
|
5
9
|
module NeetoDeploy
|
|
6
10
|
class CLI
|
|
7
11
|
module AutoscalingConfig
|
|
8
12
|
class List < CLI::Base
|
|
9
13
|
include Session
|
|
14
|
+
include Constants
|
|
10
15
|
|
|
11
16
|
attr_reader :app_slug
|
|
12
17
|
|
|
@@ -16,15 +21,22 @@ module NeetoDeploy
|
|
|
16
21
|
end
|
|
17
22
|
|
|
18
23
|
def run
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
24
|
+
ui.execute_with_loading("Fetching autoscaling configs...") do
|
|
25
|
+
@response = send_get_request(autoscaling_configs_url(app_slug), { app_slug: })
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
ui.error(@response["error"] || @response.message) and return unless @response.success?
|
|
29
|
+
|
|
30
|
+
status = JSON.parse(@response.body)["autoscaling_status"]
|
|
24
31
|
|
|
25
|
-
|
|
32
|
+
if status.nil? || status.empty?
|
|
33
|
+
ui.info("No autoscaling configs found for #{app_slug}.")
|
|
34
|
+
return
|
|
35
|
+
end
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
rows = status.map { |process_type, config| [process_type, config&.[]("enabled") ? "Yes" : "No", config&.[]("type") || "-"] }
|
|
38
|
+
table = Terminal::Table.new(headings: ["Process Type", "Enabled", "Type"], rows: rows)
|
|
39
|
+
ui.info(table)
|
|
28
40
|
end
|
|
29
41
|
end
|
|
30
42
|
end
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "thor"
|
|
4
|
+
require "terminal-table"
|
|
5
|
+
|
|
6
|
+
require_relative "../session"
|
|
7
|
+
require_relative "./constants"
|
|
4
8
|
|
|
5
9
|
module NeetoDeploy
|
|
6
10
|
class CLI
|
|
7
11
|
module Certificates
|
|
8
12
|
class List < CLI::Base
|
|
9
13
|
include Session
|
|
14
|
+
include Constants
|
|
10
15
|
|
|
11
16
|
attr_reader :app_slug
|
|
12
17
|
|
|
@@ -16,15 +21,27 @@ module NeetoDeploy
|
|
|
16
21
|
end
|
|
17
22
|
|
|
18
23
|
def run
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
24
|
+
ui.execute_with_loading("Fetching certificates...") do
|
|
25
|
+
@response = send_get_request(certificates_url, { app_slug: })
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
ui.error(@response["error"] || @response.message) and return unless @response.success?
|
|
29
|
+
|
|
30
|
+
certificates = JSON.parse(@response.body)["certificates"]
|
|
31
|
+
|
|
32
|
+
if certificates.nil? || certificates.empty?
|
|
33
|
+
ui.info("No certificates found for #{app_slug}.")
|
|
34
|
+
return
|
|
35
|
+
end
|
|
24
36
|
|
|
25
|
-
|
|
37
|
+
rows = certificates.map do |cert|
|
|
38
|
+
domains = Array(cert["domains"]).map { |d| d["hostname"] }.join(", ")
|
|
39
|
+
expiring_soon = cert["expires_before_30_days"] ? " (expiring soon)" : ""
|
|
40
|
+
[cert["name"], cert["expiration"], domains + expiring_soon]
|
|
41
|
+
end
|
|
26
42
|
|
|
27
|
-
|
|
43
|
+
table = Terminal::Table.new(headings: ["Name", "Expiration", "Domains"], rows: rows)
|
|
44
|
+
ui.info(table)
|
|
28
45
|
end
|
|
29
46
|
end
|
|
30
47
|
end
|
|
@@ -15,21 +15,39 @@ module NeetoDeploy
|
|
|
15
15
|
def process!
|
|
16
16
|
start_spinner
|
|
17
17
|
send_console_session_request
|
|
18
|
-
ui.error("
|
|
18
|
+
ui.error(@response["error"] || @response.message) and return unless @response.success?
|
|
19
19
|
|
|
20
|
+
print_connection_banner
|
|
20
21
|
start_console
|
|
22
|
+
print_session_end
|
|
21
23
|
connection_cleanup_callback
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
private
|
|
25
27
|
|
|
26
28
|
def console_executable_path
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
if ENV["DEV_ENVIRONMENT"]
|
|
30
|
+
File.expand_path("../../../exe/#{console_executable_name}", __dir__)
|
|
31
|
+
else
|
|
32
|
+
gem_spec = Gem::Specification.find_by_name("neetodeploy")
|
|
33
|
+
File.join(gem_spec.gem_dir, "exe", console_executable_name)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def print_connection_banner
|
|
38
|
+
separator = "─" * 60
|
|
39
|
+
ui.success("Connected to #{addon_name}. Type \"exit\" to end the session.")
|
|
40
|
+
ui.info(separator)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def print_session_end
|
|
44
|
+
separator = "─" * 60
|
|
45
|
+
ui.info(separator)
|
|
46
|
+
ui.info("Session ended.")
|
|
30
47
|
end
|
|
31
48
|
|
|
32
49
|
def start_console
|
|
50
|
+
@pubsub_token = @response.parsed_response["console_pubsub_token"]
|
|
33
51
|
console_access_token = @response.parsed_response["console_access_token"]
|
|
34
52
|
pod_name = @response.parsed_response["pod_name"]
|
|
35
53
|
database_url = @response.parsed_response["database_url"]
|
|
@@ -39,7 +57,7 @@ module NeetoDeploy
|
|
|
39
57
|
end
|
|
40
58
|
|
|
41
59
|
def execute_console(pod_name, console_access_token, container_name, kind, database_url)
|
|
42
|
-
system(
|
|
60
|
+
system(console_executable_path, "-podname", pod_name, "-token", console_access_token, "-kind", kind, "-container", container_name, "-url", database_url, err: File::NULL)
|
|
43
61
|
end
|
|
44
62
|
|
|
45
63
|
def send_console_session_request
|
|
@@ -49,11 +67,12 @@ module NeetoDeploy
|
|
|
49
67
|
|
|
50
68
|
def connection_cleanup_callback
|
|
51
69
|
url = "#{console_session_base_url}/#{addon_name}"
|
|
52
|
-
send_delete_request(url, { pubsub_token: @pubsub_token })
|
|
70
|
+
response = send_delete_request(url, { pubsub_token: @pubsub_token })
|
|
71
|
+
ui.error("Warning: failed to clean up session — #{response["error"] || response.message}") unless response.success?
|
|
53
72
|
end
|
|
54
73
|
|
|
55
74
|
def start_spinner
|
|
56
|
-
@spinner = TTY::Spinner.new("Setting up
|
|
75
|
+
@spinner = TTY::Spinner.new("Setting up console for #{addon_name} [:spinner]", format: :classic)
|
|
57
76
|
@spinner.auto_spin
|
|
58
77
|
end
|
|
59
78
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "terminal-table"
|
|
4
4
|
require "thor"
|
|
5
|
+
require "io/console"
|
|
5
6
|
|
|
6
7
|
require_relative "../session"
|
|
7
8
|
require_relative "./constants"
|
|
@@ -22,16 +23,14 @@ module NeetoDeploy
|
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
def run
|
|
25
|
-
|
|
26
|
-
environment_variables_url, {
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
)
|
|
26
|
+
ui.execute_with_loading("Fetching environment variables...") do
|
|
27
|
+
@response = send_get_request(environment_variables_url, { app_slug: })
|
|
28
|
+
end
|
|
30
29
|
|
|
31
|
-
ui.error(response) and return unless response.success?
|
|
30
|
+
ui.error(@response["error"] || @response.message) and return unless @response.success?
|
|
32
31
|
|
|
33
|
-
data = is_json_format ? json_data(response["environment_variables"]) : table_data(response["environment_variables"])
|
|
34
|
-
ui.
|
|
32
|
+
data = is_json_format ? json_data(@response["environment_variables"]) : table_data(@response["environment_variables"])
|
|
33
|
+
ui.info(data)
|
|
35
34
|
end
|
|
36
35
|
|
|
37
36
|
private
|
|
@@ -47,7 +46,20 @@ module NeetoDeploy
|
|
|
47
46
|
end
|
|
48
47
|
|
|
49
48
|
def table_data(envs)
|
|
50
|
-
|
|
49
|
+
rows = table_rows(envs)
|
|
50
|
+
key_width = rows.map { |r| r[0].length }.max || 10
|
|
51
|
+
val_width = [terminal_width - key_width - 7, 20].max
|
|
52
|
+
wrapped_rows = rows.map { |key, value| [key, wrap_value(value, val_width)] }
|
|
53
|
+
Terminal::Table.new(headings: table_columns, rows: wrapped_rows, style: { all_separators: false })
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def terminal_width
|
|
57
|
+
IO.console&.winsize&.last || 120
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def wrap_value(text, width)
|
|
61
|
+
return text if text.length <= width
|
|
62
|
+
text.chars.each_slice(width).map(&:join).join("\n")
|
|
51
63
|
end
|
|
52
64
|
|
|
53
65
|
def json_data(envs)
|
|
@@ -25,24 +25,11 @@ module NeetoDeploy
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def run
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
34
|
-
ui.info(table)
|
|
35
|
-
|
|
36
|
-
ui.info("Setting environment variables and restarting app...")
|
|
37
|
-
|
|
38
|
-
response = send_post_request(
|
|
39
|
-
environment_variables_url, {
|
|
40
|
-
app_slug:,
|
|
41
|
-
environment_variables:
|
|
42
|
-
}
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
ui.error(response) and return unless response.success?
|
|
28
|
+
ui.execute_with_loading("Setting environment variables and restarting app...") do
|
|
29
|
+
@response = send_post_request(environment_variables_url, { app_slug:, environment_variables: })
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
ui.error(@response["error"] || @response.message) and return unless @response.success?
|
|
46
33
|
|
|
47
34
|
ui.success("Done")
|
|
48
35
|
end
|
|
@@ -25,16 +25,11 @@ module NeetoDeploy
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def run
|
|
28
|
-
ui.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
environment_variables_url, {
|
|
32
|
-
app_slug:,
|
|
33
|
-
environment_variables:
|
|
34
|
-
}
|
|
35
|
-
)
|
|
28
|
+
ui.execute_with_loading("Unsetting #{environment_variables_string_array.join(", ")} and restarting app...") do
|
|
29
|
+
@response = send_delete_request(environment_variables_url, { app_slug:, environment_variables: })
|
|
30
|
+
end
|
|
36
31
|
|
|
37
|
-
ui.error(response) and return unless response.success?
|
|
32
|
+
ui.error(@response["error"] || @response.message) and return unless @response.success?
|
|
38
33
|
|
|
39
34
|
ui.success("Done")
|
|
40
35
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "thor"
|
|
4
|
-
require "readline"
|
|
5
4
|
require "rbconfig"
|
|
6
5
|
|
|
7
6
|
require_relative "../session"
|
|
@@ -27,18 +26,35 @@ module NeetoDeploy
|
|
|
27
26
|
def process!
|
|
28
27
|
start_spinner
|
|
29
28
|
send_console_session_request
|
|
30
|
-
ui.error("
|
|
29
|
+
ui.error(@response["error"] || @response.message) and return unless @response.success?
|
|
31
30
|
|
|
31
|
+
print_connection_banner
|
|
32
32
|
start_console
|
|
33
|
+
print_session_end
|
|
33
34
|
connection_cleanup_callback
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
private
|
|
37
38
|
|
|
38
39
|
def console_executable_path
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
if ENV["DEV_ENVIRONMENT"]
|
|
41
|
+
File.expand_path("../../../../exe/#{console_executable_name}", __dir__)
|
|
42
|
+
else
|
|
43
|
+
gem_spec = Gem::Specification.find_by_name("neetodeploy")
|
|
44
|
+
File.join(gem_spec.gem_dir, "exe", console_executable_name)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def print_connection_banner
|
|
49
|
+
separator = "─" * 60
|
|
50
|
+
ui.success("Connected to #{app_name}. Type \"exit\" to end the session.")
|
|
51
|
+
ui.info(separator)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def print_session_end
|
|
55
|
+
separator = "─" * 60
|
|
56
|
+
ui.info(separator)
|
|
57
|
+
ui.info("Session ended.")
|
|
42
58
|
end
|
|
43
59
|
|
|
44
60
|
def start_console
|
|
@@ -46,7 +62,7 @@ module NeetoDeploy
|
|
|
46
62
|
console_access_token = @response.parsed_response["console_access_token"]
|
|
47
63
|
pod_name = "#{app_name}-#{@pubsub_token}-console-deployment"
|
|
48
64
|
container_name = "#{app_name}-#{@pubsub_token}-console"
|
|
49
|
-
system(
|
|
65
|
+
system(console_executable_path, "-podname", pod_name, "-token", console_access_token, "-container", container_name, err: File::NULL)
|
|
50
66
|
end
|
|
51
67
|
|
|
52
68
|
def send_console_session_request
|
|
@@ -56,11 +72,12 @@ module NeetoDeploy
|
|
|
56
72
|
|
|
57
73
|
def connection_cleanup_callback
|
|
58
74
|
url = "#{console_session_base_url}/#{app_name}"
|
|
59
|
-
send_delete_request(url, { pubsub_token: @pubsub_token })
|
|
75
|
+
response = send_delete_request(url, { pubsub_token: @pubsub_token })
|
|
76
|
+
ui.error("Warning: failed to clean up session — #{response["error"] || response.message}") unless response.success?
|
|
60
77
|
end
|
|
61
78
|
|
|
62
79
|
def start_spinner
|
|
63
|
-
@spinner = TTY::Spinner.new("Setting up
|
|
80
|
+
@spinner = TTY::Spinner.new("Setting up console for #{app_name} [:spinner]", format: :classic)
|
|
64
81
|
@spinner.auto_spin
|
|
65
82
|
end
|
|
66
83
|
|
|
@@ -53,13 +53,15 @@ module NeetoDeploy
|
|
|
53
53
|
|
|
54
54
|
def wait_until_user_authenticates!
|
|
55
55
|
two_minutes_later = Time.now.utc + 120
|
|
56
|
-
|
|
57
|
-
while
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
authenticated = false
|
|
57
|
+
while Time.now.utc < two_minutes_later
|
|
58
|
+
if check_if_user_authenticated
|
|
59
|
+
authenticated = true
|
|
60
|
+
break
|
|
61
|
+
end
|
|
60
62
|
sleep LOGIN_STATUS_CHECK_INTERVAL_SECONDS
|
|
61
63
|
end
|
|
62
|
-
ui.
|
|
64
|
+
authenticated ? ui.success("Logged in successfully") : ui.error("Login timed out. Please try again.")
|
|
63
65
|
end
|
|
64
66
|
|
|
65
67
|
def check_if_user_authenticated
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
require "thor"
|
|
4
4
|
require "colorize"
|
|
5
5
|
|
|
6
|
-
require "aws-sdk-cloudwatchlogs"
|
|
7
|
-
require "aws-sdk-cognitoidentity"
|
|
8
6
|
require_relative "../session"
|
|
9
7
|
require_relative "./constants"
|
|
10
8
|
|
|
@@ -15,86 +13,97 @@ module NeetoDeploy
|
|
|
15
13
|
include Constants
|
|
16
14
|
include Session
|
|
17
15
|
|
|
16
|
+
POLL_INTERVAL_SECONDS = 2
|
|
17
|
+
INITIAL_LIMIT = 100
|
|
18
|
+
POLL_LIMIT = 1000
|
|
19
|
+
|
|
18
20
|
attr_reader :app_slug, :process_type
|
|
19
21
|
|
|
20
22
|
def initialize(app_slug, process_type = nil)
|
|
21
23
|
super()
|
|
22
24
|
@app_slug = app_slug
|
|
23
25
|
@process_type = process_type
|
|
24
|
-
@
|
|
26
|
+
@last_timestamp_ms = nil
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
def process!
|
|
28
30
|
start_spinner
|
|
29
|
-
response =
|
|
30
|
-
|
|
31
|
+
response = fetch_logs
|
|
32
|
+
@spinner.stop
|
|
33
|
+
|
|
34
|
+
unless response.success?
|
|
35
|
+
ui.error(response["error"] || response.message)
|
|
36
|
+
return
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
data = JSON.parse(response.body)
|
|
40
|
+
|
|
41
|
+
unless data["configured"]
|
|
42
|
+
ui.error("Logs are not configured for this app.")
|
|
43
|
+
return
|
|
44
|
+
end
|
|
31
45
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
46
|
+
# Initial batch returned DESC — reverse for chronological display
|
|
47
|
+
logs = data["logs"].reverse
|
|
48
|
+
logs.each { |log| print_log(log) }
|
|
49
|
+
# Subtract 10s buffer to account for potential client/server clock skew
|
|
50
|
+
@last_timestamp_ms = logs.any? ? logs.last["timestamp"] : ((Time.now.to_f - 10) * 1000).to_i
|
|
51
|
+
|
|
52
|
+
poll_for_logs
|
|
35
53
|
end
|
|
36
54
|
|
|
37
55
|
private
|
|
38
56
|
|
|
39
|
-
def
|
|
40
|
-
|
|
41
|
-
@spinner.stop && ui.error(response) unless response.success?
|
|
42
|
-
response
|
|
43
|
-
end
|
|
57
|
+
def fetch_logs(after_timestamp_ms: nil)
|
|
58
|
+
params = { app_slug:, process_type:, limit: INITIAL_LIMIT }
|
|
44
59
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Aws::CloudWatchLogs::Client.new(
|
|
51
|
-
region: log_params["region"],
|
|
52
|
-
access_key_id: credentials.access_key_id,
|
|
53
|
-
secret_access_key: credentials.secret_key,
|
|
54
|
-
session_token: credentials.session_token
|
|
55
|
-
)
|
|
56
|
-
end
|
|
60
|
+
if after_timestamp_ms
|
|
61
|
+
params[:after_timestamp] = ms_to_iso8601(after_timestamp_ms)
|
|
62
|
+
params[:limit] = POLL_LIMIT
|
|
63
|
+
end
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
@spinner = TTY::Spinner.new("Starting live stream session [:spinner]", format: :classic)
|
|
60
|
-
@spinner.auto_spin
|
|
65
|
+
send_get_request(logs_v2_url, params)
|
|
61
66
|
end
|
|
62
67
|
|
|
63
|
-
def
|
|
64
|
-
next_token = nil
|
|
65
|
-
@spinner.stop
|
|
68
|
+
def poll_for_logs
|
|
66
69
|
loop do
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
response
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
puts "\e[35m#{Time.at(event.timestamp / 1000)}\e[0m #{event.message}"
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
if next_token == response.next_forward_token
|
|
83
|
-
sleep 3
|
|
84
|
-
else
|
|
85
|
-
next_token = response.next_forward_token
|
|
86
|
-
end
|
|
87
|
-
rescue Aws::CloudWatchLogs::Errors::ExpiredTokenException,
|
|
88
|
-
Aws::CloudWatchLogs::Errors::UnrecognizedClientException => e
|
|
89
|
-
warn "Stream time limit has been reached"
|
|
90
|
-
exit 1
|
|
91
|
-
rescue => e
|
|
70
|
+
response = fetch_logs(after_timestamp_ms: @last_timestamp_ms)
|
|
71
|
+
|
|
72
|
+
if response.success?
|
|
73
|
+
data = JSON.parse(response.body)
|
|
74
|
+
logs = data["logs"]
|
|
75
|
+
logs.each { |log| print_log(log) }
|
|
76
|
+
@last_timestamp_ms = logs.last["timestamp"] if logs.any?
|
|
77
|
+
next if data["has_more_forward"]
|
|
78
|
+
elsif response.code.between?(400, 499)
|
|
79
|
+
ui.error("Error #{response.code}: #{response["error"] || response.message}")
|
|
80
|
+
break
|
|
81
|
+
else
|
|
92
82
|
warn "Connection lost. Reconnecting..."
|
|
93
|
-
sleep 5
|
|
94
83
|
end
|
|
95
|
-
|
|
84
|
+
|
|
85
|
+
sleep POLL_INTERVAL_SECONDS
|
|
86
|
+
rescue Interrupt
|
|
87
|
+
raise
|
|
88
|
+
rescue => e
|
|
89
|
+
warn "Error: #{e.message}. Reconnecting..."
|
|
90
|
+
sleep 5
|
|
96
91
|
end
|
|
97
92
|
end
|
|
93
|
+
|
|
94
|
+
def print_log(log)
|
|
95
|
+
timestamp = Time.at(log["timestamp"] / 1000.0)
|
|
96
|
+
puts "#{timestamp.to_s.colorize(:magenta)} #{log["message"]}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def ms_to_iso8601(ms)
|
|
100
|
+
Time.at(ms / 1000.0).utc.iso8601(3)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def start_spinner
|
|
104
|
+
@spinner = TTY::Spinner.new("Starting live stream session [:spinner]", format: :classic)
|
|
105
|
+
@spinner.auto_spin
|
|
106
|
+
end
|
|
98
107
|
end
|
|
99
108
|
end
|
|
100
109
|
end
|
|
@@ -4,10 +4,11 @@ module NeetoDeploy
|
|
|
4
4
|
class CLI
|
|
5
5
|
module Logs
|
|
6
6
|
module Constants
|
|
7
|
-
|
|
7
|
+
NEETO_DEPLOY_CLI_V2_API_BASE_URL = "#{NEETO_DEPLOY_HOST}/api/cli/v2".freeze
|
|
8
|
+
NEETO_DEPLOY_CLI_API_V2_LOGS_URL = "#{NEETO_DEPLOY_CLI_V2_API_BASE_URL}/logs".freeze
|
|
8
9
|
|
|
9
|
-
def
|
|
10
|
-
|
|
10
|
+
def logs_v2_url
|
|
11
|
+
NEETO_DEPLOY_CLI_API_V2_LOGS_URL
|
|
11
12
|
end
|
|
12
13
|
end
|
|
13
14
|
end
|
|
@@ -26,11 +26,11 @@ module NeetoDeploy
|
|
|
26
26
|
command: "CONFIG RESETSTAT"
|
|
27
27
|
}
|
|
28
28
|
)
|
|
29
|
-
ui.error(response["error"]) and return unless response.success?
|
|
29
|
+
ui.error(response["error"] || response.message) and return unless response.success?
|
|
30
30
|
|
|
31
31
|
ui.success("Stats reset successful.")
|
|
32
32
|
else
|
|
33
|
-
|
|
33
|
+
ui.info("Reset cancelled.")
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
end
|
|
@@ -23,7 +23,7 @@ module NeetoDeploy
|
|
|
23
23
|
|
|
24
24
|
def run
|
|
25
25
|
unless valid_config?
|
|
26
|
-
ui.error("Could not set
|
|
26
|
+
ui.error("Could not set config \"#{key}\". Manageable Redis configs: #{AVAILABLE_REDIS_CONFIGS_TO_EDIT.join(", ")}")
|
|
27
27
|
return
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -49,7 +49,7 @@ module NeetoDeploy
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def print_output
|
|
52
|
-
ui.error(@response["error"]) and return unless @response.success?
|
|
52
|
+
ui.error(@response["error"] || @response.message) and return unless @response.success?
|
|
53
53
|
|
|
54
54
|
ui.success("#{key} config for #{addon_name} set to #{value}")
|
|
55
55
|
end
|
data/lib/neeto_deploy/cli.rb
CHANGED
|
@@ -45,7 +45,7 @@ module NeetoDeploy
|
|
|
45
45
|
|
|
46
46
|
desc "logs", "Show logs"
|
|
47
47
|
option :app, type: :string, aliases: "-a", required: true, desc: "App slug"
|
|
48
|
-
option :process_type, type: :string, aliases: "-p",
|
|
48
|
+
option :process_type, type: :string, aliases: "-p", desc: "Process type (optional)"
|
|
49
49
|
def logs
|
|
50
50
|
CLI::Logs::Base.new(options[:app], options[:process_type]).process!
|
|
51
51
|
end
|
data/lib/neeto_deploy/version.rb
CHANGED
data/neetodeploy.gemspec
CHANGED
|
@@ -38,9 +38,13 @@ Gem::Specification.new do |spec|
|
|
|
38
38
|
spec.add_dependency "websocket-eventmachine-client"
|
|
39
39
|
spec.add_dependency "colorize"
|
|
40
40
|
spec.add_dependency "tty-spinner"
|
|
41
|
-
spec.add_dependency "aws-sdk-cloudwatchlogs"
|
|
42
|
-
spec.add_dependency "aws-sdk-cognitoidentity"
|
|
43
41
|
spec.add_dependency "rexml"
|
|
42
|
+
# Gems removed from Ruby stdlib in 3.4/4.0, required by our dependencies
|
|
43
|
+
spec.add_dependency "base64"
|
|
44
|
+
spec.add_dependency "bigdecimal"
|
|
45
|
+
spec.add_dependency "csv"
|
|
46
|
+
spec.add_dependency "logger"
|
|
47
|
+
spec.add_dependency "ostruct"
|
|
44
48
|
|
|
45
49
|
# To add the files from submodules
|
|
46
50
|
`git submodule --quiet foreach pwd`.split($\).each do |submodule_path|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: neetodeploy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.25
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Subin Siby
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-03-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: dotenv
|
|
@@ -123,7 +123,7 @@ dependencies:
|
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
124
|
version: '0'
|
|
125
125
|
- !ruby/object:Gem::Dependency
|
|
126
|
-
name:
|
|
126
|
+
name: rexml
|
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
|
128
128
|
requirements:
|
|
129
129
|
- - ">="
|
|
@@ -137,7 +137,7 @@ dependencies:
|
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
138
|
version: '0'
|
|
139
139
|
- !ruby/object:Gem::Dependency
|
|
140
|
-
name:
|
|
140
|
+
name: base64
|
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
|
142
142
|
requirements:
|
|
143
143
|
- - ">="
|
|
@@ -151,7 +151,49 @@ dependencies:
|
|
|
151
151
|
- !ruby/object:Gem::Version
|
|
152
152
|
version: '0'
|
|
153
153
|
- !ruby/object:Gem::Dependency
|
|
154
|
-
name:
|
|
154
|
+
name: bigdecimal
|
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - ">="
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: '0'
|
|
160
|
+
type: :runtime
|
|
161
|
+
prerelease: false
|
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - ">="
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '0'
|
|
167
|
+
- !ruby/object:Gem::Dependency
|
|
168
|
+
name: csv
|
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ">="
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '0'
|
|
174
|
+
type: :runtime
|
|
175
|
+
prerelease: false
|
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
177
|
+
requirements:
|
|
178
|
+
- - ">="
|
|
179
|
+
- !ruby/object:Gem::Version
|
|
180
|
+
version: '0'
|
|
181
|
+
- !ruby/object:Gem::Dependency
|
|
182
|
+
name: logger
|
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
|
184
|
+
requirements:
|
|
185
|
+
- - ">="
|
|
186
|
+
- !ruby/object:Gem::Version
|
|
187
|
+
version: '0'
|
|
188
|
+
type: :runtime
|
|
189
|
+
prerelease: false
|
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
191
|
+
requirements:
|
|
192
|
+
- - ">="
|
|
193
|
+
- !ruby/object:Gem::Version
|
|
194
|
+
version: '0'
|
|
195
|
+
- !ruby/object:Gem::Dependency
|
|
196
|
+
name: ostruct
|
|
155
197
|
requirement: !ruby/object:Gem::Requirement
|
|
156
198
|
requirements:
|
|
157
199
|
- - ">="
|
|
@@ -187,9 +229,11 @@ files:
|
|
|
187
229
|
- lib/neeto_deploy/cli/addon/info.rb
|
|
188
230
|
- lib/neeto_deploy/cli/addon/scheduled_exports_settings.rb
|
|
189
231
|
- lib/neeto_deploy/cli/autoscaling_config/commands.rb
|
|
232
|
+
- lib/neeto_deploy/cli/autoscaling_config/constants.rb
|
|
190
233
|
- lib/neeto_deploy/cli/autoscaling_config/list.rb
|
|
191
234
|
- lib/neeto_deploy/cli/base.rb
|
|
192
235
|
- lib/neeto_deploy/cli/certificates/commands.rb
|
|
236
|
+
- lib/neeto_deploy/cli/certificates/constants.rb
|
|
193
237
|
- lib/neeto_deploy/cli/certificates/list.rb
|
|
194
238
|
- lib/neeto_deploy/cli/dyno_console_manager.rb
|
|
195
239
|
- lib/neeto_deploy/cli/env/commands.rb
|