kettle-family 1.2.43 → 1.2.44

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: 7523326b5f3b8b9054549b7165c52239703ac08777908b191f9f52cc83b0bd1c
4
- data.tar.gz: aa023d785f2ca0ebe7b5ac28618e0a286f906ae0f14afd7f11e500148ab1ee83
3
+ metadata.gz: 58ad6d307a8fcb8dc7a2911a7b1cb1c62928e130af9df80e13466158b5dc0441
4
+ data.tar.gz: 0e296343ddab05057267fc2f698cc0c9292d491bfd20c300dcc117d89b3b9346
5
5
  SHA512:
6
- metadata.gz: dd3e08b8bfe3630ad23053bf62c1b45daa94e3e2b662997745f03356dd83c1e8dd1d8432cb450fed2b38f520243f729b497f43daa20fc51607a1cadea67df807
7
- data.tar.gz: 04a575545f930613a4eed5b39991949143acf9718c41eb2c764e73a884e253364ab16cec68ba4c606aaefb29653d523bc643d4057f04adeacfd9c102cf578c61
6
+ metadata.gz: 3d6ffcef4f61673a66ad8c3dc816a8fcc37c09c8b07a3ef0f2fa8b3a777517f110689922edce9c9b7f76cc5f5d9a9c64409df30e73f9c5f49df4bdd0bf10b5e0
7
+ data.tar.gz: 58d68a7bc24ef399531c17f42fb3e6b6f8d3bc03db7dd5b18049721b0c9d4372c0f1ac4209d587f28d63e2665e7ee9c7a092cbfb1ae0ca7974b8146c4843ab22
checksums.yaml.gz.sig CHANGED
Binary file
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.44] - 2026-08-09
34
+
35
+ - TAG: [v1.2.44][1.2.44t]
36
+ - COVERAGE: 93.57% -- 5091/5441 lines in 32 files
37
+ - BRANCH COVERAGE: 76.05% -- 2042/2685 branches in 32 files
38
+ - 29.72% documented
39
+
40
+ ### Changed
41
+
42
+ - Review deduplicated GitHub Actions metadata once per family, then run member pin updates in strict offline mode with configurable cache TTL.
43
+
44
+ ### Fixed
45
+
46
+ - Buffer interactive release prompts across PTY reads so split RubyGems MFA prompts are answered and surfaced through the dedicated notification line.
47
+
33
48
  ## [1.2.43] - 2026-08-08
34
49
 
35
50
  - TAG: [v1.2.43][1.2.43t]
@@ -1962,7 +1977,9 @@ Please file a bug if you notice a violation of semantic versioning.
1962
1977
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
1963
1978
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
1964
1979
 
