kettle-dev 3.0.16 → 3.0.17

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: 48b3c836485ea5f578606d3a5582aa4877084c04b7c9d8b8a85151d8203288e2
4
- data.tar.gz: 229c452a7a514cfad31324d5eacdd5d207efd5624c922c2afc20415cad5434e1
3
+ metadata.gz: f66f871d88afeaa31ae017c4160572849be5c60b352b956d03392e87c513b4b9
4
+ data.tar.gz: bf62e022d4fbfa4b5aaa30c90d5468c0c931ea815819f3f966958efcf2c4831e
5
5
  SHA512:
6
- metadata.gz: 0526d9339e4f74739f332051beaa8e079d51e7f291754604ade207d25767e3458001501aaed9a84e9887ede43ebfe74125654df94410ce66903ab09cedbee752
7
- data.tar.gz: 5301eaf6485588d958c780b0b0378d847cfa85097bf3819441a0139c8752807ade609509aad0ddbe425de46d8b3bfd7fb04f926a6c8d5e83dc9d8fa82193f83a
6
+ metadata.gz: 7c886d799dc0a7f3fb65c065e638b478bc4a81653028c57e5cf59aed0e92098065c85c14147b061f29f9177ea56523803ad87586f20fabb62bba5efdddf83332
7
+ data.tar.gz: cf5748d69de40fa374e3ec36b7a79cf4315ae29b61104c9f1a6b44ff4d4df08baf9ff16be06ffead8ac806b46cc9881cdae6b2f6d03ca1d5d6b47485edf0f994
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,21 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [3.0.17] - 2026-08-29
34
+
35
+ - TAG: [v3.0.17][3.0.17t]
36
+ - COVERAGE: 91.48% -- 5839/6383 lines in 47 files
37
+ - BRANCH COVERAGE: 76.16% -- 2240/2941 branches in 47 files
38
+ - 50.97% documented
39
+
40
+ ### Fixed
41
+
42
+ - Stage Bundler update lockfiles correctly when releasing a monorepo subgem.
43
+
44
+ - Bound RubyGems version lookups so release reconciliation cannot wait indefinitely on a live registry request.
45
+
46
+ - Keep release child commands and commit hooks on the canonical registry-backed lockfile environment.
47
+
33
48
  ## [3.0.16] - 2026-08-28
34
49
 
35
50
  - TAG: [v3.0.16][3.0.16t]
@@ -3619,7 +3634,9 @@ Please file a bug if you notice a violation of semantic versioning.
3619
3634
  - Selecting will run the selected workflow via `act`
3620
3635
  - This may move to its own gem in the future.
3621
3636
 
