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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +31 -1
- data/README.md +1 -1
- data/lib/kettle/family/command_runner.rb +32 -1
- data/lib/kettle/family/config.rb +19 -0
- data/lib/kettle/family/dependency_floor.rb +18 -0
- data/lib/kettle/family/execution_profile.rb +2 -2
- data/lib/kettle/family/release_graph.rb +83 -0
- data/lib/kettle/family/version.rb +1 -1
- data/lib/kettle/family/workflow.rb +73 -94
- data/lib/kettle/family.rb +1 -0
- data.tar.gz.sig +0 -0
- metadata +7 -6
- 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: c2e7e86419a5d960e83f3e8f7cb5aa1348f5acbe217b02b43e27ca479b9dafd9
|
|
4
|
+
data.tar.gz: cb5de478d104da8759b0746db6a58f415afb768aae9cd6622e3b4d2a42cdbd09
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
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
|
|
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)
|
data/lib/kettle/family/config.rb
CHANGED
|
@@ -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
|
-
|
|
72
|
-
name: :
|
|
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.
|
|
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
|
|
2727
|
-
#
|
|
2728
|
-
#
|
|
2729
|
-
#
|
|
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 =
|
|
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!(
|
|
3670
|
-
env.merge!(
|
|
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
|
-
|
|
5418
|
-
|
|
5389
|
+
release_graph_execution_profile(release_graph_for(family_member))
|
|
5390
|
+
end
|
|
5419
5391
|
|
|
5420
|
-
|
|
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
|
|
5424
|
-
|
|
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, :
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
447
|
-
changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.
|
|
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.
|
|
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
|