kettle-dev 1.1.10 → 1.1.12

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: e6cf7cc8f6c3a0d1dfc5f0dbc6da851e122abdad9a284c5835eb7f910214dbe7
4
- data.tar.gz: 6ff4d986c36a7eb18e5413e29707c7a78fcfdb048852a16ecc403010963d67d3
3
+ metadata.gz: 28d4d222b4d85b17a9392755d884f8f0bf0e2193140bc4a5264f54fbf55448e0
4
+ data.tar.gz: 1495e5a4b252b2b6537727725165dfdb67211c70f078d5b293a3ce912038983c
5
5
  SHA512:
6
- metadata.gz: 640a7db16fab1bb407eca866f68209f5c354f8ed54fd40b20723ec0a8e3860018b4509891bb9875a2a268a082149c7013bfe0c1426dfe08cdd857c3e057dee8a
7
- data.tar.gz: a32860690a84df178fbffddc65ffb14eb257f9481d1efd9cbcb177eb53452ce34493d370a9430c3dc2bdb3a295bd1fc55c584f7b174fcc379e102084166811dd
6
+ metadata.gz: 5861e1d242db51b88b9625e4dfcd09bb598b57ae25b505f3e3674d0a250b325133b2cae2644e111d9db649f690d5f94a31cebfc098a19bfd0d4aeba5f9efebbe
7
+ data.tar.gz: 78c1003a70069e48e768dbec76298b50a8a2207419a27d398b0ab8fef89e31cfb64d9564ed9fa3c52ca4840c982298885ce1ead3426db4e50e82e251865010c8
checksums.yaml.gz.sig CHANGED
Binary file
data/.envrc CHANGED
@@ -21,7 +21,7 @@ export K_SOUP_COV_DO=true # Means you want code coverage
21
21
  export K_SOUP_COV_COMMAND_NAME="Test Coverage"
22
22
  # Available formats are html, xml, rcov, lcov, json, tty
23
23
  export K_SOUP_COV_FORMATTERS="html,xml,rcov,lcov,json,tty"
24
- export K_SOUP_COV_MIN_BRANCH=81 # Means you want to enforce X% branch coverage
24
+ export K_SOUP_COV_MIN_BRANCH=80 # Means you want to enforce X% branch coverage
25
25
  export K_SOUP_COV_MIN_LINE=96 # Means you want to enforce X% line coverage
26
26
  export K_SOUP_COV_MIN_HARD=true # Means you want the build to fail if the coverage thresholds are not met
27
27
  export K_SOUP_COV_MULTI_FORMATTERS=true
data/CHANGELOG.md CHANGED
@@ -24,6 +24,25 @@ Please file a bug if you notice a violation of semantic versioning.
24
24
  ### Fixed
25
25
  ### Security
26
26
 
27
+ ## [1.1.12] - 2025-09-09
28
+ - TAG: [v1.1.12][1.1.12t]
29
+ - COVERAGE: 94.84% -- 3422/3608 lines in 25 files
30
+ - BRANCH COVERAGE: 78.97% -- 1386/1755 branches in 25 files
31
+ - 76.88% documented
32
+ ### Changed
33
+ - improve Gemfile updates during kettle-dev-setup
34
+ - git origin-based funding_org derivation during setup
35
+
36
+ ## [1.1.11] - 2025-09-08
37
+ - TAG: [v1.1.11][1.1.11t]
38
+ - COVERAGE: 96.56% -- 3396/3517 lines in 24 files
39
+ - BRANCH COVERAGE: 81.33% -- 1385/1703 branches in 24 files
40
+ - 77.06% documented
41
+ ### Changed
42
+ - move kettle-dev-setup logic into Kettle::Dev::SetupCLI
43
+ ### Fixed
44
+ - gem dependency detection in kettle-dev-setup to prevent duplication
45
+
27
46
  ## [1.1.10] - 2025-09-08
28
47
  - TAG: [v1.1.10][1.1.10t]
29
48
  - COVERAGE: 97.14% -- 3256/3352 lines in 23 files
@@ -571,7 +590,11 @@ Please file a bug if you notice a violation of semantic versioning.
571
590
  - Selecting will run the selected workflow via `act`
572
591
  - This may move to its own gem in the future.
573
592
 
