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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 518719f22d6967ef7bc0acde463bad118be36bb2b4e0e8de6b28cb7f91110dd6
4
- data.tar.gz: a08a4c651fde80dc3a348c517d3f43c415c985340395338fc878ee0704a14e62
3
+ metadata.gz: b449b4f69647cfff5f812dfa30c273b1add6b8dfdeb60ca419d96be75a768d37
4
+ data.tar.gz: dcebc5fdb56c81f76973f502a0491ddd640b4813a8c969f5fbb07db3df596264
5
5
  SHA512:
6
- metadata.gz: a7280eab15a0a7495a4ff364df2cc4647f340f4232488fe3231c6bda2b8ada04eed3e9a2050e90740e3495ff6e7ae124ec34abcbaa6a755fb16f32652d7da063
7
- data.tar.gz: 84600455e6d2cc3aa9e4205399f62b10a26fe58b7a49d94ccc6e7f18a6185639acd88484d8c6aca65f65d4044b5335d5f62cf01d57cb5d16807dccbe30179b37
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
- attr_reader :app_slug, :process_type
22
+ RELATIVE_TIME_PATTERN = /\A(\d+)(m|h|d)\z/
21
23
 
22
- def initialize(app_slug, process_type = nil)
24
+ attr_reader :app_slug, :process_type, :options
25
+
26
+ def initialize(options)
23
27
  super()
24
- @app_slug = app_slug
25
- @process_type = process_type
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
- start_spinner
31
- print_github_repo_name
32
- response = fetch_logs
33
- @spinner.stop
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
- data = JSON.parse(response.body)
42
+ private
41
43
 
42
- unless data["configured"]
43
- ui.error("Logs are not configured for this app.")
44
- return
44
+ def historic_mode?
45
+ options[:from] && !options[:from].empty?
45
46
  end
46
47
 
47
- # Initial batch returned DESC — reverse for chronological display
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
- poll_for_logs
54
- end
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
- private
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("Starting live stream session [:spinner]", format: :classic)
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
@@ -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[:app], options[:process_type]).process!
62
+ CLI::Logs::Base.new(options).process!
58
63
  end
59
64
 
60
65
  desc "apps", "Show app details"
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NeetoDeploy
4
- VERSION = "1.4.2"
4
+ VERSION = "1.4.3"
5
5
  CLI_API_VERSION = "v1"
6
6
  end
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.2
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: 4.0.3
300
+ rubygems_version: 3.6.9
301
301
  specification_version: 4
302
302
  summary: CLI for neetoDeploy
303
303
  test_files: []