neetodeploy 1.4.2 → 1.4.4

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: 56b531cfa4655cdd685d298c4e26a83594b12ce21b0cb11ea6586f72e6f86e00
4
+ data.tar.gz: 1350e1efc7f35ec1589c4b3c4a899d6fbf0e7ca5fe0df1c9aed5dcb086b61f08
5
5
  SHA512:
6
- metadata.gz: a7280eab15a0a7495a4ff364df2cc4647f340f4232488fe3231c6bda2b8ada04eed3e9a2050e90740e3495ff6e7ae124ec34abcbaa6a755fb16f32652d7da063
7
- data.tar.gz: 84600455e6d2cc3aa9e4205399f62b10a26fe58b7a49d94ccc6e7f18a6185639acd88484d8c6aca65f65d4044b5335d5f62cf01d57cb5d16807dccbe30179b37
6
+ metadata.gz: 304a6b67c67c2e982dc98752f5810a05025df9ce02d359c88d17f43d89d8c509bf9570564bbc1403cf22f55f62286a071440a22466512281f17f719290612214
7
+ data.tar.gz: 67de2e22263af2af6f9d10e85ba8281c00cdb08f3b675efacaa374f9e9fd1f081f20bf0962f288ac93cc491bb10dd42a715771765c87a8017d48f299867151c5
@@ -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,151 @@ 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
+ all_logs = []
67
+ limit = options[:limit] || HISTORIC_DEFAULT_LIMIT
68
+ before_timestamp = nil
69
+
70
+ loop do
71
+ remaining = limit - all_logs.size
72
+ params = build_historic_params(from_time, to_time, before_timestamp, remaining)
73
+ response = send_get_request(logs_v2_url, params)
74
+ @spinner.stop if @spinner
75
+
76
+ unless response.success?
77
+ ui.error(response["error"] || response.message)
78
+ return
79
+ end
80
+
81
+ data = JSON.parse(response.body)
82
+
83
+ unless data["configured"]
84
+ ui.error("Logs are not configured for this app.")
85
+ return
86
+ end
87
+
88
+ logs = data["logs"]
89
+ break if logs.empty?
90
+
91
+ all_logs.concat(logs)
92
+
93
+ break if all_logs.size >= limit
94
+ break unless data["has_more"]
95
+
96
+ before_timestamp = ms_to_iso8601(logs.last["timestamp"])
97
+ end
98
+
99
+ if all_logs.empty?
100
+ ui.info("No logs found for the given time range.")
101
+ return
102
+ end
103
+
104
+ # Backward pagination fetches newer-to-older — reverse for chronological display
105
+ all_logs.reverse.each { |log| print_log(log) }
106
+ ui.info("Fetched #{all_logs.size} log entries.")
107
+ end
108
+
109
+ def build_historic_params(from_time, to_time, before_timestamp, limit)
110
+ params = { app_slug:, process_type:, from_time: from_time.utc.iso8601(3), limit: }
111
+ params[:to_time] = to_time.utc.iso8601(3) if to_time
112
+ params[:before_timestamp] = before_timestamp if before_timestamp
113
+ params[:search] = options[:search] if options[:search]
114
+ params[:match_type] = "regex" if options[:regex]
115
+ params
116
+ end
117
+
118
+ def parse_time_option(value)
119
+ return nil if value.nil?
120
+
121
+ if (match = value.match(RELATIVE_TIME_PATTERN))
122
+ amount = match[1].to_i
123
+ unit = match[2]
124
+ seconds = case unit
125
+ when "m" then amount * 60
126
+ when "h" then amount * 3600
127
+ when "d" then amount * 86400
128
+ end
129
+ Time.now - seconds
130
+ else
131
+ Time.parse(value)
132
+ end
133
+ rescue ArgumentError
134
+ nil
135
+ end
136
+
137
+ # --- Live streaming mode ---
138
+
139
+ def process_live!
140
+ start_spinner("Starting live stream session")
141
+ print_github_repo_name
142
+ response = fetch_logs
143
+ @spinner.stop
144
+
145
+ unless response.success?
146
+ ui.error(response["error"] || response.message)
147
+ return
148
+ end
149
+
150
+ data = JSON.parse(response.body)
151
+
152
+ unless data["configured"]
153
+ ui.error("Logs are not configured for this app.")
154
+ return
155
+ end
156
+
157
+ # Initial batch returned DESC — reverse for chronological display
158
+ logs = data["logs"].reverse
159
+ logs.each { |log| print_log(log) }
160
+ # Subtract 10s buffer to account for potential client/server clock skew
161
+ @last_timestamp_ms = logs.any? ? logs.last["timestamp"] : ((Time.now.to_f - 10) * 1000).to_i
162
+
163
+ poll_for_logs
164
+ end
57
165
 
58
166
  def fetch_logs(after_timestamp_ms: nil)
59
167
  params = { app_slug:, process_type:, limit: INITIAL_LIMIT }
@@ -112,8 +220,8 @@ module NeetoDeploy
112
220
  Time.at(ms / 1000.0).utc.iso8601(3)
113
221
  end
114
222
 
115
- def start_spinner
116
- @spinner = TTY::Spinner.new("Starting live stream session [:spinner]", format: :classic)
223
+ def start_spinner(message = "Loading")
224
+ @spinner = TTY::Spinner.new("#{message} [:spinner]", format: :classic)
117
225
  @spinner.auto_spin
118
226
  end
119
227
  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.4"
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.4
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: []