kettle-family 1.1.8 → 1.1.9

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: 168c6823b78ae99fcbe6066d6d923b4111ea9a01fb208e100addfad6914dfb06
4
- data.tar.gz: ca1d7f9dda6ab6a9324db3fb0dfbb911a166b842f8d76c6ade2165be03be7f2a
3
+ metadata.gz: a7a1fef6a9376e6ea7f3d4f2ecd61bf5a610fdb1585037ce63631ef106ec6e24
4
+ data.tar.gz: aa9c89f697651bf26926575dde900f6ab070b487191f2ca6436ce04a3ad231c6
5
5
  SHA512:
6
- metadata.gz: 6ff9056c21b70d0cb7d218906ea402f8ede543649d4d41292a9f434271a3bfeccc6bccf02db87bca8c3526d69a9fdb99a34a7e084d332ddd4045a6d4ff39f736
7
- data.tar.gz: be9ee1ee76ac7a5b2b9d346fe54a48205e164ee6d9e89a7e9794543f23a6ef1663c5e8ad598faf4483774ee846a34bb1beffad0b441551e0a28a708fc4461b54
6
+ metadata.gz: 11993355cf52d83e6bbbdae954983f7e10233968fece69a812c62ea5c1787112ba825e2eed207c12a06d44abb3af6199af5f3a11647733845abd8ba76ed25335
7
+ data.tar.gz: 7abbd03f4c8f434c8a069dc92d6c4eb66b06b81adb73d2454cf5fc0be9dbcb5335201b248173c15d91fcff699761e408303be2c7887fdabd1a07b2dd63e38ec8
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,35 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [1.1.9] - 2026-07-25
34
+
35
+ - TAG: [v1.1.9][1.1.9t]
36
+ - COVERAGE: 95.23% -- 3197/3357 lines in 25 files
37
+ - BRANCH COVERAGE: 77.20% -- 1192/1544 branches in 25 files
38
+ - 28.16% documented
39
+
40
+ ### Added
41
+
42
+ - `kettle-family state` now marks mismatched GitHub release tags and reports
43
+ kettle-jem transfer changelog replay lag in a `T📰` column.
44
+
45
+ ### Changed
46
+
47
+ - Bare `kettle-family bump` now defaults to `--only bump`, and bare
48
+ `kettle-family release` now defaults to `--only pending`.
49
+
50
+ - kettle-jem-template-20260725-002 - Generated gemspec templates now include
51
+ `anonymous_loader` as a development dependency, and version specs use it to
52
+ execute generated `version.rb` files for coverage without redefining package
53
+ constants. Managed version specs are removed when `version_gem` is disabled
54
+ or incompatible with the project's runtime Ruby floor.
55
+
56
+ ### Fixed
57
+
58
+ - Monorepo templating now passes a shared Git operation lock to `kettle-jem`,
59
+ allowing template workers to serialize repo-wide Git config/index mutations
60
+ instead of only bootstrap commits.
61
+
33
62
  ## [1.1.8] - 2026-07-25
34
63
 
35
64
  - TAG: [v1.1.8][1.1.8t]
@@ -1043,7 +1072,9 @@ Please file a bug if you notice a violation of semantic versioning.
1043
1072
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
1044
1073
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
1045
1074
 
