kettle-jem 7.1.32 → 7.1.33

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: f62f4e109608570afb3659b7ae9f191b89f8f2ca51e6575035df53d3849f02d8
4
- data.tar.gz: 578cbde7471209ad1623eb3b274a6eac38f66ed605aa783b3e7021a79da751fe
3
+ metadata.gz: 0331126c9cd143dae738734bcfb54afe39ff65800a0c342260d3c1482f88f94d
4
+ data.tar.gz: d44de2c4ab2afc8b784e5e9eabc5f4c1b3ec1735033f361437dc33a9980d104c
5
5
  SHA512:
6
- metadata.gz: a84de8dbff686176ce7cb9e6a0ded538a1cc675f3246488f22939bd4adcf222ab618ff76c34739e9df6aae86d904778e73283b08df73934c5744d8d90098285c
7
- data.tar.gz: 860ee1f3bc66069a5e880c40cb35a3eed9485fd4cb2454c07632bad5c401397ae38d07fa2bb7dc9949d0f59c2f97db36b2e64e710db61b509b7a4d0d3c105ec0
6
+ metadata.gz: c2e9a4edcbcbf0a12f6494858fd9c5479116f44326164dbc4769719b5e87fd350d57894cea9b509b04ba5e3989100bef7dfc4cd52ddc42d40042309633f94e62
7
+ data.tar.gz: 37db58e793e5632632ec72f0d7cdd518d8bda2f29ad0937871030bde4061a1daa1d05941f4d4dd84655263be38c88c0dc9d64d7ffe491cd8eef6000161e8f9f0
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,24 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [7.1.33] - 2026-10-19
34
+
35
+ - TAG: [v7.1.33][7.1.33t]
36
+ - COVERAGE: 93.42% -- 11362/12162 lines in 17 files
37
+ - BRANCH COVERAGE: 77.04% -- 4891/6349 branches in 17 files
38
+ - 19.80% documented
39
+
40
+ ### Added
41
+
42
+ - Version gate for local gemfile wiring: _local.gemfile now declares local_gem_requirements from the same template facts style.gemfile uses (KJ|RUBOCOP_LTS_CONSTRAINT, KJ|RUBOCOP_RUBY_CONSTRAINT) and path-wires a local gem only when its checkout version satisfies the requirement; unsatisfied checkouts fall back to the registry declaration. ensure_local_gem_requirements_from_template re-injects the hash after the AST merge, which drops hash assignments absent from the destination.
43
+
44
+ ### Changed
45
+
46
+ - [kc] kettle-jem-deps-floor: Update kettle-jem template dependency floors:
47
+ - kettle-changelog (>= 1.0.12 -> >= 1.0.13)
48
+ - kettle-dev (>= 3.1.15 -> >= 3.1.18)
49
+ - kettle-family (>= 1.3.11 -> >= 1.3.22)
50
+
33
51
  ## [7.1.32] - 2026-10-17
34
52
 
35
53
  - TAG: [v7.1.32][7.1.32t]
@@ -2300,7 +2318,9 @@ Please file a bug if you notice a violation of semantic versioning.
2300
2318
  - Released kettle-jem as part of the initial StructuredMerge Ruby 7.0.0 gem set.
2301
2319
  - Included packaged templates and parser-backed merge support for Ruby gem templating.
2302
2320
 
2303
- [Unreleased]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.32...HEAD
2321
+ [Unreleased]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.33...HEAD
2322
+ [7.1.33]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.32...v7.1.33
2323
+ [7.1.33t]: https://github.com/structuredmerge/structuredmerge-ruby/releases/tag/v7.1.33
2304
2324
  [7.1.32]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.31...v7.1.32
2305
2325
  [7.1.32t]: https://github.com/structuredmerge/structuredmerge-ruby/releases/tag/v7.1.32
2306
2326
  [7.1.31]: https://github.com/structuredmerge/structuredmerge-ruby/compare/v7.1.30...v7.1.31
