kettle-family 1.2.100 → 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: 8b8d7a476e1af0096eca0bc0870cd9a8623b2ad2ccdcfa5080ec184b5b561096
4
- data.tar.gz: df5707cdac3c7838df5cbbc83731ffb7741dfe818e440a83d917792a1f4290b3
3
+ metadata.gz: c2e7e86419a5d960e83f3e8f7cb5aa1348f5acbe217b02b43e27ca479b9dafd9
4
+ data.tar.gz: cb5de478d104da8759b0746db6a58f415afb768aae9cd6622e3b4d2a42cdbd09
5
5
  SHA512:
6
- metadata.gz: 1cc3290cedbd5e35c1c77f4810b6fba3e562297e66a8f1c0d5a0f575110c231771cb460ae67617d289dcf845cde1f0ef4ed09aa996ca5774631fbe1f0c8a4556
7
- data.tar.gz: f6e8a65bfd5e7dd3c544a6b47443383a7a9d4cfc7597a52e509bbc9cf765cf1835624f8b4022db1b80b45b2cb0e40c3a5de77acb08f0cfd953c44913d6611454
6
+ metadata.gz: a60488bf1fc40699a0317e52d6b62318bf0eb243cc42f6950b39b1f7e2e785e7be4de6163a436057d2fcef5a602446dd1acbce249f6ecad7912ccfbc947d7526
7
+ data.tar.gz: 7c35f5fb09c6edbd5dc63235c0e3a97b69e9aa21a2dffff7c453fa557ab54f9383b7d1d69a9d08972bd1a7f078352dff5cc32991e203d588765570eaecc3ed18
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,17 @@ 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
+
33
44
  ## [1.2.100] - 2026-09-09
34
45
 
35
46
  - TAG: [v1.2.100][1.2.100t]
@@ -2720,7 +2731,9 @@ Please file a bug if you notice a violation of semantic versioning.
2720
2731
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
2721
2732
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
2722
2733
 
2723
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v1.2.100...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
2724
2737
  [1.2.100]: https://github.com/kettle-dev/kettle-family/compare/v1.2.99...v1.2.100
2725
2738
  [1.2.100t]: https://github.com/kettle-dev/kettle-family/releases/tag/v1.2.100
2726
2739
  [1.2.99]: https://github.com/kettle-dev/kettle-family/compare/v1.2.98...v1.2.99
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.719-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
@@ -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?
@@ -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.100"
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
@@ -3668,16 +3668,16 @@ module Kettle
3668
3668
  # changelog may live at the family root. Pass the shared paths to the
3669
3669
  # member release phase as well as to the separate family phase.
3670
3670
  def release_env_for_member(member, family_root: config.root, wave_jobs: 1)
3671
+ graph = release_graph_for(member, family_root: family_root)
3671
3672
  env = execution_profile(release_bootstrap_execution_profile, release_env)
3672
- env.merge!(release_wave_local_path_env_for(member, family_root: family_root))
3673
- 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)
3674
3675
  # A resumed child kettle-release starts in the member's mise
3675
3676
  # environment after the release candidate was already prepared.
3676
3677
  # Disable sibling paths so mise cannot reactivate them before the
3677
3678
  # resumed release validates that immutable lockfile. A new release
3678
3679
  # retains the staged local-graph policy until kettle-release reaches
3679
3680
  # its own release-lockfile boundary.
3680
- env.merge!(release_lockfile_local_path_env_overrides(member)) if release_step_resume?
3681
3681
  # Family release performs one live pin review before member releases.
3682
3682
  # Child kettle-release must consume that reviewed cache instead of
3683
3683
  # repeating the GitHub API lookup for every member.
@@ -3755,37 +3755,6 @@ module Kettle
3755
3755
  family_gemfile if File.file?(family_gemfile)
3756
3756
  end
3757
3757
 
3758
- # A release task may start with local sibling paths so a member can
3759
- # consume a selected dependency that has not been published yet. Once
3760
- # that dependency is complete in an earlier wave, use its registry
3761
- # release for the next member. Dependencies outside the selected set are
3762
- # already treated as registry dependencies; this is what makes resume
3763
- # runs work after an earlier wave has been published.
3764
- def release_wave_local_path_env_for(member, family_root: config.root)
3765
- # Monorepo members and their sibling dependencies share the same CI
3766
- # checkout, so their canonical development lockfile may keep those
3767
- # paths throughout every release wave.
3768
- if config.configured_monorepo_release?
3769
- env_name = config.family_local_path_env_name
3770
- return env_name ? {env_name => family_local_path_root_for(family_root)} : {}
3771
- end
3772
-
3773
- return {} unless config.release_local_path_strategy == "waves"
3774
-
3775
- env_name = config.family_local_path_env_name
3776
- return {} if env_name.to_s.empty?
3777
-
3778
- local_value = release_env[env_name]
3779
- return {} unless local_path_env_value?(local_value)
3780
-
3781
- selected_names = members.map(&:name)
3782
- completed_names = Array(@release_completed_member_names)
3783
- unresolved_selected_dependency = release_dependency_names(member).any? do |dependency|
3784
- selected_names.include?(dependency) && !completed_names.include?(dependency)
3785
- end
3786
- {env_name => unresolved_selected_dependency ? local_value : "false"}
3787
- end
3788
-
3789
3758
  # simplecov:disable Covered by monorepo release integration, not sibling-repository suite.
3790
3759
  def aggregate_monorepo_github_release(members)
3791
3760
  version = members.map { |member| member.version.to_s }.uniq
@@ -5417,14 +5386,61 @@ module Kettle
5417
5386
  end
