kettle-family 1.2.91 → 1.2.93
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 +27 -1
- data/README.md +30 -7
- data/lib/kettle/family/report.rb +8 -2
- data/lib/kettle/family/version.rb +1 -1
- data/lib/kettle/family/workflow.rb +171 -34
- 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: a1a24d4a7f560730b58dea502aa74c1f7c2b6b5821c92bc565ee5d0eff79d8be
|
|
4
|
+
data.tar.gz: 58f47810263c6279e727419acb896abb959ccac76bff444c83b19c88c85f64f5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7bb4085cb68b1d1ee1592ad1ab43d55afbc08a4c12a341ffffb6f1b3d712f43e51e0b8b4f59095fdd8402e1fa7c21822d6b43da8c8bd4caf340e102418d2974c
|
|
7
|
+
data.tar.gz: c12db5ebfea5489500b221fbc39f8d638fc6f4ad119eb73a210e195b779804325be2effad8970a78b65f7a5f0b837a9d324bb12242ab33c61b782877de5d6343
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,28 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [1.2.93] - 2026-09-07
|
|
34
|
+
|
|
35
|
+
- TAG: [v1.2.93][1.2.93t]
|
|
36
|
+
- COVERAGE: 93.54% -- 6165/6591 lines in 35 files
|
|
37
|
+
- BRANCH COVERAGE: 78.08% -- 2507/3211 branches in 35 files
|
|
38
|
+
- 32.41% documented
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Update family-root bundles before member bundles for bup and bupb, committing the root lockfile only when the family is a monorepo.
|
|
43
|
+
|
|
44
|
+
## [1.2.92] - 2026-09-07
|
|
45
|
+
|
|
46
|
+
- TAG: [v1.2.92][1.2.92t]
|
|
47
|
+
- COVERAGE: 93.52% -- 6144/6570 lines in 35 files
|
|
48
|
+
- BRANCH COVERAGE: 77.98% -- 2490/3193 branches in 35 files
|
|
49
|
+
- 32.41% documented
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- Preserve pre-existing monorepo member changes while templating in parallel worktrees, reject concurrent member edits without overwriting them, and count only fully materialized members as successful.
|
|
54
|
+
|
|
33
55
|
## [1.2.91] - 2026-09-06
|
|
34
56
|
|
|
35
57
|
- TAG: [v1.2.91][1.2.91t]
|
|
@@ -2605,7 +2627,11 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
2605
2627
|
- Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
|
|
2606
2628
|
- Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
|
|
2607
2629
|
|
|
2608
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.
|
|
2630
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.93...HEAD
|
|
2631
|
+
[1.2.93]: https://github.com/kettle-dev/kettle-family/compare/v1.2.92...v1.2.93
|
|
2632
|
+
[1.2.93t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.93
|
|
2633
|
+
[1.2.92]: https://github.com/kettle-dev/kettle-family/compare/v1.2.91...v1.2.92
|
|
2634
|
+
[1.2.92t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.92
|
|
2609
2635
|
[1.2.91]: https://github.com/kettle-dev/kettle-family/compare/v1.2.90...v1.2.91
|
|
2610
2636
|
[1.2.91t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.91
|
|
2611
2637
|
[1.2.90]: https://github.com/kettle-dev/kettle-family/compare/v1.2.89...v1.2.90
|
data/README.md
CHANGED
|
@@ -281,6 +281,22 @@ canonical development lockfile and CI checkout share that path graph. In both
|
|
|
281
281
|
cases, release normalization rejects every path outside the configured allow
|
|
282
282
|
list.
|
|
283
283
|
|
|
284
|
+
### Template concurrency
|
|
285
|
+
|
|
286
|
+
Executed `kettle-jem` templating for monorepo members uses detached worktrees
|
|
287
|
+
when a dependency wave contains multiple ready members and the worker budget is
|
|
288
|
+
greater than one. Before a worker starts, Family copies that member's current
|
|
289
|
+
tracked and untracked state into its worktree. This preserves development
|
|
290
|
+
lockfile changes produced by local dependency updates instead of assuming that
|
|
291
|
+
the primary checkout matches `HEAD`.
|
|
292
|
+
|
|
293
|
+
After the worker succeeds, Family verifies that the same member in the primary
|
|
294
|
+
checkout has not changed since the snapshot. It then materializes the worker's
|
|
295
|
+
final member-scoped state and serializes the normal template commit. Changes
|
|
296
|
+
outside the member root are rejected, as are concurrent edits to that member;
|
|
297
|
+
in either case the primary member is left untouched. Template summaries count
|
|
298
|
+
a member only after worktree materialization and any required commit succeed.
|
|
299
|
+
|
|
284
300
|
### Test concurrency
|
|
285
301
|
|
|
286
302
|
`kettle-family test --execute` runs independent sibling repositories in
|
|
@@ -480,8 +496,8 @@ The command names below are available from `kettle-family --help`:
|
|
|
480
496
|
| `docs` | Run the configured documentation workflow for each selected member. |
|
|
481
497
|
| `template` | Apply the configured `kettle-jem` template for each selected member. |
|
|
482
498
|
| `gha-sha-pins` | Inspect or update GitHub Actions SHA pins. |
|
|
483
|
-
| `bup` | Run `bundle update --all`, or update a named dependency, per member. |
|
|
484
|
-
| `bupb` | Run `bundle update --bundler` per member. |
|
|
499
|
+
| `bup` | Run `bundle update --all`, or update a named dependency, at the family root and per member. |
|
|
500
|
+
| `bupb` | Run `bundle update --bundler` at the family root and per member. |
|
|
485
501
|
| `bex` | Run an arbitrary `bundle exec` command per member. |
|
|
486
502
|
| `install` | Build and install selected local family gems. |
|
|
487
503
|
| `bump` | Check, plan, or execute family version alignment. |
|
|
@@ -580,10 +596,17 @@ kettle-family bup --execute
|
|
|
580
596
|
kettle-family template --execute
|
|
581
597
|
```
|
|
582
598
|
|
|
583
|
-
`bup` runs `bundle update --all`
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
599
|
+
`bup` first runs `bundle update --all` at the family root when that root has a
|
|
600
|
+
`Gemfile`, then runs it for every selected member. `bupb` follows the same
|
|
601
|
+
root-first order with `bundle update --bundler`. A root that is itself a member
|
|
602
|
+
is updated only once. A gem name may be passed to `bup` to update only that
|
|
603
|
+
dependency, for example `kettle-family bup --execute kettle-test`.
|
|
604
|
+
|
|
605
|
+
Executed member updates commit successful lockfile changes by default. The
|
|
606
|
+
family-root lockfile is also validated and committed for monorepos, where the
|
|
607
|
+
root and members share one Git repository. A sibling-repository family root is
|
|
608
|
+
updated for its orchestration bundle but is not included in any member commit.
|
|
609
|
+
Use `--no-commit` for a working-tree-only refresh, or `--commit` to state the
|
|
587
610
|
default explicitly. Local dependency environment variables are preserved when
|
|
588
611
|
explicitly supplied, so use the family's local or released dependency mode
|
|
589
612
|
deliberately before running it.
|
|
@@ -1121,7 +1144,7 @@ Thanks for RTFM. ☺️
|
|
|
1121
1144
|
[📌gitmoji]: https://gitmoji.dev
|
|
1122
1145
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
1123
1146
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
1124
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-6.
|
|
1147
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-6.591-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
1125
1148
|
[🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
|
|
1126
1149
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
1127
1150
|
[📄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/report.rb
CHANGED
|
@@ -332,11 +332,16 @@ module Kettle
|
|
|
332
332
|
template_results = results.select { |result| result.phase == "template" }
|
|
333
333
|
return if template_results.empty?
|
|
334
334
|
|
|
335
|
+
successful_names = summary_succeeded
|
|
336
|
+
template_results.select! do |result|
|
|
337
|
+
!result.ok? || successful_names.include?(result.member_name)
|
|
338
|
+
end
|
|
339
|
+
|
|
335
340
|
changed_files = template_results.sum { |result| template_changed_file_count(result) }
|
|
336
341
|
outcome_counts = template_results.map { |result| template_file_outcomes(result) }
|
|
337
342
|
lines << "template summary:"
|
|
338
|
-
lines << " #{
|
|
339
|
-
if outcome_counts.all?
|
|
343
|
+
lines << " #{successful_names.length}/#{selected_members.length} members ok"
|
|
344
|
+
if outcome_counts.any? && outcome_counts.all?
|
|
340
345
|
lines << " #{outcome_counts.sum { |outcomes| outcomes.fetch(:checksum_hits) }} checksum hits"
|
|
341
346
|
protected = outcome_counts.sum { |outcomes| outcomes.fetch(:checksum_protected) }
|
|
342
347
|
lines << " #{protected} checksum-protected changes" if protected.positive?
|
|
@@ -697,6 +702,7 @@ module Kettle
|
|
|
697
702
|
|
|
698
703
|
def resume_hint_for(result)
|
|
699
704
|
return release_resume_hint(result) if command == "release"
|
|
705
|
+
return "kettle-family #{command}" if result.phase.start_with?("family_root_") || result.phase == "commit_family_root_bundle_update"
|
|
700
706
|
|
|
701
707
|
"kettle-family #{command} --start-at #{result.member_name}"
|
|
702
708
|
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.
|
|
8
|
+
VERSION = "1.2.93"
|
|
9
9
|
end
|
|
10
10
|
# Current gem version exposed at the traditional constant location.
|
|
11
11
|
VERSION = Version::VERSION # Traditional Constant Location
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "io/console"
|
|
4
4
|
require "fileutils"
|
|
5
|
+
require "digest"
|
|
5
6
|
require "json"
|
|
6
7
|
require "etc"
|
|
7
8
|
require "open3"
|
|
@@ -574,6 +575,9 @@ module Kettle
|
|
|
574
575
|
|
|
575
576
|
runner = CommandRunner.new(execute: execute, accept: accept)
|
|
576
577
|
results = []
|
|
578
|
+
update_family_root_bundle(runner: runner, memo: results) if %w[bup bupb].include?(command)
|
|
579
|
+
return results unless results.all?(&:ok?)
|
|
580
|
+
|
|
577
581
|
if command == "gha-sha-pins" && execute
|
|
578
582
|
return results unless review_gha_sha_pins(workflow_members, runner: runner, memo: results)
|
|
579
583
|
end
|
|
@@ -616,6 +620,39 @@ module Kettle
|
|
|
616
620
|
results
|
|
617
621
|
end
|
|
618
622
|
|
|
623
|
+
def update_family_root_bundle(runner:, memo:)
|
|
624
|
+
return unless family_root_bundle_update_required?
|
|
625
|
+
|
|
626
|
+
root_member = family_member
|
|
627
|
+
memo << runner.call(
|
|
628
|
+
member: root_member,
|
|
629
|
+
phase: "family_root_#{command}",
|
|
630
|
+
command: workflow_command,
|
|
631
|
+
env: command_env
|
|
632
|
+
)
|
|
633
|
+
return unless memo.last.ok?
|
|
634
|
+
return unless family_root_bundle_commit?
|
|
635
|
+
return unless validate_bundle_update_lockfile(
|
|
636
|
+
member: root_member,
|
|
637
|
+
memo: memo,
|
|
638
|
+
phase: "family_root_bundle_update_readiness"
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
commit_family_root_bundle_update(member: root_member, runner: runner, memo: memo)
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
def family_root_bundle_update_required?
|
|
645
|
+
return false unless File.file?(File.join(config.root, "Gemfile"))
|
|
646
|
+
|
|
647
|
+
family_members.none? do |member|
|
|
648
|
+
File.expand_path(member.root) == File.expand_path(config.root)
|
|
649
|
+
end
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
def family_root_bundle_commit?
|
|
653
|
+
commit && config.family_mode == "monorepo"
|
|
654
|
+
end
|
|
655
|
+
|
|
619
656
|
# Sibling repositories have isolated working trees and can run their
|
|
620
657
|
# tests concurrently. A monorepo's default kettle-test command normally
|
|
621
658
|
# executes the shared root suite, so it is represented by one selected
|
|
@@ -1047,8 +1084,12 @@ module Kettle
|
|
|
1047
1084
|
worktree_root: worktree_root
|
|
1048
1085
|
}
|
|
1049
1086
|
entries << entry
|
|
1087
|
+
prepare_monorepo_template_worktree(entry)
|
|
1050
1088
|
results.concat(worktree_mise_trust_results(entry, runner: runner, phase: "template_member_worktree_mise_trust"))
|
|
1051
1089
|
break unless results.last.ok?
|
|
1090
|
+
rescue => error
|
|
1091
|
+
results << template_worktree_failure_result(member, error, phase: "template_member_worktree_seed")
|
|
1092
|
+
break
|
|
1052
1093
|
end
|
|
1053
1094
|
[entries, results]
|
|
1054
1095
|
end
|
|
@@ -1102,39 +1143,64 @@ module Kettle
|
|
|
1102
1143
|
def materialize_monorepo_template_changes(entry)
|
|
1103
1144
|
original_member = entry.fetch(:original_member)
|
|
1104
1145
|
worktree_root = entry.fetch(:worktree_root)
|
|
1105
|
-
relative_root =
|
|
1146
|
+
relative_root = monorepo_template_relative_root(original_member)
|
|
1106
1147
|
paths = monorepo_template_worktree_changed_paths(worktree_root)
|
|
1107
|
-
return template_worktree_failure_result(original_member, paths) if paths.is_a?(String)
|
|
1148
|
+
return template_worktree_failure_result(original_member, paths, phase: "template_worktree_materialize") if paths.is_a?(String)
|
|
1108
1149
|
|
|
1109
1150
|
invalid_paths = paths.reject { |path| path == relative_root || path.start_with?("#{relative_root}/") }
|
|
1110
1151
|
unless invalid_paths.empty?
|
|
1111
1152
|
return template_worktree_failure_result(
|
|
1112
1153
|
original_member,
|
|
1113
|
-
"isolated template worker modified shared path(s): #{invalid_paths.sort.join(", ")}; only #{relative_root}/ may be materialized"
|
|
1154
|
+
"isolated template worker modified shared path(s): #{invalid_paths.sort.join(", ")}; only #{relative_root}/ may be materialized",
|
|
1155
|
+
phase: "template_worktree_materialize"
|
|
1114
1156
|
)
|
|
1115
1157
|
end
|
|
1116
1158
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
source = Shellwords.escape(worktree_root)
|
|
1127
|
-
scope = Shellwords.escape(relative_root)
|
|
1128
|
-
["sh", "-lc", "git -C #{source} diff --binary HEAD -- #{scope} | git apply --whitespace=nowarn"]
|
|
1159
|
+
initial_state = entry.fetch(:primary_member_state)
|
|
1160
|
+
current_state = monorepo_template_member_state(config.root, relative_root)
|
|
1161
|
+
changed_primary_paths = changed_monorepo_template_state_paths(initial_state, current_state)
|
|
1162
|
+
unless changed_primary_paths.empty?
|
|
1163
|
+
return template_worktree_failure_result(
|
|
1164
|
+
original_member,
|
|
1165
|
+
"primary member changed while isolated template worker ran: #{changed_primary_paths.join(", ")}",
|
|
1166
|
+
phase: "template_worktree_materialize"
|
|
1167
|
+
)
|
|
1129
1168
|
end
|
|
1130
|
-
result = runner.call(member: control_member, phase: "template_worktree_materialize", command: command)
|
|
1131
|
-
return result unless result.ok?
|
|
1132
1169
|
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1170
|
+
materialized_paths = (entry.fetch(:primary_changed_paths) + paths).uniq.sort
|
|
1171
|
+
sync_monorepo_template_paths(worktree_root, config.root, materialized_paths)
|
|
1172
|
+
template_worktree_success_result(
|
|
1173
|
+
original_member,
|
|
1174
|
+
phase: "template_worktree_materialize",
|
|
1175
|
+
stdout: "#{materialized_paths.length} path(s) materialized from isolated worktree"
|
|
1176
|
+
)
|
|
1136
1177
|
rescue => error
|
|
1137
|
-
template_worktree_failure_result(original_member, error)
|
|
1178
|
+
template_worktree_failure_result(original_member, error, phase: "template_worktree_materialize")
|
|
1179
|
+
end
|
|
1180
|
+
|
|
1181
|
+
def prepare_monorepo_template_worktree(entry)
|
|
1182
|
+
original_member = entry.fetch(:original_member)
|
|
1183
|
+
relative_root = monorepo_template_relative_root(original_member)
|
|
1184
|
+
changed_paths = monorepo_template_worktree_changed_paths(config.root)
|
|
1185
|
+
raise Error, changed_paths if changed_paths.is_a?(String)
|
|
1186
|
+
|
|
1187
|
+
member_changed_paths = changed_paths.select do |path|
|
|
1188
|
+
path == relative_root || path.start_with?("#{relative_root}/")
|
|
1189
|
+
end
|
|
1190
|
+
sync_monorepo_template_paths(config.root, entry.fetch(:worktree_root), member_changed_paths)
|
|
1191
|
+
primary_state = monorepo_template_member_state(config.root, relative_root)
|
|
1192
|
+
worktree_state = monorepo_template_member_state(entry.fetch(:worktree_root), relative_root)
|
|
1193
|
+
unless primary_state == worktree_state
|
|
1194
|
+
changed = changed_monorepo_template_state_paths(primary_state, worktree_state)
|
|
1195
|
+
raise Error, "isolated template worker seed differs from primary member: #{changed.join(", ")}"
|
|
1196
|
+
end
|
|
1197
|
+
|
|
1198
|
+
entry[:primary_changed_paths] = member_changed_paths
|
|
1199
|
+
entry[:primary_member_state] = primary_state
|
|
1200
|
+
end
|
|
1201
|
+
|
|
1202
|
+
def monorepo_template_relative_root(member)
|
|
1203
|
+
Pathname.new(member.root).relative_path_from(Pathname.new(config.root)).to_s
|
|
1138
1204
|
end
|
|
1139
1205
|
|
|
1140
1206
|
def monorepo_template_worktree_changed_paths(worktree_root)
|
|
@@ -1155,6 +1221,28 @@ module Kettle
|
|
|
1155
1221
|
git_worktree_paths(worktree_root, %w[ls-files --others --exclude-standard -z])
|
|
1156
1222
|
end
|
|
1157
1223
|
|
|
1224
|
+
def monorepo_template_member_state(root, relative_root)
|
|
1225
|
+
paths = git_worktree_paths(root, ["ls-files", "--cached", "--others", "--exclude-standard", "-z", "--", relative_root])
|
|
1226
|
+
raise Error, paths if paths.is_a?(String)
|
|
1227
|
+
|
|
1228
|
+
paths.to_h { |path| [path, monorepo_template_path_signature(root, path)] }
|
|
1229
|
+
end
|
|
1230
|
+
|
|
1231
|
+
def monorepo_template_path_signature(root, path)
|
|
1232
|
+
full_path = File.join(root, path)
|
|
1233
|
+
stat = File.lstat(full_path)
|
|
1234
|
+
return ["symlink", File.readlink(full_path)] if stat.symlink?
|
|
1235
|
+
return ["file", stat.mode & 0o777, Digest::SHA256.file(full_path).hexdigest] if stat.file?
|
|
1236
|
+
|
|
1237
|
+
[stat.ftype, stat.mode & 0o777]
|
|
1238
|
+
rescue Errno::ENOENT
|
|
1239
|
+
["missing"]
|
|
1240
|
+
end
|
|
1241
|
+
|
|
1242
|
+
def changed_monorepo_template_state_paths(before, after)
|
|
1243
|
+
(before.keys | after.keys).select { |path| before[path] != after[path] }.sort
|
|
1244
|
+
end
|
|
1245
|
+
|
|
1158
1246
|
def git_worktree_paths(worktree_root, arguments)
|
|
1159
1247
|
stdout, stderr, status = Open3.capture3("git", *arguments, chdir: worktree_root)
|
|
1160
1248
|
return stdout.split("\0").reject(&:empty?) if status.success?
|
|
@@ -1162,14 +1250,19 @@ module Kettle
|
|
|
1162
1250
|
"git #{arguments.join(" ")} failed in #{worktree_root}: #{stderr.strip}"
|
|
1163
1251
|
end
|
|
1164
1252
|
|
|
1165
|
-
def
|
|
1166
|
-
|
|
1167
|
-
|
|
1253
|
+
def sync_monorepo_template_paths(source_root, destination_root, paths)
|
|
1254
|
+
paths.each do |path|
|
|
1255
|
+
source = File.join(source_root, path)
|
|
1256
|
+
destination = File.join(destination_root, path)
|
|
1257
|
+
FileUtils.rm_rf(destination)
|
|
1258
|
+
next unless File.exist?(source) || File.symlink?(source)
|
|
1168
1259
|
|
|
1169
|
-
source = File.join(worktree_root, path)
|
|
1170
|
-
destination = File.join(primary_root, path)
|
|
1171
1260
|
FileUtils.mkdir_p(File.dirname(destination))
|
|
1172
|
-
|
|
1261
|
+
if File.symlink?(source)
|
|
1262
|
+
File.symlink(File.readlink(source), destination)
|
|
1263
|
+
else
|
|
1264
|
+
FileUtils.cp_r(source, destination, preserve: true)
|
|
1265
|
+
end
|
|
1173
1266
|
end
|
|
1174
1267
|
end
|
|
1175
1268
|
|
|
@@ -1184,12 +1277,28 @@ module Kettle
|
|
|
1184
1277
|
end
|
|
1185
1278
|
end
|
|
1186
1279
|
|
|
1187
|
-
def
|
|
1280
|
+
def template_worktree_success_result(member, phase:, stdout:)
|
|
1281
|
+
CommandResult.new(
|
|
1282
|
+
member_name: member.name,
|
|
1283
|
+
phase: phase,
|
|
1284
|
+
command: ["internal", phase.tr("_", "-")],
|
|
1285
|
+
workdir: member.root,
|
|
1286
|
+
status: 0,
|
|
1287
|
+
success: true,
|
|
1288
|
+
stdout: stdout,
|
|
1289
|
+
stderr: "",
|
|
1290
|
+
elapsed_seconds: 0.0,
|
|
1291
|
+
skipped: false,
|
|
1292
|
+
reason: nil
|
|
1293
|
+
)
|
|
1294
|
+
end
|
|
1295
|
+
|
|
1296
|
+
def template_worktree_failure_result(member, error, phase: "template_member_worktree")
|
|
1188
1297
|
message = error.is_a?(Exception) ? "#{error.class}: #{error.message}" : error.to_s
|
|
1189
1298
|
CommandResult.new(
|
|
1190
1299
|
member_name: member.name,
|
|
1191
|
-
phase:
|
|
1192
|
-
command: ["internal", "
|
|
1300
|
+
phase: phase,
|
|
1301
|
+
command: ["internal", phase.tr("_", "-")],
|
|
1193
1302
|
workdir: member.root,
|
|
1194
1303
|
status: 1,
|
|
1195
1304
|
success: false,
|
|
@@ -4348,13 +4457,25 @@ module Kettle
|
|
|
4348
4457
|
def emit_template_progress_summary(results, progress:)
|
|
4349
4458
|
return unless progress
|
|
4350
4459
|
|
|
4351
|
-
|
|
4460
|
+
successful_names = successful_template_member_names(results)
|
|
4461
|
+
template_results = results.select do |result|
|
|
4462
|
+
result.phase == "template" && (!result.ok? || successful_names.include?(result.member_name))
|
|
4463
|
+
end
|
|
4352
4464
|
changed_files = template_results.sum { |result| template_changed_file_count(result) }
|
|
4353
4465
|
outcome_counts = template_results.map { |result| template_file_outcomes(result) }
|
|
4354
4466
|
progress.stop
|
|
4355
4467
|
progress.summary(template_progress_summary_label(template_results, changed_files, outcome_counts))
|
|
4356
4468
|
end
|
|
4357
4469
|
|
|
4470
|
+
def successful_template_member_names(results)
|
|
4471
|
+
results.group_by(&:member_name).filter_map do |member_name, member_results|
|
|
4472
|
+
next unless member_results.any? { |result| result.phase == "template" }
|
|
4473
|
+
next unless member_results.all?(&:ok?)
|
|
4474
|
+
|
|
4475
|
+
member_name
|
|
4476
|
+
end
|
|
4477
|
+
end
|
|
4478
|
+
|
|
4358
4479
|
def emit_release_progress_summary(results, progress:)
|
|
4359
4480
|
return unless progress
|
|
4360
4481
|
|
|
@@ -4815,7 +4936,7 @@ module Kettle
|
|
|
4815
4936
|
|
|
4816
4937
|
def template_progress_summary_label(template_results, changed_files, outcome_counts)
|
|
4817
4938
|
label = "template summary: #{template_results.count(&:ok?)}/#{members.length} members ok"
|
|
4818
|
-
return "#{label}, #{template_summary_label(changed_files)}"
|
|
4939
|
+
return "#{label}, #{template_summary_label(changed_files)}" if outcome_counts.empty? || !outcome_counts.all?
|
|
4819
4940
|
|
|
4820
4941
|
totals = outcome_counts.each_with_object({checksum_hits: 0, checksum_protected: 0, unchanged: 0}) do |outcomes, memo|
|
|
4821
4942
|
memo[:checksum_hits] += outcomes.fetch(:checksum_hits)
|
|
@@ -5444,13 +5565,13 @@ module Kettle
|
|
|
5444
5565
|
memo << result
|
|
5445
5566
|
end
|
|
5446
5567
|
|
|
5447
|
-
def validate_bundle_update_lockfile(member:, memo:)
|
|
5568
|
+
def validate_bundle_update_lockfile(member:, memo:, phase: "bundle_update_readiness")
|
|
5448
5569
|
return true unless execute && commit
|
|
5449
5570
|
|
|
5450
5571
|
diagnostics = bundle_update_lockfile_diagnostics(member)
|
|
5451
5572
|
result = CommandResult.new(
|
|
5452
5573
|
member.name,
|
|
5453
|
-
|
|
5574
|
+
phase,
|
|
5454
5575
|
["internal", "bundle-update-readiness"],
|
|
5455
5576
|
member.root,
|
|
5456
5577
|
diagnostics.empty? ? 0 : 1,
|
|
@@ -5465,6 +5586,22 @@ module Kettle
|
|
|
5465
5586
|
result.ok?
|
|
5466
5587
|
end
|
|
5467
5588
|
|
|
5589
|
+
def commit_family_root_bundle_update(member:, runner:, memo:)
|
|
5590
|
+
return unless family_root_bundle_commit?
|
|
5591
|
+
|
|
5592
|
+
result = runner.call(
|
|
5593
|
+
member: member,
|
|
5594
|
+
phase: "commit_family_root_bundle_update",
|
|
5595
|
+
command: [
|
|
5596
|
+
"sh",
|
|
5597
|
+
"-lc",
|
|
5598
|
+
"files=$(git ls-files --modified --others --exclude-standard -- Gemfile.lock); " \
|
|
5599
|
+
"if [ -n \"$files\" ]; then git add -- Gemfile.lock && git commit --only -m '🔒️ Update family root bundle' -- Gemfile.lock; fi"
|
|
5600
|
+
]
|
|
5601
|
+
)
|
|
5602
|
+
memo << result
|
|
5603
|
+
end
|
|
5604
|
+
|
|
5468
5605
|
def bundle_update_lockfile_diagnostics(member)
|
|
5469
5606
|
ReadinessCheck.call(
|
|
5470
5607
|
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.2.
|
|
4
|
+
version: 1.2.93
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -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.
|
|
447
|
-
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.
|
|
446
|
+
source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.93
|
|
447
|
+
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.93/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.
|
|
449
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.93
|
|
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
|