kettle-family 1.2.17 → 1.2.18

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: cb71df81d320a71ba34e6f2f09233f8a02a59f84f266c3e55e92acbe6c35cae1
4
- data.tar.gz: 0dd9fa2f1ef0f3ec59f4bbee09992245b03dc4fb8f991807d5b8eac70a817c3b
3
+ metadata.gz: e868e193e146eeb559019b4f24a860b25ccacee6f86a6f4b000fd2cdc101217d
4
+ data.tar.gz: c3dc9a386dde8a4d6ff1ee6918c966041d47b5316f96510b9c664c36a808fab5
5
5
  SHA512:
6
- metadata.gz: 2b81c2dc8313b89e2777a60fddadd2d459c8db4c5ba723f26aedb66eebec1d6e6fa91a7d4ebc57d9e040a87d1703b5cdb86758b256fb76e8e9621c999d1eeb19
7
- data.tar.gz: afe0839807601723753b1e5c78849648a2af65bf19ee13b9a2fbe50e1e3e11d4980e5ed82a9d214f1bdddfacf299e1483c9c8d30884bb1daeb5406abbfeda5d1
6
+ metadata.gz: d4665f48921ce7e0d339c17ea305790da6dd2ee4f5739df885535f99467a52db9ead2090cfca4e6c927e5b35106177f9642f97fdb86d77d06b872715773a441f
7
+ data.tar.gz: 43565531dce057aebe8f8bdc441f0d90cf33940dc13278eda9a55c0c5a02aa1a5aeb809a75fecc9b26131e12b294ab13be4fe69fbc90e19b48777911f2bf3b23
checksums.yaml.gz.sig CHANGED
@@ -1,4 +1 @@
1
- b��d];�ڑc���g���k�:Q��˺#��P�=��\W��D���@r'��~��fd�*�6����><<!7���x��іաcY��%�
2
- ���Y�`�U�����NN����fX*��&�m�n��6�&[[Q����r�==�<T�)�e�/�R�?�E�*���\����8�11��!E�P`���nF���<�ړ�
3
- ���*{�?�v�ݗ���V�B6�L
4
- �}s瘢,:�i2cr ���l�P-�gB�j+��]��T�K�0�R��j��I}]v+X`L�&6/3HTPq�{H��R!"���]��]WM)ǽ^i�/g���R�O{/-�����Ҕ ����k�ŀ(��L\ID����|X��6#R�w9�����-���Pܗ^Y� ���x��O�
1
+ ׳�_�/4�:���� s'e�s�$% �Ψ��a��_n�fK1;����|�S3?E�1�2�W9O9J�ak�\O�"��L:��P�ĸ��8́"��z� *)�6�r����p��Ndg����7Fb��%>ʹ���٢Uw�2���h.�Ne��vi�4�V�M���2�L�Hp
data/CHANGELOG.md CHANGED
@@ -30,6 +30,21 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [1.2.18] - 2026-07-31
34
+
35
+ - TAG: [v1.2.18][1.2.18t]
36
+ - COVERAGE: 94.14% -- 4277/4543 lines in 28 files
37
+ - BRANCH COVERAGE: 76.75% -- 1680/2189 branches in 28 files
38
+ - 28.88% documented
39
+
40
+ ### Added
41
+
42
+ - Family template progress and final reports now aggregate checksum write
43
+ bypasses, rendered-but-unchanged files, and changed files from Kettle-Jem
44
+ NDJSON summaries.
45
+ - Template summaries now distinguish exact checksum hits from source-matched
46
+ local destination changes protected by the template-only checksum policy.
47
+
33
48
  ## [1.2.17] - 2026-07-31
34
49
 
35
50
  - TAG: [v1.2.17][1.2.17t]
@@ -1540,7 +1555,9 @@ Please file a bug if you notice a violation of semantic versioning.
1540
1555
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
1541
1556
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
1542
1557
 
1543
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.17...HEAD
1558
+ [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.18...HEAD
1559
+ [1.2.18]: https://github.com/kettle-dev/kettle-family/compare/v1.2.17...v1.2.18
1560
+ [1.2.18t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.18
1544
1561
  [1.2.17]: https://github.com/kettle-dev/kettle-family/compare/v1.2.16...v1.2.17
1545
1562
  [1.2.17t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.17
1546
1563
  [1.2.16]: https://github.com/kettle-dev/kettle-family/compare/v1.2.15...v1.2.16
data/README.md CHANGED
@@ -819,7 +819,7 @@ Thanks for RTFM. ☺️
819
819
  [📌gitmoji]: https://gitmoji.dev
820
820
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
821
821
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
822
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.490-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
822
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.543-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
823
823
  [🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
824
824
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
825
825
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -309,8 +309,15 @@ module Kettle
309
309
  return if template_results.empty?
310
310
 
311
311
  changed_files = template_results.sum { |result| template_changed_file_count(result) }
312
+ outcome_counts = template_results.map { |result| template_file_outcomes(result) }
312
313
  lines << "template summary:"
313
314
  lines << " #{template_results.count(&:ok?)}/#{template_results.length} members ok"
315
+ if outcome_counts.all?
316
+ lines << " #{outcome_counts.sum { |outcomes| outcomes.fetch(:checksum_hits) }} checksum hits"
317
+ protected = outcome_counts.sum { |outcomes| outcomes.fetch(:checksum_protected) }
318
+ lines << " #{protected} checksum-protected changes" if protected.positive?
319
+ lines << " #{outcome_counts.sum { |outcomes| outcomes.fetch(:unchanged) }} unchanged"
320
+ end
314
321
  lines << " #{changed_files} file#{"s" unless changed_files == 1} changed"
315
322
  end
316
323
 
@@ -339,6 +346,20 @@ module Kettle
339
346
  summary&.fetch("changed_count")&.to_i
340
347
  end
341
348
 
349
+ def template_file_outcomes(result)
350
+ summary = template_events(result.stdout).reverse.find do |event|
351
+ event["type"] == "summary" && event.key?("checksum_hit_count") &&
352
+ event.key?("checksum_protected_count") && event.key?("unchanged_count")
353
+ end
354
+ return unless summary
355
+
356
+ {
357
+ checksum_hits: summary.fetch("checksum_hit_count").to_i,
358
+ checksum_protected: summary.fetch("checksum_protected_count").to_i,
359
+ unchanged: summary.fetch("unchanged_count").to_i
360
+ }
361
+ end
362
+
342
363
  def template_events(output)
343
364
  output.to_s.each_line.filter_map do |line|
344
365
  payload = JSON.parse(line)
@@ -5,7 +5,7 @@ module Kettle
5
5
  # Version namespace for this gem.
6
6
  module Version
7
7
  # Current gem version.
8
- VERSION = "1.2.17"
8
+ VERSION = "1.2.18"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
@@ -513,8 +513,8 @@ module Kettle
513
513
  dirty_paths = GitStatus.dirty_paths(member.root)
514
514
  next if dirty_paths.empty?
515
515
 
516
- if recoverable_template_lockfile_dirt?(member, dirty_paths)
517
- recover_template_lockfiles(member: member, runner: command_runner, memo: results, phase: "template_lockfile_recovery")
516
+ if recoverable_template_lockfile_dirt?(dirty_paths)
517
+ recover_dirty_template_lockfile_before_branch_checkout(member: member, runner: command_runner, memo: results)
518
518
  next if results.last&.ok? && GitStatus.dirty_paths(member.root).empty?
519
519
  end
520
520
 
@@ -522,11 +522,25 @@ module Kettle
522
522
  end
523
523
  end
524
524
 
525
- def recoverable_template_lockfile_dirt?(member, dirty_paths)
525
+ def recoverable_template_lockfile_dirt?(dirty_paths)
526
526
  return false unless command == "template" && config.normalize_lockfiles?
527
527
  return false unless dirty_paths.length == 1 && dirty_paths.first.end_with?(" Gemfile.lock")
528
528
 
529
- release_lockfile_has_local_path_remote?(member)
529
+ true
530
+ end
531
+
532
+ def recover_dirty_template_lockfile_before_branch_checkout(member:, runner:, memo:)
533
+ if release_lockfile_has_local_path_remote?(member)
534
+ recover_template_lockfiles(member: member, runner: runner, memo: memo, phase: "template_lockfile_recovery")
535
+ else
536
+ commit_normalized_lockfiles(
537
+ branch_members: [member],
538
+ runner: runner,
539
+ memo: memo,
540
+ reason: "template",
541
+ force: true
542
+ )
543
+ end
530
544
  end
531
545
 
532
546
  def branch_checkout_preflight_required?
@@ -1897,9 +1911,15 @@ module Kettle
1897
1911
  index = argv.index("kettle-jem")
1898
1912
  return command_text unless index
1899
1913
 
1900
- argv[0...index] + [RbConfig.ruby, executable] + argv[(index + 1)..]
1914
+ prefix = argv[0...index]
1915
+ prefix = prefix[0...-2] if prefix.last(2) == %w[bundle exec]
1916
+ prefix + [RbConfig.ruby, executable] + argv[(index + 1)..]
1901
1917
  else
1902
- command_text.to_s.sub(/\bkettle-jem\b/, "#{Shellwords.escape(RbConfig.ruby)} #{Shellwords.escape(executable)}")
1918
+ replacement = "#{Shellwords.escape(RbConfig.ruby)} #{Shellwords.escape(executable)}"
1919
+ text = command_text.to_s
1920
+ return text.sub(/\bbundle\s+exec\s+kettle-jem\b/, replacement) if text.match?(/\bbundle\s+exec\s+kettle-jem\b/)
1921
+
1922
+ text.sub(/\bkettle-jem\b/, replacement)
1903
1923
  end
1904
1924
  end
1905
1925
 
@@ -2040,8 +2060,7 @@ module Kettle
2040
2060
  end
2041
2061
 
2042
2062
  def template_member_finish_status(result)
2043
- changed_files = template_changed_file_count(result)
2044
- "#{changed_files} file#{"s" unless changed_files == 1} changed"
2063
+ template_summary_label(template_changed_file_count(result), template_file_outcomes(result))
2045
2064
  end
2046
2065
 
2047
2066
  def release_member_finish_status(result)
@@ -2053,8 +2072,9 @@ module Kettle
2053
2072
 
2054
2073
  template_results = results.select { |result| result.phase == "template" }
2055
2074
  changed_files = template_results.sum { |result| template_changed_file_count(result) }
2075
+ outcome_counts = template_results.map { |result| template_file_outcomes(result) }
2056
2076
  progress.stop
2057
- progress.summary("template summary: #{template_results.count(&:ok?)}/#{template_results.length} members ok, #{changed_files} file#{"s" unless changed_files == 1} changed")
2077
+ progress.summary(template_progress_summary_label(template_results, changed_files, outcome_counts))
2058
2078
  end
2059
2079
 
2060
2080
  def emit_release_progress_summary(results, progress:)
@@ -2119,7 +2139,7 @@ module Kettle
2119
2139
  when "diagnostic"
2120
2140
  emit_template_event_line(member, "!", diagnostic_event_label(event))
2121
2141
  when "summary"
2122
- emit_template_event_line(member, "done", "#{event["changed_count"].to_i} file#{"s" unless event["changed_count"].to_i == 1} changed")
2142
+ emit_template_event_line(member, "done", template_event_summary_label(event))
2123
2143
  end
2124
2144
  end
2125
2145
 
@@ -2134,7 +2154,7 @@ module Kettle
2134
2154
  when "diagnostic"
2135
2155
  diagnostic_event_label(event)
2136
2156
  when "summary"
2137
- "#{event["changed_count"].to_i} file#{"s" unless event["changed_count"].to_i == 1} changed"
2157
+ template_event_summary_label(event)
2138
2158
  end
2139
2159
  mark = template_event_status_mark(event)
2140
2160
  progress&.update(member, status: status, mark: mark) if status && !status.empty?
@@ -2394,6 +2414,61 @@ module Kettle
2394
2414
  summary&.fetch("changed_count")&.to_i
2395
2415
  end
2396
2416
 
2417
+ def template_file_outcomes(result)
2418
+ summaries = result.stdout.to_s.lines.filter_map do |line|
2419
+ event = parse_template_event(line)
2420
+ event if event && event["type"] == "summary"
2421
+ end
2422
+ summary = summaries.reverse.find { |event| template_file_outcome_event?(event) }
2423
+ return unless summary
2424
+
2425
+ {
2426
+ checksum_hits: summary.fetch("checksum_hit_count").to_i,
2427
+ checksum_protected: summary.fetch("checksum_protected_count").to_i,
2428
+ unchanged: summary.fetch("unchanged_count").to_i
2429
+ }
2430
+ end
2431
+
2432
+ def template_file_outcome_event?(event)
2433
+ event.key?("checksum_hit_count") && event.key?("checksum_protected_count") && event.key?("unchanged_count")
2434
+ end
2435
+
2436
+ def template_event_summary_label(event)
2437
+ template_summary_label(
2438
+ event["changed_count"].to_i,
2439
+ template_file_outcome_event?(event) ? {
2440
+ checksum_hits: event.fetch("checksum_hit_count").to_i,
2441
+ checksum_protected: event.fetch("checksum_protected_count").to_i,
2442
+ unchanged: event.fetch("unchanged_count").to_i
2443
+ } : nil
2444
+ )
2445
+ end
2446
+
2447
+ def template_progress_summary_label(template_results, changed_files, outcome_counts)
2448
+ label = "template summary: #{template_results.count(&:ok?)}/#{template_results.length} members ok"
2449
+ return "#{label}, #{template_summary_label(changed_files)}" unless outcome_counts.all?
2450
+
2451
+ totals = outcome_counts.each_with_object({checksum_hits: 0, checksum_protected: 0, unchanged: 0}) do |outcomes, memo|
2452
+ memo[:checksum_hits] += outcomes.fetch(:checksum_hits)
2453
+ memo[:checksum_protected] += outcomes.fetch(:checksum_protected)
2454
+ memo[:unchanged] += outcomes.fetch(:unchanged)
2455
+ memo
2456
+ end
2457
+ "#{label}, #{template_summary_label(changed_files, totals)}"
2458
+ end
2459
+
2460
+ def template_summary_label(changed_files, outcomes = nil)
2461
+ changed_label = "#{changed_files} file#{"s" unless changed_files == 1} changed"
2462
+ return changed_label unless outcomes
2463
+
2464
+ protected_label = if outcomes.fetch(:checksum_protected).positive?
2465
+ ", #{outcomes.fetch(:checksum_protected)} checksum-protected changes"
2466
+ else
2467
+ ""
2468
+ end
2469
+ "#{outcomes.fetch(:checksum_hits)} checksum hits#{protected_label}, #{outcomes.fetch(:unchanged)} unchanged, #{changed_label}"
2470
+ end
2471
+
2397
2472
  def normalize_lockfiles(member:, runner:, memo:, phase:)
2398
2473
  return unless config.normalize_lockfiles?
2399
2474
 
@@ -2574,6 +2649,22 @@ module Kettle
2574
2649
  command: template_prepare_command(member),
2575
2650
  env: template_prepare_env
2576
2651
  )
2652
+ if recoverable_bundle_failure?(result)
2653
+ recover_template_lockfiles(
2654
+ member: member,
2655
+ runner: runner,
2656
+ memo: memo,
2657
+ phase: "prepare_template_dependencies_recovery"
2658
+ )
2659
+ return false unless memo.last&.ok?
2660
+
2661
+ result = runner.call(
2662
+ member: member,
2663
+ phase: "prepare_template_dependencies",
2664
+ command: template_prepare_command(member),
2665
+ env: template_prepare_env
2666
+ )
2667
+ end
2577
2668
  memo << result
2578
2669
  result.ok?
2579
2670
  end
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.2.17
4
+ version: 1.2.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -230,7 +230,7 @@ dependencies:
230
230
  version: '3.2'
231
231
  - - ">="
232
232
  - !ruby/object:Gem::Version
233
- version: 3.2.3
233
+ version: 3.2.4
234
234
  type: :development
235
235
  prerelease: false
236
236
  version_requirements: !ruby/object:Gem::Requirement
@@ -240,7 +240,7 @@ dependencies:
240
240
  version: '3.2'
241
241
  - - ">="
242
242
  - !ruby/object:Gem::Version
243
- version: 3.2.3
243
+ version: 3.2.4
244
244
  - !ruby/object:Gem::Dependency
245
245
  name: ruby-progressbar
246
246
  requirement: !ruby/object:Gem::Requirement
@@ -264,7 +264,7 @@ dependencies:
264
264
  version: '1.0'
265
265
  - - ">="
266
266
  - !ruby/object:Gem::Version
267
- version: 1.0.7
267
+ version: 1.0.8
268
268
  type: :development
269
269
  prerelease: false
270
270
  version_requirements: !ruby/object:Gem::Requirement
@@ -274,7 +274,7 @@ dependencies:
274
274
  version: '1.0'
275
275
  - - ">="
276
276
  - !ruby/object:Gem::Version
277
- version: 1.0.7
277
+ version: 1.0.8
278
278
  - !ruby/object:Gem::Dependency
279
279
  name: gitmoji-regex
280
280
  requirement: !ruby/object:Gem::Requirement
@@ -284,7 +284,7 @@ dependencies:
284
284
  version: '2.0'
285
285
  - - ">="
286
286
  - !ruby/object:Gem::Version
287
- version: 2.0.10
287
+ version: 2.0.11
288
288
  type: :development
289
289
  prerelease: false
290
290
  version_requirements: !ruby/object:Gem::Requirement
@@ -294,7 +294,7 @@ dependencies:
294
294
  version: '2.0'
295
295
  - - ">="
296
296
  - !ruby/object:Gem::Version
297
- version: 2.0.10
297
+ version: 2.0.11
298
298
  description: "\U0001F469‍\U0001F469‍\U0001F467‍\U0001F467 Kettle::Family provides
299
299
  scripts and conventions for coordinating related Ruby gems as one family."
300
300
  email:
@@ -342,10 +342,10 @@ licenses:
342
342
  - AGPL-3.0-only
343
343
  metadata:
344
344
  homepage_uri: https://kettle-family.galtzo.com
345
- source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.17
346
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.17/CHANGELOG.md
345
+ source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.18
346
+ changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.18/CHANGELOG.md
347
347
  bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
348
- documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.17
348
+ documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.18
349
349
  funding_uri: https://github.com/sponsors/pboling
350
350
  wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
351
351
  news_uri: https://www.railsbling.com/tags/kettle-family
metadata.gz.sig CHANGED
Binary file