sktop 0.1.0

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.
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sktop
4
+ module JobActions
5
+ class << self
6
+ def retry_job(jid, source)
7
+ job = find_job(jid, source)
8
+ raise "Job not found (JID: #{jid})" unless job
9
+
10
+ job.retry
11
+ true
12
+ end
13
+
14
+ def delete_job(jid, source)
15
+ job = find_job(jid, source)
16
+ raise "Job not found (JID: #{jid})" unless job
17
+
18
+ job.delete
19
+ true
20
+ end
21
+
22
+ def kill_job(jid, source)
23
+ job = find_job(jid, source)
24
+ raise "Job not found (JID: #{jid})" unless job
25
+
26
+ job.kill
27
+ true
28
+ end
29
+
30
+ def retry_all(source)
31
+ set = get_set(source)
32
+ count = set.size
33
+ set.retry_all
34
+ count
35
+ end
36
+
37
+ def delete_all(source)
38
+ set = get_set(source)
39
+ count = set.size
40
+ set.clear
41
+ count
42
+ end
43
+
44
+ def quiet_process(identity)
45
+ process = find_process(identity)
46
+ raise "Process not found (identity: #{identity})" unless process
47
+
48
+ process.quiet!
49
+ true
50
+ end
51
+
52
+ def stop_process(identity)
53
+ process = find_process(identity)
54
+ raise "Process not found (identity: #{identity})" unless process
55
+
56
+ process.stop!
57
+ true
58
+ end
59
+
60
+ private
61
+
62
+ def get_set(source)
63
+ case source
64
+ when :retry
65
+ Sidekiq::RetrySet.new
66
+ when :dead
67
+ Sidekiq::DeadSet.new
68
+ when :scheduled
69
+ Sidekiq::ScheduledSet.new
70
+ else
71
+ raise "Unknown source: #{source}"
72
+ end
73
+ end
74
+
75
+ def find_job(jid, source)
76
+ set = get_set(source)
77
+
78
+ # Try find_job first (available in newer Sidekiq versions)
79
+ if set.respond_to?(:find_job)
80
+ job = set.find_job(jid)
81
+ return job if job
82
+ end
83
+
84
+ # Fall back to iterating through the set
85
+ set.each do |entry|
86
+ return entry if entry.jid == jid
87
+ end
88
+
89
+ nil
90
+ end
91
+
92
+ def find_process(identity)
93
+ Sidekiq::ProcessSet.new.find { |p| p["identity"] == identity }
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sktop
4
+ class StatsCollector
5
+ def initialize
6
+ @stats = Sidekiq::Stats.new
7
+ end
8
+
9
+ def refresh!
10
+ @stats = Sidekiq::Stats.new
11
+ self
12
+ end
13
+
14
+ def overview
15
+ {
16
+ processed: @stats.processed,
17
+ failed: @stats.failed,
18
+ scheduled_size: @stats.scheduled_size,
19
+ retry_size: @stats.retry_size,
20
+ dead_size: @stats.dead_size,
21
+ enqueued: @stats.enqueued,
22
+ default_queue_latency: @stats.default_queue_latency
23
+ }
24
+ end
25
+
26
+ def queues
27
+ Sidekiq::Queue.all.map do |queue|
28
+ {
29
+ name: queue.name,
30
+ size: queue.size,
31
+ latency: queue.latency,
32
+ paused: queue.paused?
33
+ }
34
+ end
35
+ end
36
+
37
+ def processes
38
+ Sidekiq::ProcessSet.new.map do |process|
39
+ # Use direct hash access for status flags - the method accessors
40
+ # like stopping? can have broader semantics in some Sidekiq versions
41
+ quiet_val = process["quiet"]
42
+ stopping_val = process["stopping"]
43
+
44
+ quiet_flag = quiet_val == true || quiet_val == "true"
45
+ stopping_flag = stopping_val == true || stopping_val == "true"
46
+
47
+ {
48
+ identity: process["identity"],
49
+ hostname: process["hostname"],
50
+ pid: process["pid"],
51
+ tag: process["tag"],
52
+ started_at: Time.at(process["started_at"]),
53
+ concurrency: process["concurrency"],
54
+ busy: process["busy"],
55
+ queues: process["queues"],
56
+ labels: process["labels"] || [],
57
+ quiet: quiet_flag,
58
+ stopping: stopping_flag,
59
+ rss: process["rss"] # Memory in KB
60
+ }
61
+ end
62
+ end
63
+
64
+ def workers
65
+ Sidekiq::Workers.new.map do |process_id, thread_id, work|
66
+ {
67
+ process_id: process_id,
68
+ thread_id: thread_id,
69
+ queue: work["queue"],
70
+ class: work["payload"]["class"],
71
+ args: work["payload"]["args"],
72
+ run_at: Time.at(work["run_at"]),
73
+ elapsed: Time.now - Time.at(work["run_at"])
74
+ }
75
+ end
76
+ end
77
+
78
+ def scheduled_jobs(limit: 10)
79
+ Sidekiq::ScheduledSet.new.first(limit).map do |job|
80
+ {
81
+ class: job.klass,
82
+ queue: job.queue,
83
+ args: job.args,
84
+ scheduled_at: job.at,
85
+ created_at: job.created_at
86
+ }
87
+ end
88
+ end
89
+
90
+ def retry_jobs(limit: 10)
91
+ Sidekiq::RetrySet.new.first(limit).map do |job|
92
+ {
93
+ jid: job.jid,
94
+ class: job.klass,
95
+ queue: job.queue,
96
+ args: job.args,
97
+ failed_at: job["failed_at"] ? Time.at(job["failed_at"]) : nil,
98
+ retry_count: job["retry_count"],
99
+ error_class: job["error_class"],
100
+ error_message: job["error_message"]
101
+ }
102
+ end
103
+ end
104
+
105
+ def dead_jobs(limit: 10)
106
+ Sidekiq::DeadSet.new.first(limit).map do |job|
107
+ {
108
+ jid: job.jid,
109
+ class: job.klass,
110
+ queue: job.queue,
111
+ args: job.args,
112
+ failed_at: job["failed_at"] ? Time.at(job["failed_at"]) : nil,
113
+ error_class: job["error_class"],
114
+ error_message: job["error_message"]
115
+ }
116
+ end
117
+ end
118
+
119
+ def history(days: 7)
120
+ stats_history = Sidekiq::Stats::History.new(days)
121
+ {
122
+ processed: stats_history.processed,
123
+ failed: stats_history.failed
124
+ }
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sktop
4
+ VERSION = "0.1.0"
5
+ end
data/lib/sktop.rb ADDED
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sidekiq"
4
+ require "sidekiq/api"
5
+ require "terminal-table"
6
+ require "pastel"
7
+ require "tty-cursor"
8
+ require "tty-screen"
9
+
10
+ require_relative "sktop/version"
11
+ require_relative "sktop/stats_collector"
12
+ require_relative "sktop/job_actions"
13
+ require_relative "sktop/display"
14
+ require_relative "sktop/cli"
15
+
16
+ module Sktop
17
+ class Error < StandardError; end
18
+
19
+ class << self
20
+ def configure_redis(url: nil, namespace: nil)
21
+ options = {}
22
+ options[:url] = url if url
23
+
24
+ Sidekiq.configure_client do |config|
25
+ config.redis = options
26
+ end
27
+ end
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sktop
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - James
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-01-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sidekiq
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: redis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: terminal-table
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pastel
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.8'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: tty-cursor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.7'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: tty-screen
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.8'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.8'
97
+ description: A terminal-based dashboard for monitoring Sidekiq, similar to the web
98
+ UI but for the command line
99
+ email:
100
+ - james@example.com
101
+ executables:
102
+ - sktop
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - README.md
107
+ - bin/sktop
108
+ - lib/sktop.rb
109
+ - lib/sktop/cli.rb
110
+ - lib/sktop/display.rb
111
+ - lib/sktop/job_actions.rb
112
+ - lib/sktop/stats_collector.rb
113
+ - lib/sktop/version.rb
114
+ homepage: https://github.com/james/sktop
115
+ licenses:
116
+ - MIT
117
+ metadata: {}
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: 2.7.0
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubygems_version: 3.5.16
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: CLI tool to monitor Sidekiq queues and processes
137
+ test_files: []