kettle-dev 3.1.19 → 3.1.20

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: bd1cd8bed8e26d699094373fa5a769a6cbb941479bc93027915bf9b6da388322
4
- data.tar.gz: a6457f10daf0e149eac4d9c3138efad3a715f0ca3acfaf0c4bdd8611434996f2
3
+ metadata.gz: 9efc79f5aed2cce009a847b8054a5ce39d54bd5c11f567d5463085e1ce11fcd6
4
+ data.tar.gz: 28b1079640e41dbd9f7ff7c0d844f377b177c4d408b504af893cc888b5abf200
5
5
  SHA512:
6
- metadata.gz: f6414ee199198649eb4a72822e9536be47f748e98e6265e861e67769549a9a3de7433685ac058fdb064bad442ef4dd4cc119a3669d29b3ce8217ae32eb020d88
7
- data.tar.gz: a3410112a6c5fffaaf1c779ec3c1c4d09ab336be891f52b4ad673847b357a8e8bdab28ad6209ad20f478c19e2175e0347ce668b29d7a5832f6aa8ed295c4e898
6
+ metadata.gz: f4982d17aabae7fc0e4d970a55fac3e8403f4daceea4d6e5399380a928c270a2a80ff879c3698eb3558276c39e3fd9a540d801a6ec226ad5e8989c96dd737ded
7
+ data.tar.gz: 7ada13071f91419315448d6b9d03be92c8a25f68d827fb5acbf94b97aad444d7173f6eb411bf7ac6ebdd6251deb4f8f86fdba868ab1edb308ce69eaf199d5eb9
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,17 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [3.1.20] - 2026-10-21
34
+
35
+ - TAG: [v3.1.20][3.1.20t]
36
+ - COVERAGE: 91.52% -- 5773/6308 lines in 48 files
37
+ - BRANCH COVERAGE: 75.66% -- 2226/2942 branches in 48 files
38
+ - 53.70% documented
39
+
40
+ ### Fixed
41
+
42
+ - Release availability probe no longer fails a release after a successful push when the gem.coop proxy lags rubygems.org: the probe alternates the gem.coop consumer-mirror validation with a live rubygems.org confirmation of the push target, backs off exponentially between attempts (10s base, 1.6x growth, 120s cap; tunable via KETTLE_DEV_RELEASE_PROBE_ATTEMPTS / KETTLE_DEV_RELEASE_PROBE_INITIAL_DELAY / KETTLE_DEV_RELEASE_PROBE_INTERVAL / KETTLE_DEV_RELEASE_PROBE_MAX_INTERVAL, also applied to release lockfile-reset retries), and proceeds with a warning when rubygems.org confirms the publish but gem.coop has not yet synced, instead of aborting the wave
43
+
33
44
  ## [3.1.19] - 2026-10-19
34
45
 
35
46
  - TAG: [v3.1.19][3.1.19t]
@@ -4189,7 +4200,9 @@ Please file a bug if you notice a violation of semantic versioning.
4189
4200
  - Selecting will run the selected workflow via `act`
4190
4201
  - This may move to its own gem in the future.
4191
4202
 
