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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +22 -1
- data/README.md +1 -1
- data/lib/kettle/family/cli.rb +44 -1
- data/lib/kettle/family/secrets.rb +14 -0
- data/lib/kettle/family/version.rb +1 -1
- data/lib/kettle/family/workflow.rb +114 -0
- data.tar.gz.sig +0 -0
- metadata +4 -4
- metadata.gz.sig +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f0da4c8794762df47df980870cdafa4ccbe25e6e1e0faae8b1b2ab13e512097
|
|
4
|
+
data.tar.gz: a8ad81e87f95aa544d51a21b3ba8b3cfa875d413939560ae603678e1ae9f64a1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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-
|
|
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
|
data/lib/kettle/family/cli.rb
CHANGED
|
@@ -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
|
|
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.
|
|
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.
|
|
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.
|
|
345
|
-
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.
|
|
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.
|
|
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
|
-
|
|
2
|
-
|
|
1
|
+
��w�<$��t�H�3��������X�+�Q�eG<�]yH=�W�mfZ�:���Z����jf��wZyԨ�2��^���3N��1�Թq�u��X��,�˅"���l�9n�.1�:f��}\�����h�g*�^L%�ɅQ��Pl"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��
|