kettle-family 1.2.92 → 1.2.94

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: c1646ca3577c370624800077609c69c0decd4a50ad6924e82cfe3c050c1757df
4
- data.tar.gz: 361e0ac1cd39a97b442d3296635055b5bd34c24ced7d3944cec4a0984df6babb
3
+ metadata.gz: 60682b3f2d2f1017624cc67b428ff954b71f03eca4fdb6034cbe229183cba970
4
+ data.tar.gz: ea873857902fa44d2b2051e6449f21d84515139f005e1a9c148dfedf5062ce2f
5
5
  SHA512:
6
- metadata.gz: fa89492878000e8fa0b08b244b95a432ddb96e11130da0d4655087580b63260242b00f28a3f76af32c3a9dc8d9d2830f815f22f960f5c835b4b3af851196b510
7
- data.tar.gz: 1a801e2a97fdb2726e8ac282f88686e87aff42b680b54e2798b09bd70aa4e6be3678e9bf45625ab057a3d2210e99cd59eeeba31fd2761880f27cabc2b53a1566
6
+ metadata.gz: a14c847688ce762dbf03be0229b1c5ac0da23d3892e2e803467b8dc6efe73df4ede2b43d64a6ffedbccaf38f11da162bfad6bbf574a44b29739dafe8b8d70cba
7
+ data.tar.gz: 8ad7d29e783dace84ef807339b81907fe9e6e7c7f35ee60c4ac95b412a0972983c6eb7d9e8dad708801b270eda6ee0bb87d8759a22e3d4e29d6f254d4bfa6ff3
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.2.94] - 2026-09-07
34
+
35
+ - TAG: [v1.2.94][1.2.94t]
36
+ - COVERAGE: 93.50% -- 6183/6613 lines in 35 files
37
+ - BRANCH COVERAGE: 78.04% -- 2512/3219 branches in 35 files
38
+ - 32.58% documented
39
+
40
+ ### Fixed
41
+
42
+ - Restrict inferred monorepo release path allowances to CI-resident paths, preserving external development overrides for local-only dependency work.
43
+
44
+ - Allow bup and bupb to commit explicitly selected development path graphs while continuing to reject unrequested local lockfile paths.
45
+
46
+ ## [1.2.93] - 2026-09-07
47
+
48
+ - TAG: [v1.2.93][1.2.93t]
49
+ - COVERAGE: 93.54% -- 6165/6591 lines in 35 files
50
+ - BRANCH COVERAGE: 78.08% -- 2507/3211 branches in 35 files
51
+ - 32.41% documented
52
+
53
+ ### Fixed
54
+
55
+ - Update family-root bundles before member bundles for bup and bupb, committing the root lockfile only when the family is a monorepo.
56
+
33
57
  ## [1.2.92] - 2026-09-07
34
58
 
35
59
  - TAG: [v1.2.92][1.2.92t]
@@ -2616,7 +2640,11 @@ Please file a bug if you notice a violation of semantic versioning.
2616
2640
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
2617
2641
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
2618
2642
 
