deployangel 0.1.4 → 0.1.6
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/http.rb +6 -0
- data/lib/deployangel/rails/metadata.rb +106 -1
- 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: 95e80de544815ec904e01198f948c81c722ddadb005f6e87761827ac1cb3a362
|
|
4
|
+
data.tar.gz: 6175861126a88ed4bc09bdaf600c03e1ce5cd88fa3361c53d7c761ff7a066b38
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d20de4305540d12d4b24223c7f4eda020b0855b8205cbeac03c51bf8befc701cb783a1e4600c3781b7d15b906a739aa63988bacc4864bc15a286e32c0138405
|
|
7
|
+
data.tar.gz: 38682cdc5941ad48af3ed324356d5bf71309bc5fe2c41fd0c582198ce0315c1863575f5896b13516b634cf6d56e69aee1124a3491c2fa4aae28c49f4b6a77a59
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.6 (2026-10-04)
|
|
4
|
+
|
|
5
|
+
- Requests to Rails' health check (`rails/health#show`, at `/up` by default)
|
|
6
|
+
are no longer recorded, wherever it's mounted. Load balancers and uptime
|
|
7
|
+
monitors call it all the time and it always answers fast, so it made apps
|
|
8
|
+
look busier and healthier than their real pages. DeployAngel already
|
|
9
|
+
leaves out `/up` from older agents.
|
|
10
|
+
|
|
11
|
+
## 0.1.5 (2026-10-03)
|
|
12
|
+
|
|
13
|
+
- Declared recurring jobs from sidekiq-cron and sidekiq-scheduler, alongside
|
|
14
|
+
Solid Queue's. sidekiq-cron jobs come from its schedule file
|
|
15
|
+
(`config/schedule.yml` unless configured otherwise); sidekiq-scheduler jobs
|
|
16
|
+
from the `:scheduler: :schedule:` section of Sidekiq's config file (the
|
|
17
|
+
`-C` file in the Procfile's sidekiq command, else `config/sidekiq.yml`),
|
|
18
|
+
with that environment's section applied. A repeating `every` or `interval`
|
|
19
|
+
is sent as `every`. Both carry the process's local time zone, which the
|
|
20
|
+
schedulers read a schedule in when it names none. Disabled jobs, jobs for
|
|
21
|
+
other environments, and one-off `at` and `in` jobs are left out. Jobs that
|
|
22
|
+
exist only in Redis aren't read.
|
|
23
|
+
|
|
3
24
|
## 0.1.4 (2026-10-03)
|
|
4
25
|
|
|
5
26
|
- A Solid Queue recurring task that runs a `command:` instead of a job class
|
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
|
|
@@ -7,6 +7,10 @@ module DeployAngel
|
|
|
7
7
|
# never the raw path, which keeps IDs out of route keys.
|
|
8
8
|
class Http
|
|
9
9
|
FORMAT_SUFFIX = "(.:format)"
|
|
10
|
+
# Rails' health check (/up by default). Load balancers and uptime
|
|
11
|
+
# monitors call it all the time and it always answers fast, so
|
|
12
|
+
# counting it would make any app look busy and healthy.
|
|
13
|
+
HEALTH_CHECK_CONTROLLER = "rails/health"
|
|
10
14
|
|
|
11
15
|
def initialize(app)
|
|
12
16
|
@app = app
|
|
@@ -31,6 +35,8 @@ module DeployAngel
|
|
|
31
35
|
|
|
32
36
|
private
|
|
33
37
|
def record(env, status, started, unhandled:, exception: nil)
|
|
38
|
+
return if env["action_dispatch.request.path_parameters"]&.dig(:controller) == HEALTH_CHECK_CONTROLLER
|
|
39
|
+
|
|
34
40
|
route = route_key(env)
|
|
35
41
|
# Unrouted successes are static files and similar; unrouted
|
|
36
42
|
# errors (such as routing 404s) still count.
|
|
@@ -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
|
|
|
@@ -77,6 +84,104 @@ module DeployAngel
|
|
|
77
84
|
[]
|
|
78
85
|
end
|
|
79
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 }
|
|
101
|
+
end
|
|
102
|
+
rescue StandardError
|
|
103
|
+
[]
|
|
104
|
+
end
|
|
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
|
+
|
|
80
185
|
# A command task runs as a job of this class, so its runs show up in
|
|
81
186
|
# job telemetry under that name.
|
|
82
187
|
def command_job_class
|
data/lib/deployangel/version.rb
CHANGED