kettle-dev 1.1.10 → 1.1.11

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: 84d3d2f6671ddba20e5cc07cb1b56051e9b6acaf6d3cb1157b8fc2bb3c76bb76
4
+ data.tar.gz: f962f26bb64d242f670899b45831a66036c4ff1db497702f0446669374f1f580
5
5
  SHA512:
6
- metadata.gz: 640a7db16fab1bb407eca866f68209f5c354f8ed54fd40b20723ec0a8e3860018b4509891bb9875a2a268a082149c7013bfe0c1426dfe08cdd857c3e057dee8a
7
- data.tar.gz: a32860690a84df178fbffddc65ffb14eb257f9481d1efd9cbcb177eb53452ce34493d370a9430c3dc2bdb3a295bd1fc55c584f7b174fcc379e102084166811dd
6
+ metadata.gz: a6f1fc475a0aa076e614c7c612ba2d6fc06327de919af2c29c01173dc6a7191c25a57a0d8392b2847c75931804b60a5360bcd610c4acac8a42c6ae835f36275f
7
+ data.tar.gz: d6163488e6f32e9633074faed8d8243f85edf29766871797678e84393a2d2dd575cad9c16ceccc7074a2e149256f2ea5861e4d739ac61381cd34a2a0e488556b
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -24,6 +24,16 @@ Please file a bug if you notice a violation of semantic versioning.
24
24
  ### Fixed
25
25
  ### Security
26
26
 
27
+ ## [1.1.11] - 2025-09-08
28
+ - TAG: [v1.1.11][1.1.11t]
29
+ - COVERAGE: 96.56% -- 3396/3517 lines in 24 files
30
+ - BRANCH COVERAGE: 81.33% -- 1385/1703 branches in 24 files
31
+ - 77.06% documented
32
+ ### Changed
33
+ - move kettle-dev-setup logic into Kettle::Dev::SetupCLI
34
+ ### Fixed
35
+ - gem dependency detection in kettle-dev-setup to prevent duplication
36
+
27
37
  ## [1.1.10] - 2025-09-08
28
38
  - TAG: [v1.1.10][1.1.10t]
29
39
  - COVERAGE: 97.14% -- 3256/3352 lines in 23 files
@@ -571,7 +581,9 @@ Please file a bug if you notice a violation of semantic versioning.
571
581
  - Selecting will run the selected workflow via `act`
572
582
  - This may move to its own gem in the future.
573
583
 
