kettle-family 1.2.99 → 1.3.0

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: e3fff3ab628dc6f5338ddf0a820485b986ce5f40aec27a905f1ee15e18450ab9
4
- data.tar.gz: fa34533d50ee506f05e2f1cd9f865c011d12e91996b1c38cbabfe76eb0f9c676
3
+ metadata.gz: c2e7e86419a5d960e83f3e8f7cb5aa1348f5acbe217b02b43e27ca479b9dafd9
4
+ data.tar.gz: cb5de478d104da8759b0746db6a58f415afb768aae9cd6622e3b4d2a42cdbd09
5
5
  SHA512:
6
- metadata.gz: 81f62b1263cd2afbe483f7af09d0a4c1b3e4efac0328938592c84e381d5d731188031e1e9dad97ce343573b025a50d9de62424ce92d12be7245beced5de9996d
7
- data.tar.gz: 95713fbc85dd8e285b7b94364bfe799735df2d2b9f19b1205d2ad0819d793f66c94e39aa3b3816826b2987dacabc14519e24f01a0447f29af1edb55567d49f95
6
+ metadata.gz: a60488bf1fc40699a0317e52d6b62318bf0eb243cc42f6950b39b1f7e2e785e7be4de6163a436057d2fcef5a602446dd1acbce249f6ecad7912ccfbc947d7526
7
+ data.tar.gz: 7c35f5fb09c6edbd5dc63235c0e3a97b69e9aa21a2dffff7c453fa557ab54f9383b7d1d69a9d08972bd1a7f078352dff5cc32991e203d588765570eaecc3ed18
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,32 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [1.3.0] - 2026-09-10
34
+
35
+ - TAG: [v1.3.0][1.3.0t]
36
+ - COVERAGE: 93.47% -- 6322/6764 lines in 36 files
37
+ - BRANCH COVERAGE: 77.95% -- 2559/3283 branches in 36 files
38
+ - 33.49% documented
39
+
40
+ ### Fixed
41
+
42
+ - Serialize explicit release dependency graph contracts so CI-resident monorepo paths are preserved while sibling and branch-target releases reject ambient local paths.
43
+
44
+ ## [1.2.100] - 2026-09-09
45
+
46
+ - TAG: [v1.2.100][1.2.100t]
47
+ - COVERAGE: 93.51% -- 6283/6719 lines in 35 files
48
+ - BRANCH COVERAGE: 78.15% -- 2553/3267 branches in 35 files
49
+ - 32.84% documented
50
+
51
+ ### Fixed
52
+
53
+ - Run each family member through its own mise runtime instead of inheriting the orchestrator runtime.
54
+
55
+ - Refresh dependent release lockfiles after a runtime sibling publishes without waiting for later Gemfile-only tooling dependencies.
56
+
57
+ - Preserve the Mise launcher path when running commands in member worktrees from a Bundler environment.
58
+
33
59
  ## [1.2.99] - 2026-09-09
34
60
 
35
61
  - TAG: [v1.2.99][1.2.99t]
@@ -2705,7 +2731,11 @@ Please file a bug if you notice a violation of semantic versioning.
2705
2731
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
2706
2732
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
2707
2733
 
