deployangel 0.1.3 → 0.1.5
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/CHANGELOG.md +21 -0
- data/README.md +4 -3
- data/lib/deployangel/rails/metadata.rb +119 -2
- data/lib/deployangel/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7d7f056df70ef5a14b11ff88312b8dd9e48330567b9f653cf57d6ddd9caf8050
|
|
4
|
+
data.tar.gz: 7d8c30865b517b59e76cea6cc61d5c0d6853ea7766b41378b790f98983a23d37
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4223fbd9e23df830ab6538e927550cbf79057856112427df383da9a77a9a1a0af2f22b5461a4934e1bd945bef60dc8160644632fba1cacd99be0183a29f40295
|
|
7
|
+
data.tar.gz: 790766bea10aa1c12f8e6212fb9d0b10a127f4f7a809bee9434c2952f4ad725c491c20fc2767a606142ec4f9d8d748a50197b6a52861e417f7bb87e0df40a6fa
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.5 (2026-10-03)
|
|
4
|
+
|
|
5
|
+
- Declared recurring jobs from sidekiq-cron and sidekiq-scheduler, alongside
|
|
6
|
+
Solid Queue's. sidekiq-cron jobs come from its schedule file
|
|
7
|
+
(`config/schedule.yml` unless configured otherwise); sidekiq-scheduler jobs
|
|
8
|
+
from the `:scheduler: :schedule:` section of Sidekiq's config file (the
|
|
9
|
+
`-C` file in the Procfile's sidekiq command, else `config/sidekiq.yml`),
|
|
10
|
+
with that environment's section applied. A repeating `every` or `interval`
|
|
11
|
+
is sent as `every`. Both carry the process's local time zone, which the
|
|
12
|
+
schedulers read a schedule in when it names none. Disabled jobs, jobs for
|
|
13
|
+
other environments, and one-off `at` and `in` jobs are left out. Jobs that
|
|
14
|
+
exist only in Redis aren't read.
|
|
15
|
+
|
|
16
|
+
## 0.1.4 (2026-10-03)
|
|
17
|
+
|
|
18
|
+
- A Solid Queue recurring task that runs a `command:` instead of a job class
|
|
19
|
+
reports the job class it runs as (`runs_as`, `SolidQueue::RecurringJob`
|
|
20
|
+
unless the app changes Solid Queue's default). DeployAngel can then expect
|
|
21
|
+
it on schedule and show it by its name in `config/recurring.yml`, instead
|
|
22
|
+
of guessing an interval from history and showing Solid Queue's wrapper.
|
|
23
|
+
|
|
3
24
|
## 0.1.3 (2026-10-02)
|
|
4
25
|
|
|
5
26
|
- Each process sends its minute of telemetry 1 to 50 seconds after the
|
data/README.md
CHANGED
|
@@ -64,9 +64,10 @@ deploy, and the dashboard says so.
|
|
|
64
64
|
- Exceptions: a stable fingerprint, the exception class, a sanitized message
|
|
65
65
|
(numbers, IDs, emails, and quoted values removed), and application frames
|
|
66
66
|
only.
|
|
67
|
-
- Once per process: the route table, job classes,
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
- Once per process: the route table, job classes, recurring schedules
|
|
68
|
+
declared for Solid Queue, sidekiq-cron, or sidekiq-scheduler, critical
|
|
69
|
+
flows, and file digests (relative paths and hashes, never file contents) so
|
|
70
|
+
DeployAngel can tell which routes changed in a release.
|
|
70
71
|
Disable digests with `DEPLOYANGEL_FILE_DIGESTS=false`.
|
|
71
72
|
|
|
72
73
|
It does not send request bodies, parameters, headers, cookies, SQL, logs, or
|
|
@@ -58,8 +58,15 @@ module DeployAngel
|
|
|
58
58
|
[]
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
#
|
|
61
|
+
# Declared recurring jobs from Solid Queue, sidekiq-cron, and
|
|
62
|
+
# sidekiq-scheduler. Each source is read on its own, so a file that
|
|
63
|
+
# can't be read leaves the others' schedules in place.
|
|
62
64
|
def schedules
|
|
65
|
+
solid_queue_schedules + sidekiq_cron_schedules + sidekiq_scheduler_schedules
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Solid Queue recurring tasks for the current environment.
|
|
69
|
+
def solid_queue_schedules
|
|
63
70
|
path = File.join(@root, "config", "recurring.yml")
|
|
64
71
|
return [] unless File.file?(path)
|
|
65
72
|
|
|
@@ -68,13 +75,123 @@ module DeployAngel
|
|
|
68
75
|
Array(tasks).filter_map do |key, task|
|
|
69
76
|
next unless task.is_a?(Hash) && task["schedule"]
|
|
70
77
|
|
|
71
|
-
{ "key" => key.to_s, "class" => task["class"]&.to_s, "schedule" => task["schedule"].to_s, "source" => "solid_queue",
|
|
78
|
+
schedule = { "key" => key.to_s, "class" => task["class"]&.to_s, "schedule" => task["schedule"].to_s, "source" => "solid_queue",
|
|
72
79
|
"time_zone" => scheduler_time_zone }
|
|
80
|
+
schedule["runs_as"] = command_job_class if task["class"].nil? && task["command"]
|
|
81
|
+
schedule
|
|
82
|
+
end
|
|
83
|
+
rescue StandardError
|
|
84
|
+
[]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# sidekiq-cron jobs from its schedule file (config/schedule.yml unless
|
|
88
|
+
# configured otherwise), which it loads when Sidekiq starts. Jobs
|
|
89
|
+
# created in code live only in Redis, which the agent doesn't read.
|
|
90
|
+
def sidekiq_cron_schedules
|
|
91
|
+
return [] unless defined?(::Sidekiq::Cron)
|
|
92
|
+
|
|
93
|
+
path = yaml_path(sidekiq_cron_schedule_file) or return []
|
|
94
|
+
jobs = load_yaml(path)
|
|
95
|
+
jobs = jobs.map { |name, job| job.is_a?(Hash) ? job.merge("name" => name) : job } if jobs.is_a?(Hash)
|
|
96
|
+
Array(jobs).filter_map do |job|
|
|
97
|
+
next unless job.is_a?(Hash) && job["cron"] && job["status"].to_s != "disabled"
|
|
98
|
+
|
|
99
|
+
{ "key" => job["name"].to_s, "class" => (job["klass"] || job["class"])&.to_s, "schedule" => job["cron"].to_s,
|
|
100
|
+
"source" => "sidekiq_cron", "time_zone" => local_time_zone }
|
|
73
101
|
end
|
|
74
102
|
rescue StandardError
|
|
75
103
|
[]
|
|
76
104
|
end
|
|
77
105
|
|
|
106
|
+
def sidekiq_cron_schedule_file
|
|
107
|
+
configured = ::Sidekiq::Cron.configuration.cron_schedule_file if ::Sidekiq::Cron.respond_to?(:configuration)
|
|
108
|
+
File.expand_path(configured || "config/schedule.yml", @root)
|
|
109
|
+
rescue StandardError
|
|
110
|
+
File.join(@root, "config", "schedule.yml")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# sidekiq-scheduler jobs from Sidekiq's config file, where it reads
|
|
114
|
+
# them under :scheduler: :schedule: (or :schedule: in older versions).
|
|
115
|
+
# A cron runs at set times; every and interval repeat from when the
|
|
116
|
+
# scheduler starts, which a deploy restarts. One-off at and in jobs
|
|
117
|
+
# aren't recurring, so they're left out.
|
|
118
|
+
def sidekiq_scheduler_schedules
|
|
119
|
+
return [] unless defined?(::SidekiqScheduler)
|
|
120
|
+
|
|
121
|
+
path = sidekiq_config_file or return []
|
|
122
|
+
config = normalize_keys(load_yaml(path) || {})
|
|
123
|
+
config = config.merge(config.delete(::Rails.env.to_s) || {}) if config.is_a?(Hash)
|
|
124
|
+
jobs = config.dig("scheduler", "schedule") || config["schedule"]
|
|
125
|
+
return [] unless jobs.is_a?(Hash)
|
|
126
|
+
|
|
127
|
+
jobs.filter_map do |name, job|
|
|
128
|
+
next unless job.is_a?(Hash) && job["enabled"] != false && scheduled_in_this_environment?(job)
|
|
129
|
+
|
|
130
|
+
type = %w[cron every at in interval].find { |key| Array(job[key]).first.to_s.strip != "" }
|
|
131
|
+
next unless %w[cron every interval].include?(type)
|
|
132
|
+
|
|
133
|
+
value = Array(job[type]).first.to_s
|
|
134
|
+
schedule = { "key" => name.to_s, "class" => (job["class"] || name).to_s, "source" => "sidekiq_scheduler",
|
|
135
|
+
"time_zone" => local_time_zone }
|
|
136
|
+
type == "cron" ? schedule.merge("schedule" => value) : schedule.merge("schedule" => nil, "every" => value)
|
|
137
|
+
end
|
|
138
|
+
rescue StandardError
|
|
139
|
+
[]
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# The file Sidekiq loads: the -C path in the Procfile's sidekiq
|
|
143
|
+
# command, else config/sidekiq.yml.
|
|
144
|
+
def sidekiq_config_file
|
|
145
|
+
procfile = File.join(@root, "Procfile")
|
|
146
|
+
named = File.read(procfile)[/\bsidekiq\b[^\n]*?\s(?:-C|--config)[\s=]+(\S+)/, 1] if File.file?(procfile)
|
|
147
|
+
named ? yaml_path(File.expand_path(named, @root)) : yaml_path(File.join(@root, "config", "sidekiq.yml"))
|
|
148
|
+
rescue StandardError
|
|
149
|
+
nil
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# sidekiq-scheduler skips a job whose rails_env doesn't list this one.
|
|
153
|
+
def scheduled_in_this_environment?(job)
|
|
154
|
+
job["rails_env"].nil? || job["rails_env"].to_s.gsub(/\s/, "").split(",").include?(::Rails.env.to_s)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# The zone sidekiq-cron and sidekiq-scheduler read a schedule in when it
|
|
158
|
+
# names none: the process's local zone, as Fugit and rufus-scheduler
|
|
159
|
+
# find it. nil if that can't be told.
|
|
160
|
+
def local_time_zone
|
|
161
|
+
::EtOrbi.determine_local_tzone&.name if defined?(::EtOrbi)
|
|
162
|
+
rescue StandardError
|
|
163
|
+
nil
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# The path, or the same name with .yaml if only that exists.
|
|
167
|
+
def yaml_path(path)
|
|
168
|
+
[ path, path.sub(/\.yml\z/, ".yaml") ].find { |candidate| File.file?(candidate) }
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def load_yaml(path)
|
|
172
|
+
YAML.safe_load(ERB.new(File.read(path), trim_mode: "-").result, permitted_classes: [ Symbol ], aliases: true)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Sidekiq's config uses symbol keys (:scheduler:); compare them as
|
|
176
|
+
# plain strings.
|
|
177
|
+
def normalize_keys(object)
|
|
178
|
+
case object
|
|
179
|
+
when Hash then object.to_h { |key, value| [ key.to_s.delete_prefix(":"), normalize_keys(value) ] }
|
|
180
|
+
when Array then object.map { |value| normalize_keys(value) }
|
|
181
|
+
else object
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# A command task runs as a job of this class, so its runs show up in
|
|
186
|
+
# job telemetry under that name.
|
|
187
|
+
def command_job_class
|
|
188
|
+
if defined?(::SolidQueue::RecurringTask) && ::SolidQueue::RecurringTask.respond_to?(:default_job_class)
|
|
189
|
+
::SolidQueue::RecurringTask.default_job_class&.name
|
|
190
|
+
end || "SolidQueue::RecurringJob"
|
|
191
|
+
rescue StandardError
|
|
192
|
+
"SolidQueue::RecurringJob"
|
|
193
|
+
end
|
|
194
|
+
|
|
78
195
|
# The zone Solid Queue reads a schedule in when the schedule names
|
|
79
196
|
# none: its own setting, config.time_zone by default. nil when that's
|
|
80
197
|
# the system's local time or Solid Queue predates the setting, and
|
data/lib/deployangel/version.rb
CHANGED