data/README.md CHANGED
@@ -807,7 +807,7 @@ If none of the available licenses suit your use case, please [contact us](mailto
807
807
  [📌gitmoji]: https://gitmoji.dev
808
808
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
809
809
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
810
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-12.125-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
810
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-12.162-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
811
811
  [🔐security]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/SECURITY.md
812
812
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
813
813
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -118,7 +118,7 @@ Gem::Specification.new do |spec|
118
118
  # and preferably a modular one (see gemfiles/modular/*.gemfile).
119
119
 
120
120
  # Dev, Test, & Release Tasks
121
- spec.add_development_dependency("kettle-dev", "~> 3.1", ">= 3.1.15") # ruby >= {KJ|MIN_TEST_RUBY}
121
+ spec.add_development_dependency("kettle-dev", "~> 3.1", ">= 3.1.18") # ruby >= {KJ|MIN_TEST_RUBY}
122
122
 
123
123
  # Security
124
124
  spec.add_development_dependency("bundler-audit", "~> 0.9.3") # ruby >= 2.0.0
@@ -8,9 +8,51 @@
8
8
 
9
9
  {KJ|LOCAL_GEMFILE_NOMONO_BOOTSTRAP}
10
10
 
11
+ # Requirements for local gems (must match the declarations in style.gemfile).
12
+ # Only gems whose local checkout version satisfies the requirement will be
13
+ # path-wired; others fall back to the registry declaration in style.gemfile.
14
+ local_gem_requirements = {
15
+ "rubocop-lts" => [{KJ|RUBOCOP_LTS_CONSTRAINT}],
16
+ "rubocop-lts-rspec" => ["~> 1.0", ">= 1.0.7"],
17
+ "{KJ|RUBOCOP_RUBY_GEM}" => [{KJ|RUBOCOP_RUBY_CONSTRAINT}],
18
+ "standard-rubocop-lts" => ["~> 2.0", ">= 2.0.14"],
19
+ }
20
+
11
21
  local_gems = %w[rubocop-lts rubocop-lts-rspec {KJ|RUBOCOP_RUBY_GEM} standard-rubocop-lts]
12
22
  declared_gems = instance_variable_get(:@dependencies).to_a.map(&:name)
13
- local_gems_to_eval = local_gems - %w[{KJ|PACKAGE_NAME}] - declared_gems
23
+
24
+ # Filter local_gems: only include gems whose local checkout version satisfies
25
+ # the declared requirement. Unsatisfied gems fall back to the registry.
26
+ def local_gem_satisfies?(gem_name, requirements, dev_root)
27
+ return false unless dev_root && File.directory?(dev_root)
28
+
29
+ # Resolve the actual gem name (handle token substitution)
30
+ actual_name = gem_name.dup
31
+ actual_name = ENV["RUBOCOP_RUBY_GEM"] if gem_name == "{KJ|RUBOCOP_RUBY_GEM}"
32
+ return false if actual_name.nil? || actual_name.empty?
33
+
34
+ # Find the gemspec
35
+ gemspec_path = Dir.glob(File.join(dev_root, actual_name, "*.gemspec")).first
36
+ return false unless gemspec_path && File.file?(gemspec_path)
37
+
38
+ # Parse version from gemspec
39
+ spec = Gem::Specification.load(gemspec_path)
40
+ return false unless spec && spec.version
41
+
42
+ # Check if version satisfies all requirements
43
+ requirements.all? do |req|
44
+ Gem::Requirement.new(req).satisfied_by?(spec.version)
45
+ end
46
+ rescue => e
47
+ warn "Warning: could not check local version for #{gem_name}: #{e.message}"
48
+ false
49
+ end
50
+
51
+ dev_root = ENV["RUBOCOP_LTS_DEV"]
52
+ local_gems_to_eval = local_gems.select do |gem|
53
+ reqs = local_gem_requirements[gem]
54
+ reqs && local_gem_satisfies?(gem, reqs, dev_root)
55
+ end - %w[{KJ|PACKAGE_NAME}] - declared_gems
14
56
 
15
57
  # The generated RuboCop config loads this plugin in both local and released
16
58
  # style modes, so the local bundle must provide it too.
@@ -29,4 +71,4 @@ platform :mri do
29
71
  debug_env: "RUBOCOP_LTS_DEBUG",
30
72
  root: ["src", "my", "rubocop-lts"]
31
73
  )
