yamine 0.6.0 → 0.6.1

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: 1fef68ea493be93af6dfb3271b446b793536a373d11b030f62e54c0d90438e88
4
- data.tar.gz: 69c64d3bd1523b05ca6277f76329e20415879f16c95cd0640e30c9adf77f69cf
3
+ metadata.gz: 8066e974d3f0f4849a0b6173c3a8d6acf5f576149afffc9bec3981623ce7faee
4
+ data.tar.gz: ded221f8f3f2c6430383c4666d69bc2d397ee924e9f1409d04b0096c750674d7
5
5
  SHA512:
6
- metadata.gz: 2864def84f4afefe2dd79adda834a2381964e964da864f99c8740321c19559669ad48874cce4485222c0a09d5a285b343d99d2fdea5f1029ee54f8dc8cd86a76
7
- data.tar.gz: fe58b77da7a315e3f6aa3fd59f7c153fe8a0d342ffdbbdf923e4199e8ccba02b039c2cde777a7c9d1855d127563ef3f021171b308c1696f5f7446b9ac068430e
6
+ metadata.gz: 28e06d646cb8f5d7e5a9a16a40f1e4a091ed544f39a5dcd6753d7bd740d11cfa331c213c23c417543670d3c2ab09485edd50717abd5eef3533034d0f1e58d883
7
+ data.tar.gz: 6b1180bffb37227e10bff3d0072faae0d790d684b2e74f462e64b3db3ded819c3a96736b27e8b30098c0ad2abcf50ed95f292babc02409e242d7887eac8d6e03
data/CHANGELOG.md CHANGED
@@ -1,6 +1,23 @@
1
1
  # Changelog
2
2
 
3
- ## [0.6.0] — Unreleased
3
+ ## [0.6.1] — 2026-09-10
4
+
5
+ ### Fixed
6
+
7
+ - **Healthchecks no longer speak TLS to the backend.** `--wait` probed
8
+ `127.0.0.1:$PORT` — the app's own listener — with the *proxy's* `tls`
9
+ flag, so any app declaring `healthcheck: { path: }` could never boot
10
+ under the default TLS-on proxy: the probe began an SSL handshake
11
+ against a plaintext Puma, which logged `Invalid HTTP format, parsing
12
+ fails. Are you trying to open an SSL connection to a non-SSL Puma?`
13
+ once per attempt until the process phase timed out and the whole boot
14
+ was torn down. TLS is the proxy's job; the proxy dials backends with a
15
+ bare `TCPSocket`, so `Readiness.probe`/`probe_http`/`wait_healthy`/
16
+ `wait_all` drop the `tls:` keyword entirely (an `https://` banner URL
17
+ never implied the backend speaks TLS). Reported from anyworkers, whose
18
+ web process healthchecks `/up`.
19
+
20
+ ## [0.6.0] — 2026-09-09
4
21
 
5
22
  `yamine start --wait` + boot readiness protocol. The remaining gap from
6
23
  “one-storey building” onwards: boot had no definitive answer — agents
data/README.md CHANGED
@@ -95,7 +95,9 @@ Optional top-level `db: false` opts out of per-worktree databases
95
95
  (exotic setups — manual `establish_connection`, shared staging DB, …);
96
96
  `db.schema_load` overrides the schema-load command. Per-process
97
97
  `healthcheck: { path: /up, timeout: 30 }` declares what `--wait` polls
98
- (TCP accept when absent).
98
+ (TCP accept when absent). The poll is plain HTTP against the app's own
99
+ `127.0.0.1:$PORT` listener — TLS is terminated by the proxy, so the path
100
+ is reached over http regardless of the `https://` URL in the banner.)
99
101
 
100
102
  `yamine init` creates the file (migrating an existing Procfile);
101
103
  Rails apps need no extra gem — yamine injects `RAILS_DEVELOPMENT_HOSTS`
@@ -183,7 +183,7 @@ module Yamine
183
183
  database_url: db_url, force: opts[:force])
184
184
  }
185
185
  end
186
- wait_result = Readiness.wait_all(apps, tls: ctx.proxy_tls, out: nil)
186
+ wait_result = Readiness.wait_all(apps, out: nil)
187
187
  failed = wait_result.select { |r| r[:status] != "ok" }