1046
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.1.8...HEAD
1075
+ [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.1.9...HEAD
1076
+ [1.1.9]: https://github.com/kettle-dev/kettle-family/compare/v1.1.8...v1.1.9
1077
+ [1.1.9t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.1.9
1047
1078
  [1.1.8]: https://github.com/kettle-dev/kettle-family/compare/v1.1.7...v1.1.8
1048
1079
  [1.1.8t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.1.8
1049
1080
  [1.1.7]: https://github.com/kettle-dev/kettle-family/compare/v1.1.6...v1.1.7
data/README.md CHANGED
@@ -669,7 +669,7 @@ Thanks for RTFM. ☺️
669
669
  [📌gitmoji]: https://gitmoji.dev
670
670
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
671
671
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
672
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-3.296-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
672
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-3.357-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
673
673
  [🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
674
674
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
675
675
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -560,8 +560,9 @@ module Kettle
560
560
  else
561
561
  Orderer.new(members: members, mode: config.order_mode, hints: config.order_hints).ordered
562
562
  end
563
- release_state_results = release_state_results_for_selection(config: config, members: ordered, only: options[:only])
564
- selected = Selection.new(members: ordered, release_state_results: release_state_results).apply(only: options[:only], exclude: options[:exclude], start_at: start_at.member)
563
+ effective_only = default_only_filter(command: command, only: options[:only])
564
+ release_state_results = release_state_results_for_selection(config: config, members: ordered, only: effective_only)
565
+ selected = Selection.new(members: ordered, release_state_results: release_state_results).apply(only: effective_only, exclude: options[:exclude], start_at: start_at.member)
565
566
  result_members = selected
566
567
  results = command_results(command: command, config: config, members: result_members, options: options, start_at: start_at)
567
568
  Report.new(
@@ -595,6 +596,14 @@ module Kettle
595
596
  ReleaseStateCheck.new(config: config, members: members).results
596
597
  end
597
598
 
599
+ def default_only_filter(command:, only:)
600
+ return only unless only.to_s.empty?
601
+ return "bump" if %w[bump bump-version].include?(command)
602
+ return "pending" if command == "release"
603
+
604
+ only
605
+ end
606
+
598
607
  def command_results_for_current_branch(command:, config:, members:, options:, start_at: StartAt.new(nil, nil))
599
608
  return bump_version_results(members: members, options: options, phase: command) if %w[bump bump-version].include?(command)
600
609
  return add_changelog_results(members: members, options: options) if command == "add-changelog"
@@ -6,14 +6,18 @@ require "open3"
6
6
  require "rbconfig"
7
7
  require "rubygems"
8
8
  require "securerandom"
9
+ require "yaml"
9
10
 
10
11
  module Kettle
11
12
  module Family
12
13
  class ReleaseStateCheck
14
+ KETTLE_JEM_CONFIG_PATHS = [".structuredmerge/kettle-jem.yml", ".kettle-jem.yml"].freeze
15
+
13
16
  def initialize(members:, config: nil)
14
17
  @members = members
15
18
  @config = config
16
19
  @github_latest_release_by_repo = {}
20
+ @transfer_changelog_lag_by_key = {}
17
21
  end
18
22
 
19
23
  def results
@@ -63,6 +67,7 @@ module Kettle
63
67
  state = state_with_computed_booleans(state) if success
64
68
  state = enrich_git_state(member.root, state) if success
65
69
  state = enrich_github_release(member.root, state) if success
70
+ state = enrich_transfer_changelog_lag(member.root, state) if success
66
71
  result(member: member, command: command, stdout: stdout, stderr: stderr, status: status.exitstatus, elapsed: elapsed, success: success, state: state, branch: branch)
67
72
  rescue JSON::ParserError => error
68
73
  result(member: member, command: command || release_state_command, stdout: stdout, stderr: stderr, status: 1, elapsed: elapsed || 0.0, success: false, state: {}, reason: "invalid release-state JSON: #{error.message}", branch: branch)
@@ -88,6 +93,7 @@ module Kettle
88
93
  state = branch_filtered_state(member, state, branch) if success && branch
89
94
  state = enrich_git_state(member.root, state) if success
90
95
  state = enrich_github_release(member.root, state) if success
96
+ state = enrich_transfer_changelog_lag(member.root, state) if success
91
97
  result(
92
98
  member: member,
93
99
  command: command,
@@ -283,7 +289,7 @@ module Kettle
283
289
  unreleased_entries = unreleased_entries?(content)
284
290
  prepared_release_pending = !version.to_s.empty? && latest_changelog_version == version
285
291
  ahead = commits_ahead_of_release(root, latest_changelog_version)
286
- enrich_github_release(root, state_with_computed_booleans(enrich_git_state(root, {
292
+ state = enrich_github_release(root, state_with_computed_booleans(enrich_git_state(root, {
287
293
  "gem_name" => config.family_name,
288
294
  "version" => version,
289
295
  "latest_released" => nil,
@@ -293,6 +299,7 @@ module Kettle
293
299
  "prepared_release_pending" => prepared_release_pending,
294
300
  "pending_release" => unreleased_entries || prepared_release_pending
295
301
  })))
302
+ enrich_transfer_changelog_lag(root, state)
296
303
  end
297
304
 
298
305
  def state_with_computed_booleans(state)
@@ -312,6 +319,74 @@ module Kettle
312
319
  state.merge("github_latest_release" => tag)
313
320
  end
314
321
 
322
+ def enrich_transfer_changelog_lag(root, state)
323
+ kettle_jem_state = kettle_jem_config_state(root)
324
+ return state unless kettle_jem_state
325
+
326
+ replay = kettle_jem_state["changelog_replay"]
327
+ replay = replay.is_a?(Hash) ? replay : {}
328
+ last_entry_key = replay["last_entry_key"] || replay[:last_entry_key]
329
+ lag = transfer_changelog_lag(last_entry_key)
330
+ return state unless lag
331
+
332
+ state.merge("transfer_changelog_lag" => lag.fetch("missing_count", 0).to_i)
333
+ end
334
+
335
+ def kettle_jem_config_state(root)
336
+ path = KETTLE_JEM_CONFIG_PATHS.map { |relative| File.join(root.to_s, relative) }.find { |candidate| File.file?(candidate) }
337
+ return nil unless path
338
+
339
+ data = YAML.safe_load_file(path, permitted_classes: [], aliases: false)
340
+ state = data.is_a?(Hash) ? data["kettle-jem"] : nil
341
+ state.is_a?(Hash) ? state : {}
342
+ rescue Psych::Exception, SystemCallError
343
+ nil
344
+ end
345
+
346
+ def transfer_changelog_lag(last_entry_key)
347
+ cache_key = last_entry_key.to_s
348
+ @transfer_changelog_lag_by_key.fetch(cache_key) do
349
+ @transfer_changelog_lag_by_key[cache_key] = in_process_transfer_changelog_lag(last_entry_key) || external_transfer_changelog_lag(cache_key)
350
+ end
351
+ rescue
352
+ nil
353
+ end
354
+
355
+ def in_process_transfer_changelog_lag(last_entry_key)
356
+ return nil unless load_kettle_jem_transfer_api
357
+
358
+ normalize_transfer_changelog_lag(::Kettle::Jem.transfer_changelog_lag(last_entry_key))
359
+ rescue
360
+ nil
361
+ end
362
+
363
+ def load_kettle_jem_transfer_api
364
+ return true if defined?(::Kettle::Jem) && ::Kettle::Jem.respond_to?(:transfer_changelog_lag)
365
+
366
+ require "kettle/jem"
367
+ defined?(::Kettle::Jem) && ::Kettle::Jem.respond_to?(:transfer_changelog_lag)
368
+ rescue LoadError
369
+ false
370
+ end
371
+
372
+ def external_transfer_changelog_lag(cache_key)
373
+ code = <<~RUBY
374
+ last_entry_key = ARGV.fetch(0, "")
375
+ last_entry_key = nil if last_entry_key.empty?
376
+ puts JSON.generate(Kettle::Jem.transfer_changelog_lag(last_entry_key))
377
+ RUBY
378
+ stdout, _stderr, status = Open3.capture3(RbConfig.ruby, "-rjson", "-rkettle/jem", "-e", code, cache_key)
379
+ status.success? ? normalize_transfer_changelog_lag(JSON.parse(stdout)) : nil
380
+ rescue JSON::ParserError, SystemCallError
381
+ nil
382
+ end
383
+
384
+ def normalize_transfer_changelog_lag(lag)
385
+ return nil unless lag.is_a?(Hash)
386
+
387
+ lag.each_with_object({}) { |(key, value), memo| memo[key.to_s] = value }
388
+ end
389
+
315
390
  def github_latest_release(root)
316
391
  repo = github_repo_slug(root)
317
392
  return nil unless repo
@@ -421,6 +421,7 @@ module Kettle
421
421
  lines << " prep: V.ch.md matches V.rb and is ready to publish"
422
422
  lines << " pend: unrel or prep"
423
423
  lines << " bump: unrel is yes and V.rb matches V.rel"
424
+ lines << " T📰: kettle-jem transfer changelog entries not yet replayed"
424
425
  rows = release_state_header
425
426
  results.each do |result|
426
427
  rows << release_state_row(result)
@@ -444,7 +445,8 @@ module Kettle
444
445
  state.fetch("version", "unknown").to_s,
445
446
  state.fetch("latest_changelog_version", nil).to_s.empty? ? "none" : state.fetch("latest_changelog_version").to_s,
446
447
  state.fetch("latest_released", nil).to_s.empty? ? "unknown" : state.fetch("latest_released").to_s,
447
- state.fetch("github_latest_release", nil).to_s.empty? ? "unknown" : state.fetch("github_latest_release").to_s,
448
+ release_state_github_release(state),
449
+ state.fetch("transfer_changelog_lag", 0).to_i.to_s,
448
450
  release_state_ahead_behind(state),
449
451
  yes_no(state.fetch("unreleased_entries", nil)),
450
452
  yes_no(state.fetch("prepared_release_pending", nil)),
@@ -457,12 +459,26 @@ module Kettle
457
459
  end
458
460
 
459
461
  def release_state_header
460
- header = [["gem", "checkout", "V.rb", "V.ch.md", "V.rel", "GH.rel", "🔼 / 🔽", "unrel", "prep", "pend", "bump"]]
462
+ header = [["gem", "checkout", "V.rb", "V.ch.md", "V.rel", "GH.rel", "T📰", "🔼 / 🔽", "unrel", "prep", "pend", "bump"]]
461
463
  return header unless release_state_has_branches?
462
464
 
463
465
  [["branch", *header.first]]
464
466
  end
465
467
 
468
+ def release_state_github_release(state)
469
+ github_release = state.fetch("github_latest_release", nil).to_s
470
+ return "unknown" if github_release.empty?
471
+
472
+ latest_released = state.fetch("latest_released", nil).to_s
473
+ return github_release if latest_released.empty? || latest_released == "unknown"
474
+
475
+ release_state_release_versions_match?(latest_released, github_release) ? github_release : "🔴 #{github_release}"
476
+ end
477
+
478
+ def release_state_release_versions_match?(latest_released, github_release)
479
+ latest_released.to_s.delete_prefix("v") == github_release.to_s.delete_prefix("v")
480
+ end
481
+
466
482
  def release_state_checkout(state)
467
483
  branch = state.fetch("current_branch", nil).to_s
468
484
  branch.empty? ? "unknown" : branch[0, 10]
@@ -3,7 +3,7 @@
3
3
  module Kettle
4
4
  module Family
5
5
  module Version
6
- VERSION = "1.1.8"
6
+ VERSION = "1.1.9"
7
7
  end
8
8
  VERSION = Version::VERSION # Traditional Constant Location
9
9
  end
@@ -1059,7 +1059,11 @@ module Kettle
1059
1059
  if command == "template"
1060
1060
  env["KETTLE_JEM_TEMPLATE_PROFILE"] = config.template_profile if config.template_profile
1061
1061
  env["KJ_REPOSITORY_TOPOLOGY"] = config.template_repository_topology if config.template_repository_topology
1062
- env["KETTLE_JEM_GIT_COMMIT_LOCK"] = template_git_commit_lock_path if monorepo_template?
1062
+ if monorepo_template?
1063
+ template_git_lock_path = template_git_commit_lock_path
1064
+ env["KETTLE_JEM_GIT_LOCK"] = template_git_lock_path
1065
+ env["KETTLE_JEM_GIT_COMMIT_LOCK"] = template_git_lock_path
1066
+ end
1063
1067
  corporate_sponsors = config.readme_corporate_sponsors
1064
1068
  unless corporate_sponsors.empty?
1065
1069
  env["KETTLE_JEM_CORPORATE_SPONSORS_JSON"] = JSON.generate(corporate_sponsors)
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kettle-family
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.8
4
+ version: 1.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -181,6 +181,26 @@ dependencies:
181
181
  - - ">="
182
182
  - !ruby/object:Gem::Version
183
183
  version: 1.0.4
184
+ - !ruby/object:Gem::Dependency
185
+ name: anonymous_loader
186
+ requirement: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - "~>"
189
+ - !ruby/object:Gem::Version
190
+ version: '0.1'
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: 0.1.3
194
+ type: :development
195
+ prerelease: false
196
+ version_requirements: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - "~>"
199
+ - !ruby/object:Gem::Version
200
+ version: '0.1'
201
+ - - ">="
202
+ - !ruby/object:Gem::Version
203
+ version: 0.1.3
184
204
  - !ruby/object:Gem::Dependency
185
205
  name: appraisal2
186
206
  requirement: !ruby/object:Gem::Requirement
@@ -319,10 +339,10 @@ licenses:
319
339
  - AGPL-3.0-only
320
340
  metadata:
321
341
  homepage_uri: https://kettle-family.galtzo.com
322
- source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.1.8
323
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.1.8/CHANGELOG.md
342
+ source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.1.9
343
+ changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.1.9/CHANGELOG.md
324
344
  bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
325
- documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.1.8
345
+ documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.1.9
326
346
  funding_uri: https://github.com/sponsors/pboling
327
347
  wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
328
348
  news_uri: https://www.railsbling.com/tags/kettle-family
metadata.gz.sig CHANGED
Binary file