2708
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.99...HEAD
2734
+ [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.3.0...HEAD
2735
+ [1.3.0]: https://github.com/kettle-dev/kettle-family/compare/v1.2.100...v1.3.0
2736
+ [1.3.0t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.3.0
2737
+ [1.2.100]: https://github.com/kettle-dev/kettle-family/compare/v1.2.99...v1.2.100
2738
+ [1.2.100t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.100
2709
2739
  [1.2.99]: https://github.com/kettle-dev/kettle-family/compare/v1.2.98...v1.2.99
2710
2740
  [1.2.99t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.99
2711
2741
  [1.2.98]: https://github.com/kettle-dev/kettle-family/compare/v1.2.97...v1.2.98
data/README.md CHANGED
@@ -1186,7 +1186,7 @@ Thanks for RTFM. ☺️
1186
1186
  [📌gitmoji]: https://gitmoji.dev
1187
1187
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
1188
1188
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
1189
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.695-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1189
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.764-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1190
1190
  [🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
1191
1191
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
1192
1192
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -10,6 +10,7 @@ module Kettle
10
10
  SENSITIVE_ENV_KEYS = [
11
11
  "KETTLE_RELEASE_GEM_SIGNING_PASSPHRASE"
12
12
  ].freeze
13
+ MISE_SESSION_ENV_PREFIX = "__MISE_"
13
14
 
14
15
  class OtpCoordinator
15
16
  def initialize(input: $stdin, output: $stdout, queue_total: nil, secrets_provider: nil, event_handler: nil)
@@ -616,11 +617,14 @@ module Kettle
616
617
 
617
618
  def process_env(member:, env:)
618
619
  base_env = unbundled_process_env
620
+ clear_outer_mise_session!(base_env) if mise_configured?(member)
619
621
  # Bundler's unbundled environment intentionally omits activation
620
622
  # variables, but it may also omit PATH. When `unsetenv_others` is used
621
623
  # for mise-managed members, dropping PATH makes nested commands such
622
624
  # as `bundle` unresolvable inside kettle-jem.
623
- base_env["PATH"] = if base_env.key?("PATH")
625
+ base_env["PATH"] = if mise_configured?(member)
626
+ mise_member_path(base_env["PATH"], ENV["PATH"])
627
+ elsif base_env.key?("PATH")
624
628
  executable_path(base_env["PATH"])
625
629
  else
626
630
  ENV["PATH"]
@@ -647,6 +651,33 @@ module Kettle
647
651
  paths.compact.map(&:to_s).flat_map { |value| value.split(File::PATH_SEPARATOR) }.reject(&:empty?).uniq.join(File::PATH_SEPARATOR)
648
652
  end
649
653
 
654
+ # `mise exec` selects a member's declared runtime. Do not let the
655
+ # orchestrator's Ruby or gem bin directory precede it on PATH: Bundler
656
+ # otherwise executes the outer runtime after `mise` has entered the
657
+ # member directory, and can rewrite that member's lockfile with the
658
+ # wrong Bundler version.
659
+ def mise_member_path(*paths)
660
+ outer_runtime_paths = [Gem.bindir, File.dirname(RbConfig.ruby)].filter_map do |entry|
661
+ canonical_path(entry) unless entry.to_s.empty?
662
+ end
663
+ paths.flat_map { |path| path.to_s.split(File::PATH_SEPARATOR) }.reject(&:empty?).reject do |entry|
664
+ outer_runtime_paths.include?(canonical_path(entry))
665
+ end.uniq.join(File::PATH_SEPARATOR)
666
+ end
667
+
668
+ # Mise records its active configuration in private `__MISE_*` variables.
669
+ # Retaining them across a nested `mise exec -C member` makes Mise reuse
670
+ # the orchestrator's tool set instead of loading the member's mise.toml.
671
+ def clear_outer_mise_session!(env)
672
+ env.delete_if { |key, _value| key.start_with?(MISE_SESSION_ENV_PREFIX) }
673
+ end
674
+
675
+ def canonical_path(path)
676
+ File.realpath(path)
677
+ rescue Errno::ENOENT, Errno::ENOTDIR
678
+ File.expand_path(path)
679
+ end
680
+
650
681
  def unbundled_process_env
651
682
  return Bundler.unbundled_env if defined?(Bundler) && Bundler.respond_to?(:unbundled_env)
652
683
  return Bundler.original_env if defined?(Bundler) && Bundler.respond_to?(:original_env)
@@ -284,6 +284,23 @@ module Kettle
284
284
  fetch_path("release", "local_path_strategy").to_s
285
285
  end
286
286
 
287
+ def release_graph_contract
288
+ configured = fetch_path("release", "graph_contract").to_s.strip
289
+ return configured unless configured.empty?
290
+
291
+ return "monorepo_ci_local" if ci_resident_local_release_graph?
292
+ return "wave_transition" if release_local_path_strategy == "waves"
293
+
294
+ "registry_only"
295
+ end
296
+
297
+ def release_branch_target_graph_contract
298
+ configured = fetch_path("release", "branch_target_graph_contract").to_s.strip
299
+ return configured unless configured.empty?
300
+
301
+ "branch_terminal"
302
+ end
303
+
287
304
  def release_allowed_local_path_roots
288
305
  inferred = ci_resident_monorepo_local_path_graph? ? [family_local_path_root] : []
289
306
  configured = Array(fetch_path("release", "allowed_local_path_roots"))
@@ -306,6 +323,8 @@ module Kettle
306
323
  path_within_root?(family_local_path_root)
307
324
  end
308
325
 
326
+ alias_method :ci_resident_local_release_graph?, :ci_resident_monorepo_local_path_graph?
327
+
309
328
  # This remains the repository-topology predicate used by release
310
329
  # scheduling. CI-safe local path inference is deliberately narrower.
311
330
  def configured_monorepo_release?
@@ -7,6 +7,24 @@ module Kettle
7
7
  class DependencyFloor
8
8
  DEPENDENCY_METHODS = %i[add_dependency add_runtime_dependency add_development_dependency].freeze
9
9
 
10
+ # Return only dependencies declared by the gemspec itself. Release
11
+ # ordering also observes Gemfile-only tooling dependencies, but those
12
+ # cannot have their published floor rewritten by this class.
13
+ def self.gemspec_dependency_names(member)
14
+ return [] unless member.gemspec_path && File.file?(member.gemspec_path)
15
+
16
+ source = File.read(member.gemspec_path)
17
+ parse_result = Kettle::Dev::VersionBump.parse_source(source, member.gemspec_path)
18
+ Kettle::Dev::VersionBump.each_node(parse_result.value).filter_map do |node|
19
+ next unless node.is_a?(Prism::CallNode) && DEPENDENCY_METHODS.include?(node.name)
20
+
21
+ name_node = node.arguments&.arguments&.first
22
+ name_node.unescaped if name_node.is_a?(Prism::StringNode)
23
+ end.uniq
24
+ rescue Kettle::Dev::Error => error
25
+ raise Error, error.message
26
+ end
27
+
10
28
  def initialize(released_members:, dependent_members:, mode: :dry_run)
11
29
  @released_members = released_members
12
30
  @dependent_members = dependent_members
@@ -68,8 +68,8 @@ module Kettle
68
68
  host_platform_policy: :active_platform_must_resolve,
69
69
  allowed_mutations: %i[canonical_release_lockfiles configured_wave_paths]
70
70
  ),
71
- release_recovery: Definition.new(
72
- name: :release_recovery,
71
+ release_wave_transition: Definition.new(
72
+ name: :release_wave_transition,
73
73
  path_gem_policy: :configured_release_wave,
74
74
  lockfile_role: :canonical,
75
75
  install_location: :disposable,
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Kettle
6
+ module Family
7
+ # Immutable release dependency graph selected for one member or branch
8
+ # target. It is passed to kettle-dev as data, never reconstructed from
9
+ # whichever *_DEV variables happened to launch the family command.
10
+ class ReleaseGraph
11
+ ENV_KEY = "KETTLE_RELEASE_GRAPH_CONTRACT_JSON"
12
+ NAMES = %w[registry_only wave_transition monorepo_ci_local branch_terminal].freeze
13
+
14
+ attr_reader :name, :ci_root, :local_path_roots, :selector_env
15
+
16
+ def initialize(name:, ci_root: nil, local_path_roots: [], selector_env: {})
17
+ @name = name.to_s
18
+ @ci_root = canonical_path(ci_root) if ci_root
19
+ @local_path_roots = Array(local_path_roots).map { |path| canonical_path(path) }.uniq.freeze
20
+ @selector_env = selector_env.to_h.transform_keys(&:to_s).transform_values(&:to_s).freeze
21
+ validate!
22
+ freeze
23
+ end
24
+
25
+ def registry_only?
26
+ name == "registry_only" || name == "branch_terminal"
27
+ end
28
+
29
+ def local_paths?
30
+ !local_path_roots.empty?
31
+ end
32
+
33
+ def environment
34
+ selector_env.merge(ENV_KEY => JSON.generate(to_h))
35
+ end
36
+
37
+ def to_h
38
+ {
39
+ "name" => name,
40
+ "ci_root" => ci_root,
41
+ "local_path_roots" => local_path_roots,
42
+ "selector_env" => selector_env
43
+ }
44
+ end
45
+
46
+ private
47
+
48
+ def validate!
49
+ raise Error, "unknown release graph contract #{name.inspect}" unless NAMES.include?(name)
50
+
51
+ if registry_only? && (local_paths? || !selector_env.empty?)
52
+ raise Error, "#{name} release graph cannot declare local paths or selectors"
53
+ end
54
+ return unless %w[wave_transition monorepo_ci_local].include?(name)
55
+
56
+ raise Error, "#{name} release graph requires at least one local path root" unless local_paths?
57
+ raise Error, "#{name} release graph requires a local path selector" if selector_env.empty?
58
+
59
+ selector_env.each_value do |value|
60
+ next if local_path_roots.any? { |root| value == root || value.start_with?("#{root}/") }
61
+
62
+ raise Error, "#{name} selector #{value.inspect} is outside its declared local path roots"
63
+ end
64
+
65
+ return unless name == "monorepo_ci_local"
66
+
67
+ raise Error, "monorepo_ci_local release graph requires a CI root" if ci_root.to_s.empty?
68
+ local_path_roots.each do |path|
69
+ next if path == ci_root || path.start_with?("#{ci_root}/")
70
+
71
+ raise Error, "monorepo_ci_local path #{path.inspect} is outside CI root #{ci_root.inspect}"
72
+ end
73
+ end
74
+
75
+ def canonical_path(path)
76
+ expanded = File.expand_path(path)
77
+ File.realpath(expanded)
78
+ rescue Errno::ENOENT
79
+ expanded
80
+ end
81
+ end
82
+ end
83
+ 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.99"
8
+ VERSION = "1.3.0"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
@@ -2723,13 +2723,16 @@ module Kettle
2723
2723
  end
2724
2724
  end
2725
2725
 
2726
- # A release-valid lockfile must resolve every selected sibling dependency
2727
- # from the registry. Defer refreshes for dependents that still require a
2728
- # selected sibling from a later release wave; their gemspec floors can be
2729
- # updated now, but Bundler cannot resolve the future sibling remotely yet.
2726
+ # A release-valid lockfile must resolve every selected sibling whose
2727
+ # gemspec floor can be rewritten by DependencyFloor. Release ordering
2728
+ # deliberately also includes Gemfile-only tooling dependencies, but
2729
+ # those do not have a floor to reconcile and must not delay a runtime
2730
+ # dependency's lockfile refresh until a later wave.
2730
2731
  def dependency_floor_refresh_ready?(member, released_members:)
2731
2732
  selected_names = members.map(&:name)
2732
- selected_dependencies = release_dependency_names(member).select { |dependency| selected_names.include?(dependency) }
2733
+ selected_dependencies = DependencyFloor.gemspec_dependency_names(member).select do |dependency|
2734
+ selected_names.include?(dependency)
2735
+ end
2733
2736
  completed_names = Array(@release_completed_member_names) + released_members.map(&:name)
2734
2737
  selected_dependencies.all? { |dependency| completed_names.include?(dependency) }
2735
2738
  end
@@ -3665,16 +3668,16 @@ module Kettle
3665
3668
  # changelog may live at the family root. Pass the shared paths to the
3666
3669
  # member release phase as well as to the separate family phase.
3667
3670
  def release_env_for_member(member, family_root: config.root, wave_jobs: 1)
3671
+ graph = release_graph_for(member, family_root: family_root)
3668
3672
  env = execution_profile(release_bootstrap_execution_profile, release_env)
3669
- env.merge!(release_wave_local_path_env_for(member, family_root: family_root))
3670
- env.merge!(release_local_path_policy_env(family_root: family_root))
3673
+ env.merge!(release_lockfile_local_path_env_overrides(member, graph: graph))
3674
+ env.merge!(graph.environment)
3671
3675
  # A resumed child kettle-release starts in the member's mise
3672
3676
  # environment after the release candidate was already prepared.
3673
3677
  # Disable sibling paths so mise cannot reactivate them before the
3674
3678
  # resumed release validates that immutable lockfile. A new release
3675
3679
  # retains the staged local-graph policy until kettle-release reaches
3676
3680
  # its own release-lockfile boundary.
3677
- env.merge!(release_lockfile_local_path_env_overrides(member)) if release_step_resume?
3678
3681
  # Family release performs one live pin review before member releases.
3679
3682
  # Child kettle-release must consume that reviewed cache instead of
3680
3683
  # repeating the GitHub API lookup for every member.
@@ -3752,37 +3755,6 @@ module Kettle
3752
3755
  family_gemfile if File.file?(family_gemfile)
3753
3756
  end
3754
3757
 
3755
- # A release task may start with local sibling paths so a member can
3756
- # consume a selected dependency that has not been published yet. Once
3757
- # that dependency is complete in an earlier wave, use its registry
3758
- # release for the next member. Dependencies outside the selected set are
3759
- # already treated as registry dependencies; this is what makes resume
3760
- # runs work after an earlier wave has been published.
3761
- def release_wave_local_path_env_for(member, family_root: config.root)
3762
- # Monorepo members and their sibling dependencies share the same CI
3763
- # checkout, so their canonical development lockfile may keep those
3764
- # paths throughout every release wave.
3765
- if config.configured_monorepo_release?
3766
- env_name = config.family_local_path_env_name
3767
- return env_name ? {env_name => family_local_path_root_for(family_root)} : {}
3768
- end
3769
-
3770
- return {} unless config.release_local_path_strategy == "waves"
3771
-
3772
- env_name = config.family_local_path_env_name
3773
- return {} if env_name.to_s.empty?
3774
-
3775
- local_value = release_env[env_name]
3776
- return {} unless local_path_env_value?(local_value)
3777
-
3778
- selected_names = members.map(&:name)
3779
- completed_names = Array(@release_completed_member_names)
3780
- unresolved_selected_dependency = release_dependency_names(member).any? do |dependency|
3781
- selected_names.include?(dependency) && !completed_names.include?(dependency)
3782
- end
3783
- {env_name => unresolved_selected_dependency ? local_value : "false"}
3784
- end
3785
-
3786
3758
  # simplecov:disable Covered by monorepo release integration, not sibling-repository suite.
3787
3759
  def aggregate_monorepo_github_release(members)
3788
3760
  version = members.map { |member| member.version.to_s }.uniq
@@ -5414,14 +5386,61 @@ module Kettle
5414
5386
  end
5415
5387
 
5416
5388
  def release_lockfile_execution_profile
5417
- return ExecutionProfile.fetch(:release_recovery) if release_recovery?
5418
- return ExecutionProfile.fetch(:release_monorepo) if preserve_monorepo_template_context?
5389
+ release_graph_execution_profile(release_graph_for(family_member))
5390
+ end
5419
5391
 
5420
- ExecutionProfile.fetch(:release_registry)
5392
+ def release_graph_execution_profile(graph)
5393
+ case graph.name
5394
+ when "monorepo_ci_local"
5395
+ ExecutionProfile.fetch(:release_monorepo)
5396
+ when "wave_transition"
5397
+ ExecutionProfile.fetch(:release_wave_transition)
5398
+ when "branch_terminal", "registry_only"
5399
+ ExecutionProfile.fetch(:release_registry)
5400
+ else
5401
+ raise Error, "unknown release graph contract #{graph.name.inspect}"
5402
+ end
5421
5403
  end
5422
5404
 
5423
- def release_recovery?
5424
- !start_step.nil? || !skip_steps.to_s.empty? || fast_recovery || skip_ci
5405
+ def release_graph_for(member, family_root: config.root)
5406
+ name = if config.release_target_branches.any?
5407
+ config.release_branch_target_graph_contract
5408
+ else
5409
+ config.release_graph_contract
5410
+ end
5411
+
5412
+ case name
5413
+ when "registry_only", "branch_terminal"
5414
+ ReleaseGraph.new(name: name, ci_root: family_root)
5415
+ when "monorepo_ci_local"
5416
+ unless config.ci_resident_local_release_graph?
5417
+ raise Error, "monorepo_ci_local requires family.local_path_root to be contained by the family repository"
5418
+ end
5419
+
5420
+ env_name = config.family_local_path_env_name
5421
+ raise Error, "monorepo_ci_local requires family.local_path_env" if env_name.to_s.empty?
5422
+
5423
+ root = family_local_path_root_for(family_root)
5424
+ ReleaseGraph.new(name: name, ci_root: family_root, local_path_roots: [root], selector_env: {env_name => root})
5425
+ when "wave_transition"
5426
+ env_name = config.family_local_path_env_name
5427
+ raise Error, "wave_transition requires family.local_path_env" if env_name.to_s.empty?
5428
+
5429
+ local_value = release_local_path_env_sources[env_name]
5430
+ return ReleaseGraph.new(name: "registry_only", ci_root: family_root) unless local_path_env_value?(local_value)
5431
+
5432
+ selected_names = members.map(&:name)
5433
+ completed_names = Array(@release_completed_member_names)
5434
+ unresolved_selected_dependency = release_dependency_names(member).any? do |dependency|
5435
+ selected_names.include?(dependency) && !completed_names.include?(dependency)
5436
+ end
5437
+ return ReleaseGraph.new(name: "registry_only", ci_root: family_root) unless unresolved_selected_dependency
5438
+
5439
+ root = File.expand_path(local_value)
5440
+ ReleaseGraph.new(name: name, ci_root: family_root, local_path_roots: [root], selector_env: {env_name => root})
5441
+ else
5442
+ raise Error, "unknown release graph contract #{name.inspect}"
5443
+ end
5425
5444
  end
5426
5445
 
5427
5446
  def release_step_resume?
@@ -5435,7 +5454,7 @@ module Kettle
5435
5454
  when :template_local
5436
5455
  env["K_JEM_TEMPLATING"] = "true"
5437
5456
  env["BUNDLE_DISABLE_CHECKSUM_VALIDATION"] = "true"
5438
- when :release_bootstrap, :release_registry, :release_monorepo, :release_recovery
5457
+ when :release_bootstrap, :release_registry, :release_monorepo, :release_wave_transition
5439
5458
  # Release profiles deliberately leave Bundler mutation decisions to
5440
5459
  # kettle-dev. The profile selection controls which local-path
5441
5460
  # overrides this workflow contributes below.
@@ -5489,12 +5508,14 @@ module Kettle
5489
5508
  end
5490
5509
 
5491
5510
  def release_lockfile_env(member = nil)
5511
+ graph = release_graph_for(member || family_member)
5492
5512
  execution_profile(
5493
- release_lockfile_execution_profile,
5513
+ release_graph_execution_profile(graph),
5494
5514
  base_release_env
5495
5515
  .merge(release_lockfile_bundler_env_resets)
5496
5516
  .merge(env_overrides)
5497
- .merge(release_lockfile_local_path_env_overrides(member))
5517
+ .merge(release_lockfile_local_path_env_overrides(member, graph: graph))
5518
+ .merge(graph.environment)
5498
5519
  )
5499
5520
  end
5500
5521
 
@@ -5505,8 +5526,8 @@ module Kettle
5505
5526
  }
5506
5527
  end
5507
5528
 
5508
- def release_allowed_local_path_roots
5509
- config.release_allowed_local_path_roots
5529
+ def release_allowed_local_path_roots(member = family_member)
5530
+ release_graph_for(member).local_path_roots
5510
5531
  end
5511
5532
 
5512
5533
  def family_local_path_root_for(family_root)
@@ -5516,38 +5537,13 @@ module Kettle
5516
5537
  File.expand_path(relative, family_root)
5517
5538
  end
5518
5539
 
5519
- def release_allowed_local_path_env_names
5520
- names = config.release_allowed_local_path_env_names
5521
- names << "K_JEM_TEMPLATING" if preserve_monorepo_template_context?
5522
- names.uniq
5523
- end
5524
-
5525
- def release_local_path_policy_env(family_root: config.root)
5526
- roots = release_allowed_local_path_roots.map do |root|
5527
- if File.expand_path(family_root) == File.expand_path(config.root)
5528
- root
5529
- else
5530
- relative = Pathname.new(root).relative_path_from(Pathname.new(config.root))
5531
- File.expand_path(relative, family_root)
5532
- end
5533
- end
5534
- env_names = release_allowed_local_path_env_names
5535
- return {} if roots.empty? && env_names.empty?
5536
-
5537
- {
5538
- "KETTLE_RELEASE_ALLOWED_LOCAL_PATH_ROOTS" => roots.join(File::PATH_SEPARATOR),
5539
- "KETTLE_RELEASE_ALLOWED_LOCAL_PATH_ENVS" => env_names.join(",")
5540
- }
5541
- end
5542
-
5543
- def release_lockfile_local_path_env_overrides(member = nil)
5540
+ def release_lockfile_local_path_env_overrides(member = nil, graph: release_graph_for(member || family_member))
5544
5541
  explicit = config.release_disable_local_path_env.to_h { |key| [key.to_s, "false"] }
5545
- explicit.delete("K_JEM_TEMPLATING") if preserve_monorepo_template_context?
5546
5542
  derived = release_local_path_env_detection_sources.each_with_object({}) do |(key, value), memo|
5547
5543
  key = key.to_s
5548
5544
  next unless key.end_with?("_LOCAL", "_DEV")
5549
5545
  next unless local_path_env_value?(value)
5550
- next if release_allowed_local_path_env_names.include?(key)
5546
+ next if graph.selector_env.key?(key)
5551
5547
 
5552
5548
  memo[key] = "false"
5553
5549
  end
@@ -5555,24 +5551,7 @@ module Kettle
5555
5551
  explicit
5556
5552
  .merge(derived)
5557
5553
  .merge(inferred)
5558
- .merge(release_member_local_path_env(member))
5559
- .merge(preserve_monorepo_template_context? ? {"K_JEM_TEMPLATING" => "true"} : {})
5560
- end
5561
-
5562
- # K_JEM_TEMPLATING activates the generated monorepo dependency closure;
5563
- # it is not an unrelated local checkout switch. Preserve it only when
5564
- # the caller explicitly enabled it for a configured monorepo release.
5565
- def preserve_monorepo_template_context?
5566
- return false unless config.configured_monorepo_release?
5567
-
5568
- value = release_local_path_env_sources["K_JEM_TEMPLATING"].to_s.strip.downcase
5569
- %w[true yes 1 on].include?(value)
5570
- end
5571
-
5572
- def release_member_local_path_env(member)
5573
- return {} unless config.configured_monorepo_release?
5574
-
5575
- release_wave_local_path_env_for(member)
5554
+ .merge(graph.selector_env)
5576
5555
  end
5577
5556
 
5578
5557
  def release_lockfile_inferred_local_path_env(member)
data/lib/kettle/family.rb CHANGED
@@ -22,6 +22,7 @@ require_relative "family/nomono_bootstrap"
22
22
  require_relative "family/debugger_bootstrap"
23
23
  require_relative "family/appraisal_bootstrap"
24
24
  require_relative "family/release_waves"
25
+ require_relative "family/release_graph"
25
26
  require_relative "family/execution_profile"
26
27
  require_relative "family/workflow"
27
28
  require_relative "family/discovery"
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.99
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -94,7 +94,7 @@ dependencies:
94
94
  version: '3.0'
95
95
  - - ">="
96
96
  - !ruby/object:Gem::Version
97
- version: 3.0.33
97
+ version: 3.1.0
98
98
  type: :runtime
99
99
  prerelease: false
100
100
  version_requirements: !ruby/object:Gem::Requirement
@@ -104,7 +104,7 @@ dependencies:
104
104
  version: '3.0'
105
105
  - - ">="
106
106
  - !ruby/object:Gem::Version
107
- version: 3.0.33
107
+ version: 3.1.0
108
108
  - !ruby/object:Gem::Dependency
109
109
  name: kettle-gha-pins
110
110
  requirement: !ruby/object:Gem::Requirement
@@ -422,6 +422,7 @@ files:
422
422
  - lib/kettle/family/nomono_bootstrap.rb
423
423
  - lib/kettle/family/orderer.rb
424
424
  - lib/kettle/family/readiness_check.rb
425
+ - lib/kettle/family/release_graph.rb
425
426
  - lib/kettle/family/release_reconciler.rb
426
427
  - lib/kettle/family/release_state_check.rb
427
428
  - lib/kettle/family/release_state_event_tape.rb
@@ -443,10 +444,10 @@ licenses:
443
444
  - AGPL-3.0-only
444
445
  metadata:
445
446
  homepage_uri: https://kettle-family.galtzo.com
446
- source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.2.99
447
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.99/CHANGELOG.md
447
+ source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v1.3.0
448
+ changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.3.0/CHANGELOG.md
448
449
  bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
449
- documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.2.99
450
+ documentation_uri: https://www.rubydoc.info/gems/kettle-family/1.3.0
450
451
  funding_uri: https://github.com/sponsors/pboling
451
452
  wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
452
453
  news_uri: https://www.railsbling.com/tags/kettle-family
metadata.gz.sig CHANGED
Binary file