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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e63a81da6b871ee1c27d813b23f7840e7c0e0a577e3a992076922ff2750574b3
4
- data.tar.gz: 859158cd1aa7b293126edb628f02eb8bc641a0fcd4797a6f097af9fe46a9ef27
3
+ metadata.gz: a1a24d4a7f560730b58dea502aa74c1f7c2b6b5821c92bc565ee5d0eff79d8be
4
+ data.tar.gz: 58f47810263c6279e727419acb896abb959ccac76bff444c83b19c88c85f64f5
5
5
  SHA512:
6
- metadata.gz: ee75302308856eb21755bfdc3a598fe9e0d3992b4a493c84825e891dbd76f514f976549fde093de80bfe55d822567d36cba2375a666c1ca2fc6b4a9844f8ae5c
7
- data.tar.gz: 3845dff951350adc5177e8ea3bb4ce9b80a155a4a6fbb2b05618acb2363e77e9e8ce158129521f03ca8d02816b8bdfd1f1c5ec80507c982a0546e95314815068
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.91...HEAD
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` for every selected member. A gem name may be
584
- passed to update only that dependency, for example `kettle-family bup --execute
585
- kettle-test`. Executed updates commit successful lockfile changes by default;
586
- use `--no-commit` for a working-tree-only refresh, or `--commit` to state the
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.528-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
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
@@ -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 << " #{template_results.count(&:ok?)}/#{selected_members.length} members ok"
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.91"
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 = Pathname.new(original_member.root).relative_path_from(Pathname.new(config.root)).to_s
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
- tracked_paths = monorepo_template_worktree_tracked_paths(worktree_root)
1118
- return template_worktree_failure_result(original_member, tracked_paths) if tracked_paths.is_a?(String)
1119
-
1120
- runner = command_runner
1121
- control_member = monorepo_template_control_member.dup
1122
- control_member.name = original_member.name
1123
- command = if tracked_paths.empty?
1124
- ["true"]
1125
- else
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
- copy_monorepo_template_untracked_paths(worktree_root, config.root, relative_root)
1134
- result.stdout = "#{paths.length} path(s) materialized from isolated worktree"
1135
- result
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 copy_monorepo_template_untracked_paths(worktree_root, primary_root, relative_root)
1166
- monorepo_template_worktree_untracked_paths(worktree_root).each do |path|
1167
- next unless path == relative_root || path.start_with?("#{relative_root}/")
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
- FileUtils.cp_r(source, destination, preserve: true)
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 template_worktree_failure_result(member, error)
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: "template_member_worktree",
1192
- command: ["internal", "template-member-worktree"],
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
- template_results = results.select { |result| result.phase == "template" }
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)}" unless outcome_counts.all?
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
- "bundle_update_readiness",
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.91
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.91
447
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.91/CHANGELOG.md
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.91
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