3622
- [Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.16...HEAD
3637
+ [Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.17...HEAD
3638
+ [3.0.17]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.16...v3.0.17
3639
+ [3.0.17t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v3.0.17
3623
3640
  [3.0.16]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.15...v3.0.16
3624
3641
  [3.0.16t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v3.0.16
3625
3642
  [3.0.15]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.14...v3.0.15
data/README.md CHANGED
@@ -503,6 +503,21 @@ What it does:
503
503
  removed when the release process exits. `BUNDLE_FROZEN=true` is not a
504
504
  substitute because it converts the same reconciliation into a failure
505
505
  when Bundler needs to write its resolved lockfile.
506
+ - Lockfile roles are explicit during a release:
507
+
508
+ | Role | Lockfile | Used by |
509
+ | --- | --- | --- |
510
+ | Family runner | The family/orchestrator bundle | May resolve local sibling gems to run `kettle-family`; it never becomes the target project's lockfile. |
511
+ | Canonical release state | Tracked `Gemfile.lock` and, when present, `Appraisal.root.gemfile.lock` | Step 1 Bundler update, steps 3-6 setup/checks/appraisals/docs/prep commit, and step 16 checksums. These commands disable local `*_DEV`/`*_LOCAL` switches. |
512
+ | Release task state | `tmp/kettle-release/lockfiles/Gemfile-<pid>.lock` | Steps 14-15 build and publish only. It begins as a copy of canonical `Gemfile.lock` and is deleted at process exit. |
513
+ | Tool state | A tool-owned Gemfile such as `kettle-changelog/gemfiles/release.gemfile` | The tool's own dependencies. The command still disables local dependency switches before it can trigger target-project work. |
514
+ | Git-hook state | Canonical release environment | Every release-created commit and amend. Hooks remain enabled, but cannot inherit the family runner's local sibling graph. |
515
+
516
+ A command must select one of these roles. Do not construct an ad hoc
517
+ `BUNDLE_GEMFILE`, `BUNDLE_LOCKFILE`, or local-development environment in
518
+ a release phase. `ReleaseCLI#release_child_command` owns canonical/tool
519
+ command construction; `ReleaseCLI#release_project_command` selects the
520
+ disposable task lock only for build and publish.
506
521
  - Tips:
507
522
  - The commit message helper `exe/kettle-commit-msg` prefers project-local `.git-hooks` (then falls back to `~/.git-hooks`).
508
523
  - The goalie file `commit-subjects-goalie.txt` controls when a footer is appended; customize `footer-template.erb.txt` as you like.
@@ -1033,7 +1048,7 @@ Thanks for RTFM. ☺️
1033
1048
  [📌gitmoji]: https://gitmoji.dev
1034
1049
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
1035
1050
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
1036
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.367-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1051
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-6.383-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
1037
1052
  [🔐security]: https://github.com/kettle-dev/kettle-dev/blob/main/SECURITY.md
1038
1053
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
1039
1054
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -71,12 +71,28 @@ module Kettle
71
71
  false
72
72
  end
73
73
 
74
+ # Stage paths reported by Git commands such as `git diff --name-only`.
75
+ # Git reports these paths relative to the repository root, even when this
76
+ # adapter operates from a monorepo subdirectory. `:(top)` keeps the
77
+ # pathspec rooted at that repository root in either topology.
78
+ #
79
+ # @param paths [Array<String>] repository-root-relative paths
80
+ # @return [Boolean] true when Git stages every path
81
+ def add_repository_paths(paths)
82
+ pathspecs = Array(paths).map { |path| ":(top)#{path}" }
83
+ git_system("add", "--", *pathspecs)
84
+ rescue => e
85
+ Kettle::Dev.debug_error(e, __method__)
86
+ false
87
+ end
88
+
74
89
  # Commit all staged/tracked changes with a message.
75
90
  #
76
91
  # @param message [String]
92
+ # @param env [Hash{String => String, nil}] environment overrides for Git hooks
77
93
  # @return [Boolean]
78
- def commit_all(message)
79
- git_system("commit", "-am", message.to_s)
94
+ def commit_all(message, env: {})
95
+ git_system("commit", "-am", message.to_s, env: env)
80
96
  rescue => e
81
97
  Kettle::Dev.debug_error(e, __method__)
82
98
  false
@@ -89,9 +105,10 @@ module Kettle
89
105
  # atomic commit.
90
106
  #
91
107
  # @param message [String]
108
+ # @param env [Hash{String => String, nil}] environment overrides for Git hooks
92
109
  # @return [Boolean]
93
- def commit_staged(message)
94
- git_system("commit", "-m", message.to_s)
110
+ def commit_staged(message, env: {})
111
+ git_system("commit", "-m", message.to_s, env: env)
95
112
  rescue => e
96
113
  Kettle::Dev.debug_error(e, __method__)
97
114
  false
@@ -99,9 +116,10 @@ module Kettle
99
116
 
100
117
  # Amend the current commit without changing its message.
101
118
  #
119
+ # @param env [Hash{String => String, nil}] environment overrides for Git hooks
102
120
  # @return [Boolean]
103
- def commit_amend_no_edit
104
- git_system("commit", "--amend", "--no-edit")
121
+ def commit_amend_no_edit(env: {})
122
+ git_system("commit", "--amend", "--no-edit", env: env)
105
123
  rescue => e
106
124
  Kettle::Dev.debug_error(e, __method__)
107
125
  false
@@ -406,11 +424,15 @@ module Kettle
406
424
  end
407
425
  end
408
426
 
409
- def git_system(*args)
427
+ def git_system(*args, env: {})
428
+ command = ["git", *args]
429
+ return system(*command) if env.empty? && @root == Dir.pwd
430
+ return system(*command, chdir: @root) if env.empty?
431
+
410
432
  if @root == Dir.pwd
411
- system("git", *args)
433
+ system(env, *command)
412
434
  else
413
- system("git", *args, chdir: @root)
435
+ system(env, *command, chdir: @root)
414
436
  end
415
437
  end
416
438
  end
@@ -506,7 +506,7 @@ module Kettle
506
506
  # Generate checksums for the just-built artifact, commit them, then validate
507
507
  version ||= detect_version
508
508
  gem_path = checksum_gem_path_for_version!(version)
509
- run_cmd!("bin/gem_checksums #{Shellwords.escape(gem_path)}")
509
+ run_cmd!(release_child_command("bin/gem_checksums #{Shellwords.escape(gem_path)}"))
510
510
  validate_checksums!(version, stage: "after release")
511
511
  end
512
512
 
@@ -802,6 +802,20 @@ module Kettle
802
802
  lockfile_reset.normalization_env
803
803
  end
804
804
 
805
+ # The release process has three distinct lockfile roles:
806
+ #
807
+ # - canonical: tracked project lockfiles, always registry-backed;
808
+ # - task: a disposable Gemfile.lock copy for build and publication; and
809
+ # - tool: a tool-owned Gemfile, such as kettle-changelog's release bundle.
810
+ #
811
+ # A family may use local sibling gems to launch this process, but no
812
+ # release child may inherit that graph. Git hooks are release children
813
+ # too: they commonly load project tooling and otherwise can silently
814
+ # rewrite the canonical lockfile with local PATH sources.
815
+ def release_git_hook_environment
816
+ release_child_environment
817
+ end
818
+
805
819
  def release_lockfile_diagnostics(path)
806
820
  lockfile_reset.diagnostics(path)
807
821
  end
@@ -869,7 +883,7 @@ module Kettle
869
883
 
870
884
  puts "Fallback release lockfile reset result: diagnostics cleared; amending release prep commit with #{paths.length} lockfile(s)."
871
885
  abort("Failed to stage reset release lockfiles.") unless @git.add_paths(paths)
872
- abort("Failed to amend release prep commit with reset lockfiles.") unless @git.commit_amend_no_edit
886
+ abort("Failed to amend release prep commit with reset lockfiles.") unless @git.commit_amend_no_edit(env: release_git_hook_environment)
873
887
  end
874
888
 
875
889
  def changed_release_lockfile_paths
@@ -910,18 +924,13 @@ module Kettle
910
924
  # of relying on an ambient installed executable.
911
925
  def release_changelog_command
912
926
  gemfile = release_changelog_gemfile
913
- return "bundle exec kettle-changelog" unless gemfile
927
+ return release_child_command("bundle exec kettle-changelog") unless gemfile
914
928
 
915
- escaped_gemfile = Shellwords.escape(gemfile)
916
- command = +"env -u BUNDLE_GEMFILE -u BUNDLE_LOCKFILE"
929
+ environment = {"BUNDLE_GEMFILE" => gemfile}
917
930
  if (local_root = release_changelog_local_root)
918
- # Changelog coverage runs before the release lockfile reset. Keep it
919
- # on the registry-backed release graph instead of reintroducing local
920
- # sibling paths through the inherited templating switch.
921
- command << " KETTLE_DEV_DEV=false K_JEM_TEMPLATING=false KETTLE_CHANGELOG_DEV_ROOT=#{Shellwords.escape(local_root)}"
931
+ environment["KETTLE_CHANGELOG_DEV_ROOT"] = local_root
922
932
  end
923
- command << " BUNDLE_GEMFILE=#{escaped_gemfile} bundle exec kettle-changelog"
924
- command
933
+ release_child_command("bundle exec kettle-changelog", environment: environment)
925
934
  end
926
935
 
927
936
  def release_changelog_gemfile
@@ -981,29 +990,44 @@ module Kettle
981
990
  release_project_command("bin/setup")
982
991
  end
983
992
 
984
- # Every command that loads the project bundle must run outside the
985
- # release tool's bundle and with local workspace dependency switches
986
- # disabled. The default task may carry a leading environment assignment,
987
- # so relying on BundlerEnvGuard's command classification alone is not
988
- # sufficient.
993
+ # Canonical release commands use the tracked project lockfiles. These
994
+ # lockfiles are shared state and are normalized/committed before release
995
+ # prep; they must never contain sibling PATH sources.
989
996
  def release_project_command(command)
990
- project_command = command
991
- unbundled_keys = (BundlerEnvGuard::RESET_ENV_KEYS + %w[RUBYLIB RUBYOPT]).uniq
992
- command = +"env #{unbundled_keys.map { |key| "-u #{Shellwords.escape(key)}" }.join(" ")}"
993
- release_lockfile_normalization_env.each do |key, value|
994
- command << " #{key}=#{Shellwords.escape(value)}"
997
+ release_child_command(command, lockfile: release_task_command?(command) ? release_task_lockfile_path : nil)
998
+ end
999
+
1000
+ def release_task_command?(command)
1001
+ ["bundle exec rake build", "bundle exec rake release"].include?(command.to_s.strip)
1002
+ end
1003
+
1004
+ # Every release child runs outside the release tool's Bundler context and
1005
+ # with local sibling switches disabled. The optional lockfile identifies
1006
+ # a disposable task lockfile; omitting it deliberately selects the
1007
+ # canonical tracked lockfile for setup, checks, docs, and checksums.
1008
+ def release_child_command(command, environment: {}, lockfile: nil)
1009
+ command_body = command
1010
+ command = +"env"
1011
+ release_child_environment.each do |key, value|
1012
+ command << " -u #{Shellwords.escape(key)}" if value.nil?
995
1013
  end
996
- if release_task_command?(project_command)
997
- if (lockfile = release_task_lockfile_path)
998
- command << " BUNDLE_LOCKFILE=#{Shellwords.escape(lockfile)}"
999
- end
1014
+ release_child_environment.each do |key, value|
1015
+ command << " #{key}=#{Shellwords.escape(value)}" unless value.nil?
1016
+ end
1017
+ environment.each do |key, value|
1018
+ command << " #{key}=#{Shellwords.escape(value)}"
1000
1019
  end
1001
- command << " #{project_command}"
1020
+ command << " BUNDLE_LOCKFILE=#{Shellwords.escape(lockfile)}" if lockfile
1021
+ command << " #{command_body}"
1002
1022
  command
1003
1023
  end
1004
1024
 
1005
- def release_task_command?(command)
1006
- ["bundle exec rake build", "bundle exec rake release"].include?(command.to_s.strip)
1025
+ def release_child_environment
1026
+ @release_child_environment ||= begin
1027
+ release_lockfile_normalization_env
1028
+ .merge(BundlerEnvGuard.unbundled_env)
1029
+ .merge("RUBYLIB" => nil, "RUBYOPT" => nil)
1030
+ end
1007
1031
  end
1008
1032
 
1009
1033
  # Bundler may reconcile the current machine's platform while loading a
@@ -1647,8 +1671,8 @@ module Kettle
1647
1671
  end
1648
1672
 
1649
1673
  puts "Committing Bundler update in #{paths.length} lockfile(s)."
1650
- abort("Failed to stage Bundler update lockfiles.") unless @git.add_paths(paths)
1651
- abort("Failed to commit Bundler update lockfiles.") unless @git.commit_staged("🔒️ Update bundle")
1674
+ abort("Failed to stage Bundler update lockfiles.") unless @git.add_repository_paths(paths)
1675
+ abort("Failed to commit Bundler update lockfiles.") unless @git.commit_staged("🔒️ Update bundle", env: release_git_hook_environment)
1652
1676
  reconcile_bundle_update_commit!
1653
1677
  true
1654
1678
  end
@@ -1678,8 +1702,8 @@ module Kettle
1678
1702
  return if paths.empty?
1679
1703
 
1680
1704
  puts "Bundler changed lockfiles while committing; amending the bundle update commit."
1681
- abort("Failed to stage post-commit Bundler lockfile changes.") unless @git.add_paths(paths)
1682
- abort("Failed to amend the bundle update commit.") unless @git.commit_amend_no_edit
1705
+ abort("Failed to stage post-commit Bundler lockfile changes.") unless @git.add_repository_paths(paths)
1706
+ abort("Failed to amend the bundle update commit.") unless @git.commit_amend_no_edit(env: release_git_hook_environment)
1683
1707
  end
1684
1708
 
1685
1709
  abort("Bundler continued changing lockfiles after the bundle update commit.") unless changed_bundle_lockfile_paths.empty?
@@ -1692,7 +1716,7 @@ module Kettle
1692
1716
 
1693
1717
  puts "Release commit hooks changed tracked files; amending the release preparation commit."
1694
1718
  abort("Failed to stage post-commit release changes.") unless @git.add_all
1695
- abort("Failed to amend the release preparation commit.") unless @git.commit_amend_no_edit
1719
+ abort("Failed to amend the release preparation commit.") unless @git.commit_amend_no_edit(env: release_git_hook_environment)
1696
1720
  end
1697
1721
 
1698
1722
  output, = git_output(["status", "--porcelain"])
@@ -1994,7 +2018,7 @@ module Kettle
1994
2018
  puts "No changes to commit for release prep (continuing)."
1995
2019
  false
1996
2020
  else
1997
- abort("Failed to commit release prep changes.") unless @git.commit_all(msg)
2021
+ abort("Failed to commit release prep changes.") unless @git.commit_all(msg, env: release_git_hook_environment)
1998
2022
  reconcile_release_prep_commit!
1999
2023
  true
2000
2024
  end
@@ -14,6 +14,8 @@ module Kettle
14
14
  module RubyGemsVersions
15
15
  CACHE_BUST_TTL_SECONDS = 30 * 24 * 60 * 60
16
16
  VERSION_CACHE_TTL_SECONDS = 30 * 24 * 60 * 60
17
+ HTTP_OPEN_TIMEOUT_SECONDS = 5
18
+ HTTP_READ_TIMEOUT_SECONDS = 10
17
19
  ENV_REFRESH = "KETTLE_RUBYGEMS_REFRESH"
18
20
  ENV_MARKER_PATH = "KETTLE_RUBYGEMS_CACHE_BUST_PATH"
19
21
  ENV_VERSION_CACHE_PATH = "KETTLE_RUBYGEMS_VERSION_CACHE_PATH"
@@ -32,7 +34,13 @@ module Kettle
32
34
  request["Cache-Control"] = "no-cache"
33
35
  request["Pragma"] = "no-cache"
34
36
  end
35
- response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
37
+ response = Net::HTTP.start(
38
+ uri.host,
39
+ uri.port,
40
+ use_ssl: uri.scheme == "https",
41
+ open_timeout: HTTP_OPEN_TIMEOUT_SECONDS,
42
+ read_timeout: HTTP_READ_TIMEOUT_SECONDS
43
+ ) do |http|
36
44
  http.request(request)
37
45
  end
38
46
  if response.code.to_i == 404
@@ -5,7 +5,7 @@ module Kettle
5
5
  # Version namespace for this gem.
6
6
  module Version
7
7
  # Current gem version.
8
- VERSION = "3.0.16"
8
+ VERSION = "3.0.17"
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kettle-dev
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.16
4
+ version: 3.0.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -400,10 +400,10 @@ licenses:
400
400
  - AGPL-3.0-only
401
401
  metadata:
402
402
  homepage_uri: https://kettle-dev.galtzo.com
403
- source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v3.0.16
404
- changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v3.0.16/CHANGELOG.md
403
+ source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v3.0.17
404
+ changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v3.0.17/CHANGELOG.md
405
405
  bug_tracker_uri: https://github.com/kettle-dev/kettle-dev/issues
406
- documentation_uri: https://www.rubydoc.info/gems/kettle-dev/3.0.16
406
+ documentation_uri: https://www.rubydoc.info/gems/kettle-dev/3.0.17
407
407
  funding_uri: https://github.com/sponsors/pboling
408
408
  wiki_uri: https://github.com/kettle-dev/kettle-dev/wiki
409
409
  news_uri: https://www.railsbling.com/tags/kettle-dev
metadata.gz.sig CHANGED
Binary file