kettle-family 1.2.10 → 1.2.11

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: 9a1ff4ce42122a1a8237a71d9310c158e18adc7fa0e9456e5928c99807f2ac30
4
- data.tar.gz: 5f3c242673496f42edb65830e644bebf77a121f777045efaa6116fd5b2d8d980
3
+ metadata.gz: 1f0da4c8794762df47df980870cdafa4ccbe25e6e1e0faae8b1b2ab13e512097
4
+ data.tar.gz: a8ad81e87f95aa544d51a21b3ba8b3cfa875d413939560ae603678e1ae9f64a1
5
5
  SHA512:
6
- metadata.gz: e4c06e78733cea8576c96392ebd353892b321ab997a0f3474c0e8d474a93d88bb585aee1a7d1a2618876046c72498ee898b26d192fd5ab73ef888728980776b5
7
- data.tar.gz: 052ec289211c7482c2321ac34233687370a40bce492c77670145f9c0fa76519ccff77700fbc28acd010e71fd2ee31e9b6ba448f295f9aab3dc072f9372723861
6
+ metadata.gz: d48cba6cd03e39932c805252a16d2f60bae16130b4ef2b3bba0ed650d35fea4a628e6be48a0ca33d6a8f625ef3f81e074aca43d16587f4a97ff2da2cb3533d0b
7
+ data.tar.gz: 577b907ce129337f155bfb0d498f00d14d8dcfb70ac3835ba57c62cf0d69431ba70e4cd81ce6a04463724e420ed36c7580e27fb08474752ca4e7eeb140cfce34
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,25 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [1.2.11] - 2026-07-30
34
+
35
+ - TAG: [v1.2.11][1.2.11t]
36
+ - COVERAGE: 93.78% -- 3784/4035 lines in 27 files
37
+ - BRANCH COVERAGE: 76.34% -- 1452/1902 branches in 27 files
38
+ - 27.83% documented
39
+
40
+ ### Added
41
+
42
+ - Release execution now prints a parent-process intent summary, a configurable
43
+ countdown, and visible release preflight progress before member release work
44
+ starts.
45
+
46
+ ### Changed
47
+
48
+ - Releases using a configured secrets provider now authorize that provider as
49
+ the first release preflight phase, making 1Password approval prompts appear
50
+ before slower checks can consume operator attention.
51
+
33
52
  ## [1.2.10] - 2026-07-30
34
53
 
35
54
  - TAG: [v1.2.10][1.2.10t]
@@ -1339,7 +1358,9 @@ Please file a bug if you notice a violation of semantic versioning.
1339
1358
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
1340
1359
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
1341
1360
 
