jobtick 0.0.1 → 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.
- checksums.yaml +4 -4
- data/README.md +123 -18
- data/Rakefile +3 -3
- data/jobtick-0.0.1.gem +0 -0
- data/lib/jobtick/client.rb +50 -0
- data/lib/jobtick/configuration.rb +13 -0
- data/lib/jobtick/monitor.rb +19 -0
- data/lib/jobtick/parsers/sidekiq.rb +48 -0
- data/lib/jobtick/parsers/solid_queue.rb +35 -0
- data/lib/jobtick/parsers/whenever.rb +36 -0
- data/lib/jobtick/railtie.rb +17 -0
- data/lib/jobtick/registry.rb +18 -0
- data/lib/jobtick/version.rb +2 -2
- data/lib/jobtick.rb +33 -2
- data/lib/tasks/jobtick.rake +35 -0
- metadata +30 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e01eac085feb7343b0410a4d7f4de783df293e27853ac8c1aae3428db83975b3
|
|
4
|
+
data.tar.gz: 76beab20811235bf0f47bd36015b3185d1b9515fb2d28fe9e465e518653481ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d3107ba0af4b2a1eb40ee86ccfd4ca92e08852467cdc7d0879b9ce772ba2ebf238c116759af69a7084f97513b441453e4920aa9e4f9ed63a5b67efaa9bf1b501
|
|
7
|
+
data.tar.gz: 3787f52f4ce0a05a6c78c4af25377526d51a4b8a0fa8e829917a308b51fe8e1c3ba63034bd24983a4f7a30f7fe2fa62c45b5bb761571a26987049e5b0294369d
|
data/README.md
CHANGED
|
@@ -1,39 +1,144 @@
|
|
|
1
|
-
#
|
|
1
|
+
# JobTick
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Rails job monitoring for Whenever, Solid Queue, and Sidekiq.**
|
|
4
|
+
Know when your scheduled jobs stop running — before your users do.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## The problem
|
|
9
|
+
|
|
10
|
+
Your error monitor catches exceptions. Mission Control shows you failed jobs.
|
|
11
|
+
Neither tells you when a recurring job **simply stops running**.
|
|
12
|
+
|
|
13
|
+
No exception. No alert. Nothing.
|
|
14
|
+
|
|
15
|
+
The Solid Queue scheduler process dies at 2am. Your nightly invoice job hasn't run in three days. Your users notice before you do.
|
|
16
|
+
|
|
17
|
+
JobTick solves this. It watches every scheduled job in your Rails app and alerts you the moment one goes silent — even when no error is raised.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Why JobTick over Healthchecks.io or Cronitor?
|
|
22
|
+
|
|
23
|
+
Those tools work — but they require you to manually add a heartbeat ping to every job. Touch 15 job files. Name each monitor by hand. Keep names in sync when jobs are renamed or removed.
|
|
24
|
+
|
|
25
|
+
JobTick reads your existing config files and registers everything automatically.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
config/schedule.rb → Whenever jobs, auto-discovered
|
|
29
|
+
config/recurring.yml → Solid Queue recurring tasks, auto-discovered
|
|
30
|
+
sidekiq.yml → Sidekiq periodic jobs, auto-discovered
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Add the gem. Add your API key. Deploy. Done.
|
|
34
|
+
|
|
35
|
+
---
|
|
6
36
|
|
|
7
37
|
## Installation
|
|
8
38
|
|
|
9
|
-
|
|
39
|
+
Add to your Gemfile:
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
gem 'jobtick'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Create an initializer:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
# config/initializers/jobtick.rb
|
|
49
|
+
JobTick.configure do |config|
|
|
50
|
+
config.api_key = ENV['JOBTICK_API_KEY']
|
|
51
|
+
end
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
That's it. On next deploy, JobTick reads your schedule config, registers a monitor for every job, and starts tracking.
|
|
10
55
|
|
|
11
|
-
|
|
56
|
+
No changes to individual job files. No manual monitor creation. No names to keep in sync.
|
|
12
57
|
|
|
13
|
-
|
|
14
|
-
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## What gets monitored
|
|
61
|
+
|
|
62
|
+
### Whenever (`config/schedule.rb`)
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
# Your existing schedule.rb — no changes needed
|
|
66
|
+
every 1.day, at: '2:00 am' do
|
|
67
|
+
runner 'InvoiceJob.perform_later'
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
every :hour do
|
|
71
|
+
runner 'SyncInventoryJob.perform_later'
|
|
72
|
+
end
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
JobTick reads this file at deploy time and creates a monitor for each job automatically.
|
|
76
|
+
|
|
77
|
+
### Solid Queue (`config/recurring.yml`)
|
|
78
|
+
|
|
79
|
+
```yaml
|
|
80
|
+
# Your existing recurring.yml — no changes needed
|
|
81
|
+
nightly_report:
|
|
82
|
+
class: NightlyReportJob
|
|
83
|
+
schedule: every day at 3am
|
|
84
|
+
|
|
85
|
+
sync_exchange_rates:
|
|
86
|
+
class: ExchangeRateJob
|
|
87
|
+
schedule: every hour
|
|
15
88
|
```
|
|
16
89
|
|
|
17
|
-
If
|
|
90
|
+
Each entry becomes a monitor. If `NightlyReportJob` doesn't run within its expected window, you get an alert.
|
|
91
|
+
|
|
92
|
+
### Sidekiq periodic jobs
|
|
18
93
|
|
|
19
|
-
```
|
|
20
|
-
|
|
94
|
+
```ruby
|
|
95
|
+
# Your existing Sidekiq config — no changes needed
|
|
96
|
+
Sidekiq.configure_server do |config|
|
|
97
|
+
config.periodic do |mgr|
|
|
98
|
+
mgr.register('0 * * * *', HourlyDigestWorker)
|
|
99
|
+
mgr.register('0 2 * * *', NightlyCleanupWorker)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
21
102
|
```
|
|
22
103
|
|
|
23
|
-
|
|
104
|
+
---
|
|
24
105
|
|
|
25
|
-
|
|
106
|
+
## What you get
|
|
26
107
|
|
|
27
|
-
|
|
108
|
+
**Silent failure detection** — alerts when a job stops running entirely, not just when it raises an exception. The failure mode your error monitor misses.
|
|
28
109
|
|
|
29
|
-
|
|
110
|
+
**Auto-sync on deploy** — add a job to your schedule, it appears in your dashboard at next deploy. Remove a job, its monitor is automatically retired.
|
|
30
111
|
|
|
31
|
-
|
|
112
|
+
**Run history** — see every execution: start time, duration, exit status. Spot when a job starts getting slower before it becomes a problem.
|
|
32
113
|
|
|
33
|
-
|
|
114
|
+
**Maintenance windows** — deploying at 3am? Snooze any monitor for a set period so you don't get paged for expected downtime.
|
|
34
115
|
|
|
35
|
-
|
|
116
|
+
**Team alerts** — email and Slack notifications. On-call rotation support on higher plans.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Requirements
|
|
121
|
+
|
|
122
|
+
- Ruby >= 3.2
|
|
123
|
+
- Rails >= 7.0
|
|
124
|
+
- One or more of: Whenever, Solid Queue, Sidekiq
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Status
|
|
129
|
+
|
|
130
|
+
> **JobTick is currently in development.**
|
|
131
|
+
> Sign up for early access at [jobtick.app](https://jobtick.app).
|
|
132
|
+
> Launching June 2026.
|
|
133
|
+
|
|
134
|
+
If you want to follow along or give early feedback, open an issue or watch the repo.
|
|
135
|
+
|
|
136
|
+
---
|
|
36
137
|
|
|
37
138
|
## License
|
|
38
139
|
|
|
39
|
-
|
|
140
|
+
MIT. See [LICENSE](LICENSE).
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
*Built by [Clearstack Labs](https://clearstacklabs.com)*
|
data/Rakefile
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
|
-
require "
|
|
4
|
+
require "rspec/core/rake_task"
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
7
7
|
|
|
8
8
|
require "rubocop/rake_task"
|
|
9
9
|
|
|
10
10
|
RuboCop::RakeTask.new
|
|
11
11
|
|
|
12
|
-
task default: %i[
|
|
12
|
+
task default: %i[spec rubocop]
|
data/jobtick-0.0.1.gem
ADDED
|
Binary file
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "uri"
|
|
6
|
+
|
|
7
|
+
module JobTick
|
|
8
|
+
class Client
|
|
9
|
+
TIMEOUT = 5
|
|
10
|
+
|
|
11
|
+
def ping(monitor_key, status:, duration: nil, message: nil)
|
|
12
|
+
return unless JobTick.config.enabled
|
|
13
|
+
return if JobTick.config.api_key.nil?
|
|
14
|
+
|
|
15
|
+
payload = { status: status }
|
|
16
|
+
payload[:duration] = duration.round(3) if duration
|
|
17
|
+
payload[:message] = message if message
|
|
18
|
+
|
|
19
|
+
post("/ping/#{monitor_key}", payload)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def register(monitors)
|
|
23
|
+
return unless JobTick.config.enabled
|
|
24
|
+
return if JobTick.config.api_key.nil?
|
|
25
|
+
|
|
26
|
+
post("/monitors/sync", { monitors: monitors })
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def post(path, body)
|
|
32
|
+
uri = URI("#{JobTick.config.endpoint}#{path}")
|
|
33
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
34
|
+
http.use_ssl = uri.scheme == "https"
|
|
35
|
+
http.open_timeout = TIMEOUT
|
|
36
|
+
http.read_timeout = TIMEOUT
|
|
37
|
+
|
|
38
|
+
request = Net::HTTP::Post.new(uri)
|
|
39
|
+
request["Content-Type"] = "application/json"
|
|
40
|
+
request["Authorization"] = "Bearer #{JobTick.config.api_key}"
|
|
41
|
+
request["User-Agent"] = "jobtick-ruby/#{JobTick::VERSION}"
|
|
42
|
+
request.body = body.to_json
|
|
43
|
+
|
|
44
|
+
http.request(request)
|
|
45
|
+
rescue StandardError => e
|
|
46
|
+
JobTick.logger.warn("[JobTick] HTTP request failed (#{path}): #{e.message}")
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JobTick
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :api_key, :endpoint, :environment, :enabled
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@endpoint = "https://api.jobtick.app/v1"
|
|
9
|
+
@environment = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "production"
|
|
10
|
+
@enabled = @environment == "production"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JobTick
|
|
4
|
+
class Monitor
|
|
5
|
+
def self.run(key)
|
|
6
|
+
return yield unless JobTick.config.enabled
|
|
7
|
+
|
|
8
|
+
started_at = Time.now
|
|
9
|
+
JobTick.client.ping(key, status: :started)
|
|
10
|
+
result = yield
|
|
11
|
+
duration = Time.now - started_at
|
|
12
|
+
JobTick.client.ping(key, status: :completed, duration: duration)
|
|
13
|
+
result
|
|
14
|
+
rescue StandardError => e
|
|
15
|
+
JobTick.client.ping(key, status: :failed, message: e.message)
|
|
16
|
+
raise
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JobTick
|
|
4
|
+
module Parsers
|
|
5
|
+
class Sidekiq
|
|
6
|
+
def self.parse
|
|
7
|
+
return [] unless defined?(::Sidekiq)
|
|
8
|
+
return parse_cron_jobs if defined?(::Sidekiq::Cron::Job)
|
|
9
|
+
return parse_periodic_jobs if defined?(::Sidekiq::Periodic::LoopSet)
|
|
10
|
+
|
|
11
|
+
[]
|
|
12
|
+
rescue StandardError => e
|
|
13
|
+
JobTick.logger.warn("[JobTick] Sidekiq parser failed: #{e.message}")
|
|
14
|
+
[]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.parse_cron_jobs
|
|
18
|
+
::Sidekiq::Cron::Job.all.map do |job|
|
|
19
|
+
{ key: "sidekiq.#{slugify(job.name)}", schedule: job.cron, source: "sidekiq", task: job.klass }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
private_class_method :parse_cron_jobs
|
|
23
|
+
|
|
24
|
+
def self.parse_periodic_jobs
|
|
25
|
+
periodic = sidekiq_periodic_config
|
|
26
|
+
(periodic || []).map do |klass, opts|
|
|
27
|
+
{ key: "sidekiq.#{slugify(klass.to_s)}", schedule: opts[:cron] || opts[:every].to_s,
|
|
28
|
+
source: "sidekiq", task: klass.to_s }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
private_class_method :parse_periodic_jobs
|
|
32
|
+
|
|
33
|
+
def self.slugify(str)
|
|
34
|
+
str.downcase.gsub(/[^a-z0-9]+/, "_").gsub(/\A_+|_+\z/, "")
|
|
35
|
+
end
|
|
36
|
+
private_class_method :slugify
|
|
37
|
+
|
|
38
|
+
def self.sidekiq_periodic_config
|
|
39
|
+
if ::Sidekiq.respond_to?(:default_configuration)
|
|
40
|
+
::Sidekiq.default_configuration[:periodic]
|
|
41
|
+
else
|
|
42
|
+
::Sidekiq.options[:periodic]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
private_class_method :sidekiq_periodic_config
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module JobTick
|
|
6
|
+
module Parsers
|
|
7
|
+
class SolidQueue
|
|
8
|
+
RECURRING_FILE = "config/recurring.yml"
|
|
9
|
+
|
|
10
|
+
def self.parse
|
|
11
|
+
return [] unless File.exist?(RECURRING_FILE)
|
|
12
|
+
|
|
13
|
+
yaml = YAML.load_file(RECURRING_FILE, aliases: true)
|
|
14
|
+
env = JobTick.config.environment
|
|
15
|
+
|
|
16
|
+
tasks = yaml[env] || yaml["default"] || yaml
|
|
17
|
+
return [] unless tasks.is_a?(Hash)
|
|
18
|
+
|
|
19
|
+
tasks.map do |key, config|
|
|
20
|
+
next unless config.is_a?(Hash)
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
key: "solid_queue.#{key}",
|
|
24
|
+
schedule: config["schedule"],
|
|
25
|
+
source: "solid_queue",
|
|
26
|
+
task: config["class"]
|
|
27
|
+
}
|
|
28
|
+
end.compact
|
|
29
|
+
rescue StandardError => e
|
|
30
|
+
JobTick.logger.warn("[JobTick] Solid Queue parser failed: #{e.message}")
|
|
31
|
+
[]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JobTick
|
|
4
|
+
module Parsers
|
|
5
|
+
class Whenever
|
|
6
|
+
SCHEDULE_FILE = "config/schedule.rb"
|
|
7
|
+
|
|
8
|
+
def self.parse
|
|
9
|
+
return [] unless defined?(::Whenever)
|
|
10
|
+
return [] unless File.exist?(SCHEDULE_FILE)
|
|
11
|
+
|
|
12
|
+
schedule = ::Whenever::JobList.new(file: SCHEDULE_FILE)
|
|
13
|
+
schedule.jobs.flat_map do |period, jobs|
|
|
14
|
+
jobs.map do |job|
|
|
15
|
+
{
|
|
16
|
+
key: job_key(job),
|
|
17
|
+
schedule: period.to_s,
|
|
18
|
+
source: "whenever",
|
|
19
|
+
task: job[:task].to_s.strip
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
rescue StandardError => e
|
|
24
|
+
JobTick.logger.warn("[JobTick] Whenever parser failed: #{e.message}")
|
|
25
|
+
[]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.job_key(job)
|
|
29
|
+
task = job[:task].to_s.strip
|
|
30
|
+
slug = task.downcase.gsub(/[^a-z0-9]+/, "_").gsub(/\A_+|_+\z/, "")
|
|
31
|
+
"whenever.#{slug}"
|
|
32
|
+
end
|
|
33
|
+
private_class_method :job_key
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JobTick
|
|
4
|
+
class Railtie < Rails::Railtie
|
|
5
|
+
initializer "jobtick.sync_registry" do
|
|
6
|
+
ActiveSupport.on_load(:after_initialize) do
|
|
7
|
+
next unless JobTick.config.enabled
|
|
8
|
+
|
|
9
|
+
JobTick::Registry.sync
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
rake_tasks do
|
|
14
|
+
load File.expand_path("../tasks/jobtick.rake", __dir__)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JobTick
|
|
4
|
+
class Registry
|
|
5
|
+
def self.sync
|
|
6
|
+
monitors = [
|
|
7
|
+
Parsers::Whenever.parse,
|
|
8
|
+
Parsers::SolidQueue.parse,
|
|
9
|
+
Parsers::Sidekiq.parse
|
|
10
|
+
].flatten.compact
|
|
11
|
+
|
|
12
|
+
return [] if monitors.empty?
|
|
13
|
+
|
|
14
|
+
JobTick.client.register(monitors)
|
|
15
|
+
monitors
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/jobtick/version.rb
CHANGED
data/lib/jobtick.rb
CHANGED
|
@@ -1,8 +1,39 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "logger"
|
|
3
4
|
require_relative "jobtick/version"
|
|
5
|
+
require_relative "jobtick/configuration"
|
|
6
|
+
require_relative "jobtick/client"
|
|
7
|
+
require_relative "jobtick/monitor"
|
|
8
|
+
require_relative "jobtick/parsers/whenever"
|
|
9
|
+
require_relative "jobtick/parsers/solid_queue"
|
|
10
|
+
require_relative "jobtick/parsers/sidekiq"
|
|
11
|
+
require_relative "jobtick/registry"
|
|
12
|
+
require_relative "jobtick/railtie" if defined?(Rails::Railtie)
|
|
4
13
|
|
|
5
|
-
module
|
|
14
|
+
module JobTick
|
|
6
15
|
class Error < StandardError; end
|
|
7
|
-
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def configure
|
|
19
|
+
yield config
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def config
|
|
23
|
+
@config ||= Configuration.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def client
|
|
27
|
+
@client ||= Client.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def logger
|
|
31
|
+
defined?(Rails) ? Rails.logger : Logger.new($stdout)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def reset!
|
|
35
|
+
@config = nil
|
|
36
|
+
@client = nil
|
|
37
|
+
end
|
|
38
|
+
end
|
|
8
39
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :jobtick do
|
|
4
|
+
desc "Sync discovered jobs with jobtick.app"
|
|
5
|
+
task sync: :environment do
|
|
6
|
+
monitors = JobTick::Registry.sync
|
|
7
|
+
count = monitors&.length || 0
|
|
8
|
+
puts "[JobTick] Synced #{count} monitor(s)"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
namespace :whenever do
|
|
12
|
+
desc "Print Whenever job_type wrappers to add to config/schedule.rb for heartbeat injection"
|
|
13
|
+
task :setup do
|
|
14
|
+
endpoint = JobTick.config.endpoint
|
|
15
|
+
puts <<~RUBY
|
|
16
|
+
# Add to config/schedule.rb to enable JobTick heartbeat injection for Whenever jobs:
|
|
17
|
+
|
|
18
|
+
job_type :jobtick_runner, %(curl -sf "#{endpoint}/ping/:monitor_key/started" ; ) \\
|
|
19
|
+
%(bundle exec rails runner ':task' :output && ) \\
|
|
20
|
+
%(curl -sf "#{endpoint}/ping/:monitor_key/completed" || ) \\
|
|
21
|
+
%(curl -sf "#{endpoint}/ping/:monitor_key/failed")
|
|
22
|
+
|
|
23
|
+
job_type :jobtick_rake, %(curl -sf "#{endpoint}/ping/:monitor_key/started" ; ) \\
|
|
24
|
+
%(bundle exec rake :task :output && ) \\
|
|
25
|
+
%(curl -sf "#{endpoint}/ping/:monitor_key/completed" || ) \\
|
|
26
|
+
%(curl -sf "#{endpoint}/ping/:monitor_key/failed")
|
|
27
|
+
|
|
28
|
+
# Then use jobtick_runner / jobtick_rake instead of runner / rake, e.g.:
|
|
29
|
+
# every 1.hour do
|
|
30
|
+
# jobtick_runner "InvoiceJob.perform_later", monitor_key: "invoice_job"
|
|
31
|
+
# end
|
|
32
|
+
RUBY
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jobtick
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Clearstack Labs
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
-
dependencies:
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: logger
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.6'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.6'
|
|
12
26
|
description: Auto-discovers and monitors all scheduled jobs in your Rails app. Zero
|
|
13
27
|
configuration per job.
|
|
14
28
|
email:
|
|
@@ -21,8 +35,18 @@ files:
|
|
|
21
35
|
- LICENSE.txt
|
|
22
36
|
- README.md
|
|
23
37
|
- Rakefile
|
|
38
|
+
- jobtick-0.0.1.gem
|
|
24
39
|
- lib/jobtick.rb
|
|
40
|
+
- lib/jobtick/client.rb
|
|
41
|
+
- lib/jobtick/configuration.rb
|
|
42
|
+
- lib/jobtick/monitor.rb
|
|
43
|
+
- lib/jobtick/parsers/sidekiq.rb
|
|
44
|
+
- lib/jobtick/parsers/solid_queue.rb
|
|
45
|
+
- lib/jobtick/parsers/whenever.rb
|
|
46
|
+
- lib/jobtick/railtie.rb
|
|
47
|
+
- lib/jobtick/registry.rb
|
|
25
48
|
- lib/jobtick/version.rb
|
|
49
|
+
- lib/tasks/jobtick.rake
|
|
26
50
|
- sig/jobtick.rbs
|
|
27
51
|
homepage: https://jobtick.app
|
|
28
52
|
licenses:
|
|
@@ -30,8 +54,9 @@ licenses:
|
|
|
30
54
|
metadata:
|
|
31
55
|
allowed_push_host: https://rubygems.org
|
|
32
56
|
homepage_uri: https://jobtick.app
|
|
33
|
-
source_code_uri: https://github.com/
|
|
34
|
-
changelog_uri: https://github.com/
|
|
57
|
+
source_code_uri: https://github.com/clearstack-labs/jobtick
|
|
58
|
+
changelog_uri: https://github.com/clearstack-labs/jobtick/blob/main/CHANGELOG.md
|
|
59
|
+
rubygems_mfa_required: 'true'
|
|
35
60
|
rdoc_options: []
|
|
36
61
|
require_paths:
|
|
37
62
|
- lib
|
|
@@ -39,7 +64,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
39
64
|
requirements:
|
|
40
65
|
- - ">="
|
|
41
66
|
- !ruby/object:Gem::Version
|
|
42
|
-
version: 3.
|
|
67
|
+
version: 3.3.0
|
|
43
68
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
44
69
|
requirements:
|
|
45
70
|
- - ">="
|