kettle-family 1.2.95 → 1.2.96

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: 9b482c620e7f5e341caead6b52476cc8dc26e56c5d0ecde281ca27f7aaacc3e3
4
- data.tar.gz: 78c5c14ec0d5a90a34341650d010216e241fb4927983dff9507b66d9e9ccaf6d
3
+ metadata.gz: d2f776638603414ab6c27f457473dea3f6088bae63f58a45fcbc0f165fe0e026
4
+ data.tar.gz: b44e74efb8e840e929df24573b815df7b7a749f27e67f86f76561ab654b406ea
5
5
  SHA512:
6
- metadata.gz: 61ab40f1165dc584b786cf9e5f72005390aa786bcc2f6d0b1f4e9c172d566d8efd310625bac5a218312b221e5f8577071d699dc0eadaa6b1260cdb7ac4444736
7
- data.tar.gz: f4097829a8ca4127f0a2a6dc4f53ea84886d14e68c9df2ec82db9319904482ba9d6f2339d087596b048419d7c63707462f5c38e45e8bf475149e295fc4479868
6
+ metadata.gz: 77f9c29784b453d8d270398c675ed52e18d8ba5f374fc8add80465bce88f59a679ab254080e3fe349685bff16134bb97541ef087d5c32116123c95e443b64685
7
+ data.tar.gz: 4e750df4e8f1f7f92ee8a653bac8ed02a95ee7ddfa5daa8f8eef92003b78e2c58c93b41498b0b93f8649626dcaf3a645f36fdedabf78928e35653131f14431c3
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,23 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [1.2.96] - 2026-09-08
34
+
35
+ - TAG: [v1.2.96][1.2.96t]
36
+ - COVERAGE: 93.49% -- 6235/6669 lines in 35 files
37
+ - BRANCH COVERAGE: 77.99% -- 2523/3235 branches in 35 files
38
+ - 32.75% documented
39
+
40
+ ### Fixed
41
+
42
+ - Render branch-worktree template events in distinct live progress rows.
43
+
44
+ - Run release-state branch targets concurrently and render distinct branch progress rows.
45
+
46
+ - Preserve configured release waves for ordinary sibling members when another member has release-target branches.
47
+
48
+ - Restore the version_gem runtime dependency and preserve worktree integration documentation during template updates.
49
+
33
50
  ## [1.2.95] - 2026-09-07
34
51
 
35
52
  - TAG: [v1.2.95][1.2.95t]
@@ -2651,7 +2668,9 @@ Please file a bug if you notice a violation of semantic versioning.
2651
2668
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
2652
2669
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
2653
2670
 