2619
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.92...HEAD
2643
+ [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.94...HEAD
2644
+ [1.2.94]: https://github.com/kettle-dev/kettle-family/compare/v1.2.93...v1.2.94
2645
+ [1.2.94t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.94
2646
+ [1.2.93]: https://github.com/kettle-dev/kettle-family/compare/v1.2.92...v1.2.93
2647
+ [1.2.93t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.93
2620
2648
  [1.2.92]: https://github.com/kettle-dev/kettle-family/compare/v1.2.91...v1.2.92
2621
2649
  [1.2.92t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.92
2622
2650
  [1.2.91]: https://github.com/kettle-dev/kettle-family/compare/v1.2.90...v1.2.91
data/README.md CHANGED
@@ -255,13 +255,19 @@ release:
255
255
  - kettle-jem
256
256
  ```
257
257
 
258
- `family.local_path_env` names the environment variable that activates local
259
- path dependencies for this family. For an explicitly configured
260
- `family.mode: monorepo`, `kettle-family` derives a release-lock policy that
261
- allows only `family.members_root` (or the configured member root) and passes
262
- that policy to each `kettle-release` child. Those paths are valid because the
263
- same monorepo checkout exists in CI. The tracked `Gemfile.lock` remains a
264
- development lockfile and may retain those member `PATH` sources.
258
+ `family.local_path_env` names the environment variable that activates the
259
+ family's development path graph. It does not, by itself, make a path valid in
260
+ CI or safe to retain in a release lockfile. For an explicitly configured
261
+ `family.mode: monorepo`, `kettle-family` derives a release-lock policy only
262
+ when that path is contained by the family repository; this is the case when
263
+ the CI checkout contains the same member graph. The tracked `Gemfile.lock`
264
+ may then retain those member `PATH` sources.
265
+
266
+ A monorepo can also reference a separate checkout, for example a shared base
267
+ gem in its parent workspace. That is an external development override, not an
268
+ internal monorepo path: it is removed for release unless
269
+ `release.allowed_local_path_roots` and `release.allowed_local_path_env`
270
+ explicitly describe matching CI checkout provisioning.
265
271
 
266
272
  Sibling-repository families do not receive this exception. Their canonical
267
273
  lockfiles must use released dependencies: a local Kettle Dev workspace, or
@@ -496,8 +502,8 @@ The command names below are available from `kettle-family --help`:
496
502
  | `docs` | Run the configured documentation workflow for each selected member. |
497
503
  | `template` | Apply the configured `kettle-jem` template for each selected member. |
498
504
  | `gha-sha-pins` | Inspect or update GitHub Actions SHA pins. |
499
- | `bup` | Run `bundle update --all`, or update a named dependency, per member. |
500
- | `bupb` | Run `bundle update --bundler` per member. |
505
+ | `bup` | Run `bundle update --all`, or update a named dependency, at the family root and per member. |
506
+ | `bupb` | Run `bundle update --bundler` at the family root and per member. |
501
507
  | `bex` | Run an arbitrary `bundle exec` command per member. |
502
508
  | `install` | Build and install selected local family gems. |
503
509
  | `bump` | Check, plan, or execute family version alignment. |
@@ -596,13 +602,24 @@ kettle-family bup --execute
596
602
  kettle-family template --execute
597
603
  ```
598
604
 
599
- `bup` runs `bundle update --all` for every selected member. A gem name may be
600
- passed to update only that dependency, for example `kettle-family bup --execute
601
- kettle-test`. Executed updates commit successful lockfile changes by default;
602
- use `--no-commit` for a working-tree-only refresh, or `--commit` to state the
603
- default explicitly. Local dependency environment variables are preserved when
604
- explicitly supplied, so use the family's local or released dependency mode
605
- deliberately before running it.
605
+ `bup` first runs `bundle update --all` at the family root when that root has a
606
+ `Gemfile`, then runs it for every selected member. `bupb` follows the same
607
+ root-first order with `bundle update --bundler`. A root that is itself a member
608
+ is updated only once. A gem name may be passed to `bup` to update only that
609
+ dependency, for example `kettle-family bup --execute kettle-test`.
610
+
611
+ Executed member updates commit successful lockfile changes by default. The
612
+ family-root lockfile is also validated and committed for monorepos, where the
613
+ root and members share one Git repository. A sibling-repository family root is
614
+ updated for its orchestration bundle but is not included in any member commit.
615
+ Use `--no-commit` for a working-tree-only refresh, or `--commit` to state the
616
+ default explicitly. `bup` and `bupb` validate the development graph selected
617
+ by their explicit local-path environment and commit it by default; this keeps
618
+ template synchronization from being blocked by expected local-path lockfile
619
+ changes. Release workflows separately normalize and validate their
620
+ CI/release graph. A normal family-local graph and an unreleased external
621
+ Kettle toolchain are separate choices; project task wrappers should not
622
+ silently enable the latter for ordinary `bup` or `bupb` runs.
606
623
 
607
624
  Plan or update GitHub Actions workflow SHA pins across the selected family
608
625
  members:
@@ -1137,7 +1154,7 @@ Thanks for RTFM. ☺️
1137
1154
  [📌gitmoji]: https://gitmoji.dev
1138
1155
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
1139
1156
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
1140
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.570-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1157
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.613-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1141
1158
  [🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
1142
1159
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
1143
1160
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -285,18 +285,29 @@ module Kettle
285
285
  end
286
286
 
287
287
  def release_allowed_local_path_roots
288
- inferred = configured_monorepo_release? ? [family_local_path_root] : []
288
+ inferred = ci_resident_monorepo_local_path_graph? ? [family_local_path_root] : []
289
289
  configured = Array(fetch_path("release", "allowed_local_path_roots"))
290
290
  .map { |path| expand_config_relative_path(path) }
291
291
  (inferred + configured).uniq
292
292
  end
293
293
 
294
294
  def release_allowed_local_path_env_names
295
- inferred = configured_monorepo_release? ? [family_local_path_env_name] : []
295
+ inferred = ci_resident_monorepo_local_path_graph? ? [family_local_path_env_name] : []
296
296
  configured = Array(fetch_path("release", "allowed_local_path_env"))
297
297
  (inferred + configured).compact.map(&:to_s).reject(&:empty?).uniq
298
298
  end
299
299
 
300
+ # A monorepo may still use an environment switch to reach a separate
301
+ # sibling checkout. Only a path contained by this repository can be
302
+ # inferred as CI-resident and retained in release lockfiles.
303
+ def ci_resident_monorepo_local_path_graph?
304
+ return false unless fetch_path("family", "mode") == "monorepo"
305
+
306
+ path_within_root?(family_local_path_root)
307
+ end
308
+
309
+ # This remains the repository-topology predicate used by release
310
+ # scheduling. CI-safe local path inference is deliberately narrower.
300
311
  def configured_monorepo_release?
301
312
  fetch_path("family", "mode") == "monorepo"
302
313
  end
@@ -371,6 +382,12 @@ module Kettle
371
382
 
372
383
  private
373
384
 
385
+ def path_within_root?(path)
386
+ expanded_path = File.expand_path(path)
387
+ expanded_root = File.expand_path(root)
388
+ expanded_path == expanded_root || expanded_path.start_with?("#{expanded_root}/")
389
+ end
390
+
374
391
  def expand_config_relative_path(value)
375
392
  text = value.to_s
376
393
  return text if text.start_with?("/")
@@ -702,6 +702,7 @@ module Kettle
702
702
 
703
703
  def resume_hint_for(result)
704
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"
705
706
 
706
707
  "kettle-family #{command} --start-at #{result.member_name}"
707
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.92"
8
+ VERSION = "1.2.94"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
@@ -575,6 +575,9 @@ module Kettle
575
575
 
576
576
  runner = CommandRunner.new(execute: execute, accept: accept)
577
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
+
578
581
  if command == "gha-sha-pins" && execute
579
582
  return results unless review_gha_sha_pins(workflow_members, runner: runner, memo: results)
580
583
  end
@@ -617,6 +620,39 @@ module Kettle
617
620
  results
618
621
  end
619
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
+
620
656
  # Sibling repositories have isolated working trees and can run their
621
657
  # tests concurrently. A monorepo's default kettle-test command normally
622
658
  # executes the shared root suite, so it is represented by one selected
@@ -5529,13 +5565,13 @@ module Kettle
5529
5565
  memo << result
5530
5566
  end
5531
5567
 
5532
- def validate_bundle_update_lockfile(member:, memo:)
5568
+ def validate_bundle_update_lockfile(member:, memo:, phase: "bundle_update_readiness")
5533
5569
  return true unless execute && commit
5534
5570
 
5535
5571
  diagnostics = bundle_update_lockfile_diagnostics(member)
5536
5572
  result = CommandResult.new(
5537
5573
  member.name,
5538
- "bundle_update_readiness",
5574
+ phase,
5539
5575
  ["internal", "bundle-update-readiness"],
5540
5576
  member.root,
5541
5577
  diagnostics.empty? ? 0 : 1,
@@ -5544,18 +5580,61 @@ module Kettle
5544
5580
  "",
5545
5581
  0.0,
5546
5582
  false,
5547
- diagnostics.empty? ? nil : "bundle update produced release-invalid lockfile"
5583
+ diagnostics.empty? ? nil : "bundle update produced an unexpected local-path lockfile"
5548
5584
  )
5549
5585
  memo << result
5550
5586
  result.ok?
5551
5587
  end
5552
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
+
5553
5605
  def bundle_update_lockfile_diagnostics(member)
5554
5606
  ReadinessCheck.call(
5555
5607
  member: member,
5556
5608
  config: config,
5557
- allowed_local_path_roots: release_allowed_local_path_roots
5558
- ).stdout.lines.grep(/^release lockfile has local path remote at /).map(&:chomp)
5609
+ allowed_local_path_roots: bundle_update_allowed_local_path_roots
5610
+ ).stdout.lines.grep(/^release lockfile has local path remote at /).map do |line|
5611
+ line.sub("release lockfile", "bundle update lockfile").chomp
5612
+ end
5613
+ end
5614
+
5615
+ # Bundle updates record the graph intentionally selected by the caller.
5616
+ # Release validation remains stricter: it accepts only CI-resident paths.
5617
+ def bundle_update_allowed_local_path_roots
5618
+ explicit = explicit_local_path_env_overrides
5619
+ roots = release_allowed_local_path_roots.dup
5620
+ family_env_name = config.family_local_path_env_name
5621
+
5622
+ explicit.each do |name, value|
5623
+ next unless local_path_env_value?(value)
5624
+
5625
+ if name == family_env_name
5626
+ roots << config.family_local_path_root
5627
+ elsif explicit_local_path_root?(value)
5628
+ roots << File.expand_path(value, config.root)
5629
+ end
5630
+ end
5631
+
5632
+ roots.uniq
5633
+ end
5634
+
5635
+ def explicit_local_path_root?(value)
5636
+ text = value.to_s.strip
5637
+ !%w[true yes 1 on enabled].include?(text.downcase)
5559
5638
  end
5560
5639
 
5561
5640
  def commit_bex_changes(member:, runner:, memo:)
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.92
4
+ version: 1.2.94
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.92
447
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.92/CHANGELOG.md
446
+ source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.94
447
+ changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.94/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.92
449
+ documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.94
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