5418
5387
 
5419
5388
  def release_lockfile_execution_profile
5420
- return ExecutionProfile.fetch(:release_recovery) if release_recovery?
5421
- return ExecutionProfile.fetch(:release_monorepo) if preserve_monorepo_template_context?
5389
+ release_graph_execution_profile(release_graph_for(family_member))
5390
+ end
5422
5391
 
5423
- 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
5424
5403
  end
5425
5404
 
5426
- def release_recovery?
5427
- !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
5428
5444
  end
5429
5445
 
5430
5446
  def release_step_resume?
@@ -5438,7 +5454,7 @@ module Kettle
5438
5454
  when :template_local
5439
5455
  env["K_JEM_TEMPLATING"] = "true"
5440
5456
  env["BUNDLE_DISABLE_CHECKSUM_VALIDATION"] = "true"
5441
- when :release_bootstrap, :release_registry, :release_monorepo, :release_recovery
5457
+ when :release_bootstrap, :release_registry, :release_monorepo, :release_wave_transition
5442
5458
  # Release profiles deliberately leave Bundler mutation decisions to
5443
5459
  # kettle-dev. The profile selection controls which local-path
5444
5460
  # overrides this workflow contributes below.
@@ -5492,12 +5508,14 @@ module Kettle
5492
5508
  end
5493
5509
 
5494
5510
  def release_lockfile_env(member = nil)
5511
+ graph = release_graph_for(member || family_member)
5495
5512
  execution_profile(
5496
- release_lockfile_execution_profile,
5513
+ release_graph_execution_profile(graph),
5497
5514
  base_release_env
5498
5515
  .merge(release_lockfile_bundler_env_resets)
5499
5516
  .merge(env_overrides)
5500
- .merge(release_lockfile_local_path_env_overrides(member))
5517
+ .merge(release_lockfile_local_path_env_overrides(member, graph: graph))
5518
+ .merge(graph.environment)
5501
5519
  )
5502
5520
  end
5503
5521
 
@@ -5508,8 +5526,8 @@ module Kettle
5508
5526
  }
5509
5527
  end
5510
5528
 
5511
- def release_allowed_local_path_roots
5512
- config.release_allowed_local_path_roots
5529
+ def release_allowed_local_path_roots(member = family_member)
5530
+ release_graph_for(member).local_path_roots
5513
5531
  end
5514
5532
 
5515
5533
  def family_local_path_root_for(family_root)
@@ -5519,38 +5537,13 @@ module Kettle
5519
5537
  File.expand_path(relative, family_root)
5520
5538
  end
5521
5539
 
5522
- def release_allowed_local_path_env_names
5523
- names = config.release_allowed_local_path_env_names
5524
- names << "K_JEM_TEMPLATING" if preserve_monorepo_template_context?
5525
- names.uniq
5526
- end
5527
-
5528
- def release_local_path_policy_env(family_root: config.root)
5529
- roots = release_allowed_local_path_roots.map do |root|
5530
- if File.expand_path(family_root) == File.expand_path(config.root)
5531
- root
5532
- else
5533
- relative = Pathname.new(root).relative_path_from(Pathname.new(config.root))
5534
- File.expand_path(relative, family_root)
5535
- end
5536
- end
5537
- env_names = release_allowed_local_path_env_names
5538
- return {} if roots.empty? && env_names.empty?
5539
-
5540
- {
5541
- "KETTLE_RELEASE_ALLOWED_LOCAL_PATH_ROOTS" => roots.join(File::PATH_SEPARATOR),
5542
- "KETTLE_RELEASE_ALLOWED_LOCAL_PATH_ENVS" => env_names.join(",")
5543
- }
5544
- end
5545
-
5546
- 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))
5547
5541
  explicit = config.release_disable_local_path_env.to_h { |key| [key.to_s, "false"] }
5548
- explicit.delete("K_JEM_TEMPLATING") if preserve_monorepo_template_context?
5549
5542
  derived = release_local_path_env_detection_sources.each_with_object({}) do |(key, value), memo|
5550
5543
  key = key.to_s
5551
5544
  next unless key.end_with?("_LOCAL", "_DEV")
5552
5545
  next unless local_path_env_value?(value)
5553
- next if release_allowed_local_path_env_names.include?(key)
5546
+ next if graph.selector_env.key?(key)
5554
5547
 
5555
5548
  memo[key] = "false"
5556
5549
  end
@@ -5558,24 +5551,7 @@ module Kettle
5558
5551
  explicit
5559
5552
  .merge(derived)
5560
5553
  .merge(inferred)
5561
- .merge(release_member_local_path_env(member))
5562
- .merge(preserve_monorepo_template_context? ? {"K_JEM_TEMPLATING" => "true"} : {})
5563
- end
5564
-
5565
- # K_JEM_TEMPLATING activates the generated monorepo dependency closure;
5566
- # it is not an unrelated local checkout switch. Preserve it only when
5567
- # the caller explicitly enabled it for a configured monorepo release.
5568
- def preserve_monorepo_template_context?
5569
- return false unless config.configured_monorepo_release?
5570
-
5571
- value = release_local_path_env_sources["K_JEM_TEMPLATING"].to_s.strip.downcase
5572
- %w[true yes 1 on].include?(value)
5573
- end
5574
-
5575
- def release_member_local_path_env(member)
5576
- return {} unless config.configured_monorepo_release?
5577
-
5578
- release_wave_local_path_env_for(member)
5554
+ .merge(graph.selector_env)
5579
5555
  end
5580
5556
 
5581
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.100
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.34
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.34
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.100
447
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v1.2.100/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.100
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