2654
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.95...HEAD
2671
+ [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.96...HEAD
2672
+ [1.2.96]: https://github.com/kettle-dev/kettle-family/compare/v1.2.95...v1.2.96
2673
+ [1.2.96t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.96
2655
2674
  [1.2.95]: https://github.com/kettle-dev/kettle-family/compare/v1.2.94...v1.2.95
2656
2675
  [1.2.95t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.95
2657
2676
  [1.2.94]: https://github.com/kettle-dev/kettle-family/compare/v1.2.93...v1.2.94
data/README.md CHANGED
@@ -1171,7 +1171,7 @@ Thanks for RTFM. ☺️
1171
1171
  [📌gitmoji]: https://gitmoji.dev
1172
1172
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
1173
1173
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
1174
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.631-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1174
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.669-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1175
1175
  [🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
1176
1176
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
1177
1177
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -709,7 +709,7 @@ module Kettle
709
709
  display_selected_members = display_members_for(command: command, config: config, members: selected, selected_members: selected)
710
710
  print_execution_intent(command: command, config: config, members: display_selected_members, options: options, start_at: start_at)
711
711
  started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
712
- state_progress = release_state_progress(command: command, members: result_members, options: options)
712
+ state_progress = release_state_progress(command: command, config: config, members: result_members, options: options)
713
713
  state_progress&.start
714
714
  state_event_handler = release_state_event_handler(
715
715
  event_tape: state_event_tape,
@@ -901,21 +901,36 @@ module Kettle
901
901
  stdout
902
902
  end
903
903
 
904
- def release_state_progress(command:, members:, options:)
904
+ def release_state_progress(command:, config:, members:, options:)
905
905
  return nil unless command == "release-state"
906
906
  return nil if options[:events] || options[:json]
907
907
  return nil unless stdout.respond_to?(:tty?) && stdout.tty?
908
908
 
909
+ targets = release_state_progress_targets(config: config, members: members)
910
+
909
911
  WorkflowProgress.new(
910
912
  io: stdout,
911
913
  label: "release state",
912
- total: members.length,
913
- jobs: Concurrency.wave_jobs(requested: options[:jobs], item_count: members.length),
914
- members: members,
915
- heading: "release state #{members.length} member#{"s" unless members.length == 1}:"
914
+ total: targets.length,
915
+ jobs: Concurrency.wave_jobs(requested: options[:jobs], item_count: targets.length),
916
+ members: targets.map { |target| target.fetch(:progress_member) },
917
+ heading: "release state #{targets.length} target#{"s" unless targets.length == 1}:"
916
918
  )
917
919
  end
918
920
 
921
+ def release_state_progress_targets(config:, members:)
922
+ members.flat_map do |member|
923
+ member_config = member_release_config(member: member, config: config)
924
+ branches = member_config&.release_target_branches.to_a
925
+ next [{member: member, key: member.name, label: member.name, progress_member: member}] if branches.empty?
926
+
927
+ branches.map do |branch|
928
+ label = "#{member.name}@#{branch}"
929
+ {member: member, key: label, label: label, progress_member: member.dup.tap { |copy| copy.name = label }}
930
+ end
931
+ end
932
+ end
933
+
919
934
  def release_state_event_handler(event_tape:, progress:, members:)
920
935
  return nil unless event_tape || progress
921
936
 
@@ -927,18 +942,21 @@ module Kettle
927
942
  member = members_by_name[event["member"]]
928
943
  next unless member
929
944
 
945
+ branch = event["branch"].to_s
946
+ progress_key = branch.empty? ? member.name : "#{member.name}@#{branch}"
947
+
930
948
  action = event["action"].to_s
931
949
  status = event["status"].to_s
932
950
  case action
933
951
  when "member_start"
934
- progress.start_member(member, total: 5, status: "release_state")
952
+ progress.start_member(member, total: 5, status: "release_state", key: progress_key, label: progress_key)
935
953
  when "member_complete"
936
- progress.finish_member(member, success: status == "ok", status: status)
954
+ progress.finish_member(member, success: status == "ok", status: status, key: progress_key, label: progress_key)
937
955
  when "changelog_command", "computed_booleans", "git_state", "github_release", "transfer_changelog"
938
956
  if status == "running"
939
- progress.update(member, status: action)
957
+ progress.update(member, status: action, key: progress_key, label: progress_key)
940
958
  elsif status == "ok" || status == "failed"
941
- progress.advance(member, status: action, success: status == "ok")
959
+ progress.advance(member, status: action, success: status == "ok", key: progress_key, label: progress_key)
942
960
  end
943
961
  end
944
962
  end
@@ -5,6 +5,11 @@ require "open3"
5
5
  module Kettle
6
6
  module Family
7
7
  class Discovery
8
+ # Dir.chdir changes process-global state. Legacy gemspecs require this
9
+ # evaluation context, so serialize only that boundary; discovery callers
10
+ # may still resolve separate members and worktrees concurrently.
11
+ GEMSPEC_EVALUATION_MUTEX = Mutex.new
12
+
8
13
  def initialize(config:, release_dependency_member_names: nil)
9
14
  @config = config
10
15
  @release_dependency_member_names = release_dependency_member_names&.map(&:to_s)
@@ -174,9 +179,11 @@ module Kettle
174
179
  # evaluates gemspecs relative to the current process directory.
175
180
  # Gem::Specification.load caches by path, which is wrong for branch-stack
176
181
  # workflows that checkout different contents at the same path.
177
- # rubocop:disable ThreadSafety/DirChdir
178
- spec = Dir.chdir(File.dirname(path)) { eval_gemspec(path) }
179
- # rubocop:enable ThreadSafety/DirChdir
182
+ spec = GEMSPEC_EVALUATION_MUTEX.synchronize do
183
+ # rubocop:disable ThreadSafety/DirChdir
184
+ Dir.chdir(File.dirname(path)) { eval_gemspec(path) }
185
+ # rubocop:enable ThreadSafety/DirChdir
186
+ end
180
187
  raise Error, "could not load gemspec #{path}" unless spec
181
188
 
182
189
  spec
@@ -56,20 +56,23 @@ module Kettle
56
56
  def branch_results(event_handler: nil)
57
57
  root = git_root
58
58
  selected_names = members.map(&:name)
59
- release_target_branches.each_with_object([]) do |branch, memo|
60
- with_branch_worktree(root: root, branch: branch) do |worktree_root|
61
- branch_members = discover_branch_members(worktree_root: worktree_root, selected_names: selected_names)
62
- if shared_changelog?
63
- results = parallel_map(branch_members) { |member| check_shared_changelog_member_or_local(member, branch: branch, event_handler: event_handler) }
64
- memo.concat(normalize_shared_version_bump(results))
65
- next
66
- end
59
+ parallel_map(release_target_branches) do |branch|
60
+ branch_results_for(branch: branch, root: root, selected_names: selected_names, event_handler: event_handler)
61
+ end.flatten
62
+ end
67
63
 
68
- memo.concat(parallel_map(branch_members) { |member| check_member(member, branch: branch, event_handler: event_handler) })
64
+ def branch_results_for(branch:, root:, selected_names:, event_handler:)
65
+ with_branch_worktree(root: root, branch: branch) do |worktree_root|
66
+ branch_members = discover_branch_members(worktree_root: worktree_root, selected_names: selected_names)
67
+ if shared_changelog?
68
+ results = parallel_map(branch_members) { |member| check_shared_changelog_member_or_local(member, branch: branch, event_handler: event_handler) }
69
+ next normalize_shared_version_bump(results)
69
70
  end
70
- rescue Error => error
71
- memo << error_result(branch: branch, error: error)
71
+
72
+ parallel_map(branch_members) { |member| check_member(member, branch: branch, event_handler: event_handler) }
72
73
  end
74
+ rescue Error => error
75
+ [error_result(branch: branch, error: error)]
73
76
  end
74
77
 
75
78
  def check_member(member, branch: nil, event_handler: nil)
@@ -27,12 +27,12 @@ module Kettle
27
27
  "timestamp" => @wall_clock.call,
28
28
  "elapsed_seconds" => @clock.call - @started_at
29
29
  )
30
- member = payload.fetch("member", "family").to_s
30
+ target = target_key(payload)
31
31
  @mutex.synchronize do
32
- @sequence_by_member[member] += 1
33
- payload["sequence"] = @sequence_by_member.fetch(member)
32
+ @sequence_by_member[target] += 1
33
+ payload["sequence"] = @sequence_by_member.fetch(target)
34
34
  line = JSON.generate(payload)
35
- File.open(path_for(member), "a") { |file| file.puts(line) }
35
+ File.open(path_for(target), "a") { |file| file.puts(line) }
36
36
  @stream&.puts(line)
37
37
  @stream.flush if @stream&.respond_to?(:flush)
38
38
  end
@@ -43,8 +43,14 @@ module Kettle
43
43
 
44
44
  private
45
45
 
46
- def path_for(member)
47
- filename = member.gsub(/[^A-Za-z0-9_.-]+/, "_")
46
+ def target_key(payload)
47
+ member = payload.fetch("member", "family").to_s
48
+ branch = payload["branch"].to_s
49
+ branch.empty? ? member : "#{member}@#{branch}"
50
+ end
51
+
52
+ def path_for(target)
53
+ filename = target.gsub(/[^A-Za-z0-9_.-]+/, "_")
48
54
  File.join(@directory, "#{filename}.ndjson")
49
55
  end
50
56
  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.95"
8
+ VERSION = "1.2.96"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
@@ -1411,13 +1411,20 @@ module Kettle
1411
1411
  )
1412
1412
  end
1413
1413
 
1414
- def template_results_for_member(member, progress: nil, commit_changes: true, wave_jobs: 1)
1415
- progress&.start_member(member, total: template_phase_total(member), status: template_initial_status(member))
1414
+ def template_results_for_member(member, progress: nil, progress_key: nil, progress_label: nil, commit_changes: true, wave_jobs: 1)
1415
+ progress_key ||= member.name
1416
+ progress_label ||= member.name
1417
+ progress&.start_member(
1418
+ member,
1419
+ total: template_phase_total(member),
1420
+ status: template_initial_status(member),
1421
+ **template_progress_identity_options(member, progress_key, progress_label)
1422
+ )
1416
1423
  runner = CommandRunner.new(execute: execute, accept: accept)
1417
1424
  [].tap do |memo|
1418
1425
  if config.normalize_lockfiles?
1419
1426
  normalize_lockfiles(member: member, runner: runner, memo: memo, phase: "prepare_lockfiles", commit_changes: commit_changes)
1420
- emit_member_result_progress(member, memo.last, progress: progress)
1427
+ emit_member_result_progress(member, memo.last, progress: progress, progress_key: progress_key, progress_label: progress_label)
1421
1428
  return memo unless memo.last.ok?
1422
1429
  end
1423
1430
 
@@ -1428,7 +1435,7 @@ module Kettle
1428
1435
  commit_changes: commit_changes,
1429
1436
  wave_jobs: wave_jobs
1430
1437
  )
1431
- emit_member_result_progress(member, memo.last, progress: progress) if memo.last&.phase == "prepare_template_dependencies"
1438
+ emit_member_result_progress(member, memo.last, progress: progress, progress_key: progress_key, progress_label: progress_label) if memo.last&.phase == "prepare_template_dependencies"
1432
1439
  return memo if prepared == false
1433
1440
 
1434
1441
  memo << runner.call(
@@ -1436,22 +1443,23 @@ module Kettle
1436
1443
  phase: command,
1437
1444
  command: workflow_command(member),
1438
1445
  env: template_command_env(wave_jobs: wave_jobs),
1439
- stdout_line_handler: template_event_line_handler(member, progress: progress)
1446
+ stdout_line_handler: template_event_line_handler(member, progress: progress, progress_key: progress_key, progress_label: progress_label)
1440
1447
  )
1441
- emit_member_result_progress(member, memo.last, progress: progress)
1448
+ emit_member_result_progress(member, memo.last, progress: progress, progress_key: progress_key, progress_label: progress_label)
1442
1449
  return memo unless memo.last.ok?
1443
1450
 
1444
1451
  normalize_lockfiles(member: member, runner: runner, memo: memo, phase: "normalize_lockfiles", commit_changes: commit_changes)
1445
- emit_member_result_progress(member, memo.last, progress: progress)
1452
+ emit_member_result_progress(member, memo.last, progress: progress, progress_key: progress_key, progress_label: progress_label)
1446
1453
  commit_template_changes(member: member, runner: runner, memo: memo) if commit_changes
1447
- emit_member_result_progress(member, memo.last, progress: progress) if memo.last&.phase == "commit_template"
1454
+ emit_member_result_progress(member, memo.last, progress: progress, progress_key: progress_key, progress_label: progress_label) if memo.last&.phase == "commit_template"
1448
1455
  ensure
1449
1456
  template_result = memo.find { |result| result.phase == "template" } || memo.last
1450
1457
  if template_result
1451
1458
  progress&.finish_member(
1452
1459
  member,
1453
1460
  success: memo.all?(&:ok?),
1454
- status: template_member_finish_status(template_result)
1461
+ status: template_member_finish_status(template_result),
1462
+ **template_progress_identity_options(member, progress_key, progress_label)
1455
1463
  )
1456
1464
  end
1457
1465
  end
@@ -1568,7 +1576,13 @@ module Kettle
1568
1576
 
1569
1577
  if outcomes.all?(&:ok?)
1570
1578
  emit_template_event_line(member, ">", "branch worktrees #{entries.length} branches, #{template_jobs(entries)} jobs")
1571
- outcomes.concat(template_branch_worktree_entries_results(entries))
1579
+ progress = start_template_progress(
1580
+ entries.map { |entry| template_branch_progress_member(entry) },
1581
+ heading: "templating #{entries.length} branch worktree#{"s" unless entries.length == 1} with #{template_jobs(entries)} job#{"s" unless template_jobs(entries) == 1}:"
1582
+ )
1583
+ branch_results = template_branch_worktree_entries_results(entries, progress: progress)
1584
+ emit_template_progress_summary(branch_results, progress: progress)
1585
+ outcomes.concat(branch_results)
1572
1586
  end
1573
1587
  end
1574
1588
  end
@@ -1658,7 +1672,7 @@ module Kettle
1658
1672
  []
1659
1673
  end
1660
1674
 
1661
- def template_branch_worktree_entries_results(entries)
1675
+ def template_branch_worktree_entries_results(entries, progress: nil)
1662
1676
  results = template_branch_worktree_sync_results(entries)
1663
1677
  return results unless results.all?(&:ok?)
1664
1678
 
@@ -1681,7 +1695,7 @@ module Kettle
1681
1695
  loop do
1682
1696
  break if mutex.synchronize { stop }
1683
1697
  index, entry = queue.pop(true)
1684
- branch_results = template_branch_worktree_entry_results(entry, wave_jobs: wave_jobs)
1698
+ branch_results = template_branch_worktree_entry_results(entry, progress: progress, wave_jobs: wave_jobs)
1685
1699
  mutex.synchronize do
1686
1700
  ordered_results[index] = branch_results
1687
1701
  stop = true unless branch_results.all?(&:ok?)
@@ -1694,18 +1708,36 @@ module Kettle
1694
1708
  results + ordered_results.compact.flatten
1695
1709
  end
1696
1710
 
1697
- def template_branch_worktree_entry_results(entry, wave_jobs: 1)
1711
+ def template_branch_worktree_entry_results(entry, progress: nil, wave_jobs: 1)
1698
1712
  member = entry.fetch(:member)
1699
1713
  branch = entry.fetch(:branch)
1700
1714
  # Bootstrap and upstream synchronization are deliberately performed
1701
1715
  # for the complete branch set before workers start. The member body is
1702
1716
  # the same method normal template waves invoke.
1703
- results = template_results_for_member(member, wave_jobs: wave_jobs)
1717
+ results = template_results_for_member(
1718
+ member,
1719
+ progress: progress,
1720
+ progress_key: template_branch_progress_key(entry),
1721
+ progress_label: template_branch_progress_label(entry),
1722
+ wave_jobs: wave_jobs
1723
+ )
1704
1724
  tag_branch_results(results, branch)
1705
1725
  emit_template_event_line(member, results.all?(&:ok?) ? "." : "F", "branch #{branch}")
1706
1726
  results
1707
1727
  end
1708
1728
 
1729
+ def template_branch_progress_member(entry)
1730
+ entry.fetch(:member).dup.tap { |member| member.name = template_branch_progress_label(entry) }
1731
+ end
1732
+
1733
+ def template_branch_progress_key(entry)
1734
+ template_branch_progress_label(entry)
1735
+ end
1736
+
1737
+ def template_branch_progress_label(entry)
1738
+ "#{entry.fetch(:member).name}@#{entry.fetch(:branch)}"
1739
+ end
1740
+
1709
1741
  def template_branch_worktree_sync_results(entries)
1710
1742
  runner = command_runner
1711
1743
  entries.filter_map do |entry|
@@ -2002,18 +2034,21 @@ module Kettle
2002
2034
  end
2003
2035
 
2004
2036
  def release_member_local_branch_target_results
2005
- runner = command_runner
2037
+ target_members, current_branch_members = members.partition do |member|
2038
+ member_local_release_config(member)
2039
+ end
2006
2040
  results = []
2007
- append_family_changelog_result(runner: runner, memo: results) unless skip_changelog
2041
+
2042
+ # A member with release targets must not force unrelated sibling
2043
+ # repositories through one-at-a-time release workflows. Run those
2044
+ # members through the normal release scheduler first so configured
2045
+ # waves and their worker budget remain effective.
2046
+ results.concat(current_branch_results(current_branch_members)) unless current_branch_members.empty?
2008
2047
  return results unless results.all?(&:ok?)
2009
2048
 
2010
- members.each_with_object(results) do |member, memo|
2049
+ target_members.each_with_object(results) do |member, memo|
2011
2050
  member_config = member_local_release_config(member)
2012
- if member_config
2013
- memo.concat(member_local_workflow(member: member, member_config: member_config).results)
2014
- else
2015
- memo.concat(release_member_results([member], include_family_changelog: false))
2016
- end
2051
+ memo.concat(member_local_workflow(member: member, member_config: member_config).results)
2017
2052
  break memo unless memo.last&.ok?
2018
2053
  end
2019
2054
  end
@@ -4362,13 +4397,14 @@ module Kettle
4362
4397
  append_command_args(command_text, args)
4363
4398
  end
4364
4399
 
4365
- def start_template_progress(workflow_members)
4400
+ def start_template_progress(workflow_members, heading: nil)
4366
4401
  progress = WorkflowProgress.new(
4367
4402
  io: progress_io,
4368
4403
  label: "templating",
4369
4404
  total: workflow_members.length,
4370
4405
  jobs: template_jobs(workflow_members),
4371
- members: workflow_members
4406
+ members: workflow_members,
4407
+ heading: heading
4372
4408
  )
4373
4409
  progress.start
4374
4410
  progress
@@ -4472,10 +4508,16 @@ module Kettle
4472
4508
  kettle_jem_template_command?(command_text)
4473
4509
  end
4474
4510
 
4475
- def emit_member_result_progress(member, result, progress:)
4511
+ def emit_member_result_progress(member, result, progress:, progress_key: member.name, progress_label: member.name)
4476
4512
  return unless result
4477
4513
 
4478
- progress&.advance(member, status: result.phase, success: result.ok?, mark: command_result_progress_mark(result))
4514
+ progress&.advance(
4515
+ member,
4516
+ status: result.phase,
4517
+ success: result.ok?,
4518
+ mark: command_result_progress_mark(result),
4519
+ **template_progress_identity_options(member, progress_key, progress_label)
4520
+ )
4479
4521
  end
4480
4522
 
4481
4523
  def command_result_progress_mark(result)
@@ -4522,7 +4564,7 @@ module Kettle
4522
4564
  progress.summary("release summary: #{release_results.count(&:ok?)}/#{release_results.length} members ok")
4523
4565
  end
4524
4566
 
4525
- def template_event_line_handler(member, progress: nil)
4567
+ def template_event_line_handler(member, progress: nil, progress_key: member.name, progress_label: member.name)
4526
4568
  lambda do |line|
4527
4569
  event = parse_template_event(line)
4528
4570
  next false unless event
@@ -4531,7 +4573,7 @@ module Kettle
4531
4573
  if verbose || debug
4532
4574
  emit_template_event_progress(member, event)
4533
4575
  elsif progress&.tty?
4534
- emit_template_event_status(member, event, progress: progress)
4576
+ emit_template_event_status(member, event, progress: progress, progress_key: progress_key, progress_label: progress_label)
4535
4577
  end
4536
4578
  end
4537
4579
  true
@@ -4649,7 +4691,7 @@ module Kettle
4649
4691
  end
4650
4692
  end
4651
4693
 
4652
- def emit_template_event_status(member, event, progress:)
4694
+ def emit_template_event_status(member, event, progress:, progress_key: member.name, progress_label: member.name)
4653
4695
  status = case event["type"]
4654
4696
  when "phase_start", "phase_finish"
4655
4697
  event["phase"].to_s
@@ -4663,7 +4705,20 @@ module Kettle
4663
4705
  template_event_summary_label(event)
4664
4706
  end
4665
4707
  mark = template_event_status_mark(event)
4666
- progress&.update(member, status: status, mark: mark) if status && !status.empty?
4708
+ if status && !status.empty?
4709
+ progress&.update(
4710
+ member,
4711
+ status: status,
4712
+ mark: mark,
4713
+ **template_progress_identity_options(member, progress_key, progress_label)
4714
+ )
4715
+ end
4716
+ end
4717
+
4718
+ def template_progress_identity_options(member, progress_key, progress_label)
4719
+ return {} if progress_key == member.name && progress_label == member.name
4720
+
4721
+ {key: progress_key, label: progress_label}
4667
4722
  end
4668
4723
 
4669
4724
  def emit_release_event_progress(member, event)
@@ -27,6 +27,7 @@ module Kettle
27
27
  @started = false
28
28
  @stopped = false
29
29
  @member_totals = {}
30
+ @member_labels = @line_order.to_h { |member_name| [member_name, member_name] }
30
31
  @member_counts = Hash.new(0)
31
32
  @member_started_at = {}
32
33
  @member_finished_elapsed = {}
@@ -55,62 +56,66 @@ module Kettle
55
56
  end
56
57
  end
57
58
 
58
- def start_member(member, total:, status:)
59
+ def start_member(member, total:, status:, key: member.name, label: member.name)
59
60
  return unless @enabled
60
61
 
61
62
  synchronize do
62
- @member_totals[member.name] = total.to_i
63
- @member_counts[member.name] = 0
64
- @member_started_at[member.name] ||= monotonic_now
65
- @member_finished_elapsed.delete(member.name)
63
+ @member_labels[key] = label
64
+ @member_totals[key] = total.to_i
65
+ @member_counts[key] = 0
66
+ @member_started_at[key] ||= monotonic_now
67
+ @member_finished_elapsed.delete(key)
66
68
  if @tty
67
- render(member, status: status)
69
+ render(key, status: status)
68
70
  else
69
- write_line(non_tty_line(member, mark: ">", status: status))
71
+ write_line(non_tty_line(key, mark: ">", status: status))
70
72
  end
71
73
  end
72
74
  end
73
75
 
74
- def advance(member, status:, success: true, mark: nil)
76
+ def advance(member, status:, success: true, mark: nil, key: member.name, label: member.name)
75
77
  return unless @enabled
76
78
 
77
79
  synchronize do
80
+ @member_labels[key] = label
78
81
  event_mark = mark || (success ? "." : "F")
79
- increment_member_count(member)
82
+ increment_member_count(key)
80
83
  if @tty
81
- append_event(member, event_mark)
82
- render(member, status: status)
84
+ append_event(key, event_mark)
85
+ render(key, status: status)
83
86
  else
84
- write_line(non_tty_line(member, mark: event_mark, status: status))
87
+ write_line(non_tty_line(key, mark: event_mark, status: status))
85
88
  end
86
89
  end
87
90
  end
88
91
 
89
- def update(member, status:, mark: nil)
92
+ def update(member, status:, mark: nil, key: member.name, label: member.name)
90
93
  return unless @enabled
91
94
  return if status.to_s.empty?
92
95
 
93
96
  synchronize do
97
+ @member_labels[key] = label
94
98
  if @tty
95
- append_event(member, mark) if mark
96
- render(member, status: status)
99
+ append_event(key, mark) if mark
100
+ render(key, status: status)
97
101
  else
98
- write_line(non_tty_line(member, mark: mark || ">", status: status))
102
+ write_line(non_tty_line(key, mark: mark || ">", status: status))
99
103
  end
100
104
  end
101
105
  end
102
106
 
103
- def finish_member(member, success:, status:)
107
+ def finish_member(member, success:, status:, key: member.name, label: member.name)
104
108
  return unless @enabled
105
109
 
106
110
  synchronize do
107
- total = @member_totals[member.name].to_i
108
- @member_counts[member.name] = total if total.positive?
109
- @member_finished_elapsed[member.name] = elapsed_seconds(member)
111
+ @member_labels[key] = label
112
+ total = @member_totals[key].to_i
113
+ @member_counts[key] = total if total.positive?
114
+ @member_finished_elapsed[key] = elapsed_seconds_by_name(key)
110
115
  if @tty
111
- render(member, status: status)
116
+ render(key, status: status)
112
117
  else
113
- write_line(non_tty_line(member, mark: success ? "done" : "failed", status: status))
118
+ write_line(non_tty_line(key, mark: success ? "done" : "failed", status: status))
114
119
  end
115
120
  end
116
121
  end
@@ -168,15 +173,15 @@ module Kettle
168
173
 
169
174
  private
170
175
 
171
- def render(member, status:)
172
- render_name(member.name, status: status)
176
+ def render(member_key, status:)
177
+ render_name(member_key, status: status)
173
178
  render_tty_block if @tty
174
179
  end
175
180
 
176
- def append_event(member, mark)
181
+ def append_event(member_key, mark)
177
182
  return if mark.to_s.empty?
178
183
 
179
- @member_events[member.name] = (@member_events[member.name] + mark.to_s).chars.last(EVENT_WIDTH).join
184
+ @member_events[member_key] = (@member_events[member_key] + mark.to_s).chars.last(EVENT_WIDTH).join
180
185
  end
181
186
 
182
187
  def render_name(member_name, status:)
@@ -222,7 +227,7 @@ module Kettle
222
227
  def tty_line(member_name)
223
228
  Kernel.format(
224
229
  FORMAT,
225
- member: member_name,
230
+ member: @member_labels.fetch(member_name, member_name),
226
231
  progress: progress_text(member_name),
227
232
  duration: elapsed_text(member_name),
228
233
  events: @member_events[member_name].rjust(EVENT_WIDTH),
@@ -234,15 +239,15 @@ module Kettle
234
239
  "s" unless count == 1
235
240
  end
236
241
 
237
- def non_tty_line(member, mark:, status:)
238
- "[#{member.name}] #{progress_text(member.name)} #{elapsed_text(member.name)} #{mark} #{status}"
242
+ def non_tty_line(member_key, mark:, status:)
243
+ label = @member_labels.fetch(member_key, member_key)
244
+ "[#{label}] #{progress_text(member_key)} #{elapsed_text(member_key)} #{mark} #{status}"
239
245
  end
240
246
 
241
- def increment_member_count(member)
242
- member_name = member.name
243
- total = @member_totals[member_name].to_i
244
- @member_counts[member_name] += 1
245
- @member_counts[member_name] = total if total.positive? && @member_counts[member_name] > total
247
+ def increment_member_count(member_key)
248
+ total = @member_totals[member_key].to_i
249
+ @member_counts[member_key] += 1
250
+ @member_counts[member_key] = total if total.positive? && @member_counts[member_key] > total
246
251
  end
247
252
 
248
253
  def progress_text(member_name)
@@ -256,10 +261,6 @@ module Kettle
256
261
  format_elapsed(elapsed_seconds_by_name(member_name)).rjust(ELAPSED_WIDTH)
257
262
  end
258
263
 
259
- def elapsed_seconds(member)
260
- elapsed_seconds_by_name(member.name)
261
- end
262
-
263
264
  def elapsed_seconds_by_name(member_name)
264
265
  return @member_finished_elapsed[member_name] if @member_finished_elapsed.key?(member_name)
265
266
 
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.95
4
+ version: 1.2.96
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -89,22 +89,22 @@ dependencies:
89
89
  name: kettle-dev
90
90
  requirement: !ruby/object:Gem::Requirement
91
91
  requirements:
92
- - - ">="
92
+ - - "~>"
93
93
  - !ruby/object:Gem::Version
94
- version: 3.0.32
95
- - - "<"
94
+ version: '3.0'
95
+ - - ">="
96
96
  - !ruby/object:Gem::Version
97
- version: 4.0.0
97
+ version: 3.0.33
98
98
  type: :runtime
99
99
  prerelease: false
100
100
  version_requirements: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - ">="
102
+ - - "~>"
103
103
  - !ruby/object:Gem::Version
104
- version: 3.0.32
105
- - - "<"
104
+ version: '3.0'
105
+ - - ">="
106
106
  - !ruby/object:Gem::Version
107
- version: 4.0.0
107
+ version: 3.0.33
108
108
  - !ruby/object:Gem::Dependency
109
109
  name: kettle-gha-pins
110
110
  requirement: !ruby/object:Gem::Requirement
@@ -131,20 +131,20 @@ dependencies:
131
131
  requirements:
132
132
  - - "~>"
133
133
  - !ruby/object:Gem::Version
134
- version: '7.0'
134
+ version: '7.1'
135
135
  - - ">="
136
136
  - !ruby/object:Gem::Version
137
- version: 7.0.0
137
+ version: 7.1.23
138
138
  type: :runtime
139
139
  prerelease: false
140
140
  version_requirements: !ruby/object:Gem::Requirement
141
141
  requirements:
142
142
  - - "~>"
143
143
  - !ruby/object:Gem::Version
144
- version: '7.0'
144
+ version: '7.1'
145
145
  - - ">="
146
146
  - !ruby/object:Gem::Version
147
- version: 7.0.0
147
+ version: 7.1.23
148
148
  - !ruby/object:Gem::Dependency
149
149
  name: kettle-test
150
150
  requirement: !ruby/object:Gem::Requirement
@@ -304,7 +304,7 @@ dependencies:
304
304
  version: '3.2'
305
305
  - - ">="
306
306
  - !ruby/object:Gem::Version
307
- version: 3.2.2
307
+ version: 3.2.3
308
308
  type: :development
309
309
  prerelease: false
310
310
  version_requirements: !ruby/object:Gem::Requirement
@@ -314,7 +314,7 @@ dependencies:
314
314
  version: '3.2'
315
315
  - - ">="
316
316
  - !ruby/object:Gem::Version
317
- version: 3.2.2
317
+ version: 3.2.3
318
318
  - !ruby/object:Gem::Dependency
319
319
  name: turbo_tests2
320
320
  requirement: !ruby/object:Gem::Requirement
@@ -324,7 +324,7 @@ dependencies:
324
324
  version: '3.2'
325
325
  - - ">="
326
326
  - !ruby/object:Gem::Version
327
- version: 3.2.6
327
+ version: 3.2.7
328
328
  type: :development
329
329
  prerelease: false
330
330
  version_requirements: !ruby/object:Gem::Requirement
@@ -334,7 +334,7 @@ dependencies:
334
334
  version: '3.2'
335
335
  - - ">="
336
336
  - !ruby/object:Gem::Version
337
- version: 3.2.6
337
+ version: 3.2.7
338
338
  - !ruby/object:Gem::Dependency
339
339
  name: ruby-progressbar
340
340
  requirement: !ruby/object:Gem::Requirement
@@ -443,10 +443,10 @@ licenses:
443
443
  - AGPL-3.0-only
444
444
  metadata:
445
445
  homepage_uri: https://kettle-family.galtzo.com
446
- source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.95
447
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.95/CHANGELOG.md
446
+ source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.96
447
+ changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.96/CHANGELOG.md
448
448
  bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
449
- documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.95
449
+ documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.96
450
450
  funding_uri: https://github.com/sponsors/pboling
451
451
  wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
452
452
  news_uri: https://www.railsbling.com/tags/kettle-family
metadata.gz.sig CHANGED
Binary file