yamine 0.15.0 → 0.15.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: 8ed2f817c57a50bb8b6d1d06b801c11e321314f4106592797a5554398dab57c5
4
- data.tar.gz: a8d5de965cd99d455bea51bc0403b968d05a5eb3af988f94eb7cbe631c8d4fac
3
+ metadata.gz: cb9ab4ec4d7e32278f307f3ebb9366b2a23ef7ec5b65016e6888c240ee90d951
4
+ data.tar.gz: 13f8a0d5340f03de8132101f5eb996b533aaf9b60a96b21bfbdddc6839ae4641
5
5
  SHA512:
6
- metadata.gz: c6b57abccf1ba59aa2ac525f1f44cb9ed36bf3e6183ed409affdb42af5dec4bd6674a7df6fc6b259d77aa48ece1279aea552a44ffe50479ace3974614b29fc1c
7
- data.tar.gz: e075319032988c9efcaf70ca3835a46bcb3a0f55986a35bfe42b765483b24c89611c4893fc26d5b1437a9a49d263cd1f9aabed27e27f56705069e43e2832544c
6
+ metadata.gz: 10b5b826f5cd2c007e181feaf9f671e0b654a80b13f922c3251d72912987ecd294fa3cdc1d17fa7e5f58dbed22aab8961598844b711c896301e4cb7b46a59399
7
+ data.tar.gz: cb4962d0c9db1711963bbee31b59f3ca062613562714dfeebed81cdff5c896caf0fb3cdc16296e5245db0885da8debf03b6a1ec9b527e98bb79c3b5e4e46ff08
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.15.1] — 2026-09-22
4
+
5
+ ### Fixed
6
+
7
+ - **Stale merged worktrees now lose their branch too.** A worktree whose
8
+ directory was already gone was pruned from git, but its branch was
9
+ left behind — so `remove` and `clean` reported success while the
10
+ merged branch lingered, unlike a normal teardown. Stale entries now
11
+ delete a merged branch the same way (`git branch -d` after the
12
+ prune), and an unmerged branch is kept with the reason said out loud
13
+ unless `--force`. Regression coverage pins all three: stale + merged
14
+ deletes, stale + unmerged keeps, and `clean` doing the same sweep.
15
+
3
16
  ## [0.15.0] — 2026-09-15
4
17
 
5
18
  ### Changed
@@ -16,6 +29,22 @@
16
29
  boot. Interleaving matches the foreman/without-yamine experience; a
17
30
  per-process opt-in can be added later if needed.
18
31
 
32
+ ## [0.14.3] — 2026-09-15
33
+
34
+ ### Fixed
35
+
36
+ - **WebSocket upgrades carry the same forwarded headers as every other
37
+ request.** Upgrade requests (ActionCable, Turbo Streams over cable)
38
+ were piped to the backend byte-for-byte, so `X-Forwarded-Proto`,
39
+ `X-Forwarded-For`, and `X-Forwarded-Host` never arrived — the one
40
+ request class that skipped `set_forwarded`. An app behind the proxy
41
+ then saw a `wss://` connection as plain `http`, and Rails' ActionCable
42
+ origin check (Origin vs. scheme + Host) compared
43
+ `https://app.localhost` against `http://app.localhost` and refused the
44
+ socket with "Request origin not allowed". Upgrades now run the same
45
+ `set_forwarded` path as every other request before the head is
46
+ forwarded; the bytes after the header block still pipe raw.
47
+
19
48
  ## [0.14.2] — 2026-09-15
20
49
 
21
50
  ### Added
@@ -210,7 +210,7 @@ module Yamine
210
210
  end
211
211
 
212
212
  unless entry.exists?
213
- abandon(ctx, repo, entry)
213
+ abandon(ctx, repo, entry, force: opts[:force])
214
214
  return
215
215
  end
216
216
 
@@ -251,6 +251,7 @@ module Yamine
251
251
 
252
252
  failures = 0
253
253
  changed = false
254
+ stale = []
254
255
  plan.each do |step|
255
256
  case step[0]
256
257
  when :drop_db
@@ -266,12 +267,14 @@ module Yamine
266
267
  failures += 1
267
268
  end
268
269
  when :prune
270
+ stale << step[1]
269
271
  changed = true
270
272
  when :keep
271
273
  puts " kept #{step[1]} — #{step[2]}"