188
188
 
189
189
  if failed.empty?
@@ -3,7 +3,6 @@
3
3
  require "json"
4
4
  require "net/http"
5
5
  require "open3"
6
- require "openssl"
7
6
  require "socket"
8
7
  require "timeout"
9
8
  require "uri"
@@ -89,11 +88,17 @@ module Yamine
89
88
  # Poll an HTTP route until healthy or timeout. Healthy = healthcheck
90
89
  # path returns 2xx-3xx, or (no healthcheck) TCP accept on the port.
91
90
  # Returns [healthy, detail].
92
- def wait_healthy(hostname, port:, tls:, path: nil, timeout: 30)
91
+ #
92
+ # Probes are plaintext by construction: the target is the app's OWN
93
+ # listener (127.0.0.1:$PORT). TLS is terminated by the proxy, which
94
+ # dials backends with a bare TCPSocket (Proxy#connect_backend), so a
95
+ # `tls:` flag taken from the proxy must never reach here — an
96
+ # https:// URL in the banner does not mean the backend speaks TLS.
97
+ def wait_healthy(hostname, port:, path: nil, timeout: 30)
93
98
  deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
94
99
  last_error = "not yet attempted"
95
100
  until Process.clock_gettime(Process::CLOCK_MONOTONIC) > deadline
96
- ok, detail = probe(hostname, port, tls: tls, path: path)
101
+ ok, detail = probe(hostname, port, path: path)
97
102
  return [true, nil] if ok
98
103
 
99
104
  last_error = detail
@@ -102,9 +107,9 @@ module Yamine
102
107
  [false, last_error]
103
108
  end
104
109
 
105
- def probe(hostname, port, tls:, path: nil)
110
+ def probe(hostname, port, path: nil)
106
111
  if path
107
- probe_http(hostname, port, tls: tls, path: path)
112
+ probe_http(hostname, port, path: path)
108
113
  else
109
114
  probe_tcp(port)
110
115
  end
@@ -117,10 +122,8 @@ module Yamine
117
122
  [false, e.message.lines.first&.strip]
118
123
  end
119
124
 
120
- def probe_http(hostname, port, tls:, path:)
125
+ def probe_http(hostname, port, path:)
121
126
  http = Net::HTTP.new("127.0.0.1", port)
122
- http.use_ssl = tls
123
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE if tls
124
127
  http.open_timeout = 3
125
128
  http.read_timeout = 5
126
129
  res = http.get(path, { "Host" => hostname })
@@ -140,17 +143,17 @@ module Yamine
140
143
  # or "timeout" (never answered within its healthcheck timeout).
141
144
  # Dead-pid short-circuit: a reaped backend fails immediately
142
145
  # instead of burning its full timeout on connection-refused.
143
- def wait_all(apps, tls: true, out: nil)
146
+ def wait_all(apps, out: nil)
144
147
  threads = apps.map do |name, slot|
145
148
  Thread.new do
146
- Thread.current[:result] = wait_one(name, slot, tls: tls, out: out)
149
+ Thread.current[:result] = wait_one(name, slot, out: out)
147
150
  end
148
151
  end
149
152
  threads.each(&:join)
150
153
  threads.map { |t| t[:result] }
151
154
  end
152
155
 
153
- def wait_one(name, slot, tls:, out:)
156
+ def wait_one(name, slot, out:)
154
157
  item = slot[:item]
155
158
  app = slot[:app]
156
159
  hc = item[:entry]["healthcheck"] || {}
@@ -165,7 +168,7 @@ module Yamine
165
168
  return fail_result(name, started, ms,
166
169
  "process exited before becoming healthy — see log/yamine-#{name}.log", out)
167
170
  end
168
- ok, detail = probe(item[:hostname], item[:port], tls: tls, path: path)
171
+ ok, detail = probe(item[:hostname], item[:port], path: path)
169
172
  if ok
170
173
  ms = elapsed_ms(started)
171
174
  return ok_result(name, started, ms, path, out)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Yamine
4
- VERSION = "0.6.0"
4
+ VERSION = "0.6.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yamine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto