neetodeploy 1.4.2 → 1.4.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/lib/neeto_deploy/cli/logs/base.rb +131 -26
- data/lib/neeto_deploy/cli.rb +7 -2
- data/lib/neeto_deploy/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b449b4f69647cfff5f812dfa30c273b1add6b8dfdeb60ca419d96be75a768d37
|
|
4
|
+
data.tar.gz: dcebc5fdb56c81f76973f502a0491ddd640b4813a8c969f5fbb07db3df596264
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 90f7a087123601f9dabffb8c3113e04049d9fe15e60d0439979bc66aea8481397c8e0d0e59bcd79089d300d98e5f5d99204ea600137431ffcfce4a4fb6b23d5c
|
|
7
|
+
data.tar.gz: 8f765320a00cbc9b3e264e1d903a5b6b4a2c2849197a1965deba8fc60df1972ca0674a7519d05a4104aad283d73373e2c70ee1ae30c1c0576212edab71077633
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "thor"
|
|
4
|
+
require "time"
|
|
4
5
|
require "colorize"
|
|
5
6
|
|
|
6
7
|
require_relative "../session"
|
|
@@ -16,44 +17,148 @@ module NeetoDeploy
|
|
|
16
17
|
POLL_INTERVAL_SECONDS = 2
|
|
17
18
|
INITIAL_LIMIT = 100
|
|
18
19
|
POLL_LIMIT = 1000
|
|
20
|
+
HISTORIC_DEFAULT_LIMIT = 500
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
RELATIVE_TIME_PATTERN = /\A(\d+)(m|h|d)\z/
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
attr_reader :app_slug, :process_type, :options
|
|
25
|
+
|
|
26
|
+
def initialize(options)
|
|
23
27
|
super()
|
|
24
|
-
@
|
|
25
|
-
@
|
|
28
|
+
@options = options
|
|
29
|
+
@app_slug = options[:app]
|
|
30
|
+
@process_type = options[:process_type]
|
|
26
31
|
@last_timestamp_ms = nil
|
|
27
32
|
end
|
|
28
33
|
|
|
29
34
|
def process!
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
unless response.success?
|
|
36
|
-
ui.error(response["error"] || response.message)
|
|
37
|
-
return
|
|
35
|
+
if historic_mode?
|
|
36
|
+
process_historic!
|
|
37
|
+
else
|
|
38
|
+
process_live!
|
|
38
39
|
end
|
|
40
|
+
end
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
private
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return
|
|
44
|
+
def historic_mode?
|
|
45
|
+
options[:from] && !options[:from].empty?
|
|
45
46
|
end
|
|
46
47
|
|
|
47
|
-
#
|
|
48
|
-
logs = data["logs"].reverse
|
|
49
|
-
logs.each { |log| print_log(log) }
|
|
50
|
-
# Subtract 10s buffer to account for potential client/server clock skew
|
|
51
|
-
@last_timestamp_ms = logs.any? ? logs.last["timestamp"] : ((Time.now.to_f - 10) * 1000).to_i
|
|
48
|
+
# --- Historic mode ---
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
def process_historic!
|
|
51
|
+
from_time = parse_time_option(options[:from])
|
|
52
|
+
unless from_time
|
|
53
|
+
ui.error("Invalid --from time: #{options[:from]}")
|
|
54
|
+
return
|
|
55
|
+
end
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
to_time = options[:to] ? parse_time_option(options[:to]) : nil
|
|
58
|
+
if options[:to] && !to_time
|
|
59
|
+
ui.error("Invalid --to time: #{options[:to]}")
|
|
60
|
+
return
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
start_spinner("Fetching historic logs")
|
|
64
|
+
print_github_repo_name
|
|
65
|
+
|
|
66
|
+
total_count = 0
|
|
67
|
+
limit = options[:limit] || HISTORIC_DEFAULT_LIMIT
|
|
68
|
+
before_timestamp = nil
|
|
69
|
+
|
|
70
|
+
loop do
|
|
71
|
+
params = build_historic_params(from_time, to_time, before_timestamp, limit)
|
|
72
|
+
response = send_get_request(logs_v2_url, params)
|
|
73
|
+
@spinner.stop if @spinner
|
|
74
|
+
|
|
75
|
+
unless response.success?
|
|
76
|
+
ui.error(response["error"] || response.message)
|
|
77
|
+
return
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
data = JSON.parse(response.body)
|
|
81
|
+
|
|
82
|
+
unless data["configured"]
|
|
83
|
+
ui.error("Logs are not configured for this app.")
|
|
84
|
+
return
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
logs = data["logs"]
|
|
88
|
+
if logs.empty?
|
|
89
|
+
ui.info("No logs found for the given time range.") if total_count == 0
|
|
90
|
+
break
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Backward pagination returns DESC — reverse for chronological display
|
|
94
|
+
logs.reverse.each { |log| print_log(log) }
|
|
95
|
+
total_count += logs.size
|
|
96
|
+
|
|
97
|
+
break if total_count >= limit
|
|
98
|
+
break unless data["has_more"]
|
|
99
|
+
|
|
100
|
+
before_timestamp = ms_to_iso8601(logs.last["timestamp"])
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
ui.info("Fetched #{total_count} log entries.") if total_count > 0
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def build_historic_params(from_time, to_time, before_timestamp, limit)
|
|
107
|
+
params = { app_slug:, process_type:, from_time: from_time.utc.iso8601(3), limit: }
|
|
108
|
+
params[:to_time] = to_time.utc.iso8601(3) if to_time
|
|
109
|
+
params[:before_timestamp] = before_timestamp if before_timestamp
|
|
110
|
+
params[:search] = options[:search] if options[:search]
|
|
111
|
+
params[:match_type] = "regex" if options[:regex]
|
|
112
|
+
params
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def parse_time_option(value)
|
|
116
|
+
return nil if value.nil?
|
|
117
|
+
|
|
118
|
+
if (match = value.match(RELATIVE_TIME_PATTERN))
|
|
119
|
+
amount = match[1].to_i
|
|
120
|
+
unit = match[2]
|
|
121
|
+
seconds = case unit
|
|
122
|
+
when "m" then amount * 60
|
|
123
|
+
when "h" then amount * 3600
|
|
124
|
+
when "d" then amount * 86400
|
|
125
|
+
end
|
|
126
|
+
Time.now - seconds
|
|
127
|
+
else
|
|
128
|
+
Time.parse(value)
|
|
129
|
+
end
|
|
130
|
+
rescue ArgumentError
|
|
131
|
+
nil
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# --- Live streaming mode ---
|
|
135
|
+
|
|
136
|
+
def process_live!
|
|
137
|
+
start_spinner("Starting live stream session")
|
|
138
|
+
print_github_repo_name
|
|
139
|
+
response = fetch_logs
|
|
140
|
+
@spinner.stop
|
|
141
|
+
|
|
142
|
+
unless response.success?
|
|
143
|
+
ui.error(response["error"] || response.message)
|
|
144
|
+
return
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
data = JSON.parse(response.body)
|
|
148
|
+
|
|
149
|
+
unless data["configured"]
|
|
150
|
+
ui.error("Logs are not configured for this app.")
|
|
151
|
+
return
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Initial batch returned DESC — reverse for chronological display
|
|
155
|
+
logs = data["logs"].reverse
|
|
156
|
+
logs.each { |log| print_log(log) }
|
|
157
|
+
# Subtract 10s buffer to account for potential client/server clock skew
|
|
158
|
+
@last_timestamp_ms = logs.any? ? logs.last["timestamp"] : ((Time.now.to_f - 10) * 1000).to_i
|
|
159
|
+
|
|
160
|
+
poll_for_logs
|
|
161
|
+
end
|
|
57
162
|
|
|
58
163
|
def fetch_logs(after_timestamp_ms: nil)
|
|
59
164
|
params = { app_slug:, process_type:, limit: INITIAL_LIMIT }
|
|
@@ -112,8 +217,8 @@ module NeetoDeploy
|
|
|
112
217
|
Time.at(ms / 1000.0).utc.iso8601(3)
|
|
113
218
|
end
|
|
114
219
|
|
|
115
|
-
def start_spinner
|
|
116
|
-
@spinner = TTY::Spinner.new("
|
|
220
|
+
def start_spinner(message = "Loading")
|
|
221
|
+
@spinner = TTY::Spinner.new("#{message} [:spinner]", format: :classic)
|
|
117
222
|
@spinner.auto_spin
|
|
118
223
|
end
|
|
119
224
|
end
|
data/lib/neeto_deploy/cli.rb
CHANGED
|
@@ -50,11 +50,16 @@ module NeetoDeploy
|
|
|
50
50
|
CLI::Exec::Base.new(options[:app]).process!
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
desc "logs", "Show logs"
|
|
53
|
+
desc "logs", "Show logs (live stream or historic)"
|
|
54
54
|
option :app, type: :string, aliases: "-a", required: true, desc: "App slug"
|
|
55
55
|
option :process_type, type: :string, aliases: "-p", desc: "Process type (optional)"
|
|
56
|
+
option :from, type: :string, desc: "Start time for historic logs (e.g. '2024-01-15T10:30:00Z' or '2h' for 2 hours ago)"
|
|
57
|
+
option :to, type: :string, desc: "End time for historic logs (e.g. '2024-01-15T12:30:00Z', defaults to now)"
|
|
58
|
+
option :search, type: :string, aliases: "-s", desc: "Search filter for log messages"
|
|
59
|
+
option :regex, type: :boolean, default: false, desc: "Treat search as regex pattern"
|
|
60
|
+
option :limit, type: :numeric, aliases: "-n", desc: "Max number of logs to fetch (default: 500, max: 2000)"
|
|
56
61
|
def logs
|
|
57
|
-
CLI::Logs::Base.new(options
|
|
62
|
+
CLI::Logs::Base.new(options).process!
|
|
58
63
|
end
|
|
59
64
|
|
|
60
65
|
desc "apps", "Show app details"
|
data/lib/neeto_deploy/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: neetodeploy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.4.
|
|
4
|
+
version: 1.4.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Subin Siby
|
|
@@ -297,7 +297,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
297
297
|
- !ruby/object:Gem::Version
|
|
298
298
|
version: '0'
|
|
299
299
|
requirements: []
|
|
300
|
-
rubygems_version:
|
|
300
|
+
rubygems_version: 3.6.9
|
|
301
301
|
specification_version: 4
|
|
302
302
|
summary: CLI for neetoDeploy
|
|
303
303
|
test_files: []
|