574
- [Unreleased]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.10...HEAD
593
+ [Unreleased]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.12...HEAD
594
+ [1.1.12]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.11...v1.1.12
595
+ [1.1.12t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.1.12
596
+ [1.1.11]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.10...v1.1.11
597
+ [1.1.11t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.1.11
575
598
  [1.1.10]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.9...v1.1.10
576
599
  [1.1.10t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.1.10
577
600
  [1.1.9]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.8...v1.1.9
data/README.md CHANGED
@@ -907,7 +907,7 @@ Thanks for RTFM. ☺️
907
907
  [📌gitmoji]:https://gitmoji.dev
908
908
  [📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
909
909
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
910
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-3.352-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
910
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-3.608-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
911
911
  [🔐security]: SECURITY.md
912
912
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
913
913
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
data/README.md.example CHANGED
@@ -507,7 +507,7 @@ Thanks for RTFM. ☺️
507
507
  [📌gitmoji]:https://gitmoji.dev
508
508
  [📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
509
509
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
510
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-3.352-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
510
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-3.608-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
511
511
  [🔐security]: SECURITY.md
512
512
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
513
513
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
data/Rakefile.example CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # kettle-dev Rakefile v1.1.10 - 2025-09-08
3
+ # kettle-dev Rakefile v1.1.12 - 2025-09-09
4
4
  # Ruby 2.3 (Safe Navigation) or higher required
5
5
  #
6
6
  # MIT License (see License.txt)
data/exe/kettle-dev-setup CHANGED
@@ -19,7 +19,7 @@ $stdout.sync = true
19
19
  $stderr.sync = true
20
20
 
21
21
  script_basename = File.basename(__FILE__)
22
- running_as_script = File.basename($PROGRAM_NAME)
22
+ File.basename($PROGRAM_NAME)
23
23
 
24
24
  begin
25
25
  require "kettle/dev"
@@ -31,273 +31,37 @@ rescue StandardError => e
31
31
  warn("[kettle-dev-setup] Warning: could not fully load kettle/dev (#{e.class}: #{e.message}). Proceeding with limited features.")
32
32
  end
33
33
 
34
- module Kettle
35
- module Dev
36
- class SetupCLI
37
- def initialize(argv)
38
- debug("initialize(argv=#{argv.inspect})")
39
- @argv = argv
40
- @passthrough = []
41
- @options = {}
42
- parse!
43
- end
34
+ # Always execute when this file is loaded (e.g., via a Bundler binstub).
35
+ # Do not guard with __FILE__ == $PROGRAM_NAME because binstubs use Kernel.load.
36
+ if ARGV.include?("-h") || ARGV.include?("--help")
37
+ puts <<~USAGE
38
+ Usage: kettle-dev-setup [options]
44
39
 
45
- def run!
46
- debug("run!")
47
- say("Starting kettle-dev setup…")
48
- prechecks!
49
- ensure_dev_deps!
50
- ensure_bin_setup!
51
- ensure_rakefile!
52
- run_bin_setup!
53
- run_bundle_binstubs!
54
- commit_bootstrap_changes!
55
- run_kettle_install!
56
- say("kettle-dev setup complete.")
57
- end
40
+ Bootstraps a host gem repository to use kettle-dev tooling.
58
41
 
59
- private
42
+ Options (passed through to rake kettle:dev:install):
43
+ --allowed=VAL
44
+ --force
45
+ --hook_templates=VAL
46
+ --only=VAL
60
47
 
61
- def debug(msg)
62
- return unless ENV.fetch("DEBUG", "false").casecmp("true").zero?
63
- $stderr.puts("[kettle-dev-setup] DEBUG: #{msg}")
64
- end
65
-
66
- def parse!
67
- debug("parse! argv_before=#{@argv.inspect}")
68
- parser = OptionParser.new do |opts|
69
- opts.banner = "Usage: kettle-dev-setup [options]"
70
- # Map CLI flags to environment-style assignments for Rake (key=value)
71
- opts.on("--allowed=VAL", "Pass through to kettle:dev:install") { |v| @passthrough << "allowed=#{v}" }
72
- opts.on("--force", "Pass through to kettle:dev:install") { @passthrough << "force=true" }
73
- opts.on("--hook_templates=VAL", "Pass through to kettle:dev:install") { |v| @passthrough << "hook_templates=#{v}" }
74
- opts.on("--only=VAL", "Pass through to kettle:dev:install") { |v| @passthrough << "only=#{v}" }
75
- opts.on("-h", "--help", "Show help") do
76
- puts opts
77
- exit(0)
78
- end
79
- end
80
- begin
81
- parser.parse!(@argv)
82
- rescue OptionParser::ParseError => e
83
- warn("[kettle-dev-setup] #{e.class}: #{e.message}")
84
- puts parser
85
- exit(2)
86
- end
87
- # Any remaining args pass through as-is
88
- @passthrough.concat(@argv)
89
- debug("parse! argv_after=#{@argv.inspect} passthrough=#{@passthrough.inspect}")
90
- end
91
-
92
- def say(msg)
93
- puts "[kettle-dev-setup] #{msg}"
94
- end
95
-
96
- def abort!(msg)
97
- debug("abort!(msg=#{msg.inspect})")
98
- raise SystemExit, "[kettle-dev-setup] ERROR: #{msg}"
99
- end
100
-
101
- def sh!(cmd, env: {})
102
- debug("sh!(cmd=#{cmd.inspect}, env_keys=#{env.keys.inspect})")
103
- say("exec: #{cmd}")
104
- stdout_str, stderr_str, status = Open3.capture3(env, cmd)
105
- $stdout.print(stdout_str) unless stdout_str.empty?
106
- $stderr.print(stderr_str) unless stderr_str.empty?
107
- abort!("Command failed: #{cmd}") unless status.success?
108
- end
109
-
110
- # 1. Prechecks
111
- def prechecks!
112
- debug("prechecks!")
113
- abort!("Not inside a git repository (missing .git).") unless Dir.exist?(".git")
114
-
115
- # 1a. git clean
116
- begin
117
- # Use GitAdapter if available; otherwise shell out
118
- if defined?(Kettle::Dev::GitAdapter)
119
- ga = Kettle::Dev::GitAdapter.new
120
- dirty = !ga.clean?
121
- else
122
- stdout, _stderr, _status = Open3.capture3("git status --porcelain")
123
- dirty = !stdout.strip.empty?
124
- end
125
- abort!("Git working tree is not clean. Please commit/stash changes and try again.") if dirty
126
- rescue StandardError
127
- # Fallback if anything goes wrong: be conservative and require clean tree via shell
128
- stdout, _stderr, _status = Open3.capture3("git status --porcelain")
129
- abort!("Git working tree is not clean. Please commit/stash changes and try again.") unless stdout.strip.empty?
130
- end
131
-
132
- # 1b. target is a gem (has *.gemspec)
133
- gemspecs = Dir["*.gemspec"]
134
- abort!("No gemspec found in current directory.") if gemspecs.empty?
135
- @gemspec_path = gemspecs.first
136
-
137
- # 1c. target uses bundler (Gemfile)
138
- abort!("No Gemfile found; bundler is required.") unless File.exist?("Gemfile")
139
- end
140
-
141
- # 3. Sync dev dependencies from this gem's example gemspec into target gemspec
142
- def ensure_dev_deps!
143
- debug("ensure_dev_deps!")
144
- source_example = installed_path("kettle-dev.gemspec.example")
145
- abort!("Internal error: kettle-dev.gemspec.example not found within the installed gem.") unless source_example && File.exist?(source_example)
146
-
147
- example = File.read(source_example)
148
- example = example.gsub("{KETTLE|DEV|GEM}", "kettle-dev")
149
-
150
- wanted_lines = example.each_line.map(&:rstrip).select do |line|
151
- line =~ /add_development_dependency\s*\(/
152
- end
153
-
154
- return if wanted_lines.empty?
155
-
156
- target = File.read(@gemspec_path)
157
-
158
- # Build a map from gem name to the desired line
159
- wanted = {}
160
- wanted_lines.each do |line|
161
- if (m = line.match(/add_development_dependency\s*\(\s*["']([^"']+)["']/))
162
- wanted[m[1]] = line
163
- end
164
- end
165
-
166
- modified = target.dup
167
- wanted.each do |gem_name, desired_line|
168
- # Replace existing add_development_dependency for this gem, else append near the end
169
- if /add_development_dependency\s*\(\s*["']#{Regexp.escape(gem_name)}["']/ =~ modified
170
- modified = modified.gsub(/^[^\n]*add_development_dependency\s*\(\s*["']#{Regexp.escape(gem_name)}["'][^\n]*\)$/) do |_|
171
- desired_line
172
- end
173
- elsif (idx = modified.rindex(/\nend\s*\z/))
174
- # Insert before the last end, if present, otherwise append
175
- before = modified[0...idx]
176
- after = modified[idx..-1]
177
- insertion = "\n #{desired_line.strip}\n"
178
- modified = before + insertion + after
179
- else
180
- modified << "\n#{desired_line}\n"
181
- end
182
- end
183
-
184
- if modified != target
185
- File.write(@gemspec_path, modified)
186
- say("Updated development dependencies in #{@gemspec_path}.")
187
- else
188
- say("Development dependencies already up to date.")
189
- end
190
- end
191
-
192
- # 4. Ensure bin/setup present (copy from gem if missing)
193
- def ensure_bin_setup!
194
- debug("ensure_bin_setup!")
195
- target = File.join("bin", "setup")
196
- return say("bin/setup present.") if File.exist?(target)
197
-
198
- source = installed_path(File.join("bin", "setup"))
199
- abort!("Internal error: source bin/setup not found within installed gem.") unless source && File.exist?(source)
200
- FileUtils.mkdir_p("bin")
201
- FileUtils.cp(source, target)
202
- FileUtils.chmod("+x", target)
203
- say("Copied bin/setup.")
204
- end
205
-
206
- # 5. Ensure Rakefile matches example (replace or create)
207
- def ensure_rakefile!
208
- debug("ensure_rakefile!")
209
- source = installed_path("Rakefile.example")
210
- abort!("Internal error: Rakefile.example not found within installed gem.") unless source && File.exist?(source)
211
-
212
- content = File.read(source)
213
- if File.exist?("Rakefile")
214
- say("Replacing existing Rakefile with kettle-dev Rakefile.example.")
215
- else
216
- say("Creating Rakefile from kettle-dev Rakefile.example.")
217
- end
218
- File.write("Rakefile", content)
219
- end
220
-
221
- # 6. Run bin/setup
222
- def run_bin_setup!
223
- debug("run_bin_setup!")
224
- sh!(Shellwords.join([File.join("bin", "setup")]))
225
- end
226
-
227
- # 7. Run bundle binstubs --all
228
- def run_bundle_binstubs!
229
- debug("run_bundle_binstubs!")
230
- sh!("bundle exec bundle binstubs --all")
231
- end
232
-
233
- # 8. Stage and commit any changes made by the setup before install
234
- def commit_bootstrap_changes!
235
- debug("commit_bootstrap_changes!")
236
- # Only operate if we are inside a git repo (prechecked earlier), and if dirty
237
- dirty = begin
238
- if defined?(Kettle::Dev::GitAdapter)
239
- !Kettle::Dev::GitAdapter.new.clean?
240
- else
241
- out, _st = Open3.capture2("git", "status", "--porcelain")
242
- !out.strip.empty?
243
- end
244
- rescue StandardError
245
- out, _st = Open3.capture2("git", "status", "--porcelain")
246
- !out.strip.empty?
247
- end
248
- unless dirty
249
- say("No changes to commit from template bootstrap.")
250
- return
251
- end
252
- # Stage all changes
253
- sh!(Shellwords.join(["git", "add", "-A"]))
254
- # Compose commit message exactly as required
255
- script_name = File.basename(__FILE__)
256
- msg = "🎨 Template bootstrap by #{script_name} v#{Kettle::Dev::Version::VERSION}"
257
- # Commit; allow empty to be false (we already detected dirty)
258
- sh!(Shellwords.join(["git", "commit", "-m", msg]))
259
- say("Committed template bootstrap changes.")
260
- end
261
-
262
- # 9. Run rake kettle:dev:install with passthrough args
263
- def run_kettle_install!
264
- debug("run_kettle_install! passthrough=#{@passthrough.inspect}")
265
- cmd = ["bin/rake", "kettle:dev:install"] + @passthrough
266
- sh!(Shellwords.join(cmd))
267
- end
268
-
269
- # Locate files shipped with this gem installation. Handles execution under a checkout as well.
270
- def installed_path(rel)
271
- debug("installed_path(rel=#{rel.inspect})")
272
- # Prefer real gem installation location
273
- if defined?(Gem) && (spec = Gem.loaded_specs["kettle-dev"])
274
- path = File.join(spec.full_gem_path, rel)
275
- return path if File.exist?(path)
276
- end
277
- # Fallback: relative to this file (useful when running from repo)
278
- here = File.expand_path(File.join(__dir__, "..")) # move from exe/ to project root
279
- path = File.join(here, rel)
280
- return path if File.exist?(path)
281
- nil
282
- end
283
- end
284
- end
48
+ Environment:
49
+ DEBUG=true # print backtraces on errors
50
+ USAGE
51
+ exit 0
285
52
  end
286
53
 
287
- if running_as_script == script_basename
288
- begin
289
- puts "== Kettle::Dev::SetupCLI.new => run! =="
290
- Kettle::Dev::SetupCLI.new(ARGV).run!
291
- rescue SystemExit => e
292
- warn(e.message)
293
- exit(e.status || 1)
294
- rescue Exception => e
295
- warn("[kettle-dev-setup] FATAL: #{e.class}: #{e.message}")
296
- if ENV.fetch("DEBUG", "false").casecmp("true").zero?
297
- warn(e.backtrace.join("\n"))
298
- end
299
- exit(1)
300
- end
301
- else
302
- puts "== #{script_basename} v#{Kettle::Dev::Version::VERSION} fin (#{running_as_script}) == (#{script_basename}) =="
54
+ begin
55
+ Kettle::Dev::SetupCLI.new(ARGV).run!
56
+ rescue LoadError => e
57
+ warn("#{script_basename}: could not load dependency: #{e.class}: #{e.message}")
58
+ warn(e.backtrace.join("\n")) if ENV["DEBUG"]
59
+ exit(1)
60
+ rescue SystemExit => e
61
+ warn("#{script_basename}: exited (status=#{e.status}, msg=#{e.message})") if e.status != 0
62
+ raise
63
+ rescue StandardError => e
64
+ warn("#{script_basename}: unexpected error: #{e.class}: #{e.message}")
65
+ warn(e.backtrace.join("\n")) if ENV["DEBUG"]
66
+ exit(1)
303
67
  end
@@ -2,7 +2,7 @@
2
2
  # visibility and discoverability on RubyGems.org.
3
3
  # However, this gem sits underneath all my other gems, and also "depends on" many of them.
4
4
  # So instead of depending on them directly it injects them into the other gem's gemspec on install.
5
- # This gem its injected dev dependencies, will install on Ruby down to 2.3.x.
5
+ # This gem, and its injected dev dependencies, will install on Ruby down to 2.3.x.
6
6
  # This gem does not inject runtime dependencies.
7
7
  # Thus, dev dependencies injected into gemspecs must have
8
8
  #
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kettle
4
+ module Dev
5
+ # Utilities for copying modular Gemfiles and related directories
6
+ # in a DRY fashion. Used by both the template rake task and the
7
+ # setup CLI to ensure gemfiles/modular/* are present before use.
8
+ module ModularGemfiles
9
+ MODULAR_GEMFILE_DIR = "gemfiles/modular"
10
+
11
+ module_function
12
+
13
+ # Copy the modular gemfiles and nested directories from the gem
14
+ # checkout into the target project, prompting where appropriate
15
+ # via the provided helpers.
16
+ #
17
+ # @param helpers [Kettle::Dev::TemplateHelpers] helper API
18
+ # @param project_root [String] destination project root
19
+ # @param gem_checkout_root [String] kettle-dev checkout root (source)
20
+ # @param min_ruby [Gem::Version, nil] minimum Ruby version (for style.gemfile tuning)
21
+ # @return [void]
22
+ def sync!(helpers:, project_root:, gem_checkout_root:, min_ruby: nil)
23
+ # 4a) gemfiles/modular/*.gemfile except style.gemfile (handled below)
24
+ modular_gemfiles = %w[
25
+ coverage
26
+ debug
27
+ documentation
28
+ injected
29
+ optional
30
+ runtime_heads
31
+ x_std_libs
32
+ ]
33
+ modular_gemfiles.each do |base|
34
+ modular_gemfile = "#{base}.gemfile"
35
+ src = helpers.prefer_example(File.join(gem_checkout_root, MODULAR_GEMFILE_DIR, modular_gemfile))
36
+ dest = File.join(project_root, MODULAR_GEMFILE_DIR, modular_gemfile)
37
+ helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true)
38
+ end
39
+
40
+ # 4b) gemfiles/modular/style.gemfile with dynamic rubocop constraints
41
+ modular_gemfile = "style.gemfile"
42
+ src = helpers.prefer_example(File.join(gem_checkout_root, MODULAR_GEMFILE_DIR, modular_gemfile))
43
+ dest = File.join(project_root, MODULAR_GEMFILE_DIR, modular_gemfile)
44
+ if File.basename(src).sub(/\.example\z/, "") == "style.gemfile"
45
+ helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true) do |content|
46
+ # Adjust rubocop-lts constraint based on min_ruby
47
+ version_map = [
48
+ [Gem::Version.new("1.8"), "~> 0.1"],
49
+ [Gem::Version.new("1.9"), "~> 2.0"],
50
+ [Gem::Version.new("2.0"), "~> 4.0"],
51
+ [Gem::Version.new("2.1"), "~> 6.0"],
52
+ [Gem::Version.new("2.2"), "~> 8.0"],
53
+ [Gem::Version.new("2.3"), "~> 10.0"],
54
+ [Gem::Version.new("2.4"), "~> 12.0"],
55
+ [Gem::Version.new("2.5"), "~> 14.0"],
56
+ [Gem::Version.new("2.6"), "~> 16.0"],
57
+ [Gem::Version.new("2.7"), "~> 18.0"],
58
+ [Gem::Version.new("3.0"), "~> 20.0"],
59
+ [Gem::Version.new("3.1"), "~> 22.0"],
60
+ [Gem::Version.new("3.2"), "~> 24.0"],
61
+ [Gem::Version.new("3.3"), "~> 26.0"],
62
+ [Gem::Version.new("3.4"), "~> 28.0"],
63
+ ]
64
+ new_constraint = nil
65
+ rubocop_ruby_gem_version = nil
66
+ ruby1_8 = version_map.first
67
+ begin
68
+ if min_ruby
69
+ version_map.reverse_each do |min, req|
70
+ if min_ruby >= min
71
+ new_constraint = req
72
+ rubocop_ruby_gem_version = min.segments.join("_")
73
+ break
74
+ end
75
+ end
76
+ end
77
+ if !new_constraint || !rubocop_ruby_gem_version
78
+ # A gem with no declared minimum ruby is effectively >= 1.8.7
79
+ new_constraint = ruby1_8[1]
80
+ rubocop_ruby_gem_version = ruby1_8[0].segments.join("_")
81
+ end
82
+ rescue StandardError => e
83
+ Kettle::Dev.debug_error(e, __method__) if defined?(Kettle::Dev.debug_error)
84
+ # ignore, use default
85
+ ensure
86
+ new_constraint ||= ruby1_8[1]
87
+ rubocop_ruby_gem_version ||= ruby1_8[0].segments.join("_")
88
+ end
89
+ if new_constraint && rubocop_ruby_gem_version
90
+ token = "{RUBOCOP|LTS|CONSTRAINT}"
91
+ content.gsub!(token, new_constraint) if content.include?(token)
92
+ token = "{RUBOCOP|RUBY|GEM}"
93
+ content.gsub!(token, "rubocop-ruby#{rubocop_ruby_gem_version}") if content.include?(token)
94
+ end
95
+ content
96
+ end
97
+ else
98
+ helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true)
99
+ end
100
+
101
+ # 4c) Copy modular directories with nested/versioned files
102
+ %w[erb mutex_m stringio x_std_libs].each do |dir|
103
+ src_dir = File.join(gem_checkout_root, MODULAR_GEMFILE_DIR, dir)
104
+ dest_dir = File.join(project_root, MODULAR_GEMFILE_DIR, dir)
105
+ helpers.copy_dir_with_prompt(src_dir, dest_dir)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,405 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "shellwords"
5
+ require "open3"
6
+ require "optparse"
7
+
8
+ module Kettle
9
+ module Dev
10
+ # SetupCLI bootstraps a host gem repository to use kettle-dev tooling.
11
+ # It performs prechecks, syncs development dependencies, ensures bin/setup and
12
+ # Rakefile templates, runs setup tasks, and invokes kettle:dev:install.
13
+ #
14
+ # Usage:
15
+ # Kettle::Dev::SetupCLI.new(ARGV).run!
16
+ #
17
+ # Options are parsed from argv and passed through to the rake task as
18
+ # key=value pairs (e.g., --force => force=true).
19
+ class SetupCLI
20
+ # @param argv [Array<String>] CLI arguments
21
+ def initialize(argv)
22
+ @argv = argv
23
+ @passthrough = []
24
+ @options = {}
25
+ parse!
26
+ end
27
+
28
+ # Execute the full setup workflow.
29
+ # @return [void]
30
+ def run!
31
+ say("Starting kettle-dev setup…")
32
+ prechecks!
33
+ ensure_dev_deps!
34
+ ensure_gemfile_from_example!
35
+ ensure_modular_gemfiles!
36
+ ensure_bin_setup!
37
+ ensure_rakefile!
38
+ run_bin_setup!
39
+ run_bundle_binstubs!
40
+ commit_bootstrap_changes!
41
+ run_kettle_install!
42
+ say("kettle-dev setup complete.")
43
+ end
44
+
45
+ private
46
+
47
+ def debug(msg)
48
+ return if ENV.fetch("DEBUG", "false").casecmp("true").nonzero?
49
+ $stderr.puts("[kettle-dev-setup] DEBUG: #{msg}")
50
+ end
51
+
52
+ # Attempt to derive a funding organization from the git remote 'origin' when
53
+ # not explicitly provided via env or .opencollective.yml.
54
+ # This is a soft helper that only sets ENV["FUNDING_ORG"] if a plausible
55
+ # GitHub org can be parsed from the origin URL.
56
+ # @return [void]
57
+ def derive_funding_org_from_git_if_missing!
58
+ # Respect explicit bypass
59
+ env_val = ENV["FUNDING_ORG"]
60
+ return if env_val && env_val.to_s.strip.casecmp("false").zero?
61
+
62
+ # If already provided via env, do nothing
63
+ return if ENV["FUNDING_ORG"].to_s.strip != ""
64
+ return if ENV["OPENCOLLECTIVE_HANDLE"].to_s.strip != ""
65
+
66
+ # If project provides an .opencollective.yml with org, do nothing
67
+ begin
68
+ oc_path = File.join(Dir.pwd, ".opencollective.yml")
69
+ if File.file?(oc_path)
70
+ txt = File.read(oc_path)
71
+ return if txt =~ /\borg:\s*([\w\-]+)/i
72
+ end
73
+ rescue StandardError => e
74
+ debug("Reading .opencollective.yml failed: #{e.class}: #{e.message}")
75
+ end
76
+
77
+ # Attempt to get origin URL and parse GitHub org
78
+ begin
79
+ ga = Kettle::Dev::GitAdapter.new
80
+ origin_url = nil
81
+ origin_url = ga.remote_url("origin") if ga.respond_to?(:remote_url)
82
+ if origin_url.nil? && ga.respond_to?(:remotes_with_urls)
83
+ begin
84
+ urls = ga.remotes_with_urls
85
+ origin_url = urls["origin"] if urls
86
+ rescue StandardError => e
87
+ # graceful fallback if adapter backend errs; keep silent behavior
88
+ debug("remotes_with_urls failed: #{e.class}: #{e.message}")
89
+ end
90
+ end
91
+ origin_url = origin_url.to_s.strip
92
+ if (m = origin_url.match(%r{github\.com[/:]([^/]+)/}i))
93
+ org = m[1].to_s
94
+ if !org.empty?
95
+ ENV["FUNDING_ORG"] = org
96
+ debug("Derived FUNDING_ORG from git origin: #{org}")
97
+ end
98
+ end
99
+ rescue StandardError => e
100
+ # Be silent; this is a best-effort and shouldn't fail setup
101
+ debug("Could not derive funding org from git: #{e.class}: #{e.message}")
102
+ end
103
+ end
104
+
105
+ def parse!
106
+ parser = OptionParser.new do |opts|
107
+ opts.banner = "Usage: kettle-dev-setup [options]"
108
+ opts.on("--allowed=VAL", "Pass through to kettle:dev:install") { |v| @passthrough << "allowed=#{v}" }
109
+ opts.on("--force", "Pass through to kettle:dev:install") { @passthrough << "force=true" }
110
+ opts.on("--hook_templates=VAL", "Pass through to kettle:dev:install") { |v| @passthrough << "hook_templates=#{v}" }
111
+ opts.on("--only=VAL", "Pass through to kettle:dev:install") { |v| @passthrough << "only=#{v}" }
112
+ opts.on("-h", "--help", "Show help") do
113
+ puts opts
114
+ Kettle::Dev::ExitAdapter.exit(0)
115
+ end
116
+ end
117
+ begin
118
+ parser.parse!(@argv)
119
+ rescue OptionParser::ParseError => e
120
+ warn("[kettle-dev-setup] #{e.class}: #{e.message}")
121
+ puts parser
122
+ Kettle::Dev::ExitAdapter.exit(2)
123
+ end
124
+ @passthrough.concat(@argv)
125
+ end
126
+
127
+ def say(msg)
128
+ puts "[kettle-dev-setup] #{msg}"
129
+ end
130
+
131
+ def abort!(msg)
132
+ Kettle::Dev::ExitAdapter.abort("[kettle-dev-setup] ERROR: #{msg}")
133
+ end
134
+
135
+ def sh!(cmd, env: {})
136
+ say("exec: #{cmd}")
137
+ stdout_str, stderr_str, status = Open3.capture3(env, cmd)
138
+ $stdout.print(stdout_str) unless stdout_str.empty?
139
+ $stderr.print(stderr_str) unless stderr_str.empty?
140
+ abort!("Command failed: #{cmd}") unless status.success?
141
+ end
142
+
143
+ # 1. Prechecks
144
+ def prechecks!
145
+ abort!("Not inside a git repository (missing .git).") unless Dir.exist?(".git")
146
+
147
+ # Ensure clean working tree
148
+ begin
149
+ if defined?(Kettle::Dev::GitAdapter)
150
+ dirty = !Kettle::Dev::GitAdapter.new.clean?
151
+ else
152
+ stdout, _stderr, _status = Open3.capture3("git status --porcelain")
153
+ dirty = !stdout.strip.empty?
154
+ end
155
+ abort!("Git working tree is not clean. Please commit/stash changes and try again.") if dirty
156
+ rescue StandardError
157
+ stdout, _stderr, _status = Open3.capture3("git status --porcelain")
158
+ abort!("Git working tree is not clean. Please commit/stash changes and try again.") unless stdout.strip.empty?
159
+ end
160
+
161
+ # gemspec
162
+ gemspecs = Dir["*.gemspec"]
163
+ abort!("No gemspec found in current directory.") if gemspecs.empty?
164
+ @gemspec_path = gemspecs.first
165
+
166
+ # Gemfile
167
+ abort!("No Gemfile found; bundler is required.") unless File.exist?("Gemfile")
168
+
169
+ # Seed FUNDING_ORG from git remote origin org when not provided elsewhere
170
+ derive_funding_org_from_git_if_missing!
171
+ end
172
+
173
+ # 3. Sync dev dependencies from this gem's example gemspec into target gemspec
174
+ def ensure_dev_deps!
175
+ source_example = installed_path("kettle-dev.gemspec.example")
176
+ abort!("Internal error: kettle-dev.gemspec.example not found within the installed gem.") unless source_example && File.exist?(source_example)
177
+
178
+ example = File.read(source_example)
179
+ example = example.gsub("{KETTLE|DEV|GEM}", "kettle-dev")
180
+
181
+ wanted_lines = example.each_line.map(&:rstrip).select { |line| line =~ /add_development_dependency\s*\(?/ }
182
+ return if wanted_lines.empty?
183
+
184
+ target = File.read(@gemspec_path)
185
+
186
+ # Build gem=>desired line map
187
+ wanted = {}
188
+ wanted_lines.each do |line|
189
+ if (m = line.match(/add_development_dependency\s*\(?\s*["']([^"']+)["']/))
190
+ wanted[m[1]] = line
191
+ end
192
+ end
193
+
194
+ modified = target.dup
195
+ wanted.each do |gem_name, desired_line|
196
+ lines = modified.lines
197
+ found = false
198
+ lines.map! do |ln|
199
+ if ln =~ /add_development_dependency\s*\(?\s*["']#{Regexp.escape(gem_name)}["']/
200
+ found = true
201
+ indent = ln[/^\s*/] || ""
202
+ "#{indent}#{desired_line.strip}\n"
203
+ else
204
+ ln
205
+ end
206
+ end
207
+ modified = lines.join
208
+
209
+ next if found
210
+
211
+ if (idx = modified.rindex(/\nend\s*\z/))
212
+ before = modified[0...idx]
213
+ after = modified[idx..-1]
214
+ insertion = "\n #{desired_line.strip}\n"
215
+ modified = before + insertion + after
216
+ else
217
+ modified << "\n#{desired_line}\n"
218
+ end
219
+ end
220
+
221
+ if modified != target
222
+ File.write(@gemspec_path, modified)
223
+ say("Updated development dependencies in #{@gemspec_path}.")
224
+ else
225
+ say("Development dependencies already up to date.")
226
+ end
227
+ end
228
+
229
+ # 4. Ensure bin/setup present (copy from gem if missing)
230
+ def ensure_bin_setup!
231
+ target = File.join("bin", "setup")
232
+ return say("bin/setup present.") if File.exist?(target)
233
+
234
+ source = installed_path(File.join("bin", "setup"))
235
+ abort!("Internal error: source bin/setup not found within installed gem.") unless source && File.exist?(source)
236
+ FileUtils.mkdir_p("bin")
237
+ FileUtils.cp(source, target)
238
+ FileUtils.chmod("+x", target)
239
+ say("Copied bin/setup.")
240
+ end
241
+
242
+ # 3b. Ensure Gemfile contains required lines from example without duplicating directives
243
+ # - Copies source, git_source, gemspec, and eval_gemfile lines that are missing
244
+ # - Idempotent (running multiple times does not duplicate entries)
245
+ def ensure_gemfile_from_example!
246
+ source_path = installed_path("Gemfile.example")
247
+ abort!("Internal error: Gemfile.example not found within installed gem.") unless source_path && File.exist?(source_path)
248
+
249
+ example = File.read(source_path)
250
+ target_path = "Gemfile"
251
+ target = File.exist?(target_path) ? File.read(target_path) : ""
252
+
253
+ # Extract interesting lines from example
254
+ ex_sources = []
255
+ ex_git_sources = [] # names (e.g., :github)
256
+ ex_git_source_lines = {}
257
+ ex_has_gemspec = false
258
+ ex_eval_paths = []
259
+
260
+ example.each_line do |ln|
261
+ s = ln.strip
262
+ next if s.empty?
263
+ if s.start_with?("source ")
264
+ ex_sources << ln.rstrip
265
+ elsif (m = s.match(/^git_source\(\s*:(\w+)\s*\)/))
266
+ name = m[1]
267
+ ex_git_sources << name
268
+ ex_git_source_lines[name] = ln.rstrip
269
+ elsif s.start_with?("gemspec")
270
+ ex_has_gemspec = true
271
+ elsif (m = s.match(/^eval_gemfile\s+["']([^"']+)["']/))
272
+ ex_eval_paths << m[1]
273
+ end
274
+ end
275
+
276
+ # Scan target for presence
277
+ tg_sources = target.each_line.map(&:rstrip).select { |l| l.strip.start_with?("source ") }
278
+ tg_git_sources = {}
279
+ target.each_line do |ln|
280
+ if (m = ln.strip.match(/^git_source\(\s*:(\w+)\s*\)/))
281
+ tg_git_sources[m[1]] = true
282
+ end
283
+ end
284
+ tg_has_gemspec = !!target.each_line.find { |l| l.strip.start_with?("gemspec") }
285
+ tg_eval_paths = target.each_line.map do |ln|
286
+ if (m = ln.strip.match(/^eval_gemfile\s+["']([^"']+)["']/))
287
+ m[1]
288
+ end
289
+ end.compact
290
+
291
+ additions = []
292
+ # Add missing sources (exact line match)
293
+ ex_sources.each do |src_line|
294
+ additions << src_line unless tg_sources.include?(src_line)
295
+ end
296
+ # Add missing git_source by name
297
+ ex_git_sources.each do |name|
298
+ additions << ex_git_source_lines[name] unless tg_git_sources[name]
299
+ end
300
+ # Add gemspec if example has it and target lacks it
301
+ additions << "gemspec" if ex_has_gemspec && !tg_has_gemspec
302
+ # Add missing eval_gemfile paths (recreate the exact example line when possible)
303
+ ex_eval_paths.each do |path|
304
+ next if tg_eval_paths.include?(path)
305
+ additions << "eval_gemfile \"#{path}\""
306
+ end
307
+
308
+ return say("Gemfile already contains required entries from example.") if additions.empty?
309
+
310
+ # Ensure file ends with a newline
311
+ target << "\n" unless target.end_with?("\n") || target.empty?
312
+ new_content = target + additions.join("\n") + "\n"
313
+ File.write(target_path, new_content)
314
+ say("Updated Gemfile with entries from Gemfile.example (added #{additions.size}).")
315
+ end
316
+
317
+ # 3c. Ensure gemfiles/modular/* are present (copied like template task)
318
+ def ensure_modular_gemfiles!
319
+ helpers = Kettle::Dev::TemplateHelpers
320
+ project_root = helpers.project_root
321
+ gem_checkout_root = helpers.gem_checkout_root
322
+ # Gather min_ruby for style.gemfile adjustments
323
+ min_ruby = begin
324
+ md = helpers.gemspec_metadata(project_root)
325
+ md[:min_ruby]
326
+ rescue StandardError
327
+ nil
328
+ end
329
+ Kettle::Dev::ModularGemfiles.sync!(
330
+ helpers: helpers,
331
+ project_root: project_root,
332
+ gem_checkout_root: gem_checkout_root,
333
+ min_ruby: min_ruby,
334
+ )
335
+ end
336
+
337
+ # 5. Ensure Rakefile matches example (replace or create)
338
+ def ensure_rakefile!
339
+ source = installed_path("Rakefile.example")
340
+ abort!("Internal error: Rakefile.example not found within installed gem.") unless source && File.exist?(source)
341
+
342
+ content = File.read(source)
343
+ if File.exist?("Rakefile")
344
+ say("Replacing existing Rakefile with kettle-dev Rakefile.example.")
345
+ else
346
+ say("Creating Rakefile from kettle-dev Rakefile.example.")
347
+ end
348
+ File.write("Rakefile", content)
349
+ end
350
+
351
+ # 6. Run bin/setup
352
+ def run_bin_setup!
353
+ sh!(Shellwords.join([File.join("bin", "setup")]))
354
+ end
355
+
356
+ # 7. Run bundle binstubs --all
357
+ def run_bundle_binstubs!
358
+ sh!("bundle exec bundle binstubs --all")
359
+ end
360
+
361
+ # 8. Commit template bootstrap changes if any
362
+ def commit_bootstrap_changes!
363
+ dirty = begin
364
+ if defined?(Kettle::Dev::GitAdapter)
365
+ !Kettle::Dev::GitAdapter.new.clean?
366
+ else
367
+ out, _st = Open3.capture2("git", "status", "--porcelain")
368
+ !out.strip.empty?
369
+ end
370
+ rescue StandardError
371
+ out, _st = Open3.capture2("git", "status", "--porcelain")
372
+ !out.strip.empty?
373
+ end
374
+ unless dirty
375
+ say("No changes to commit from template bootstrap.")
376
+ return
377
+ end
378
+ sh!(Shellwords.join(["git", "add", "-A"]))
379
+ msg = "🎨 Template bootstrap by kettle-dev-setup v#{Kettle::Dev::Version::VERSION}"
380
+ sh!(Shellwords.join(["git", "commit", "-m", msg]))
381
+ say("Committed template bootstrap changes.")
382
+ end
383
+
384
+ # 9. Invoke rake install task with passthrough
385
+ def run_kettle_install!
386
+ cmd = ["bin/rake", "kettle:dev:install"] + @passthrough
387
+ sh!(Shellwords.join(cmd))
388
+ end
389
+
390
+ # Resolve a path to files shipped within the gem or repo checkout
391
+ # @param rel [String]
392
+ # @return [String, nil]
393
+ def installed_path(rel)
394
+ if defined?(Gem) && (spec = Gem.loaded_specs["kettle-dev"])
395
+ path = File.join(spec.full_gem_path, rel)
396
+ return path if File.exist?(path)
397
+ end
398
+ here = File.expand_path(File.join(__dir__, "..", "..", "..")) # lib/kettle/dev/ -> project root
399
+ path = File.join(here, rel)
400
+ return path if File.exist?(path)
401
+ nil
402
+ end
403
+ end
404
+ end
405
+ end
@@ -104,91 +104,13 @@ module Kettle
104
104
  allow_replace: true,
105
105
  )
106
106
 
107
- # 4a) gemfiles/modular/*.gemfile
108
- # from gem's gemfiles/modular,
109
- # except `style.gemfile` which has special handling below
110
- modular_gemfiles = %w[
111
- coverage
112
- debug
113
- documentation
114
- injected
115
- optional
116
- runtime_heads
117
- x_std_libs
118
- ]
119
- modular_gemfiles.each do |base|
120
- modular_gemfile = "#{base}.gemfile"
121
- src = helpers.prefer_example(File.join(gem_checkout_root, MODULAR_GEMFILE_DIR, modular_gemfile))
122
- dest = File.join(project_root, MODULAR_GEMFILE_DIR, modular_gemfile)
123
- helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true)
124
- end
125
-
126
- # 4b) gemfiles/modular/style.gemfile
127
- modular_gemfile = "style.gemfile"
128
- src = helpers.prefer_example(File.join(gem_checkout_root, MODULAR_GEMFILE_DIR, modular_gemfile))
129
- dest = File.join(project_root, MODULAR_GEMFILE_DIR, modular_gemfile)
130
- if File.basename(src).sub(/\.example\z/, "") == "style.gemfile"
131
- helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true) do |content|
132
- # Adjust rubocop-lts constraint based on min_ruby
133
- version_map = [
134
- [Gem::Version.new("1.8"), "~> 0.1"],
135
- [Gem::Version.new("1.9"), "~> 2.0"],
136
- [Gem::Version.new("2.0"), "~> 4.0"],
137
- [Gem::Version.new("2.1"), "~> 6.0"],
138
- [Gem::Version.new("2.2"), "~> 8.0"],
139
- [Gem::Version.new("2.3"), "~> 10.0"],
140
- [Gem::Version.new("2.4"), "~> 12.0"],
141
- [Gem::Version.new("2.5"), "~> 14.0"],
142
- [Gem::Version.new("2.6"), "~> 16.0"],
143
- [Gem::Version.new("2.7"), "~> 18.0"],
144
- [Gem::Version.new("3.0"), "~> 20.0"],
145
- [Gem::Version.new("3.1"), "~> 22.0"],
146
- [Gem::Version.new("3.2"), "~> 24.0"],
147
- [Gem::Version.new("3.3"), "~> 26.0"],
148
- [Gem::Version.new("3.4"), "~> 28.0"],
149
- ]
150
- new_constraint = nil
151
- rubocop_ruby_gem_version = nil
152
- ruby1_8 = version_map.first
153
- begin
154
- if min_ruby
155
- version_map.reverse_each do |min, req|
156
- if min_ruby >= min
157
- new_constraint = req
158
- rubocop_ruby_gem_version = min.segments.join("_")
159
- break
160
- end
161
- end
162
- end
163
- if !new_constraint || !rubocop_ruby_gem_version
164
- # A gem with no declared minimum ruby is effectively >= 1.8.7
165
- new_constraint = ruby1_8[1]
166
- rubocop_ruby_gem_version = ruby1_8[0].segments.join("_")
167
- end
168
- rescue StandardError => e
169
- Kettle::Dev.debug_error(e, __method__)
170
- # ignore, use default
171
- ensure
172
- new_constraint ||= ruby1_8[1]
173
- rubocop_ruby_gem_version ||= ruby1_8[0].segments.join("_")
174
- end
175
- if new_constraint && rubocop_ruby_gem_version
176
- token = "{RUBOCOP|LTS|CONSTRAINT}"
177
- content.gsub!(token, new_constraint) if content.include?(token)
178
- token = "{RUBOCOP|RUBY|GEM}"
179
- content.gsub!(token, "rubocop-ruby#{rubocop_ruby_gem_version}") if content.include?(token)
180
- end
181
- end
182
- else
183
- helpers.copy_file_with_prompt(src, dest, allow_create: true, allow_replace: true)
184
- end
185
-
186
- # 4c) Copy modular directories with nested/versioned files
187
- %w[erb mutex_m stringio x_std_libs].each do |dir|
188
- src_dir = File.join(gem_checkout_root, MODULAR_GEMFILE_DIR, dir)
189
- dest_dir = File.join(project_root, MODULAR_GEMFILE_DIR, dir)
190
- helpers.copy_dir_with_prompt(src_dir, dest_dir)
191
- end
107
+ # 4) gemfiles/modular/* and nested directories (delegated for DRYness)
108
+ Kettle::Dev::ModularGemfiles.sync!(
109
+ helpers: helpers,
110
+ project_root: project_root,
111
+ gem_checkout_root: gem_checkout_root,
112
+ min_ruby: min_ruby,
113
+ )
192
114
 
193
115
  # 5) spec/spec_helper.rb (no create)
194
116
  dest_spec_helper = File.join(project_root, "spec/spec_helper.rb")
@@ -6,7 +6,7 @@ module Kettle
6
6
  module Version
7
7
  # The gem version.
8
8
  # @return [String]
9
- VERSION = "1.1.10"
9
+ VERSION = "1.1.12"
10
10
 
11
11
  module_function
12
12
 
data/lib/kettle/dev.rb CHANGED
@@ -24,7 +24,9 @@ module Kettle
24
24
  autoload :ReadmeBackers, "kettle/dev/readme_backers"
25
25
  autoload :ReleaseCLI, "kettle/dev/release_cli"
26
26
  autoload :PreReleaseCLI, "kettle/dev/pre_release_cli"
27
+ autoload :SetupCLI, "kettle/dev/setup_cli"
27
28
  autoload :TemplateHelpers, "kettle/dev/template_helpers"
29
+ autoload :ModularGemfiles, "kettle/dev/modular_gemfiles"
28
30
  autoload :Version, "kettle/dev/version"
29
31
  autoload :Versioning, "kettle/dev/versioning"
30
32
 
@@ -0,0 +1,12 @@
1
+ module Kettle
2
+ module Dev
3
+ module ModularGemfiles
4
+ def self.sync!: (
5
+ helpers: untyped,
6
+ project_root: String,
7
+ gem_checkout_root: String,
8
+ min_ruby: untyped
9
+ ) -> void
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ module Kettle
2
+ module Dev
3
+ class SetupCLI
4
+ def initialize: (Array[String] argv) -> void
5
+ def run!: () -> void
6
+ end
7
+ end
8
+ end
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: 1.1.10
4
+ version: 1.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -333,6 +333,7 @@ files:
333
333
  - lib/kettle/dev/git_adapter.rb
334
334
  - lib/kettle/dev/git_commit_footer.rb
335
335
  - lib/kettle/dev/input_adapter.rb
336
+ - lib/kettle/dev/modular_gemfiles.rb
336
337
  - lib/kettle/dev/pre_release_cli.rb
337
338
  - lib/kettle/dev/rakelib/appraisal.rake
338
339
  - lib/kettle/dev/rakelib/bench.rake
@@ -347,6 +348,7 @@ files:
347
348
  - lib/kettle/dev/rakelib/yard.rake
348
349
  - lib/kettle/dev/readme_backers.rb
349
350
  - lib/kettle/dev/release_cli.rb
351
+ - lib/kettle/dev/setup_cli.rb
350
352
  - lib/kettle/dev/tasks.rb
351
353
  - lib/kettle/dev/tasks/ci_task.rb
352
354
  - lib/kettle/dev/tasks/install_task.rb
@@ -366,9 +368,11 @@ files:
366
368
  - sig/kettle/dev/git_adapter.rbs
367
369
  - sig/kettle/dev/git_commit_footer.rbs
368
370
  - sig/kettle/dev/input_adapter.rbs
371
+ - sig/kettle/dev/modular_gemfiles.rbs
369
372
  - sig/kettle/dev/pre_release_cli.rbs
370
373
  - sig/kettle/dev/readme_backers.rbs
371
374
  - sig/kettle/dev/release_cli.rbs
375
+ - sig/kettle/dev/setup_cli.rbs
372
376
  - sig/kettle/dev/tasks.rbs
373
377
  - sig/kettle/dev/tasks/ci_task.rbs
374
378
  - sig/kettle/dev/tasks/install_task.rbs
@@ -381,10 +385,10 @@ licenses:
381
385
  - MIT
382
386
  metadata:
383
387
  homepage_uri: https://kettle-dev.galtzo.com/
384
- source_code_uri: https://github.com/kettle-rb/kettle-dev/tree/v1.1.10
385
- changelog_uri: https://github.com/kettle-rb/kettle-dev/blob/v1.1.10/CHANGELOG.md
388
+ source_code_uri: https://github.com/kettle-rb/kettle-dev/tree/v1.1.12
389
+ changelog_uri: https://github.com/kettle-rb/kettle-dev/blob/v1.1.12/CHANGELOG.md
386
390
  bug_tracker_uri: https://github.com/kettle-rb/kettle-dev/issues
387
- documentation_uri: https://www.rubydoc.info/gems/kettle-dev/1.1.10
391
+ documentation_uri: https://www.rubydoc.info/gems/kettle-dev/1.1.12
388
392
  funding_uri: https://github.com/sponsors/pboling
389
393
  wiki_uri: https://github.com/kettle-rb/kettle-dev/wiki
390
394
  news_uri: https://www.railsbling.com/tags/kettle-dev
metadata.gz.sig CHANGED
Binary file