272
274
  end
273
275
  end
274
276
  Worktrees.prune(repo) if repo
277
+ stale.each { |entry| delete_stale_branch(repo, entry) } if repo
275
278
  resync_hosts(ctx) if repo && changed
276
279
  ran = plan.count { |s| %i[drop_db teardown prune].include?(s[0]) }
277
280
  summary = "Cleaned #{ran} item(s)"
@@ -285,7 +288,7 @@ module Yamine
285
288
  case action
286
289
  when :drop_db then "drop database #{subject} (#{reason} is gone)"
287
290
  when :teardown then "remove worktree #{subject.path} (#{reason})"
288
- when :prune then "prune stale git entry #{subject}"
291
+ when :prune then "prune stale git entry #{subject.path}"
289
292
  when :keep then "keep #{subject} — #{reason}"
290
293
  end
291
294
  end
@@ -303,7 +306,7 @@ module Yamine
303
306
  next if entry.bare || entry.main?
304
307
 
305
308
  unless entry.exists?
306
- plan << Plan.new(:prune, entry.path)
309
+ plan << Plan.new(:prune, entry)
307
310
  next
308
311
  end
309
312
  if Worktrees.dirty?(entry.path, ignore: LOCAL_CONFIG_FILES)
@@ -370,8 +373,10 @@ module Yamine
370
373
  end
371
374
 
372
375
  # A worktree whose directory is already gone: nothing to stop or
373
- # remove, just its database claim and the stale git entry.
374
- def abandon(ctx, repo, entry)
376
+ # remove, just its database claim, the stale git entry, and — when
377
+ # the branch is merged — the branch itself, matching what a normal
378
+ # removal would have done.
379
+ def abandon(ctx, repo, entry, force: false)
375
380
  map = Database.load_map(ctx.store.dir)
376
381
  claim = claim_for(map, entry.path)
377
382
  if claim
@@ -379,6 +384,26 @@ module Yamine
379
384
  end
380
385
  Worktrees.prune(repo)
381
386
  puts " pruned #{entry.path} (directory was already gone)"
387
+ delete_stale_branch(repo, entry, force: force)
388
+ end
389
+
390
+ # Prune first so git will allow the delete; an unmerged branch is
391
+ # kept unless force, exactly as teardown does for a live worktree.
392
+ def delete_stale_branch(repo, entry, force: false)
393
+ return unless entry.branch && Worktrees.branch_exists?(repo, entry.branch)
394
+
395
+ merged = Worktrees.merged?(repo, entry.branch,
396
+ into: Worktrees.default_branch(repo))
397
+ if force || merged
398
+ flag = force ? "-D" : "-d"
399
+ _out, status = Open3.capture2("git", "branch", flag, entry.branch,
400
+ chdir: repo, err: File::NULL)
401
+ if status&.success?
402
+ puts " deleted branch #{entry.branch}"
403
+ return
404
+ end
405
+ end
406
+ puts " kept branch #{entry.branch} (git refuses to delete an unmerged branch)"
382
407
  end
383
408
 
384
409
  # The teardown shared by `remove` and `clean`. Returns true when
data/lib/yamine/proxy.rb CHANGED
@@ -320,12 +320,18 @@ module Yamine
320
320
 
321
321
  def handle_upgrade(sock, head, buf)
322
322
  routes = cached_routes
323
- _method, _target, headers = parse_head(head)
323
+ method, target, headers = parse_head(head)
324
324
  entry = route(headers["host"].to_s, routes)
325
325
  return unless entry
326
326
 
327
+ # Upgrades carry the same forwarded headers as every other request:
328
+ # an app behind the proxy (ActionCable's origin check) reads the
329
+ # scheme from X-Forwarded-Proto, and a verbatim head would tell a
330
+ # wss:// connection it arrived as http.
331
+ set_forwarded(headers, sock, tls: @tls)
332
+
327
333
  backend = dial(entry)
328
- backend.write(head)
334
+ backend.write(rebuild_head(method, target, headers))
329
335
  backend.write(buf) unless buf.empty?
330
336
  pipe_both(sock, backend)
331
337
  rescue SystemCallError, OpenSSL::SSL::SSLError, IOError => e
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Yamine
4
- VERSION = "0.15.0"
4
+ VERSION = "0.15.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.15.0
4
+ version: 0.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto