kettle-family 1.2.14 → 1.2.15

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: 417f2f02e871abec85184783775b1449c32b0caac52e7b18b4744840af2ac17d
4
- data.tar.gz: c5013fc308ed12715d2184edf21ad6dea41dea2a5ae5df0bc521d6aba53b6c4a
3
+ metadata.gz: 9ccc9063ef81ee8d6b8bee27424903371b47863d9fb2903b4461c2e132b757d9
4
+ data.tar.gz: ec46a0708e899590fb95d67d1f8e9dbe002195b21cf9a635c2c8b4a726a693c9
5
5
  SHA512:
6
- metadata.gz: '091796994cdd4db8bb7e50d5cd8104e42a4680eaba541aa0897675ab835a3a51eed13ba63adbdd4fd1b8d099d426424d39f5b0a3b09e83235fba18f8dac63a65'
7
- data.tar.gz: e744049cdd349d806bf4f14db21781e94a57b1e8a0b4fcb71e132637d6dedbc80e389b82031e9871f8ae82473836f9e0e1dce8e75aa1a3bf9c2bb1e79a61e68f
6
+ metadata.gz: 883955e631cd8b19ee2219a98267465307dec049d35c7f209f61ce23465eeb05665a88954b0dcac39e70f980aa39a02bdc6f2539c4897ef6418412a7d5ad1054
7
+ data.tar.gz: 8c584b648f8fe2914ac5077b8b181f398b2fe1507eb30278927c13408f5b3b7e6c4a7b1beba417f1dcc1a0ae80a0e7a460d433bb1336615a6ccb3759e2abee34
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,19 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [1.2.15] - 2026-07-31
34
+
35
+ - TAG: [v1.2.15][1.2.15t]
36
+ - COVERAGE: 94.03% -- 4192/4458 lines in 28 files
37
+ - BRANCH COVERAGE: 76.46% -- 1634/2137 branches in 28 files
38
+ - 28.88% documented
39
+
40
+ ### Fixed
41
+
42
+ - Family-owned release OTP lookups now render as structured secret-provider
43
+ progress events, including a visible `👀 🔒` authorization prompt cue, instead
44
+ of printing loose MFA status lines through the release tape.
45
+
33
46
  ## [1.2.14] - 2026-07-31
34
47
 
35
48
  - TAG: [v1.2.14][1.2.14t]
@@ -1499,7 +1512,9 @@ Please file a bug if you notice a violation of semantic versioning.
1499
1512
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
1500
1513
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
1501
1514
 
1502
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.14...HEAD
1515
+ [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.15...HEAD
1516
+ [1.2.15]: https://github.com/kettle-dev/kettle-family/compare/v1.2.14...v1.2.15
1517
+ [1.2.15t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.15
1503
1518
  [1.2.14]: https://github.com/kettle-dev/kettle-family/compare/v1.2.13...v1.2.14
1504
1519
  [1.2.14t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.14
1505
1520
  [1.2.13]: https://github.com/kettle-dev/kettle-family/compare/v1.2.12...v1.2.13
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.431-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
822
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-4.458-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
@@ -12,10 +12,11 @@ module Kettle
12
12
  ].freeze
13
13
 
14
14
  class OtpCoordinator
15
- def initialize(input: $stdin, output: $stdout, queue_total: nil, secrets_provider: nil)
15
+ def initialize(input: $stdin, output: $stdout, queue_total: nil, secrets_provider: nil, event_handler: nil)
16
16
  @input = input
17
17
  @output = output
18
18
  @secrets_provider = secrets_provider
19
+ @event_handler = event_handler
19
20
  @mutex = Mutex.new
20
21
  @condition = ConditionVariable.new
21
22
  @prompting = false
@@ -41,7 +42,7 @@ module Kettle
41
42
  if @prompting
42
43
  generation = @generation
43
44
  @queued_count += 1
44
- render_queue_status_locked
45
+ render_queue_status_locked(member_name: member_name)
45
46
  return wait_for_response(generation)
46
47
  end
47
48
 
@@ -53,7 +54,7 @@ module Kettle
53
54
  start_prompt_locked(member_name: member_name)
54
55
  end
55
56
 
56
- response = read_response(chunk: chunk)
57
+ response = read_response(chunk: chunk, member_name: member_name)
57
58
  close_queue
58
59
  @mutex.synchronize do
59
60
  @response = response
@@ -67,7 +68,7 @@ module Kettle
67
68
 
68
69
  private
69
70
 
70
- attr_reader :secrets_provider
71
+ attr_reader :secrets_provider, :event_handler
71
72
 
72
73
  def wait_for_response(generation)
73
74
  @condition.wait(@mutex) while @prompting
@@ -83,22 +84,49 @@ module Kettle
83
84
  end
84
85
 
85
86
  def start_prompt_locked(member_name:)
86
- @output.puts
87
- @output.puts("[#{member_name}] RubyGems MFA requested.")
88
- render_queue_status_locked
87
+ unless emit_event(
88
+ member_name: member_name,
89
+ action: "mfa_requested",
90
+ label: "RubyGems MFA",
91
+ status: "requested",
92
+ mark: ">"
93
+ )
94
+ @output.puts
95
+ @output.puts("[#{member_name}] RubyGems MFA requested.")
96
+ end
97
+ render_queue_status_locked(member_name: member_name)
98
+ return if event_handler
99
+
89
100
  @output.puts("Queued prompts at entry will share this code; later prompts will ask again.")
90
101
  end
91
102
 
92
- def render_queue_status_locked
103
+ def render_queue_status_locked(member_name:)
93
104
  suffix = @queue_total ? " / #{@queue_total}" : ""
105
+ return if emit_event(
106
+ member_name: member_name,
107
+ action: "otp_queue",
108
+ label: "RubyGems MFA prompts",
109
+ status: "queued",
110
+ mark: ">",
111
+ queued: @queued_count,
112
+ total: @queue_total
113
+ )
114
+
94
115
  @output.puts("RubyGems MFA prompts queued: #{@queued_count}#{suffix}")
95
116
  @output.flush if @output.respond_to?(:flush)
96
117
  end
97
118
 
98
- def read_response(chunk:)
99
- provided = read_provider_response
119
+ def read_response(chunk:, member_name:)
120
+ provided = read_provider_response(member_name: member_name)
100
121
  unless provided.empty?
101
- @output.puts("RubyGems MFA code loaded from configured secrets provider.")
122
+ emit_event(
123
+ member_name: member_name,
124
+ action: "prompt_response",
125
+ label: "RubyGems MFA code",
126
+ source: secret_provider_source,
127
+ status: "ok",
128
+ mark: "."
129
+ ) || @output.puts("RubyGems MFA code loaded from configured secrets provider.")
102
130
  return provided
103
131
  end
104
132
 
@@ -117,17 +145,57 @@ module Kettle
117
145
  chunk.to_s.lines.last&.strip.to_s.empty? ? "Code:" : chunk.to_s.lines.last.strip
118
146
  end
119
147
 
120
- def read_provider_response
148
+ def read_provider_response(member_name:)
121
149
  return "" unless secrets_provider
122
150
 
123
151
  @output.print("\a") if @output.respond_to?(:print)
152
+ emit_event(
153
+ member_name: member_name,
154
+ action: "prompt_request",
155
+ label: "👀 🔒 watch for authorization prompt",
156
+ source: secret_provider_source,
157
+ status: "started",
158
+ mark: ">"
159
+ )
124
160
  secrets_provider.rubygems_otp.to_s
125
161
  rescue Error => error
126
162
  raise unless @input.respond_to?(:tty?) && @input.tty?
127
163
 
164
+ emit_event(
165
+ member_name: member_name,
166
+ action: "prompt_response",
167
+ label: "RubyGems MFA code",
168
+ source: secret_provider_source,
169
+ status: "failed",
170
+ mark: "F"
171
+ )
128
172
  @output.puts("#{error.message}; falling back to manual OTP entry.")
129
173
  ""
130
174
  end
175
+
176
+ def emit_event(member_name:, action:, label:, status:, mark:, **payload)
177
+ return false unless event_handler
178
+
179
+ event_handler.call(
180
+ member_name,
181
+ {
182
+ "event_version" => 1,
183
+ "type" => "secret_provider",
184
+ "action" => action,
185
+ "label" => label,
186
+ "status" => status,
187
+ "mark" => mark
188
+ }.merge(payload.transform_keys(&:to_s))
189
+ )
190
+ true
191
+ end
192
+
193
+ def secret_provider_source
194
+ return "" unless secrets_provider
195
+ return "1Password" if secrets_provider.is_a?(Secrets::OnePassword)
196
+
197
+ secrets_provider.class.name.to_s.split("::").last.to_s
198
+ end
131
199
  end
132
200
 
133
201
  FailureStatus = Struct.new(:exitstatus) do
@@ -31,7 +31,7 @@ module Kettle
31
31
  end
32
32
 
33
33
  def rubygems_otp
34
- super
34
+ with_release_secret_alert_suppressed { super }
35
35
  rescue => error
36
36
  raise Error, error.message
37
37
  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 = "1.2.14"
8
+ VERSION = "1.2.15"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
@@ -806,7 +806,29 @@ module Kettle
806
806
  def release_otp_coordinator
807
807
  return nil unless execute && release_command_interactive?
808
808
 
809
- @release_otp_coordinator ||= CommandRunner::OtpCoordinator.new(secrets_provider: @secrets_provider)
809
+ @release_otp_coordinator ||= CommandRunner::OtpCoordinator.new(
810
+ secrets_provider: @secrets_provider,
811
+ event_handler: method(:handle_release_otp_event)
812
+ )
813
+ end
814
+
815
+ def handle_release_otp_event(member_name, event)
816
+ progress = @release_progress
817
+ return unless progress
818
+
819
+ member = release_progress_member_for(member_name)
820
+ if progress_io
821
+ if verbose || debug
822
+ emit_release_event_progress(member, event)
823
+ elsif progress.tty?
824
+ emit_release_event_status(member, event, progress: progress)
825
+ end
826
+ end
827
+ end
828
+
829
+ def release_progress_member_for(member_name)
830
+ @release_progress_members_by_name ||= members.to_h { |member| [member.name, member] }
831
+ @release_progress_members_by_name.fetch(member_name.to_s) { PreflightProgressMember.new(member_name.to_s) }
810
832
  end
811
833
 
812
834
  def parallel_release_members?(release_members)
@@ -2214,6 +2236,7 @@ module Kettle
2214
2236
  parts << purpose unless purpose.empty?
2215
2237
  parts << label unless label.empty?
2216
2238
  parts << source if purpose.empty? && label.empty? && !source.empty?
2239
+ parts << "#{event["queued"]}/#{event["total"]}" if event["queued"] && event["total"]
2217
2240
  parts.join(":")
2218
2241
  end
2219
2242
 
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.14
4
+ version: 1.2.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -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.14
346
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.14/CHANGELOG.md
345
+ source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.15
346
+ changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.15/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.14
348
+ documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.15
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