1965
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.43...HEAD
1980
+ [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.44...HEAD
1981
+ [1.2.44]: https://github.com/kettle-dev/kettle-family/compare/v1.2.43...v1.2.44
1982
+ [1.2.44t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.44
1966
1983
  [1.2.43]: https://github.com/kettle-dev/kettle-family/compare/v1.2.42...v1.2.43
1967
1984
  [1.2.43t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.43
1968
1985
  [1.2.42]: https://github.com/kettle-dev/kettle-family/compare/v1.2.41...v1.2.42
data/README.md CHANGED
@@ -398,11 +398,16 @@ members:
398
398
  kettle-family gha-sha-pins
399
399
  kettle-family gha-sha-pins --execute
400
400
  kettle-family gha-sha-pins --execute --upgrade minor
401
+ kettle-family gha-sha-pins --execute --upgrade major --ttl 0
401
402
  ```
402
403
 
403
- The family command name is retained for compatibility, but it delegates to
404
- the current `kettle-gha-pins` executable for each selected member. The older
405
- `kettle-gha-sha-pins` executable is a legacy direct entrypoint.
404
+ The family command inventories action repositories across the selected members,
405
+ reviews the deduplicated set once using the shared cache, and then runs each
406
+ member's pin update in strict offline mode. `--ttl DAYS` controls when the
407
+ family review refreshes cached action metadata; `--ttl 0` forces a refresh. The
408
+ member-level `--upgrade` and `--check --cooldown-days DAYS` options remain
409
+ responsible for deciding whether individual pins need changes or should block a
410
+ release. The older `kettle-gha-sha-pins` executable is a legacy direct entrypoint.
406
411
 
407
412
  Run release prep/build phases without publishing:
408
413
 
@@ -845,7 +850,7 @@ Thanks for RTFM. ☺️
845
850
  [📌gitmoji]: https://gitmoji.dev
846
851
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
847
852
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
848
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-5.402-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
853
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-5.441-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
849
854
  [🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
850
855
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
851
856
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -156,6 +156,7 @@ module Kettle
156
156
  check: truthy_option?(:check),
157
157
  from_version: nil,
158
158
  gha_sha_pins_upgrade: "patch",
159
+ gha_sha_pins_ttl_days: options[:ttl],
159
160
  publish: false,
160
161
  release_start_step: nil,
161
162
  release_skip_steps: nil,
@@ -389,10 +390,15 @@ module Kettle
389
390
  option :upgrade, value: {type: String, usage: "LEVEL"}, desc: "SHA pin upgrade strategy: major, minor, patch" do |value|
390
391
  options[:upgrade] = parse_gha_sha_pins_upgrade(value)
391
392
  end
393
+ option :ttl, value: {type: Float, usage: "DAYS"}, desc: "Cache TTL for action metadata review (default: 1 day)" do |value|
394
+ raise OptionParser::InvalidArgument, "--ttl must be non-negative" if value.negative?
395
+
396
+ options[:ttl] = value
397
+ end
392
398
 
393
399
  def run(*args)
394
400
  unexpected_arguments!(args)
395
- run_family("gha-sha-pins", gha_sha_pins_upgrade: options[:upgrade] || "patch")
401
+ run_family("gha-sha-pins", gha_sha_pins_upgrade: options[:upgrade] || "patch", gha_sha_pins_ttl_days: options[:ttl])
396
402
  end
397
403
  end
398
404
 
@@ -783,6 +789,7 @@ module Kettle
783
789
  auto_dependency_floors: options[:release_auto_dependency_floors],
784
790
  gha_sha_pins_upgrade: options[:gha_sha_pins_upgrade],
785
791
  gha_sha_pins_check: options[:check],
792
+ gha_sha_pins_ttl_days: options[:gha_sha_pins_ttl_days],
786
793
  env_overrides: options[:workflow_env],
787
794
  debug: options[:debug],
788
795
  verbose: options[:verbose],
@@ -349,6 +349,7 @@ module Kettle
349
349
  stdout = +""
350
350
  stderr = +""
351
351
  stdout_line_buffer = +""
352
+ prompt_buffer = +""
352
353
  status = nil
353
354
  PTY.spawn(env, *argv, chdir: chdir, **process_options) do |output, input, pid|
354
355
  begin
@@ -362,7 +363,7 @@ module Kettle
362
363
  stdout << chunk
363
364
  transcript_write(log_io, chunk)
364
365
  stdout_line_buffer = print_interactive_stdout(stdout_line_buffer, chunk, stdout_line_handler, passthrough_output: passthrough_output)
365
- handle_interactive_prompt(input, chunk, member_name: member_name)
366
+ prompt_buffer = handle_interactive_prompt(input, chunk, member_name: member_name, prompt_buffer: prompt_buffer)
366
367
  else
367
368
  chunk = $stdin.readpartial(1024)
368
369
  input.write(chunk)
@@ -387,6 +388,7 @@ module Kettle
387
388
  captured_stdout = +""
388
389
  captured_stderr = +""
389
390
  stdout_line_buffer = +""
391
+ prompt_buffer = +""
390
392
  status = nil
391
393
  Open3.popen3(env, *argv, chdir: chdir, **process_options) do |input, output, error, wait_thread|
392
394
  readers = [output, error]
@@ -398,7 +400,7 @@ module Kettle
398
400
  if reader.equal?($stdin)
399
401
  input.write($stdin.readpartial(1024))
400
402
  else
401
- stdout_line_buffer = read_interactive_stream(
403
+ stdout_line_buffer, prompt_buffer = read_interactive_stream(
402
404
  reader,
403
405
  output,
404
406
  input,
@@ -409,7 +411,8 @@ module Kettle
409
411
  stdout_line_buffer: stdout_line_buffer,
410
412
  stdout_line_handler: stdout_line_handler,
411
413
  log_io: log_io,
412
- passthrough_output: passthrough_output
414
+ passthrough_output: passthrough_output,
415
+ prompt_buffer: prompt_buffer
413
416
  )
414
417
  end
415
418
  end
@@ -437,6 +440,7 @@ module Kettle
437
440
  member_name:,
438
441
  stdout_line_buffer:,
439
442
  stdout_line_handler:,
443
+ prompt_buffer:,
440
444
  log_io: nil,
441
445
  passthrough_output: true
442
446
  )
@@ -450,11 +454,11 @@ module Kettle
450
454
  transcript_write(log_io, chunk)
451
455
  $stderr.print(chunk) if passthrough_output
452
456
  end
453
- handle_interactive_prompt(input, chunk, member_name: member_name)
454
- stdout_line_buffer
457
+ prompt_buffer = handle_interactive_prompt(input, chunk, member_name: member_name, prompt_buffer: prompt_buffer)
458
+ [stdout_line_buffer, prompt_buffer]
455
459
  rescue EOFError
456
460
  readers.delete(reader)
457
- stdout_line_buffer
461
+ [stdout_line_buffer, prompt_buffer]
458
462
  end
459
463
 
460
464
  def with_transcript(log_path, argv:, chdir:)
@@ -528,18 +532,26 @@ module Kettle
528
532
  input.flush
529
533
  end
530
534
 
531
- def handle_interactive_prompt(input, chunk, member_name: nil)
532
- if otp_prompt?(chunk)
533
- write_otp_response(input, chunk, member_name: member_name) if otp_coordinator && otp_response_prompt?(chunk)
534
- return
535
+ def handle_interactive_prompt(input, chunk, member_name: nil, prompt_buffer: nil)
536
+ prompt_buffer = prompt_buffer.to_s.dup
537
+ prompt_buffer << chunk.to_s
538
+ prompt_buffer = prompt_buffer.byteslice([prompt_buffer.bytesize - 4096, 0].max, 4096).to_s
539
+
540
+ if otp_prompt?(prompt_buffer)
541
+ if otp_coordinator && otp_response_prompt?(prompt_buffer)
542
+ write_otp_response(input, prompt_buffer, member_name: member_name)
543
+ return +""
544
+ end
545
+ return prompt_buffer
535
546
  end
536
547
 
537
- if accept_confirmation_prompt?(chunk)
548
+ if accept_confirmation_prompt?(prompt_buffer)
538
549
  write_accept_response(input) if accept
539
- return
550
+ return +""
540
551
  end
541
552
 
542
- write_signing_password(input, chunk)
553
+ write_signing_password(input, prompt_buffer)
554
+ prompt_buffer
543
555
  end
544
556
 
545
557
  def write_accept_response(input)
@@ -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.43"
8
+ VERSION = "1.2.44"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
@@ -102,7 +102,7 @@ module Kettle
102
102
  REGISTRY_WAIT_ATTEMPTS = 15
103
103
  REGISTRY_WAIT_INTERVAL_SECONDS = 15
104
104
 
105
- def initialize(command:, config:, members:, execute: false, accept: true, commit: true, allow_dirty: false, autostash: true, publish: false, push: false, tag: false, start_step: nil, skip_steps: nil, local_ci: false, continue_ci_failures: false, ci_workflows: nil, skip_bundle_audit: false, skip_remotes: nil, required_remotes: nil, auto_dependency_floors: nil, gha_sha_pins_upgrade: "patch", gha_sha_pins_check: false, env_overrides: {}, debug: false, verbose: false, gem_signing_password: nil, secrets_provider: nil, jobs: nil, progress_io: nil, reset_target: nil, bup_args: [], bex_args: [], start_member: nil, start_branch: nil, **options)
105
+ def initialize(command:, config:, members:, execute: false, accept: true, commit: true, allow_dirty: false, autostash: true, publish: false, push: false, tag: false, start_step: nil, skip_steps: nil, local_ci: false, continue_ci_failures: false, ci_workflows: nil, skip_bundle_audit: false, skip_remotes: nil, required_remotes: nil, auto_dependency_floors: nil, gha_sha_pins_upgrade: "patch", gha_sha_pins_check: false, gha_sha_pins_ttl_days: nil, env_overrides: {}, debug: false, verbose: false, gem_signing_password: nil, secrets_provider: nil, jobs: nil, progress_io: nil, reset_target: nil, bup_args: [], bex_args: [], start_member: nil, start_branch: nil, **options)
106
106
  @command = command
107
107
  @config = config
108
108
  @members = members
@@ -125,6 +125,7 @@ module Kettle
125
125
  @auto_dependency_floors = auto_dependency_floors.nil? ? config.release_auto_dependency_floors? : auto_dependency_floors
126
126
  @gha_sha_pins_upgrade = gha_sha_pins_upgrade
127
127
  @gha_sha_pins_check = gha_sha_pins_check
128
+ @gha_sha_pins_ttl_days = gha_sha_pins_ttl_days.nil? ? 1.0 : gha_sha_pins_ttl_days.to_f
128
129
  @env_overrides = env_overrides
129
130
  @debug = debug
130
131
  @verbose = verbose
@@ -165,7 +166,7 @@ module Kettle
165
166
 
166
167
  private
167
168
 
168
- attr_reader :command, :config, :members, :execute, :accept, :commit, :allow_dirty, :autostash, :publish, :push, :tag, :start_step, :skip_steps, :local_ci, :continue_ci_failures, :ci_workflows, :skip_bundle_audit, :skip_remotes, :required_remotes, :auto_dependency_floors, :gha_sha_pins_upgrade, :gha_sha_pins_check, :env_overrides, :debug, :verbose, :jobs, :progress_io, :reset_target, :bup_args, :bex_args, :start_member, :start_branch
169
+ attr_reader :command, :config, :members, :execute, :accept, :commit, :allow_dirty, :autostash, :publish, :push, :tag, :start_step, :skip_steps, :local_ci, :continue_ci_failures, :ci_workflows, :skip_bundle_audit, :skip_remotes, :required_remotes, :auto_dependency_floors, :gha_sha_pins_upgrade, :gha_sha_pins_check, :gha_sha_pins_ttl_days, :env_overrides, :debug, :verbose, :jobs, :progress_io, :reset_target, :bup_args, :bex_args, :start_member, :start_branch
169
170
 
170
171
  def template_with_worktree_sync_results
171
172
  runner = command_runner
@@ -441,8 +442,12 @@ module Kettle
441
442
  return template_member_workflow_results(workflow_members) if command == "template" && execute
442
443
 
443
444
  runner = CommandRunner.new(execute: execute, accept: accept)
445
+ results = []
446
+ if command == "gha-sha-pins" && execute
447
+ return results unless review_gha_sha_pins(workflow_members, runner: runner, memo: results)
448
+ end
444
449
  gha_progress = (command == "gha-sha-pins") ? start_gha_sha_pins_progress(workflow_members) : nil
445
- results = workflow_members.each_with_object([]) do |member, memo|
450
+ workflow_members.each_with_object(results) do |member, memo|
446
451
  if command == "template" && config.normalize_lockfiles?
447
452
  normalize_lockfiles(member: member, runner: runner, memo: memo, phase: "prepare_lockfiles")
448
453
  break memo unless memo.last.ok?
@@ -480,6 +485,58 @@ module Kettle
480
485
  results
481
486
  end
482
487
 
488
+ def review_gha_sha_pins(workflow_members, runner:, memo:)
489
+ return true if workflow_members.empty?
490
+
491
+ repositories = []
492
+ workflow_members.each do |member|
493
+ result = runner.call(
494
+ member: member,
495
+ phase: "gha_sha_pins_list",
496
+ command: gha_sha_pins_command(mode: :list),
497
+ env: command_env
498
+ )
499
+ memo << result
500
+ return false unless result.ok?
501
+
502
+ payload = JSON.parse(result.stdout.to_s)
503
+ repositories.concat(Array(payload["repositories"]))
504
+ rescue JSON::ParserError => error
505
+ memo << CommandResult.new(
506
+ member_name: member.name,
507
+ phase: "gha_sha_pins_list",
508
+ command: gha_sha_pins_command(mode: :list),
509
+ workdir: member.root,
510
+ status: 1,
511
+ success: false,
512
+ stdout: result&.stdout.to_s,
513
+ stderr: "invalid kettle-gha-pins list JSON: #{error.message}",
514
+ elapsed_seconds: 0.0,
515
+ skipped: false,
516
+ reason: "invalid list output"
517
+ )
518
+ return false
519
+ end
520
+
521
+ review_dir = File.join(config.root, "tmp", "kettle-family")
522
+ FileUtils.mkdir_p(review_dir)
523
+ review_path = File.join(
524
+ review_dir,
525
+ "gha-sha-pins-review-#{Process.pid}-#{object_id}.json"
526
+ )
527
+ File.write(review_path, JSON.pretty_generate("schema_version" => 1, "repositories" => repositories.uniq.sort))
528
+ review_result = runner.call(
529
+ member: workflow_members.first,
530
+ phase: "gha_sha_pins_review",
531
+ command: gha_sha_pins_command(mode: :review, input: review_path),
532
+ env: command_env
533
+ )
534
+ memo << review_result
535
+ review_result.ok?
536
+ ensure
537
+ File.delete(review_path) if review_path && File.file?(review_path)
538
+ end
539
+
483
540
  def template_member_workflow_results(workflow_members)
484
541
  debugger_results = template_debugger_bootstrap_results(workflow_members)
485
542
  return debugger_results unless debugger_results.all?(&:ok?)
@@ -2341,11 +2398,18 @@ module Kettle
2341
2398
  ["bundle", "exec", *Array(bex_args).map(&:to_s)]
2342
2399
  end
2343
2400
 
2344
- def gha_sha_pins_command
2401
+ def gha_sha_pins_command(mode: nil, input: nil)
2345
2402
  command_text = command_for(command)
2346
2403
  args = []
2404
+ if mode == :list
2405
+ return append_command_args(command_text, ["--list", "--json"])
2406
+ end
2407
+ if mode == :review
2408
+ return append_command_args(command_text, ["--review", "--input", input.to_s, "--ttl", gha_sha_pins_ttl_days.to_s, "--json"])
2409
+ end
2347
2410
  args << (gha_sha_pins_check ? "--check" : "--write") unless command_includes_any?(command_text, %w[--check --write])
2348
2411
  args.concat(["--upgrade", gha_sha_pins_upgrade]) unless command_includes_arg?(command_text, "--upgrade")
2412
+ args << "--offline" if execute && !command_includes_arg?(command_text, "--offline")
2349
2413
  args << "--events" unless command_includes_arg?(command_text, "--events")
2350
2414
  append_command_args(command_text, args)
2351
2415
  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.43
4
+ version: 1.2.44
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -360,10 +360,10 @@ licenses:
360
360
  - AGPL-3.0-only
361
361
  metadata:
362
362
  homepage_uri: https://kettle-family.galtzo.com
363
- source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.43
364
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.43/CHANGELOG.md
363
+ source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.44
364
+ changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.44/CHANGELOG.md
365
365
  bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
366
- documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.43
366
+ documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.44
367
367
  funding_uri: https://github.com/sponsors/pboling
368
368
  wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
369
369
  news_uri: https://www.railsbling.com/tags/kettle-family
metadata.gz.sig CHANGED
Binary file