574
- [Unreleased]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.10...HEAD
584
+ [Unreleased]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.11...HEAD
585
+ [1.1.11]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.10...v1.1.11
586
+ [1.1.11t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.1.11
575
587
  [1.1.10]: https://github.com/kettle-rb/kettle-dev/compare/v1.1.9...v1.1.10
576
588
  [1.1.10t]: https://github.com/kettle-rb/kettle-dev/releases/tag/v1.1.10
577
589
  [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.517-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.517-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.11 - 2025-09-08
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
@@ -0,0 +1,252 @@
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_bin_setup!
35
+ ensure_rakefile!
36
+ run_bin_setup!
37
+ run_bundle_binstubs!
38
+ commit_bootstrap_changes!
39
+ run_kettle_install!
40
+ say("kettle-dev setup complete.")
41
+ end
42
+
43
+ private
44
+
45
+ def debug(msg)
46
+ return if ENV.fetch("DEBUG", "false").casecmp("true").nonzero?
47
+ $stderr.puts("[kettle-dev-setup] DEBUG: #{msg}")
48
+ end
49
+
50
+ def parse!
51
+ parser = OptionParser.new do |opts|
52
+ opts.banner = "Usage: kettle-dev-setup [options]"
53
+ opts.on("--allowed=VAL", "Pass through to kettle:dev:install") { |v| @passthrough << "allowed=#{v}" }
54
+ opts.on("--force", "Pass through to kettle:dev:install") { @passthrough << "force=true" }
55
+ opts.on("--hook_templates=VAL", "Pass through to kettle:dev:install") { |v| @passthrough << "hook_templates=#{v}" }
56
+ opts.on("--only=VAL", "Pass through to kettle:dev:install") { |v| @passthrough << "only=#{v}" }
57
+ opts.on("-h", "--help", "Show help") do
58
+ puts opts
59
+ Kettle::Dev::ExitAdapter.exit(0)
60
+ end
61
+ end
62
+ begin
63
+ parser.parse!(@argv)
64
+ rescue OptionParser::ParseError => e
65
+ warn("[kettle-dev-setup] #{e.class}: #{e.message}")
66
+ puts parser
67
+ Kettle::Dev::ExitAdapter.exit(2)
68
+ end
69
+ @passthrough.concat(@argv)
70
+ end
71
+
72
+ def say(msg)
73
+ puts "[kettle-dev-setup] #{msg}"
74
+ end
75
+
76
+ def abort!(msg)
77
+ Kettle::Dev::ExitAdapter.abort("[kettle-dev-setup] ERROR: #{msg}")
78
+ end
79
+
80
+ def sh!(cmd, env: {})
81
+ say("exec: #{cmd}")
82
+ stdout_str, stderr_str, status = Open3.capture3(env, cmd)
83
+ $stdout.print(stdout_str) unless stdout_str.empty?
84
+ $stderr.print(stderr_str) unless stderr_str.empty?
85
+ abort!("Command failed: #{cmd}") unless status.success?
86
+ end
87
+
88
+ # 1. Prechecks
89
+ def prechecks!
90
+ abort!("Not inside a git repository (missing .git).") unless Dir.exist?(".git")
91
+
92
+ # Ensure clean working tree
93
+ begin
94
+ if defined?(Kettle::Dev::GitAdapter)
95
+ dirty = !Kettle::Dev::GitAdapter.new.clean?
96
+ else
97
+ stdout, _stderr, _status = Open3.capture3("git status --porcelain")
98
+ dirty = !stdout.strip.empty?
99
+ end
100
+ abort!("Git working tree is not clean. Please commit/stash changes and try again.") if dirty
101
+ rescue StandardError
102
+ stdout, _stderr, _status = Open3.capture3("git status --porcelain")
103
+ abort!("Git working tree is not clean. Please commit/stash changes and try again.") unless stdout.strip.empty?
104
+ end
105
+
106
+ # gemspec
107
+ gemspecs = Dir["*.gemspec"]
108
+ abort!("No gemspec found in current directory.") if gemspecs.empty?
109
+ @gemspec_path = gemspecs.first
110
+
111
+ # Gemfile
112
+ abort!("No Gemfile found; bundler is required.") unless File.exist?("Gemfile")
113
+ end
114
+
115
+ # 3. Sync dev dependencies from this gem's example gemspec into target gemspec
116
+ def ensure_dev_deps!
117
+ source_example = installed_path("kettle-dev.gemspec.example")
118
+ abort!("Internal error: kettle-dev.gemspec.example not found within the installed gem.") unless source_example && File.exist?(source_example)
119
+
120
+ example = File.read(source_example)
121
+ example = example.gsub("{KETTLE|DEV|GEM}", "kettle-dev")
122
+
123
+ wanted_lines = example.each_line.map(&:rstrip).select { |line| line =~ /add_development_dependency\s*\(?/ }
124
+ return if wanted_lines.empty?
125
+
126
+ target = File.read(@gemspec_path)
127
+
128
+ # Build gem=>desired line map
129
+ wanted = {}
130
+ wanted_lines.each do |line|
131
+ if (m = line.match(/add_development_dependency\s*\(?\s*["']([^"']+)["']/))
132
+ wanted[m[1]] = line
133
+ end
134
+ end
135
+
136
+ modified = target.dup
137
+ wanted.each do |gem_name, desired_line|
138
+ lines = modified.lines
139
+ found = false
140
+ lines.map! do |ln|
141
+ if ln =~ /add_development_dependency\s*\(?\s*["']#{Regexp.escape(gem_name)}["']/
142
+ found = true
143
+ indent = ln[/^\s*/] || ""
144
+ "#{indent}#{desired_line.strip}\n"
145
+ else
146
+ ln
147
+ end
148
+ end
149
+ modified = lines.join
150
+
151
+ next if found
152
+
153
+ if (idx = modified.rindex(/\nend\s*\z/))
154
+ before = modified[0...idx]
155
+ after = modified[idx..-1]
156
+ insertion = "\n #{desired_line.strip}\n"
157
+ modified = before + insertion + after
158
+ else
159
+ modified << "\n#{desired_line}\n"
160
+ end
161
+ end
162
+
163
+ if modified != target
164
+ File.write(@gemspec_path, modified)
165
+ say("Updated development dependencies in #{@gemspec_path}.")
166
+ else
167
+ say("Development dependencies already up to date.")
168
+ end
169
+ end
170
+
171
+ # 4. Ensure bin/setup present (copy from gem if missing)
172
+ def ensure_bin_setup!
173
+ target = File.join("bin", "setup")
174
+ return say("bin/setup present.") if File.exist?(target)
175
+
176
+ source = installed_path(File.join("bin", "setup"))
177
+ abort!("Internal error: source bin/setup not found within installed gem.") unless source && File.exist?(source)
178
+ FileUtils.mkdir_p("bin")
179
+ FileUtils.cp(source, target)
180
+ FileUtils.chmod("+x", target)
181
+ say("Copied bin/setup.")
182
+ end
183
+
184
+ # 5. Ensure Rakefile matches example (replace or create)
185
+ def ensure_rakefile!
186
+ source = installed_path("Rakefile.example")
187
+ abort!("Internal error: Rakefile.example not found within installed gem.") unless source && File.exist?(source)
188
+
189
+ content = File.read(source)
190
+ if File.exist?("Rakefile")
191
+ say("Replacing existing Rakefile with kettle-dev Rakefile.example.")
192
+ else
193
+ say("Creating Rakefile from kettle-dev Rakefile.example.")
194
+ end
195
+ File.write("Rakefile", content)
196
+ end
197
+
198
+ # 6. Run bin/setup
199
+ def run_bin_setup!
200
+ sh!(Shellwords.join([File.join("bin", "setup")]))
201
+ end
202
+
203
+ # 7. Run bundle binstubs --all
204
+ def run_bundle_binstubs!
205
+ sh!("bundle exec bundle binstubs --all")
206
+ end
207
+
208
+ # 8. Commit template bootstrap changes if any
209
+ def commit_bootstrap_changes!
210
+ dirty = begin
211
+ if defined?(Kettle::Dev::GitAdapter)
212
+ !Kettle::Dev::GitAdapter.new.clean?
213
+ else
214
+ out, _st = Open3.capture2("git", "status", "--porcelain")
215
+ !out.strip.empty?
216
+ end
217
+ rescue StandardError
218
+ out, _st = Open3.capture2("git", "status", "--porcelain")
219
+ !out.strip.empty?
220
+ end
221
+ unless dirty
222
+ say("No changes to commit from template bootstrap.")
223
+ return
224
+ end
225
+ sh!(Shellwords.join(["git", "add", "-A"]))
226
+ msg = "🎨 Template bootstrap by kettle-dev-setup v#{Kettle::Dev::Version::VERSION}"
227
+ sh!(Shellwords.join(["git", "commit", "-m", msg]))
228
+ say("Committed template bootstrap changes.")
229
+ end
230
+
231
+ # 9. Invoke rake install task with passthrough
232
+ def run_kettle_install!
233
+ cmd = ["bin/rake", "kettle:dev:install"] + @passthrough
234
+ sh!(Shellwords.join(cmd))
235
+ end
236
+
237
+ # Resolve a path to files shipped within the gem or repo checkout
238
+ # @param rel [String]
239
+ # @return [String, nil]
240
+ def installed_path(rel)
241
+ if defined?(Gem) && (spec = Gem.loaded_specs["kettle-dev"])
242
+ path = File.join(spec.full_gem_path, rel)
243
+ return path if File.exist?(path)
244
+ end
245
+ here = File.expand_path(File.join(__dir__, "..", "..", "..")) # lib/kettle/dev/ -> project root
246
+ path = File.join(here, rel)
247
+ return path if File.exist?(path)
248
+ nil
249
+ end
250
+ end
251
+ end
252
+ end
@@ -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.11"
10
10
 
11
11
  module_function
12
12
 
data/lib/kettle/dev.rb CHANGED
@@ -24,6 +24,7 @@ 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"
28
29
  autoload :Version, "kettle/dev/version"
29
30
  autoload :Versioning, "kettle/dev/versioning"
@@ -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.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -347,6 +347,7 @@ files:
347
347
  - lib/kettle/dev/rakelib/yard.rake
348
348
  - lib/kettle/dev/readme_backers.rb
349
349
  - lib/kettle/dev/release_cli.rb
350
+ - lib/kettle/dev/setup_cli.rb
350
351
  - lib/kettle/dev/tasks.rb
351
352
  - lib/kettle/dev/tasks/ci_task.rb
352
353
  - lib/kettle/dev/tasks/install_task.rb
@@ -369,6 +370,7 @@ files:
369
370
  - sig/kettle/dev/pre_release_cli.rbs
370
371
  - sig/kettle/dev/readme_backers.rbs
371
372
  - sig/kettle/dev/release_cli.rbs
373
+ - sig/kettle/dev/setup_cli.rbs
372
374
  - sig/kettle/dev/tasks.rbs
373
375
  - sig/kettle/dev/tasks/ci_task.rbs
374
376
  - sig/kettle/dev/tasks/install_task.rbs
@@ -381,10 +383,10 @@ licenses:
381
383
  - MIT
382
384
  metadata:
383
385
  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
386
+ source_code_uri: https://github.com/kettle-rb/kettle-dev/tree/v1.1.11
387
+ changelog_uri: https://github.com/kettle-rb/kettle-dev/blob/v1.1.11/CHANGELOG.md
386
388
  bug_tracker_uri: https://github.com/kettle-rb/kettle-dev/issues
387
- documentation_uri: https://www.rubydoc.info/gems/kettle-dev/1.1.10
389
+ documentation_uri: https://www.rubydoc.info/gems/kettle-dev/1.1.11
388
390
  funding_uri: https://github.com/sponsors/pboling
389
391
  wiki_uri: https://github.com/kettle-rb/kettle-dev/wiki
390
392
  news_uri: https://www.railsbling.com/tags/kettle-dev
metadata.gz.sig CHANGED
Binary file