kettle-family 1.2.79 → 1.2.80
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 +20 -1
- data/README.md +1 -1
- data/lib/kettle/family/version.rb +1 -1
- data/lib/kettle/family/workflow.rb +176 -8
- data.tar.gz.sig +0 -0
- metadata +4 -4
- 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: 6ccfe598bd7e6f842c4020a1763565a3a626ac61c6726f7a37652227494d6947
|
|
4
|
+
data.tar.gz: a49bf719e3d7bbf184baa6e9a2f3de8080a56af789dcc08b1b8df81ae074656c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4656fb34c6cffb5ebbd33c3052a62dec50a2c075e6abce72f2ff58d392d4918f1bf8cee49de268f09c2130ccad4bf2bde5c60537092e83de586e6d3ac42f6ed8
|
|
7
|
+
data.tar.gz: 0c681c8436b1246fcd0882c2dcd89a77255d6d77942ab29dfca35471f9a4f69f7b0efdfe09f2019fb55a6e2b0c775ac79c741b132c8124403aa026770c49d1e1
|
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.80] - 2026-09-01
|
|
34
|
+
|
|
35
|
+
- TAG: [v1.2.80][1.2.80t]
|
|
36
|
+
- COVERAGE: 93.26% -- 5662/6071 lines in 33 files
|
|
37
|
+
- BRANCH COVERAGE: 76.39% -- 2316/3032 branches in 33 files
|
|
38
|
+
- 30.69% documented
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- Template member target branches concurrently in isolated Git worktrees, while retaining the primary checkout for its current branch.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- Keep non-targeted members in the dependency-aware parallel template scheduler when another family member has release target branches.
|
|
47
|
+
|
|
48
|
+
- Leave an unrelated primary checkout untouched while templating configured target branches in worktrees.
|
|
49
|
+
|
|
33
50
|
## [1.2.79] - 2026-09-01
|
|
34
51
|
|
|
35
52
|
- TAG: [v1.2.79][1.2.79t]
|
|
@@ -2449,7 +2466,9 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
2449
2466
|
- Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
|
|
2450
2467
|
- Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
|
|
2451
2468
|
|
|
2452
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.
|
|
2469
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.80...HEAD
|
|
2470
|
+
[1.2.80]: https://github.com/kettle-dev/kettle-family/compare/v1.2.79...v1.2.80
|
|
2471
|
+
[1.2.80t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.80
|
|
2453
2472
|
[1.2.79]: https://github.com/kettle-dev/kettle-family/compare/v1.2.78...v1.2.79
|
|
2454
2473
|
[1.2.79t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.79
|
|
2455
2474
|
[1.2.78]: https://github.com/kettle-dev/kettle-family/compare/v1.2.77...v1.2.78
|
data/README.md
CHANGED
|
@@ -1049,7 +1049,7 @@ Thanks for RTFM. ☺️
|
|
|
1049
1049
|
[📌gitmoji]: https://gitmoji.dev
|
|
1050
1050
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
1051
1051
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
1052
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-
|
|
1052
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-6.071-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
1053
1053
|
[🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
|
|
1054
1054
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
1055
1055
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
|
@@ -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.80"
|
|
9
9
|
end
|
|
10
10
|
# Current gem version exposed at the traditional constant location.
|
|
11
11
|
VERSION = Version::VERSION # Traditional Constant Location
|
|
@@ -457,7 +457,7 @@ module Kettle
|
|
|
457
457
|
end
|
|
458
458
|
|
|
459
459
|
def template_sync_members_for(candidates)
|
|
460
|
-
candidates.each_with_object([]) do |member, unique_members|
|
|
460
|
+
candidates.reject { |member| template_worktree_only_member?(member) }.each_with_object([]) do |member, unique_members|
|
|
461
461
|
unique_members << member unless unique_members.any? { |candidate| git_root_for(candidate) == git_root_for(member) }
|
|
462
462
|
end
|
|
463
463
|
end
|
|
@@ -925,15 +925,181 @@ module Kettle
|
|
|
925
925
|
def member_local_branch_target_results
|
|
926
926
|
return release_member_local_branch_target_results if command == "release"
|
|
927
927
|
|
|
928
|
-
members.
|
|
928
|
+
target_members, current_branch_members = members.partition do |member|
|
|
929
|
+
member_local_release_config(member)
|
|
930
|
+
end
|
|
931
|
+
results = []
|
|
932
|
+
results = current_branch_results(current_branch_members) unless current_branch_members.empty?
|
|
933
|
+
return results unless results.all?(&:ok?)
|
|
934
|
+
|
|
935
|
+
target_members.each do |member|
|
|
929
936
|
member_config = member_local_release_config(member)
|
|
930
|
-
if member_config
|
|
931
|
-
|
|
937
|
+
if template_branch_worktrees?(member_config)
|
|
938
|
+
results.concat(template_branch_worktree_results(member: member, member_config: member_config))
|
|
932
939
|
else
|
|
933
|
-
|
|
940
|
+
results.concat(member_local_workflow(member: member, member_config: member_config).results)
|
|
934
941
|
end
|
|
935
|
-
break
|
|
942
|
+
break unless results.last&.ok?
|
|
936
943
|
end
|
|
944
|
+
results
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
# Release target branches are independent checkouts. Templating them in
|
|
948
|
+
# linked worktrees avoids repeatedly switching the primary checkout and
|
|
949
|
+
# allows the branch stack to use the requested template worker budget.
|
|
950
|
+
def template_branch_worktree_results(member:, member_config:)
|
|
951
|
+
branches = BranchTargetConfig.branch_targets_for(command, member_config.release_target_branches)
|
|
952
|
+
return [] if branches.empty?
|
|
953
|
+
|
|
954
|
+
entries = []
|
|
955
|
+
outcomes = []
|
|
956
|
+
runner = command_runner
|
|
957
|
+
fetch = runner.call(member: member, phase: "template_branch_fetch", command: %w[git fetch --all --prune])
|
|
958
|
+
outcomes << fetch
|
|
959
|
+
if fetch.ok?
|
|
960
|
+
current_branch = template_current_branch(member)
|
|
961
|
+
if current_branch.is_a?(CommandResult)
|
|
962
|
+
outcomes << current_branch
|
|
963
|
+
else
|
|
964
|
+
branches.each do |branch|
|
|
965
|
+
if branch == current_branch
|
|
966
|
+
entries << {branch: branch, member: member, worktree_root: nil}
|
|
967
|
+
next
|
|
968
|
+
end
|
|
969
|
+
|
|
970
|
+
worktree_root = template_branch_worktree_path(member, branch)
|
|
971
|
+
add = runner.call(
|
|
972
|
+
member: member,
|
|
973
|
+
phase: "template_worktree_add",
|
|
974
|
+
command: ["git", "worktree", "add", worktree_root, branch]
|
|
975
|
+
)
|
|
976
|
+
add.branch = branch
|
|
977
|
+
outcomes << add
|
|
978
|
+
break unless add.ok?
|
|
979
|
+
|
|
980
|
+
entries << {branch: branch, member: relocate_member_to_worktree(member, worktree_root), worktree_root: worktree_root}
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
if outcomes.all?(&:ok?)
|
|
984
|
+
emit_template_event_line(member, ">", "branch worktrees #{entries.length} branches, #{template_jobs(entries)} jobs")
|
|
985
|
+
outcomes.concat(template_branch_worktree_entries_results(entries))
|
|
986
|
+
end
|
|
987
|
+
end
|
|
988
|
+
end
|
|
989
|
+
outcomes
|
|
990
|
+
ensure
|
|
991
|
+
outcomes&.concat(template_branch_worktree_cleanup_results(entries, runner: runner, control_member: member))
|
|
992
|
+
end
|
|
993
|
+
|
|
994
|
+
def template_branch_worktrees?(member_config)
|
|
995
|
+
command == "template" && execute && !member_config&.release_target_branches.to_a.empty?
|
|
996
|
+
end
|
|
997
|
+
|
|
998
|
+
def template_worktree_only_member?(member)
|
|
999
|
+
member_config = member_local_release_config(member)
|
|
1000
|
+
return false unless template_branch_worktrees?(member_config)
|
|
1001
|
+
|
|
1002
|
+
current_branch = template_current_branch(member)
|
|
1003
|
+
return false if current_branch.is_a?(CommandResult)
|
|
1004
|
+
|
|
1005
|
+
!member_config.release_target_branches.include?(current_branch)
|
|
1006
|
+
end
|
|
1007
|
+
|
|
1008
|
+
def template_current_branch(member)
|
|
1009
|
+
stdout, stderr, status = Open3.capture3("git", "branch", "--show-current", chdir: member.root)
|
|
1010
|
+
branch = stdout.strip
|
|
1011
|
+
return branch if status.success? && !branch.empty?
|
|
1012
|
+
|
|
1013
|
+
template_branch_failure_result(member, "could not determine current branch: #{stderr}")
|
|
1014
|
+
end
|
|
1015
|
+
|
|
1016
|
+
def template_branch_worktree_path(member, branch)
|
|
1017
|
+
safe_member = member.name.to_s.gsub(/[^A-Za-z0-9_.-]+/, "_")
|
|
1018
|
+
safe_branch = branch.to_s.gsub(/[^A-Za-z0-9_.-]+/, "_")
|
|
1019
|
+
File.join(config.root, "tmp", "kettle-family", "template-worktrees", safe_member, "#{Process.pid}-#{safe_branch}")
|
|
1020
|
+
end
|
|
1021
|
+
|
|
1022
|
+
def relocate_member_to_worktree(member, worktree_root)
|
|
1023
|
+
relocate = lambda do |path|
|
|
1024
|
+
next unless path
|
|
1025
|
+
|
|
1026
|
+
relative = Pathname.new(path).relative_path_from(Pathname.new(member.root))
|
|
1027
|
+
File.join(worktree_root, relative)
|
|
1028
|
+
end
|
|
1029
|
+
member.dup.tap do |worktree_member|
|
|
1030
|
+
worktree_member.root = worktree_root
|
|
1031
|
+
worktree_member.gemspec_path = relocate.call(member.gemspec_path)
|
|
1032
|
+
worktree_member.version_file = relocate.call(member.version_file)
|
|
1033
|
+
end
|
|
1034
|
+
end
|
|
1035
|
+
|
|
1036
|
+
def template_branch_worktree_entries_results(entries)
|
|
1037
|
+
queue = Queue.new
|
|
1038
|
+
entries.each_with_index { |entry, index| queue << [index, entry] }
|
|
1039
|
+
ordered_results = Array.new(entries.length)
|
|
1040
|
+
mutex = Mutex.new
|
|
1041
|
+
stop = false
|
|
1042
|
+
Array.new(template_jobs(entries)) do
|
|
1043
|
+
Thread.new do # rubocop:disable ThreadSafety/NewThread -- independent branch worktrees are safe to template concurrently.
|
|
1044
|
+
loop do
|
|
1045
|
+
break if mutex.synchronize { stop }
|
|
1046
|
+
index, entry = queue.pop(true)
|
|
1047
|
+
branch_results = template_branch_worktree_entry_results(entry)
|
|
1048
|
+
mutex.synchronize do
|
|
1049
|
+
ordered_results[index] = branch_results
|
|
1050
|
+
stop = true unless branch_results.all?(&:ok?)
|
|
1051
|
+
end
|
|
1052
|
+
rescue ThreadError
|
|
1053
|
+
break
|
|
1054
|
+
end
|
|
1055
|
+
end
|
|
1056
|
+
end.each(&:join)
|
|
1057
|
+
ordered_results.compact.flatten
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
def template_branch_worktree_entry_results(entry)
|
|
1061
|
+
member = entry.fetch(:member)
|
|
1062
|
+
branch = entry.fetch(:branch)
|
|
1063
|
+
runner = command_runner
|
|
1064
|
+
results = []
|
|
1065
|
+
if git_upstream_for(member)
|
|
1066
|
+
results << runner.call(member: member, phase: "template_branch_sync", command: ["git", "rebase", "@{upstream}"])
|
|
1067
|
+
end
|
|
1068
|
+
results.concat(template_results_for_member(member)) if results.all?(&:ok?)
|
|
1069
|
+
tag_branch_results(results, branch)
|
|
1070
|
+
emit_template_event_line(member, results.all?(&:ok?) ? "." : "F", "branch #{branch}")
|
|
1071
|
+
results
|
|
1072
|
+
end
|
|
1073
|
+
|
|
1074
|
+
def template_branch_worktree_cleanup_results(entries, runner:, control_member:)
|
|
1075
|
+
entries.filter_map do |entry|
|
|
1076
|
+
worktree_root = entry[:worktree_root]
|
|
1077
|
+
next unless worktree_root && Dir.exist?(worktree_root)
|
|
1078
|
+
|
|
1079
|
+
result = runner.call(
|
|
1080
|
+
member: control_member,
|
|
1081
|
+
phase: "template_worktree_remove",
|
|
1082
|
+
command: ["git", "worktree", "remove", "--force", worktree_root]
|
|
1083
|
+
)
|
|
1084
|
+
result.branch = entry.fetch(:branch)
|
|
1085
|
+
result
|
|
1086
|
+
end
|
|
1087
|
+
end
|
|
1088
|
+
|
|
1089
|
+
def template_branch_failure_result(member, message)
|
|
1090
|
+
CommandResult.new(
|
|
1091
|
+
member_name: member.name,
|
|
1092
|
+
phase: "template_worktree",
|
|
1093
|
+
command: ["internal", "template-worktree"],
|
|
1094
|
+
workdir: member.root,
|
|
1095
|
+
status: 1,
|
|
1096
|
+
success: false,
|
|
1097
|
+
stdout: "",
|
|
1098
|
+
stderr: message,
|
|
1099
|
+
elapsed_seconds: 0.0,
|
|
1100
|
+
skipped: false,
|
|
1101
|
+
reason: "template branch worktree failed"
|
|
1102
|
+
)
|
|
937
1103
|
end
|
|
938
1104
|
|
|
939
1105
|
def branch_generated_lockfile_cleanup_required?
|
|
@@ -1150,7 +1316,9 @@ module Kettle
|
|
|
1150
1316
|
end
|
|
1151
1317
|
|
|
1152
1318
|
def branch_checkout_preflight_members
|
|
1153
|
-
members_with_targets = members.select
|
|
1319
|
+
members_with_targets = members.select do |member|
|
|
1320
|
+
member_local_release_config(member) && !template_worktree_only_member?(member)
|
|
1321
|
+
end
|
|
1154
1322
|
members_with_targets = [family_member] if !config.release_target_branches.empty?
|
|
1155
1323
|
members_with_targets
|
|
1156
1324
|
end
|
|
@@ -3793,7 +3961,7 @@ module Kettle
|
|
|
3793
3961
|
end
|
|
3794
3962
|
|
|
3795
3963
|
def emit_template_event_line(member, mark, label)
|
|
3796
|
-
return if label.to_s.empty?
|
|
3964
|
+
return if label.to_s.empty? || !progress_io
|
|
3797
3965
|
|
|
3798
3966
|
synchronize_template_progress do
|
|
3799
3967
|
progress_io.puts("[#{member.name}] #{mark} #{label}")
|
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.80
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -441,10 +441,10 @@ licenses:
|
|
|
441
441
|
- AGPL-3.0-only
|
|
442
442
|
metadata:
|
|
443
443
|
homepage_uri: https://kettle-family.galtzo.com
|
|
444
|
-
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.
|
|
445
|
-
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.
|
|
444
|
+
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.80
|
|
445
|
+
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.80/CHANGELOG.md
|
|
446
446
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
|
|
447
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.
|
|
447
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.80
|
|
448
448
|
funding_uri: https://github.com/sponsors/pboling
|
|
449
449
|
wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
|
|
450
450
|
news_uri: https://www.railsbling.com/tags/kettle-family
|
metadata.gz.sig
CHANGED
|
Binary file
|