32
- end
74
+ end
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency "{KJ|SHIMMED_GEM_NAME}"
31
31
 
32
32
  # Dev, Test, & Release Tasks
33
- spec.add_development_dependency("kettle-dev", "~> 3.1", ">= 3.1.15") # ruby >= {KJ|MIN_TEST_RUBY}
33
+ spec.add_development_dependency("kettle-dev", "~> 3.1", ">= 3.1.18") # ruby >= {KJ|MIN_TEST_RUBY}
34
34
 
35
35
  # Tasks
36
36
  spec.add_development_dependency "rake", "~> 13.0"
@@ -5,7 +5,7 @@ module Kettle
5
5
  # Version namespace for this gem.
6
6
  module Version
7
7
  # Current gem version.
8
- VERSION = "7.1.32"
8
+ VERSION = "7.1.33"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
data/lib/kettle/jem.rb CHANGED
@@ -63,10 +63,10 @@ module Kettle
63
63
  TEMPLATE_MANAGED_DEPENDENCIES = [
64
64
  {name: PACKAGE_NAME, requirements: ["~> 7.1", ">= 7.1.28"], bootstrap: false},
65
65
  {name: "nomono", requirements: ["~> 1.1", ">= 1.1.8"], bootstrap: true},
66
- {name: "kettle-dev", requirements: ["~> 3.1", ">= 3.1.15"], bootstrap: true},
66
+ {name: "kettle-dev", requirements: ["~> 3.1", ">= 3.1.18"], bootstrap: true},
67
67
  {
68
68
  name: "kettle-changelog",
69
- requirements: ["~> 1.0", ">= 1.0.12"],
69
+ requirements: ["~> 1.0", ">= 1.0.13"],
70
70
  bootstrap: true,
71
71
  ruby_requirement: ">= 4.0.0"
72
72
  }
@@ -5120,9 +5120,9 @@ module Kettle
5120
5120
  [
5121
5121
  {name: "appraisal2", source: %(gem "appraisal2", "~> 3.2", ">= 3.2.5"\n)},
5122
5122
  {name: "bundler-audit", source: %(gem "bundler-audit", "~> 0.9.3"\n)},
5123
- {name: "kettle-dev", source: %(gem "kettle-dev", "~> 3.1", ">= 3.1.15"\n)},
5123
+ {name: "kettle-dev", source: %(gem "kettle-dev", "~> 3.1", ">= 3.1.18"\n)},
5124
5124
  {name: "kettle-drift", source: %(gem "kettle-drift", "~> 1.0", ">= 1.0.17"\n)},
5125
- {name: "kettle-family", source: %(gem "kettle-family", "~> 1.3", ">= 1.3.11"\n)},
5125
+ {name: "kettle-family", source: %(gem "kettle-family", "~> 1.3", ">= 1.3.22"\n)},
5126
5126
  {name: PACKAGE_NAME, source: kettle_jem_dependency_source},
5127
5127
  {name: "kettle-test", source: %(gem "kettle-test", "~> 2.0", ">= 2.0.27"\n)},
5128
5128
  {name: "rake", source: %(gem "rake", "~> 13.0"\n)},
@@ -6870,7 +6870,7 @@ module Kettle
6870
6870
  end
6871
6871
  end
6872
6872
  if strategy.empty? || strategy == "merge"
6873
- merged = merge_config_template_source(recipe, resolved, original, facts: facts, env: env)
6873
+ merged = merge_config_template_source(recipe, resolved, original, facts: facts, env: env, project_root: project_root)
6874
6874
  merged = preserve_mise_project_settings(recipe, merged, original, project_root: project_root, facts: facts) if template_file_type(recipe) == :toml
6875
6875
  return finalize_template_source_content(recipe, sync_kettle_config_env_overrides(merged, env, project_root: project_root)) if recipe.fetch(:target_path) == KETTLE_CONFIG_PATH
6876
6876
 
@@ -7617,7 +7617,7 @@ module Kettle
7617
7617
  ensure_trailing_newline(lines.join("\n").gsub(/\n{3,}/, "\n\n").strip)
7618
7618
  end
7619
7619
 
7620
- def merge_config_template_source(recipe, template_content, destination_content, facts: nil, env: ENV)
7620
+ def merge_config_template_source(recipe, template_content, destination_content, facts: nil, env: ENV, project_root: nil)
7621
7621
  file_type = template_file_type(recipe)
7622
7622
  trivial_outcome = Ast::Template::Strategies.trivial_merge_outcome(template_content, destination_content)
7623
7623
  if trivial_outcome == :empty_destination
@@ -7754,6 +7754,8 @@ module Kettle
7754
7754
 
7755
7755
  output = normalize_local_gemfile_nomono_bootstrap(output) if local_gemfile_template_recipe?(recipe)
7756
7756
  output = remove_gemfile_percent_w_entries(output, [facts.to_h.dig(:package, :name)]) if local_gemfile_template_recipe?(recipe)
7757
+ output = ensure_local_gem_requirements_from_template(output, template_content) if local_gemfile_template_recipe?(recipe)
7758
+
7757
7759
  return output if recipe.dig(:template_preference, :strategy).to_s == "accept_template"
7758
7760
  return output unless local_gemfile_template_recipe?(recipe)
7759
7761
 
@@ -8765,6 +8767,7 @@ module Kettle
8765
8767
  output = replace_source_range_lines(output, first_line, marker.fetch(:start_line) - 1, bootstrap) if first_line < marker.fetch(:start_line)
8766
8768
  output = remove_local_gemfile_nomono_loader_blocks_after_marker(output)
8767
8769
  output = remove_redundant_nomono_bundler_requires_after_marker(output)
8770
+ output = apply_local_gem_exclusions(output)
8768
8771
  return ensure_trailing_newline(output.rstrip)
8769
8772
  end
8770
8773
 
@@ -8781,7 +8784,9 @@ module Kettle
8781
8784
  without_duplicates = require_records.reject { |record| record.equal?(first) }.sort_by { |record| -record.fetch(:start_line) }.reduce(output) do |memo, record|
8782
8785
  replace_source_range_lines(memo, record.fetch(:start_line), expand_line_range_through_following_blanks(memo, record.fetch(:end_line)), "")
8783
8786
  end
8784
- return replace_source_range_lines(without_duplicates, first.fetch(:start_line), first.fetch(:end_line), bootstrap)
8787
+ output = replace_source_range_lines(without_duplicates, first.fetch(:start_line), first.fetch(:end_line), bootstrap)
8788
+ output = apply_local_gem_exclusions(output)
8789
+ return ensure_trailing_newline(output.rstrip)
8785
8790
  end
8786
8791
 
8787
8792
  if (record = local_gems_assignment_record(output))
@@ -8789,6 +8794,8 @@ module Kettle
8789
8794
  else
8790
8795
  ensure_trailing_newline([output.to_s.rstrip, bootstrap.rstrip].reject(&:empty?).join("\n\n"))
8791
8796
  end
8797
+ output = apply_local_gem_exclusions(output)
8798
+ ensure_trailing_newline(output.rstrip)
8792
8799
  end
8793
8800
 
8794
8801
  def local_gemfile_nomono_bootstrap_marker(content)
@@ -8974,6 +8981,67 @@ module Kettle
8974
8981
  word_array_assignment_record(content, :local_gems)
8975
8982
  end
8976
8983
 
8984
+ # Apply local gem exclusions from KETTLE_JEM_LOCAL_GEM_EXCLUSIONS env var
8985
+ # to the local_gems word array in _local.gemfile templates.
8986
+ def apply_local_gem_exclusions(content)
8987
+ exclusions_env = ENV["KETTLE_JEM_LOCAL_GEM_EXCLUSIONS"]
8988
+ return content if exclusions_env.nil? || exclusions_env.strip.empty?
8989
+
8990
+ exclusions = exclusions_env.split(",").map(&:strip).reject(&:empty?)
8991
+ return content if exclusions.empty?
8992
+
8993
+ record = local_gems_assignment_record(content)
8994
+ return content unless record
8995
+
8996
+ current_gems = record.fetch(:names)
8997
+ filtered_gems = current_gems.reject { |gem| exclusions.include?(gem) }
8998
+ return content if filtered_gems == current_gems
8999
+
9000
+ replace_word_array_assignment(content, :local_gems, filtered_gems)
9001
+ end
9002
+
9003
+ # The AST merge can drop the template's local_gem_requirements hash when the
9004
+ # destination predates it. The token-resolved template content is the source
9005
+ # of truth for these requirements (the same facts style.gemfile declares),
9006
+ # so re-inject the assignment from the template after the merge, replacing
9007
+ # any stale copy in the merged output.
9008
+ def ensure_local_gem_requirements_from_template(output, template_content)
9009
+ template_record = local_variable_assignment_record(template_content, :local_gem_requirements)
9010
+ return output unless template_record
9011
+
9012
+ replacement = template_record.fetch(:source)
9013
+ output_record = local_variable_assignment_record(output, :local_gem_requirements)
9014
+ if output_record
9015
+ return output if output_record.fetch(:source) == replacement
9016
+
9017
+ replace_source_range_lines(output, output_record.fetch(:start_line), output_record.fetch(:end_line), replacement)
9018
+ else
9019
+ anchor = local_variable_assignment_record(output, :local_gems) ||
9020
+ local_variable_assignment_record(output, :declared_gems)
9021
+ return output unless anchor
9022
+
9023
+ lines = output.lines
9024
+ lines.insert(anchor.fetch(:start_line) - 1, "#{replacement}\n\n")
9025
+ ensure_trailing_newline(lines.join)
9026
+ end
9027
+ end
9028
+
9029
+ def local_variable_assignment_record(content, name)
9030
+ result = prism_parse_success(content)
9031
+ return unless result
9032
+
9033
+ node = result.value.breadth_first_search_all do |candidate|
9034
+ candidate.is_a?(::Prism::LocalVariableWriteNode) && candidate.name == name
9035
+ end.first
9036
+ return unless node
9037
+
9038
+ {
9039
+ source: content.lines[(node.location.start_line - 1)...node.location.end_line].join.chomp,
9040
+ start_line: node.location.start_line,
9041
+ end_line: node.location.end_line
9042
+ }
9043
+ end
9044
+
8977
9045
  def word_array_assignment(content, name)
8978
9046
  word_array_assignment_record(content, name)&.fetch(:names) || []
8979
9047
  end
@@ -15827,7 +15895,7 @@ module Kettle
15827
15895
  def main_gemfile_kettle_family_gem(package_name)
15828
15896
  return "" if package_name.to_s == "kettle-family"
15829
15897
 
15830
- %(gem "kettle-family", "~> 1.3", ">= 1.3.11"\n)
15898
+ %(gem "kettle-family", "~> 1.3", ">= 1.3.22"\n)
15831
15899
  end
15832
15900
 
15833
15901
  def main_gemfile_nomono_bootstrap(package_name)
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kettle-jem
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.32
4
+ version: 7.1.33
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -997,10 +997,10 @@ licenses:
997
997
  - PolyForm-Small-Business-1.0.0
998
998
  metadata:
999
999
  homepage_uri: https://structuredmerge.org
1000
- source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.32
1001
- changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.1.32/CHANGELOG.md
1000
+ source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.33
1001
+ changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.1.33/CHANGELOG.md
1002
1002
  bug_tracker_uri: https://github.com/structuredmerge/structuredmerge-ruby/issues
1003
- documentation_uri: https://www.rubydoc.info/gems/kettle-jem/7.1.32
1003
+ documentation_uri: https://www.rubydoc.info/gems/kettle-jem/7.1.33
1004
1004
  funding_uri: https://github.com/sponsors/pboling
1005
1005
  wiki_uri: https://github.com/structuredmerge/structuredmerge-ruby/wiki
1006
1006
  news_uri: https://www.railsbling.com/tags/kettle-jem
metadata.gz.sig CHANGED
Binary file