kettle-family 1.3.26 → 1.3.28
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 +29 -1
- data/README.md +1 -1
- data/lib/kettle/family/cli.rb +42 -6
- data/lib/kettle/family/report.rb +28 -9
- data/lib/kettle/family/selection.rb +24 -4
- data/lib/kettle/family/version.rb +1 -1
- data/lib/kettle/family/workflow.rb +32 -4
- data.tar.gz.sig +0 -0
- metadata +6 -6
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 367d63bb92d37a073b9f7bfd89c81dd888abc3096ea0f40a38a84420676baa1b
|
|
4
|
+
data.tar.gz: f90a8ff1e68e5906b016813c2b7f9b1bb863e22df3a217ac18aac97820b05b14
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2475a71506fa039212ababd06370f8fe2472147324f9d928e33fe5857e88bdf370fb63b24ffa70f08fe0d0d0bc632ac227bc146878160b26eae6d64f958010a3
|
|
7
|
+
data.tar.gz: 7bb3b6df1a83fed732d8b6dcf43004efb5f1a00ff21dbd51ba084b7336292d4d1b6a3a2ffdbad5504e6a007265ec23fd8623fd9972c5a5cdb21aea994837f43c
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,30 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [1.3.28] - 2026-10-21
|
|
34
|
+
|
|
35
|
+
- TAG: [v1.3.28][1.3.28t]
|
|
36
|
+
- COVERAGE: 93.04% -- 7461/8019 lines in 44 files
|
|
37
|
+
- BRANCH COVERAGE: 77.50% -- 2994/3863 branches in 44 files
|
|
38
|
+
- 37.45% documented
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Order the release progress pane by wave plan (execution order), then queueing order within each wave, instead of raw member order
|
|
43
|
+
|
|
44
|
+
## [1.3.27] - 2026-10-20
|
|
45
|
+
|
|
46
|
+
- TAG: [v1.3.27][1.3.27t]
|
|
47
|
+
- COVERAGE: 93.04% -- 7460/8018 lines in 44 files
|
|
48
|
+
- BRANCH COVERAGE: 77.50% -- 2994/3863 branches in 44 files
|
|
49
|
+
- 37.45% documented
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- Preserve branch identity in template reports and exact-branch resume hints, and support MEMBER@BRANCH selection and resume targets.
|
|
54
|
+
|
|
55
|
+
- Retry template preparation once when Bundler reports a cached gemspec evaluating to nil.
|
|
56
|
+
|
|
33
57
|
## [1.3.26] - 2026-10-20
|
|
34
58
|
|
|
35
59
|
- TAG: [v1.3.26][1.3.26t]
|
|
@@ -3165,7 +3189,11 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
3165
3189
|
- Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
|
|
3166
3190
|
- Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
|
|
3167
3191
|
|
|
3168
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.3.
|
|
3192
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.3.28...HEAD
|
|
3193
|
+
[1.3.28]: https://github.com/kettle-dev/kettle-family/compare/v1.3.27...v1.3.28
|
|
3194
|
+
[1.3.28t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.3.28
|
|
3195
|
+
[1.3.27]: https://github.com/kettle-dev/kettle-family/compare/v1.3.26...v1.3.27
|
|
3196
|
+
[1.3.27t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.3.27
|
|
3169
3197
|
[1.3.26]: https://github.com/kettle-dev/kettle-family/compare/v1.3.25...v1.3.26
|
|
3170
3198
|
[1.3.26t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.3.26
|
|
3171
3199
|
[1.3.25]: https://github.com/kettle-dev/kettle-family/compare/v1.3.24...v1.3.25
|
data/README.md
CHANGED
|
@@ -1225,7 +1225,7 @@ Thanks for RTFM. ☺️
|
|
|
1225
1225
|
[📌gitmoji]: https://gitmoji.dev
|
|
1226
1226
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
1227
1227
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
1228
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-
|
|
1228
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-8.019-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
1229
1229
|
[🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
|
|
1230
1230
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
1231
1231
|
[📄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
|
@@ -49,7 +49,7 @@ module Kettle
|
|
|
49
49
|
|
|
50
50
|
module SelectionOptions
|
|
51
51
|
def self.included(base)
|
|
52
|
-
base.option :only, value: {type: String, usage: "MEMBERS"}, desc: "Select comma-separated members, or release-state tokens: unreleased/unrel, prepared/prep, pending/pend, bump"
|
|
52
|
+
base.option :only, value: {type: String, usage: "MEMBERS"}, desc: "Select comma-separated members or branch targets (MEMBER@BRANCH), or release-state tokens: unreleased/unrel, prepared/prep, pending/pend, bump"
|
|
53
53
|
base.option :exclude, value: {type: String, usage: "MEMBERS"}, desc: "Exclude comma-separated members"
|
|
54
54
|
base.option :start_at, long: "--start-at", value: {type: String, usage: "MEMBER[@BRANCH]"}, desc: "Select from member through the end of order"
|
|
55
55
|
end
|
|
@@ -823,7 +823,7 @@ module Kettle
|
|
|
823
823
|
def build_report(command, options)
|
|
824
824
|
config = Config.load(root: options[:root], path: options[:config])
|
|
825
825
|
start_at = parse_start_at(options[:start_at])
|
|
826
|
-
Selection.validate_positional_start_at!(command, start_at.member)
|
|
826
|
+
Selection.validate_positional_start_at!(command, start_at.member, branch: start_at.branch)
|
|
827
827
|
effective_only = default_only_filter(command: command, only: options[:only])
|
|
828
828
|
discovery = Discovery.new(
|
|
829
829
|
config: config,
|
|
@@ -841,6 +841,7 @@ module Kettle
|
|
|
841
841
|
Orderer.new(members: members, mode: config.order_mode, hints: config.order_hints).ordered(allow_cycles: command == "release-state")
|
|
842
842
|
end
|
|
843
843
|
state_event_tape = release_state_event_tape(command: command, config: config, options: options)
|
|
844
|
+
branch_selection = template_branch_selection(command: command, config: config, members: ordered, only: effective_only)
|
|
844
845
|
Selection.validate_release_state_only_filter!(effective_only)
|
|
845
846
|
release_state_results = release_state_results_for_selection(
|
|
846
847
|
command: command,
|
|
@@ -855,6 +856,7 @@ module Kettle
|
|
|
855
856
|
members: ordered,
|
|
856
857
|
release_state_results: release_state_results
|
|
857
858
|
).apply(only: effective_only, exclude: options[:exclude], start_at: start_at.member)
|
|
859
|
+
options = options.merge(template_branch_selection: branch_selection)
|
|
858
860
|
result_members = selected
|
|
859
861
|
display_members = display_members_for(command: command, config: config, members: ordered, selected_members: selected)
|
|
860
862
|
display_selected_members = display_members_for(command: command, config: config, members: selected, selected_members: selected)
|
|
@@ -889,7 +891,7 @@ module Kettle
|
|
|
889
891
|
config_path: config.path,
|
|
890
892
|
branch_lanes: config.branch_lanes,
|
|
891
893
|
release_target_branches: release_target_branches(command: command, config: config, start_at: start_at),
|
|
892
|
-
member_release_target_branches: member_release_target_branches(command: command, members: selected, config: config, start_at: start_at),
|
|
894
|
+
member_release_target_branches: member_release_target_branches(command: command, members: selected, config: config, start_at: start_at, branch_selection: branch_selection),
|
|
893
895
|
release_mode: release_mode(command: command, options: options),
|
|
894
896
|
release_resume_arguments: release_resume_arguments(command: command, options: options),
|
|
895
897
|
command: command,
|
|
@@ -955,7 +957,32 @@ module Kettle
|
|
|
955
957
|
names = only.to_s.split(",").map(&:strip).reject(&:empty?)
|
|
956
958
|
return nil if names.empty? || names.any? { |name| Selection.status_token?(name) }
|
|
957
959
|
|
|
958
|
-
|
|
960
|
+
Selection.member_names(only)
|
|
961
|
+
end
|
|
962
|
+
|
|
963
|
+
def template_branch_selection(command:, config:, members:, only:)
|
|
964
|
+
selectors = Selection.branch_selectors(only)
|
|
965
|
+
unless command == "template"
|
|
966
|
+
raise Error, "MEMBER@BRANCH selectors are only supported for template" unless selectors.empty?
|
|
967
|
+
|
|
968
|
+
return {}
|
|
969
|
+
end
|
|
970
|
+
return {} if selectors.empty?
|
|
971
|
+
|
|
972
|
+
by_name = members.to_h { |member| [member.name, member] }
|
|
973
|
+
selectors.group_by(&:first).transform_values do |member_selectors|
|
|
974
|
+
member_name = member_selectors.first.first
|
|
975
|
+
member = by_name[member_name]
|
|
976
|
+
member_config = member && member_release_config(member: member, config: config)
|
|
977
|
+
branches = member_config && BranchTargetConfig.branch_targets_for(command, member_config.release_target_branches)
|
|
978
|
+
raise Error, "member #{member_name.inspect} has no template branch targets" unless branches
|
|
979
|
+
|
|
980
|
+
selected_branches = member_selectors.map(&:last).uniq
|
|
981
|
+
unknown = selected_branches - branches
|
|
982
|
+
raise Error, "unknown branch target(s) for #{member_name}: #{unknown.join(", ")}" unless unknown.empty?
|
|
983
|
+
|
|
984
|
+
selected_branches
|
|
985
|
+
end
|
|
959
986
|
end
|
|
960
987
|
|
|
961
988
|
def display_members_for(command:, config:, members:, selected_members:)
|
|
@@ -1031,6 +1058,7 @@ module Kettle
|
|
|
1031
1058
|
bex_args: options[:bex_args],
|
|
1032
1059
|
start_member: start_at.member,
|
|
1033
1060
|
start_branch: start_at.branch,
|
|
1061
|
+
template_branch_selection: options[:template_branch_selection],
|
|
1034
1062
|
event_tape: state_event_tape,
|
|
1035
1063
|
release_state_results: release_state_results
|
|
1036
1064
|
).results
|
|
@@ -1560,10 +1588,18 @@ module Kettle
|
|
|
1560
1588
|
slice_branch_targets(branch_targets, start_at.branch)
|
|
1561
1589
|
end
|
|
1562
1590
|
|
|
1563
|
-
def member_release_target_branches(command:, members:, config:, start_at:)
|
|
1591
|
+
def member_release_target_branches(command:, members:, config:, start_at:, branch_selection: {})
|
|
1564
1592
|
members.each_with_object({}) do |member, memo|
|
|
1565
1593
|
member_config = member_release_config(member: member, config: config)
|
|
1566
|
-
|
|
1594
|
+
next unless member_config
|
|
1595
|
+
|
|
1596
|
+
branches = if command == "template" && branch_selection.key?(member.name)
|
|
1597
|
+
branch_selection.fetch(member.name)
|
|
1598
|
+
else
|
|
1599
|
+
member_branch_targets(command: command, member: member, member_config: member_config, start_at: start_at)
|
|
1600
|
+
end
|
|
1601
|
+
branches = slice_branch_targets(branches, start_at.branch) if command == "template" && start_at.branch && start_at.member == member.name
|
|
1602
|
+
memo[member.name] = branches
|
|
1567
1603
|
end
|
|
1568
1604
|
end
|
|
1569
1605
|
|
data/lib/kettle/family/report.rb
CHANGED
|
@@ -122,7 +122,7 @@ module Kettle
|
|
|
122
122
|
|
|
123
123
|
lines << "results:"
|
|
124
124
|
visible_results.each do |result|
|
|
125
|
-
lines << " #{result_state(result)} #{result
|
|
125
|
+
lines << " #{result_state(result)} #{result_identity(result)} #{result.phase} #{result.reason || ""}".rstrip
|
|
126
126
|
append_result_stdout(lines, result)
|
|
127
127
|
append_indented_output(lines, result.stderr) if !result.ok? && !result.stderr.to_s.empty?
|
|
128
128
|
resume_hint = resume_hint_for(result)
|
|
@@ -436,20 +436,39 @@ module Kettle
|
|
|
436
436
|
end
|
|
437
437
|
|
|
438
438
|
def selected_names
|
|
439
|
-
selected_members.map(&:name)
|
|
439
|
+
return selected_members.map(&:name) unless branch_stack_template?
|
|
440
|
+
|
|
441
|
+
selected_members.flat_map do |member|
|
|
442
|
+
branches = member_release_target_branches[member.name]
|
|
443
|
+
branches ? branches.map { |branch| "#{member.name}@#{branch}" } : [member.name]
|
|
444
|
+
end
|
|
440
445
|
end
|
|
441
446
|
|
|
442
447
|
def selected_member_results
|
|
443
448
|
return {} unless member_result_command?
|
|
444
449
|
|
|
450
|
+
selected_identities = selected_names
|
|
445
451
|
built = visible_results.each_with_object(Hash.new { |hash, key| hash[key] = [] }) do |result, memo|
|
|
446
|
-
|
|
452
|
+
identity = result_identity(result)
|
|
453
|
+
next unless selected_identities.include?(identity)
|
|
447
454
|
|
|
448
|
-
memo[
|
|
455
|
+
memo[identity] << result
|
|
449
456
|
end
|
|
450
457
|
built.sort.to_h
|
|
451
458
|
end
|
|
452
459
|
|
|
460
|
+
def result_identity(result)
|
|
461
|
+
if branch_stack_template? && result.branch && member_release_target_branches.fetch(result.member_name, []).include?(result.branch)
|
|
462
|
+
"#{result.member_name}@#{result.branch}"
|
|
463
|
+
else
|
|
464
|
+
result.member_name
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def branch_stack_template?
|
|
469
|
+
command == "template" && !member_release_target_branches.empty?
|
|
470
|
+
end
|
|
471
|
+
|
|
453
472
|
def summary_succeeded
|
|
454
473
|
selected_member_results.filter_map do |member_name, member_results|
|
|
455
474
|
next if member_results.empty?
|
|
@@ -479,7 +498,7 @@ module Kettle
|
|
|
479
498
|
# Alphabetical summary lists, regardless of parallel completion order.
|
|
480
499
|
visible_results.reject(&:ok?).sort_by.with_index { |result, index| [result.member_name, index] }.map do |result|
|
|
481
500
|
{
|
|
482
|
-
"member" => result
|
|
501
|
+
"member" => result_identity(result),
|
|
483
502
|
"phase" => result.phase,
|
|
484
503
|
"reason" => result.reason || "command failed"
|
|
485
504
|
}
|
|
@@ -511,9 +530,9 @@ module Kettle
|
|
|
511
530
|
|
|
512
531
|
def summary_ran_member_names
|
|
513
532
|
if command == "template"
|
|
514
|
-
failed = visible_results.reject(&:ok?).map(
|
|
515
|
-
templated = selected_member_results.filter_map do |
|
|
516
|
-
|
|
533
|
+
failed = visible_results.reject(&:ok?).map { |result| result_identity(result) }
|
|
534
|
+
templated = selected_member_results.filter_map do |identity, member_results|
|
|
535
|
+
identity if member_results.any? { |result| result.phase == "template" }
|
|
517
536
|
end
|
|
518
537
|
return (failed + templated).uniq
|
|
519
538
|
end
|
|
@@ -778,7 +797,7 @@ module Kettle
|
|
|
778
797
|
return "kettle-family #{command}" if family_root_phase?(result.phase)
|
|
779
798
|
# A positional --start-at would silently drop pending members that are
|
|
780
799
|
# ordered ahead of this one, so wave-dispatched commands resume by name.
|
|
781
|
-
return "kettle-family #{command} --only #{result
|
|
800
|
+
return "kettle-family #{command} --only #{result_identity(result)}" if non_positional_dispatch?
|
|
782
801
|
|
|
783
802
|
"kettle-family #{command} --start-at #{result.member_name}"
|
|
784
803
|
end
|
|
@@ -41,13 +41,18 @@ module Kettle
|
|
|
41
41
|
# than letting it silently drop members. Mirrors the class-method validation
|
|
42
42
|
# convention used by validate_release_state_only_filter! so callers in the CLI
|
|
43
43
|
# do not have to know which commands are affected.
|
|
44
|
-
def self.validate_positional_start_at!(command, start_at)
|
|
44
|
+
def self.validate_positional_start_at!(command, start_at, branch: nil)
|
|
45
45
|
return if start_at.nil? || start_at.to_s.empty?
|
|
46
46
|
return unless non_positional_dispatch?(command)
|
|
47
|
+
return if command == "template" && !branch.to_s.empty?
|
|
47
48
|
|
|
49
|
+
detail = if command == "template"
|
|
50
|
+
"Use --only #{start_at} (or the prior report's retry hint), or MEMBER@BRANCH for a branch-stack resume."
|
|
51
|
+
else
|
|
52
|
+
"Use --only #{start_at} (or the resume hint from the previous report) instead."
|
|
53
|
+
end
|
|
48
54
|
raise Error, "--start-at is not supported for #{command}: members are dispatched in dependency " \
|
|
49
|
-
"waves, so a positional resume would silently skip members that never ran. "
|
|
50
|
-
"Use --only #{start_at} (or the resume hint from the previous report) instead."
|
|
55
|
+
"waves, so a positional resume would silently skip members that never ran. #{detail}"
|
|
51
56
|
end
|
|
52
57
|
|
|
53
58
|
def self.status_tokens
|
|
@@ -73,6 +78,20 @@ module Kettle
|
|
|
73
78
|
end.map(&:branch).compact.uniq
|
|
74
79
|
end
|
|
75
80
|
|
|
81
|
+
def self.member_names(only)
|
|
82
|
+
Array(only.to_s.split(",").map(&:strip).reject(&:empty?)).map { |selector| selector.split("@", 2).first }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.branch_selectors(only)
|
|
86
|
+
only.to_s.split(",").map(&:strip).reject(&:empty?).filter_map do |selector|
|
|
87
|
+
member_name, branch = selector.split("@", 2)
|
|
88
|
+
next unless branch
|
|
89
|
+
raise Error, "invalid branch selector #{selector.inspect}; expected MEMBER@BRANCH" if member_name.empty? || branch.empty?
|
|
90
|
+
|
|
91
|
+
[member_name, branch]
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
76
95
|
def self.validate_release_state_only_filter!(only)
|
|
77
96
|
names = only.to_s.split(",").map(&:strip).reject(&:empty?)
|
|
78
97
|
status_tokens = names.select { |name| status_token?(name) }
|
|
@@ -112,9 +131,10 @@ module Kettle
|
|
|
112
131
|
return select_release_state_status(selected, status_tokens)
|
|
113
132
|
end
|
|
114
133
|
|
|
115
|
-
unknown =
|
|
134
|
+
unknown = self.class.member_names(only) - members.map(&:name)
|
|
116
135
|
raise Error, "unknown member(s): #{unknown.join(", ")}" unless unknown.empty?
|
|
117
136
|
|
|
137
|
+
names = self.class.member_names(only)
|
|
118
138
|
selected.select { |candidate| names.include?(candidate.name) }
|
|
119
139
|
end
|
|
120
140
|
|
|
@@ -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.3.
|
|
8
|
+
VERSION = "1.3.28"
|
|
9
9
|
end
|
|
10
10
|
# Current gem version exposed at the traditional constant location.
|
|
11
11
|
VERSION = Version::VERSION # Traditional Constant Location
|
|
@@ -107,8 +107,9 @@ module Kettle
|
|
|
107
107
|
RUBY
|
|
108
108
|
REGISTRY_WAIT_ATTEMPTS = 15
|
|
109
109
|
REGISTRY_WAIT_INTERVAL_SECONDS = 15
|
|
110
|
+
TEMPLATE_GEMSPEC_RETRY_DELAY_SECONDS = 0.25
|
|
110
111
|
|
|
111
|
-
def initialize(command:, config:, members:, family_members: nil, execute: false, accept: true, commit: true, allow_dirty: false, autostash: true, template_cleanup: true, publish: false, push: false, tag: false, start_step: nil, skip_steps: nil, fast_recovery: nil, fast_recovery_members: nil, skip_ci: false, skip_changelog: false, skip_appraisals: false, local_ci: false, continue_ci_failures: false, ci_workflows: nil, skip_bundle_audit: false, skip_remotes: nil, required_remotes: nil, auto_dependency_floors: nil, validate_ci_bundles: 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, release_state_results: nil, event_tape: nil, **options)
|
|
112
|
+
def initialize(command:, config:, members:, family_members: nil, execute: false, accept: true, commit: true, allow_dirty: false, autostash: true, template_cleanup: true, publish: false, push: false, tag: false, start_step: nil, skip_steps: nil, fast_recovery: nil, fast_recovery_members: nil, skip_ci: false, skip_changelog: false, skip_appraisals: false, local_ci: false, continue_ci_failures: false, ci_workflows: nil, skip_bundle_audit: false, skip_remotes: nil, required_remotes: nil, auto_dependency_floors: nil, validate_ci_bundles: 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, template_branch_selection: {}, release_state_results: nil, event_tape: nil, **options)
|
|
112
113
|
@command = command
|
|
113
114
|
@config = config
|
|
114
115
|
@members = members
|
|
@@ -162,6 +163,7 @@ module Kettle
|
|
|
162
163
|
@bex_args = bex_args
|
|
163
164
|
@start_member = start_member
|
|
164
165
|
@start_branch = start_branch
|
|
166
|
+
@template_branch_selection = template_branch_selection
|
|
165
167
|
@release_state_results = Array(release_state_results)
|
|
166
168
|
@event_tape = event_tape
|
|
167
169
|
@template_commit_mutex = Mutex.new
|
|
@@ -1634,7 +1636,10 @@ module Kettle
|
|
|
1634
1636
|
# linked worktrees avoids repeatedly switching the primary checkout and
|
|
1635
1637
|
# allows the branch stack to use the requested template worker budget.
|
|
1636
1638
|
def template_branch_worktree_results(member:, member_config:)
|
|
1637
|
-
branches =
|
|
1639
|
+
branches = @template_branch_selection.fetch(member.name) do
|
|
1640
|
+
BranchTargetConfig.branch_targets_for(command, member_config.release_target_branches)
|
|
1641
|
+
end
|
|
1642
|
+
branches = slice_branch_targets(branches, start_branch) if start_member == member.name && start_branch
|
|
1638
1643
|
return [] if branches.empty?
|
|
1639
1644
|
|
|
1640
1645
|
entries = []
|
|
@@ -4870,15 +4875,18 @@ module Kettle
|
|
|
4870
4875
|
end
|
|
4871
4876
|
|
|
4872
4877
|
def start_release_progress(release_members)
|
|
4873
|
-
|
|
4878
|
+
waves = release_waves(release_members)
|
|
4879
|
+
wave_numbers = waves.each_with_index.flat_map do |wave, index|
|
|
4874
4880
|
wave.map { |member| [member.name, index + 1] }
|
|
4875
4881
|
end.to_h
|
|
4882
|
+
# The pane is a schedule view: list members in execution order (wave
|
|
4883
|
+
# plan order, queueing order within each wave), not raw member order.
|
|
4876
4884
|
progress = WorkflowProgress.new(
|
|
4877
4885
|
io: progress_io,
|
|
4878
4886
|
label: release_progress_label,
|
|
4879
4887
|
total: release_members.length,
|
|
4880
4888
|
jobs: release_jobs(release_members),
|
|
4881
|
-
members:
|
|
4889
|
+
members: waves.flatten,
|
|
4882
4890
|
waves: wave_numbers
|
|
4883
4891
|
)
|
|
4884
4892
|
progress.start
|
|
@@ -5595,6 +5603,13 @@ module Kettle
|
|
|
5595
5603
|
commit_normalized_lockfiles(branch_members: [member], runner: runner, memo: memo, reason: "reset", force: true)
|
|
5596
5604
|
end
|
|
5597
5605
|
|
|
5606
|
+
def transient_cached_gemspec_failure?(result)
|
|
5607
|
+
return false unless execute && result && !result.ok?
|
|
5608
|
+
|
|
5609
|
+
output = [result.stdout, result.stderr].join("\n")
|
|
5610
|
+
output.include?("Bundler::GemspecError") && output.match?(/isn't a Gem::Specification \(NilClass instead\)/)
|
|
5611
|
+
end
|
|
5612
|
+
|
|
5598
5613
|
def recoverable_bundle_failure?(result)
|
|
5599
5614
|
return false unless execute && !result.ok?
|
|
5600
5615
|
|
|
@@ -5794,6 +5809,19 @@ module Kettle
|
|
|
5794
5809
|
command: template_prepare_command(member),
|
|
5795
5810
|
env: template_prepare_env(wave_jobs: wave_jobs, member: member)
|
|
5796
5811
|
)
|
|
5812
|
+
if transient_cached_gemspec_failure?(result)
|
|
5813
|
+
sleep(TEMPLATE_GEMSPEC_RETRY_DELAY_SECONDS)
|
|
5814
|
+
retry_result = runner.call(
|
|
5815
|
+
member: member,
|
|
5816
|
+
phase: "prepare_template_dependencies_retry",
|
|
5817
|
+
command: template_prepare_command(member),
|
|
5818
|
+
env: template_prepare_env(wave_jobs: wave_jobs, member: member)
|
|
5819
|
+
)
|
|
5820
|
+
unless retry_result.ok?
|
|
5821
|
+
retry_result.stderr = [result.stderr, "Initial attempt failed with a transient cached-gemspec error.", retry_result.stderr].compact.reject(&:empty?).join("\n")
|
|
5822
|
+
end
|
|
5823
|
+
result = retry_result
|
|
5824
|
+
end
|
|
5797
5825
|
if recoverable_bundle_failure?(result)
|
|
5798
5826
|
recover_template_lockfiles(
|
|
5799
5827
|
member: member,
|
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.3.
|
|
4
|
+
version: 1.3.28
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -134,7 +134,7 @@ dependencies:
|
|
|
134
134
|
version: '3.1'
|
|
135
135
|
- - ">="
|
|
136
136
|
- !ruby/object:Gem::Version
|
|
137
|
-
version: 3.1.
|
|
137
|
+
version: 3.1.20
|
|
138
138
|
type: :runtime
|
|
139
139
|
prerelease: false
|
|
140
140
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -144,7 +144,7 @@ dependencies:
|
|
|
144
144
|
version: '3.1'
|
|
145
145
|
- - ">="
|
|
146
146
|
- !ruby/object:Gem::Version
|
|
147
|
-
version: 3.1.
|
|
147
|
+
version: 3.1.20
|
|
148
148
|
- !ruby/object:Gem::Dependency
|
|
149
149
|
name: kettle-gha-pins
|
|
150
150
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -567,10 +567,10 @@ licenses:
|
|
|
567
567
|
- AGPL-3.0-only
|
|
568
568
|
metadata:
|
|
569
569
|
homepage_uri: https://kettle-family.galtzo.com
|
|
570
|
-
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.3.
|
|
571
|
-
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.3.
|
|
570
|
+
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.3.28
|
|
571
|
+
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.3.28/CHANGELOG.md
|
|
572
572
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
|
|
573
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.3.
|
|
573
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.3.28
|
|
574
574
|
funding_uri: https://github.com/sponsors/pboling
|
|
575
575
|
wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
|
|
576
576
|
news_uri: https://www.railsbling.com/tags/kettle-family
|
metadata.gz.sig
CHANGED
|
Binary file
|