4192
- [Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v3.1.19...HEAD
4203
+ [Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v3.1.20...HEAD
4204
+ [3.1.20]: https://github.com/kettle-dev/kettle-dev/compare/v3.1.19...v3.1.20
4205
+ [3.1.20t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v3.1.20
4193
4206
  [3.1.19]: https://github.com/kettle-dev/kettle-dev/compare/v3.1.18...v3.1.19
4194
4207
  [3.1.19t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v3.1.19
4195
4208
  [3.1.18]: https://github.com/kettle-dev/kettle-dev/compare/v3.1.17...v3.1.18
data/README.md CHANGED
@@ -1084,7 +1084,7 @@ Thanks for RTFM. ☺️
1084
1084
  [📌gitmoji]: https://gitmoji.dev
1085
1085
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
1086
1086
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
1087
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.284-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1087
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.308-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1088
1088
  [🔐security]: https://github.com/kettle-dev/kettle-dev/blob/main/SECURITY.md
1089
1089
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
1090
1090
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -71,6 +71,9 @@ module Kettle
71
71
  ].freeze
72
72
  DEBUG_TRUE_VALUES = %w[1 true yes on].freeze
73
73
  RELEASE_VALIDATION_SOURCE = "https://gem.coop"
74
+ # The push target registry. gem.coop is a caching proxy that can lag it,
75
+ # so this is authoritative for whether a pushed gem landed at all.
76
+ RELEASE_UPSTREAM_SOURCE = "https://rubygems.org"
74
77
  FAMILY_MEMBER_PUBLISH_MODE_ENV = "KETTLE_RELEASE_FAMILY_MEMBER_PUBLISH"
75
78
  FAMILY_MEMBER_FINALIZE_MODE_ENV = "KETTLE_RELEASE_FAMILY_MEMBER_FINALIZE"
76
79
 
@@ -887,7 +890,7 @@ module Kettle
887
890
  )
888
891
  if retryable
889
892
  puts "Release lockfile reset could not resolve a newly published workspace gem from #{RELEASE_VALIDATION_SOURCE}; waiting before retry #{attempt + 1}/#{attempts}."
890
- sleep(release_availability_probe_interval)
893
+ sleep(release_availability_probe_interval(attempt))
891
894
  next
892
895
  end
893
896
 
@@ -908,7 +911,7 @@ module Kettle
908
911
  raise unless retryable
909
912
 
910
913
  puts "Release lockfile reset could not resolve a gem from #{RELEASE_VALIDATION_SOURCE}; waiting before retry #{attempt + 1}/#{attempts}."
911
- sleep(release_availability_probe_interval)
914
+ sleep(release_availability_probe_interval(attempt))
912
915
  end
913
916
  end
914
917
 
@@ -2222,6 +2225,7 @@ module Kettle
2222
2225
  attempts = release_availability_probe_attempts
2223
2226
  last_stderr = nil
2224
2227
  last_status = nil
2228
+ upstream_confirmed = false
2225
2229
  sleep(release_availability_probe_initial_delay)
2226
2230
  attempts.times do |index|
2227
2231
  attempt = index + 1
@@ -2247,33 +2251,99 @@ module Kettle
2247
2251
 
2248
2252
  last_stderr = stderr_str
2249
2253
  last_status = status
2254
+ # Alternate the consumer-mirror check with the push target:
2255
+ # #{RELEASE_UPSTREAM_SOURCE} answers "did the push land at all",
2256
+ # while #{RELEASE_VALIDATION_SOURCE} is a caching proxy that can lag
2257
+ # it. A confirmed push gets the full backoff window to sync.
2258
+ upstream_confirmed ||= release_probe_upstream_published?(candidate)
2259
+ if upstream_confirmed
2260
+ puts("#{candidate.gem_name} #{candidate.version} is published on #{RELEASE_UPSTREAM_SOURCE}; " \
2261
+ "waiting for #{RELEASE_VALIDATION_SOURCE} to sync (attempt #{attempt}/#{attempts}).")
2262
+ end
2250
2263
  emit_release_probe_event(
2251
2264
  action: "availability",
2252
- status: (attempt < attempts) ? "retrying" : "failed",
2265
+ status: (attempt < attempts || upstream_confirmed) ? "retrying" : "failed",
2253
2266
  candidate: candidate,
2254
2267
  attempt: attempt,
2255
2268
  attempts: attempts,
2256
- reason: "exit #{status.exitstatus}"
2269
+ reason: "exit #{status.exitstatus}",
2270
+ upstream: upstream_confirmed ? "confirmed" : "unconfirmed"
2271
+ )
2272
+ sleep(release_availability_probe_interval(attempt)) if attempt < attempts
2273
+ end
2274
+
2275
+ if upstream_confirmed
2276
+ warn(<<~MSG)
2277
+ WARNING: #{candidate.gem_name} #{candidate.version} is published on #{RELEASE_UPSTREAM_SOURCE}, but #{RELEASE_VALIDATION_SOURCE} has not synced it after #{attempts} attempt(s).
2278
+ Proceeding because the push target is authoritative for the publish; downstream release steps resolve from #{RELEASE_VALIDATION_SOURCE} and retry on their own.
2279
+ If a later step fails to resolve this gem, wait for #{RELEASE_VALIDATION_SOURCE} to sync and resume that member.
2280
+ MSG
2281
+ emit_release_probe_event(
2282
+ action: "availability",
2283
+ status: "ok",
2284
+ candidate: candidate,
2285
+ attempt: attempts,
2286
+ attempts: attempts,
2287
+ reason: "confirmed on #{RELEASE_UPSTREAM_SOURCE}; #{RELEASE_VALIDATION_SOURCE} sync pending",
2288
+ upstream: "confirmed"
2257
2289
  )
2258
- sleep(release_availability_probe_interval) if attempt < attempts
2290
+ return true
2259
2291
  end
2260
2292
 
2261
2293
  diag = last_stderr.to_s.empty? ? "" : "\n--- STDERR ---\n#{last_stderr}".rstrip
2262
- abort("Published gem availability probe failed for #{candidate.gem_name} #{candidate.version} after #{attempts} attempt(s) (exit #{last_status.exitstatus})#{diag}")
2294
+ abort("Published gem availability probe failed for #{candidate.gem_name} #{candidate.version} after #{attempts} attempt(s) (exit #{last_status.exitstatus}); " \
2295
+ "the version is not visible on #{RELEASE_UPSTREAM_SOURCE} either, so the push itself may have failed#{diag}")
2263
2296
  ensure
2264
2297
  FileUtils.rm_f(script_path) if script_path
2265
2298
  end
2266
2299
 
2267
2300
  def release_availability_probe_attempts
2268
- 15
2301
+ env_positive_integer("KETTLE_DEV_RELEASE_PROBE_ATTEMPTS") || 15
2269
2302
  end
2270
2303
 
2271
2304
  def release_availability_probe_initial_delay
2272
- 5
2305
+ env_positive_integer("KETTLE_DEV_RELEASE_PROBE_INITIAL_DELAY") || 5
2306
+ end
2307
+
2308
+ # Exponential backoff between probe attempts: the base interval grows per
2309
+ # attempt, capped, because the consumer-mirror proxy (gem.coop) can lag
2310
+ # the push target (rubygems.org) by minutes. With defaults the worst case
2311
+ # is ~20 minutes across 15 attempts, and it only accrues when the
2312
+ # registry genuinely lags — a fast sync still passes on attempt 1.
2313
+ def release_availability_probe_interval(attempt = 1)
2314
+ base = env_positive_integer("KETTLE_DEV_RELEASE_PROBE_INTERVAL") || 10
2315
+ cap = env_positive_integer("KETTLE_DEV_RELEASE_PROBE_MAX_INTERVAL") || 120
2316
+ [(base * (1.6**(attempt - 1))).round, cap].min
2317
+ end
2318
+
2319
+ # Whether the push target registry already serves the candidate.
2320
+ # confirm: true forces a live registry read, never a stale cache entry,
2321
+ # because this answer decides whether a failed mirror probe is fatal.
2322
+ def release_probe_upstream_published?(candidate)
2323
+ versions = Kettle::Dev::RubyGemsVersions.published_version_numbers(
2324
+ candidate.gem_name,
2325
+ source: RELEASE_UPSTREAM_SOURCE,
2326
+ version: candidate.version,
2327
+ confirm: true
2328
+ )
2329
+ return false if versions.nil?
2330
+
2331
+ versions.include?(candidate.version.to_s)
2332
+ rescue => error
2333
+ warn("Upstream availability check against #{RELEASE_UPSTREAM_SOURCE} failed: #{error.class}: #{error.message}")
2334
+ false
2273
2335
  end
2274
2336
 
2275
- def release_availability_probe_interval
2276
- 10
2337
+ def env_positive_integer(name)
2338
+ raw = ENV.fetch(name, nil).to_s.strip
2339
+ return nil if raw.empty?
2340
+
2341
+ value = begin
2342
+ Integer(raw)
2343
+ rescue ArgumentError, TypeError
2344
+ nil
2345
+ end
2346
+ value&.positive? ? value : nil
2277
2347
  end
2278
2348
 
2279
2349
  def with_release_availability_probe_gem_home
@@ -2627,7 +2697,7 @@ module Kettle
2627
2697
  )
2628
2698
  end
2629
2699
 
2630
- def emit_release_probe_event(action:, status:, candidate:, attempt:, attempts:, reason: nil)
2700
+ def emit_release_probe_event(action:, status:, candidate:, attempt:, attempts:, reason: nil, upstream: nil)
2631
2701
  Kettle::Ndjson.emit_event(
2632
2702
  @event_recorder,
2633
2703
  "release_probe",
@@ -2640,6 +2710,7 @@ module Kettle
2640
2710
  attempt: attempt,
2641
2711
  attempts: attempts,
2642
2712
  reason: reason,
2713
+ upstream: upstream,
2643
2714
  mark: release_progress_mark(status)
2644
2715
  )
2645
2716
  end
@@ -5,7 +5,7 @@ module Kettle
5
5
  # Version namespace for this gem.
6
6
  module Version
7
7
  # Current gem version.
8
- VERSION = "3.1.19"
8
+ VERSION = "3.1.20"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kettle-dev
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.19
4
+ version: 3.1.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -423,10 +423,10 @@ licenses:
423
423
  - AGPL-3.0-only
424
424
  metadata:
425
425
  homepage_uri: https://kettle-dev.galtzo.com
426
- source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v3.1.19
427
- changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v3.1.19/CHANGELOG.md
426
+ source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v3.1.20
427
+ changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v3.1.20/CHANGELOG.md
428
428
  bug_tracker_uri: https://github.com/kettle-dev/kettle-dev/issues
429
- documentation_uri: https://www.rubydoc.info/gems/kettle-dev/3.1.19
429
+ documentation_uri: https://www.rubydoc.info/gems/kettle-dev/3.1.20
430
430
  funding_uri: https://github.com/sponsors/pboling
431
431
  wiki_uri: https://github.com/kettle-dev/kettle-dev/wiki
432
432
  news_uri: https://www.railsbling.com/tags/kettle-dev
metadata.gz.sig CHANGED
Binary file