1342
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.10...HEAD
1361
+ [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.11...HEAD
1362
+ [1.2.11]: https://github.com/kettle-dev/kettle-family/compare/v1.2.10...v1.2.11
1363
+ [1.2.11t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.11
1343
1364
  [1.2.10]: https://github.com/kettle-dev/kettle-family/compare/v1.2.9...v1.2.10
1344
1365
  [1.2.10t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.10
1345
1366
  [1.2.9]: https://github.com/kettle-dev/kettle-family/compare/v1.2.8...v1.2.9
data/README.md CHANGED
@@ -814,7 +814,7 @@ Thanks for RTFM. ☺️
814
814
  [📌gitmoji]: https://gitmoji.dev
815
815
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
816
816
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
817
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-3.934-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
817
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.035-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
818
818
  [🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
819
819
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
820
820
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -590,6 +590,7 @@ module Kettle
590
590
  release_state_results = release_state_results_for_selection(config: config, members: ordered, only: effective_only)
591
591
  selected = Selection.new(members: ordered, release_state_results: release_state_results).apply(only: effective_only, exclude: options[:exclude], start_at: start_at.member)
592
592
  result_members = selected
593
+ print_execution_intent(command: command, config: config, members: result_members, options: options, start_at: start_at)
593
594
  results = command_results(command: command, config: config, members: result_members, options: options, start_at: start_at)
594
595
  Report.new(
595
596
  family_name: config.family_name,
@@ -676,13 +677,55 @@ module Kettle
676
677
  end
677
678
 
678
679
  def progress_io(command, options)
679
- return nil unless command == "template"
680
+ return nil unless %w[release template].include?(command)
680
681
  return nil unless options[:execute]
681
682
  return nil if options[:json]
682
683
 
683
684
  stdout
684
685
  end
685
686
 
687
+ def print_execution_intent(command:, config:, members:, options:, start_at:)
688
+ return unless command == "release"
689
+ return unless options[:execute]
690
+ return if options[:json]
691
+
692
+ stdout.puts("release intent:")
693
+ stdout.puts(" family: #{config.family_name}")
694
+ stdout.puts(" mode: #{options[:publish] ? "publish" : "build-only"}")
695
+ selected_label = (members.length == 1) ? "1 member" : "#{members.length} members"
696
+ stdout.puts(" selected: #{selected_label}")
697
+ start_label = start_at.branch ? "#{start_at.member}@#{start_at.branch}" : start_at.member
698
+ stdout.puts(" start: #{start_label}") if start_at.member
699
+ stdout.puts(" members: #{members.map(&:name).join(", ")}")
700
+ stdout.flush
701
+ countdown_before_execution
702
+ end
703
+
704
+ def countdown_before_execution
705
+ seconds = release_countdown_seconds
706
+ return if seconds <= 0
707
+ return unless stdout.respond_to?(:tty?) && stdout.tty?
708
+
709
+ stdout.print("Continuing in #{seconds}...")
710
+ stdout.flush
711
+ seconds.downto(1) do |remaining|
712
+ sleep 1
713
+ next if remaining == 1
714
+
715
+ stdout.print(" #{remaining - 1}...")
716
+ stdout.flush
717
+ end
718
+ stdout.puts(" running...")
719
+ stdout.flush
720
+ end
721
+
722
+ def release_countdown_seconds
723
+ value = ENV.fetch("KETTLE_FAMILY_RELEASE_COUNTDOWN", "3")
724
+ Integer(value, 10)
725
+ rescue ArgumentError
726
+ 3
727
+ end
728
+
686
729
  def release_secrets_provider(command:, config:, options:)
687
730
  return nil unless command == "release"
688
731
 
@@ -18,6 +18,12 @@ module Kettle
18
18
  Kettle::Dev::ReleaseSecrets::OnePassword.configured?(name)
19
19
  end
20
20
 
21
+ def authorize!
22
+ gem_signing_passphrase
23
+ rescue => error
24
+ raise Error, error.message
25
+ end
26
+
21
27
  def gem_signing_passphrase
22
28
  super
23
29
  rescue => error
@@ -32,6 +38,10 @@ module Kettle
32
38
  end
33
39
  else
34
40
  class Provider
41
+ def authorize!
42
+ nil
43
+ end
44
+
35
45
  def gem_signing_passphrase
36
46
  nil
37
47
  end
@@ -64,6 +74,10 @@ module Kettle
64
74
  item_field("gem_signing_passphrase_field")
65
75
  end
66
76
 
77
+ def authorize!
78
+ gem_signing_passphrase
79
+ end
80
+
67
81
  def rubygems_otp
68
82
  reference = string_config("rubygems_otp_reference")
69
83
  return read_reference(reference) unless reference.empty?
@@ -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.10"
8
+ VERSION = "1.2.11"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
@@ -137,6 +137,16 @@ module Kettle
137
137
  end
138
138
 
139
139
  def results
140
+ if command == "release" && execute
141
+ release_preflight = release_preflight_results
142
+ return release_preflight unless release_preflight.empty?
143
+
144
+ return branch_target_results unless config.release_target_branches.empty?
145
+ return member_local_branch_target_results if member_local_branch_targets?
146
+
147
+ return current_branch_results(members)
148
+ end
149
+
140
150
  preflight = branch_checkout_dirty_preflight_results
141
151
  return preflight unless preflight.empty?
142
152
 
@@ -328,6 +338,79 @@ module Kettle
328
338
  end
329
339
  end
330
340
 
341
+ def release_preflight_results
342
+ phases = release_preflight_phases
343
+ emit_release_preflight_start(phases)
344
+ phases.each_with_index do |phase, index|
345
+ emit_release_preflight_phase(phase.fetch(:label), index: index, total: phases.length)
346
+ result = send(phase.fetch(:method))
347
+ emit_release_preflight_phase_finish(phase.fetch(:label), result)
348
+ return result unless result.empty?
349
+ end
350
+ emit_release_preflight_summary(phases)
351
+ []
352
+ end
353
+
354
+ def release_preflight_phases
355
+ dirty_phase = {label: "branch checkout readiness", method: :release_preflight_branch_checkout_dirty_results}
356
+ signing_phase = {label: "gem signing password", method: :release_preflight_signing_password_results}
357
+ secrets_phase = release_secrets_authorization_required? ? {label: "secrets provider authorization", method: :release_preflight_secrets_authorization_results} : nil
358
+
359
+ if secrets_phase
360
+ [secrets_phase, dirty_phase, signing_phase]
361
+ else
362
+ [dirty_phase, signing_phase]
363
+ end
364
+ end
365
+
366
+ def release_preflight_branch_checkout_dirty_results
367
+ branch_checkout_dirty_preflight_results
368
+ end
369
+
370
+ def release_preflight_secrets_authorization_results
371
+ authorize_release_secrets
372
+ []
373
+ rescue Error => error
374
+ [release_preflight_error_result("secrets_provider_authorization", error.message)]
375
+ end
376
+
377
+ def release_preflight_signing_password_results
378
+ prompt_for_gem_signing_password if release_signing_prompt_required?
379
+ []
380
+ rescue Error => error
381
+ [release_preflight_error_result("gem_signing_password", error.message)]
382
+ end
383
+
384
+ def authorize_release_secrets
385
+ secret = if @secrets_provider.respond_to?(:authorize!)
386
+ @secrets_provider.authorize!
387
+ else
388
+ @secrets_provider.gem_signing_passphrase
389
+ end
390
+ @gem_signing_password = secret.to_s unless secret.to_s.empty?
391
+ @gem_signing_password
392
+ end
393
+
394
+ def release_secrets_authorization_required?
395
+ @secrets_provider.is_a?(Secrets::OnePassword)
396
+ end
397
+
398
+ def release_preflight_error_result(phase, message)
399
+ CommandResult.new(
400
+ member_name: family_member.name,
401
+ phase: phase,
402
+ command: ["internal", phase],
403
+ workdir: config.root,
404
+ status: 1,
405
+ success: false,
406
+ stdout: "",
407
+ stderr: message,
408
+ elapsed_seconds: 0.0,
409
+ skipped: false,
410
+ reason: "release preflight failed"
411
+ )
412
+ end
413
+
331
414
  def branch_checkout_dirty_preflight_results
332
415
  return [] unless execute
333
416
  return [] if allow_dirty
@@ -1282,6 +1365,7 @@ module Kettle
1282
1365
 
1283
1366
  def release_signing_prompt_required?
1284
1367
  return false unless gem_signing_required?
1368
+ return false if @gem_signing_password && !@gem_signing_password.to_s.empty?
1285
1369
  return true if publish
1286
1370
 
1287
1371
  members.any? { |member| signed_gemspec?(member) }
@@ -1638,6 +1722,36 @@ module Kettle
1638
1722
  progress.summary("release summary: #{release_results.count(&:ok?)}/#{release_results.length} members ok")
1639
1723
  end
1640
1724
 
1725
+ def emit_release_preflight_start(phases)
1726
+ return unless progress_io
1727
+
1728
+ phase_label = (phases.length == 1) ? "phase" : "phases"
1729
+ progress_io.puts("release preflight #{phases.length} #{phase_label}:")
1730
+ progress_io.flush
1731
+ end
1732
+
1733
+ def emit_release_preflight_phase(label, index:, total:)
1734
+ return unless progress_io
1735
+
1736
+ progress_io.puts("[release preflight] (#{index + 1}/#{total}) > #{label}")
1737
+ progress_io.flush
1738
+ end
1739
+
1740
+ def emit_release_preflight_phase_finish(label, results)
1741
+ return unless progress_io
1742
+
1743
+ mark = results.empty? ? "." : "F"
1744
+ progress_io.puts("[release preflight] #{mark} #{label}")
1745
+ progress_io.flush
1746
+ end
1747
+
1748
+ def emit_release_preflight_summary(phases)
1749
+ return unless progress_io
1750
+
1751
+ progress_io.puts("release preflight summary: #{phases.length}/#{phases.length} phases ok")
1752
+ progress_io.flush
1753
+ end
1754
+
1641
1755
  def template_event_line_handler(member, progress: nil)
1642
1756
  lambda do |line|
1643
1757
  event = parse_template_event(line)
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.10
4
+ version: 1.2.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -341,10 +341,10 @@ licenses:
341
341
  - AGPL-3.0-only
342
342
  metadata:
343
343
  homepage_uri: https://kettle-family.galtzo.com
344
- source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.10
345
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.10/CHANGELOG.md
344
+ source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.11
345
+ changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.11/CHANGELOG.md
346
346
  bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
347
- documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.10
347
+ documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.11
348
348
  funding_uri: https://github.com/sponsors/pboling
349
349
  wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
350
350
  news_uri: https://www.railsbling.com/tags/kettle-family
metadata.gz.sig CHANGED
@@ -1,2 +1,3 @@
1
- ��`є4S�<����<2(s��:Q��81u �2�B��Y�o :�z�hĽwqg���\d���e��Tf�|ft���fq+U��O2}Z��,A�"�ZTb�^�S�?�E1S�����f9 ����TEּ�έ�jӶ�f��0����i
2
- S��ai�\R�bc�˔gR��b����$��m\�-�4g��֠ :/�sҜ�s#�v���'C�G�Wj����s ���v�*�@o�#O��i �ҩ������j���1�b0��xz"P�k����2xEt]�6���ϕz���U�/��Zc���2B��Q�r�g��rK��+0��(ͤ�� "5#�s���*�kD�@��c�Nb �e���&�Җ��
1
+ ��w�<$��t�H�3��������X�+�Q�eG<�]yH=�WmfZ�:���Z����jf��wZyԨ�2��^���3N��1�Թqu��X��,�˅"���l9n�.1�:f��}\�����hg*� ^L%�ɅQ��P l"4"���{����^C/�<=UsYV�/ ����x�@�cT_۷�=����n]���I����:�t'i��&�7 VI_}g5�i��Y
2
+ �@��������c�1�:?�d
3
+ ]���؈8��i�0d~�P(;dIzl@u0��� �!�|3w��Ҵcf�����v��1P�g5Z��:%��6D�P��A�%*J�m�2���C_ј\�9��& &Q���3�2 �r��8`�.%�n.恭D��