ratatui_ruby-devtools 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/LICENSES/BSD-2-Clause.txt +9 -0
- data/README.md +1 -1
- data/exe/hbs +149 -209
- data/exe/scaffold +169 -282
- data/lib/ratatui_ruby/devtools/tasks/lint.rake +3 -7
- data/lib/ratatui_ruby/devtools/tasks/release/ci_run.rb +44 -0
- data/lib/ratatui_ruby/devtools/tasks/release/github_cli.rb +36 -0
- data/lib/ratatui_ruby/devtools/tasks/release/native_gem_version.rb +70 -0
- data/lib/ratatui_ruby/devtools/tasks/release/platform_gem.rb +63 -0
- data/lib/ratatui_ruby/devtools/tasks/release/versioned_binary.rb +36 -0
- data/lib/ratatui_ruby/devtools/tasks/release.rake +87 -0
- data/lib/ratatui_ruby/devtools/tasks/resources/rubies.yml +1 -1
- data/lib/ratatui_ruby/devtools/templates/.github/workflows/build-gems.yml.erb +124 -0
- data/lib/ratatui_ruby/devtools/templates/.github/workflows/ci.yml.erb +77 -0
- data/lib/ratatui_ruby/devtools/templates/.gitignore.erb +2 -0
- data/lib/ratatui_ruby/devtools/templates/.rubocop.yml.erb +6 -4
- data/lib/ratatui_ruby/devtools/templates/AGENTS.md.erb +101 -28
- data/lib/ratatui_ruby/devtools/templates/CODE_OF_CONDUCT.md.erb +44 -0
- data/lib/ratatui_ruby/devtools/templates/CONTRIBUTING.md.erb +80 -0
- data/lib/ratatui_ruby/devtools/templates/Gemfile.erb +2 -2
- data/lib/ratatui_ruby/devtools/templates/README.md.erb +46 -13
- data/lib/ratatui_ruby/devtools/templates/REUSE.toml.erb +2 -2
- data/lib/ratatui_ruby/devtools/templates/Rakefile.erb +4 -4
- data/lib/ratatui_ruby/devtools/templates/bin/setup.erb +21 -11
- data/lib/ratatui_ruby/devtools/templates/bin/setup.ps1.erb +105 -0
- data/lib/ratatui_ruby/devtools/templates/doc/custom.css.erb +3 -3
- data/lib/ratatui_ruby/devtools/templates/doc/getting_started/quickstart.md.erb +3 -3
- data/lib/ratatui_ruby/devtools/templates/doc/index.md.erb +2 -2
- data/lib/ratatui_ruby/devtools/templates/ext/.cargo/config.toml.erb +15 -0
- data/lib/ratatui_ruby/devtools/templates/ext/.gitignore.erb +6 -0
- data/lib/ratatui_ruby/devtools/templates/ext/Cargo.toml.erb +20 -0
- data/lib/ratatui_ruby/devtools/templates/ext/clippy.toml.erb +9 -0
- data/lib/ratatui_ruby/devtools/templates/ext/extconf.rb.erb +23 -0
- data/lib/ratatui_ruby/devtools/templates/ext/src/lib.rs.erb +13 -0
- data/lib/ratatui_ruby/devtools/templates/gemspec.erb +21 -14
- data/lib/ratatui_ruby/devtools/templates/lib/main.rb.erb +19 -0
- data/lib/ratatui_ruby/devtools/templates/lib/test_helper.rb.erb +26 -0
- data/lib/ratatui_ruby/devtools/templates/lib/version.rb.erb +10 -0
- data/lib/ratatui_ruby/devtools/templates/mise.toml.erb +3 -3
- data/lib/ratatui_ruby/devtools/templates/tasks/example_viewer.html.erb +23 -23
- data/lib/ratatui_ruby/devtools/templates/tasks/resources/index.html.erb +15 -16
- data/lib/ratatui_ruby/devtools/templates/tasks/resources/rubies.yml.erb +5 -6
- data/lib/ratatui_ruby/devtools/templates/test/test_helper.rb.erb +15 -0
- data/lib/ratatui_ruby/devtools/version.rb +1 -1
- data/lib/ratatui_ruby/devtools.rb +2 -2
- metadata +24 -5
- data/lib/ratatui_ruby/devtools/tasks/resources/build.yml.erb +0 -54
- data/lib/ratatui_ruby/devtools/tasks/sourcehut.rake +0 -94
- data/lib/ratatui_ruby/devtools/templates/tasks/resources/build.yml.erb +0 -62
- /data/lib/ratatui_ruby/devtools/templates/{vendor/goodcop/base.yml → lib/rubocop.yml} +0 -0
data/exe/scaffold
CHANGED
|
@@ -6,15 +6,19 @@
|
|
|
6
6
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
7
7
|
#++
|
|
8
8
|
|
|
9
|
-
# Scaffolds a
|
|
9
|
+
# Scaffolds or updates a RatatuiRuby ecosystem gem.
|
|
10
10
|
#
|
|
11
11
|
# Creating a new ecosystem gem requires many files with consistent formatting:
|
|
12
|
-
# gemspec, Rakefile, AGENTS.md, CI
|
|
12
|
+
# gemspec, Rakefile, AGENTS.md, CI workflows, license headers, and more.
|
|
13
13
|
# Manually copying and editing templates is tedious and error-prone.
|
|
14
14
|
#
|
|
15
|
-
# This script renders all devtools templates into a
|
|
16
|
-
#
|
|
17
|
-
#
|
|
15
|
+
# This script renders all devtools templates into a gem directory with the
|
|
16
|
+
# correct gem name, copyright holder, and license. Use it to bootstrap a
|
|
17
|
+
# new gem or to update an existing one when devtools templates change.
|
|
18
|
+
#
|
|
19
|
+
# In update mode (<tt>--update</tt>), new files are written automatically.
|
|
20
|
+
# Existing files that differ from the template show a diff and prompt for
|
|
21
|
+
# overwrite. Unchanged files are skipped silently.
|
|
18
22
|
#
|
|
19
23
|
# == Usage
|
|
20
24
|
#
|
|
@@ -26,13 +30,17 @@
|
|
|
26
30
|
# --email EMAIL Copyright holder email (default: from git config)
|
|
27
31
|
# --license ID SPDX license identifier (default: AGPL-3.0-or-later)
|
|
28
32
|
# --dir PATH Parent directory for new gem (default: current directory)
|
|
33
|
+
# --update Update existing project (diff + prompt for conflicts)
|
|
34
|
+
# --force Remove existing directory if present
|
|
35
|
+
# --rust Include Rust native extension support
|
|
29
36
|
# --help Show this help
|
|
30
37
|
#
|
|
31
38
|
# == Examples
|
|
32
39
|
#
|
|
33
|
-
# scaffold
|
|
40
|
+
# scaffold rooibos
|
|
34
41
|
# scaffold ratatui_ruby-kit --license MIT
|
|
35
42
|
# scaffold ratatui_ruby-framework --dir ~/Developer
|
|
43
|
+
# scaffold rooibos --update --dir ~/Developer
|
|
36
44
|
|
|
37
45
|
require "erb"
|
|
38
46
|
require "fileutils"
|
|
@@ -94,7 +102,7 @@ class ScaffoldConfig
|
|
|
94
102
|
|
|
95
103
|
# Ruby versions to test in CI.
|
|
96
104
|
def ruby_versions
|
|
97
|
-
%w[3.2 3.3 3.4 4.0.
|
|
105
|
+
%w[3.2 3.3 3.4 4.0.1]
|
|
98
106
|
end
|
|
99
107
|
end
|
|
100
108
|
|
|
@@ -108,10 +116,11 @@ class TemplateRenderer
|
|
|
108
116
|
# [config] The ScaffoldConfig with gem parameters.
|
|
109
117
|
# [templates_dir] Path to the ERB templates.
|
|
110
118
|
# [output_dir] Path where rendered files are written.
|
|
111
|
-
def initialize(config, templates_dir, output_dir)
|
|
119
|
+
def initialize(config, templates_dir, output_dir, update_mode: false)
|
|
112
120
|
@config = config
|
|
113
121
|
@templates_dir = templates_dir
|
|
114
122
|
@output_dir = output_dir
|
|
123
|
+
@update_mode = update_mode
|
|
115
124
|
end
|
|
116
125
|
|
|
117
126
|
# Renders all templates into the output directory.
|
|
@@ -122,7 +131,9 @@ class TemplateRenderer
|
|
|
122
131
|
render_template(".pre-commit-config.yaml.erb", ".pre-commit-config.yaml")
|
|
123
132
|
render_template(".rubocop.yml.erb", ".rubocop.yml")
|
|
124
133
|
render_template("AGENTS.md.erb", "AGENTS.md")
|
|
125
|
-
render_template("CHANGELOG.md.erb", "CHANGELOG.md")
|
|
134
|
+
render_template("CHANGELOG.md.erb", "CHANGELOG.md") unless @update_mode
|
|
135
|
+
render_template("CONTRIBUTING.md.erb", "CONTRIBUTING.md")
|
|
136
|
+
render_template("CODE_OF_CONDUCT.md.erb", "CODE_OF_CONDUCT.md")
|
|
126
137
|
render_template("Gemfile.erb", "Gemfile")
|
|
127
138
|
render_template("README.md.erb", "README.md")
|
|
128
139
|
render_template("REUSE.toml.erb", "REUSE.toml")
|
|
@@ -132,13 +143,17 @@ class TemplateRenderer
|
|
|
132
143
|
|
|
133
144
|
# bin/ scripts
|
|
134
145
|
render_template("bin/setup.erb", "bin/setup", executable: true)
|
|
146
|
+
render_template("bin/setup.ps1.erb", "bin/setup.ps1")
|
|
135
147
|
render_template("bin/console.erb", "bin/console", executable: true)
|
|
136
148
|
|
|
137
|
-
#
|
|
138
|
-
|
|
149
|
+
# GitHub Actions CI workflow
|
|
150
|
+
render_template(".github/workflows/ci.yml.erb", ".github/workflows/ci.yml")
|
|
139
151
|
|
|
140
|
-
#
|
|
141
|
-
|
|
152
|
+
# GitHub Actions build-gems workflow (Rust only)
|
|
153
|
+
render_template(".github/workflows/build-gems.yml.erb", ".github/workflows/build-gems.yml") if @config.has_rust
|
|
154
|
+
|
|
155
|
+
# task resources
|
|
156
|
+
copy_task_resources
|
|
142
157
|
|
|
143
158
|
# doc files (RDoc styling)
|
|
144
159
|
copy_doc_files
|
|
@@ -158,17 +173,44 @@ class TemplateRenderer
|
|
|
158
173
|
|
|
159
174
|
FileUtils.mkdir_p(File.dirname(output_path))
|
|
160
175
|
|
|
161
|
-
|
|
162
|
-
if template_name.end_with?(".erb")
|
|
163
|
-
content = ERB.new(File.read(template_path), trim_mode: "-").result(binding_for_config)
|
|
164
|
-
else
|
|
165
|
-
content = File.read(template_path)
|
|
166
|
-
end
|
|
167
|
-
File.write(output_path, content)
|
|
168
|
-
FileUtils.chmod(0o755, output_path) if executable
|
|
169
|
-
puts " Created #{output_name}"
|
|
170
|
-
else
|
|
176
|
+
unless File.exist?(template_path)
|
|
171
177
|
warn " WARNING: Template not found: #{template_name}"
|
|
178
|
+
return
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
content = if template_name.end_with?(".erb")
|
|
182
|
+
ERB.new(File.read(template_path), trim_mode: "-").result(binding_for_config)
|
|
183
|
+
else
|
|
184
|
+
File.read(template_path)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
if @update_mode && File.exist?(output_path)
|
|
188
|
+
existing = File.read(output_path)
|
|
189
|
+
if existing == content
|
|
190
|
+
puts " Unchanged #{output_name}"
|
|
191
|
+
return
|
|
192
|
+
end
|
|
193
|
+
puts " Conflict #{output_name}"
|
|
194
|
+
show_diff(output_path, content)
|
|
195
|
+
print " Overwrite #{output_name}? [y/N] "
|
|
196
|
+
answer = $stdin.gets&.strip
|
|
197
|
+
unless answer&.match?(/^[Yy]/)
|
|
198
|
+
puts " Skipped #{output_name}"
|
|
199
|
+
return
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
File.write(output_path, content)
|
|
204
|
+
FileUtils.chmod(0o755, output_path) if executable
|
|
205
|
+
puts " Created #{output_name}"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
private def show_diff(existing_path, new_content)
|
|
209
|
+
require "tempfile"
|
|
210
|
+
Tempfile.create(["scaffold", File.extname(existing_path)]) do |tmp|
|
|
211
|
+
tmp.write(new_content)
|
|
212
|
+
tmp.flush
|
|
213
|
+
system("git", "--no-pager", "diff", "--no-index", "--color", existing_path, tmp.path)
|
|
172
214
|
end
|
|
173
215
|
end
|
|
174
216
|
|
|
@@ -177,10 +219,7 @@ class TemplateRenderer
|
|
|
177
219
|
tasks_dir = File.join(@output_dir, "tasks")
|
|
178
220
|
FileUtils.mkdir_p(resources_dir)
|
|
179
221
|
|
|
180
|
-
# Copy task resource files
|
|
181
|
-
template_resources = File.join(@templates_dir, "tasks", "resources")
|
|
182
222
|
# Render ERB templates to tasks/resources/
|
|
183
|
-
render_template("tasks/resources/build.yml.erb", "tasks/resources/build.yml.erb")
|
|
184
223
|
render_template("tasks/resources/rubies.yml.erb", "tasks/resources/rubies.yml")
|
|
185
224
|
render_template("tasks/resources/index.html.erb", "tasks/resources/index.html.erb")
|
|
186
225
|
|
|
@@ -195,20 +234,6 @@ class TemplateRenderer
|
|
|
195
234
|
puts " Created exe/.gitkeep"
|
|
196
235
|
end
|
|
197
236
|
|
|
198
|
-
private def copy_vendor_files
|
|
199
|
-
vendor_dir = File.join(@output_dir, "vendor", "goodcop")
|
|
200
|
-
FileUtils.mkdir_p(vendor_dir)
|
|
201
|
-
|
|
202
|
-
src = File.join(@templates_dir, "vendor", "goodcop", "base.yml")
|
|
203
|
-
dst = File.join(vendor_dir, "base.yml")
|
|
204
|
-
if File.exist?(src)
|
|
205
|
-
FileUtils.cp(src, dst)
|
|
206
|
-
puts " Copied vendor/goodcop/base.yml"
|
|
207
|
-
else
|
|
208
|
-
warn " WARNING: vendor/goodcop/base.yml not found"
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
|
|
212
237
|
private def copy_doc_files
|
|
213
238
|
doc_dir = File.join(@output_dir, "doc")
|
|
214
239
|
FileUtils.mkdir_p(doc_dir)
|
|
@@ -234,193 +259,32 @@ class TemplateRenderer
|
|
|
234
259
|
lib_path = File.join(@output_dir, "lib", @config.require_path)
|
|
235
260
|
FileUtils.mkdir_p(lib_path)
|
|
236
261
|
|
|
237
|
-
#
|
|
238
|
-
|
|
262
|
+
# RuboCop config shipped inside the gem (resolved via inherit_gem)
|
|
263
|
+
render_template("lib/rubocop.yml", "lib/#{@config.require_path}/rubocop.yml")
|
|
239
264
|
|
|
240
265
|
# Main require file
|
|
241
|
-
|
|
242
|
-
# REUSE-IgnoreStart
|
|
243
|
-
File.write(main_file, <<~RUBY)
|
|
244
|
-
# frozen_string_literal: true
|
|
245
|
-
|
|
246
|
-
#--
|
|
247
|
-
# SPDX-FileCopyrightText: #{@config.year} #{@config.copyright_holder}
|
|
248
|
-
# SPDX-License-Identifier: #{@config.license}
|
|
249
|
-
#++
|
|
250
|
-
|
|
251
|
-
require_relative "#{@config.gem_name.split('-').last}/version"
|
|
252
|
-
|
|
253
|
-
# Entry point for the #{@config.gem_name} gem.
|
|
254
|
-
#
|
|
255
|
-
# Ruby libraries benefit from a clear namespace. Gems need a central module.
|
|
256
|
-
#
|
|
257
|
-
# This module serves as the namespace root. All classes and utilities live here.
|
|
258
|
-
#
|
|
259
|
-
# Require this file to load the library.
|
|
260
|
-
#{generate_nested_module(module_parts, '# Namespace for library functionality.')}
|
|
261
|
-
RUBY
|
|
262
|
-
# REUSE-IgnoreEnd
|
|
263
|
-
puts " Created lib/#{@config.require_path}.rb"
|
|
266
|
+
render_template("lib/main.rb.erb", "lib/#{@config.require_path}.rb")
|
|
264
267
|
|
|
265
268
|
# Version file
|
|
266
|
-
|
|
267
|
-
# REUSE-IgnoreStart
|
|
268
|
-
File.write(version_file, <<~RUBY)
|
|
269
|
-
# frozen_string_literal: true
|
|
269
|
+
render_template("lib/version.rb.erb", "lib/#{@config.require_path}/version.rb")
|
|
270
270
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
# SPDX-License-Identifier: #{@config.license}
|
|
274
|
-
#++
|
|
275
|
-
|
|
276
|
-
#{generate_nested_module_with_version(module_parts)}
|
|
277
|
-
RUBY
|
|
278
|
-
# REUSE-IgnoreEnd
|
|
279
|
-
puts " Created lib/#{@config.require_path}/version.rb"
|
|
271
|
+
# Test helper shipped inside the gem
|
|
272
|
+
render_template("lib/test_helper.rb.erb", "lib/#{@config.require_path}/test_helper.rb")
|
|
280
273
|
|
|
281
274
|
# Test directory with placeholder
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
# REUSE-IgnoreStart
|
|
285
|
-
File.write(File.join(test_path, "test_helper.rb"), <<~RUBY)
|
|
286
|
-
# frozen_string_literal: true
|
|
287
|
-
|
|
288
|
-
#--
|
|
289
|
-
# SPDX-FileCopyrightText: #{@config.year} #{@config.copyright_holder}
|
|
290
|
-
# SPDX-License-Identifier: #{@config.license}
|
|
291
|
-
#++
|
|
292
|
-
|
|
293
|
-
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
294
|
-
require "#{@config.require_path}"
|
|
295
|
-
require "minitest/autorun"
|
|
296
|
-
RUBY
|
|
297
|
-
# REUSE-IgnoreEnd
|
|
298
|
-
puts " Created test/test_helper.rb"
|
|
275
|
+
FileUtils.mkdir_p(File.join(@output_dir, "test"))
|
|
276
|
+
render_template("test/test_helper.rb.erb", "test/test_helper.rb")
|
|
299
277
|
end
|
|
300
278
|
|
|
301
279
|
private def create_rust_extension
|
|
302
280
|
ext_name = @config.gem_name.tr("-", "_")
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
# SPDX-FileCopyrightText: #{@config.year} #{@config.copyright_holder}
|
|
311
|
-
# SPDX-License-Identifier: #{@config.license}
|
|
312
|
-
|
|
313
|
-
[package]
|
|
314
|
-
name = "#{ext_name}"
|
|
315
|
-
version = "0.1.0"
|
|
316
|
-
edition = "2021"
|
|
317
|
-
|
|
318
|
-
[lib]
|
|
319
|
-
crate-type = ["cdylib", "staticlib"]
|
|
320
|
-
|
|
321
|
-
[dependencies]
|
|
322
|
-
magnus = "0.8"
|
|
323
|
-
ratatui = { version = "0.30", features = ["widget-calendar", "layout-cache", "unstable-rendered-line-info"] }
|
|
324
|
-
|
|
325
|
-
# Optional: Arena allocation to avoid Box::leak for long-lived Ruby objects
|
|
326
|
-
# bumpalo = "3.16"
|
|
327
|
-
# lazy_static = "1.4"
|
|
328
|
-
TOML
|
|
329
|
-
# REUSE-IgnoreEnd
|
|
330
|
-
puts " Created ext/#{ext_name}/Cargo.toml"
|
|
331
|
-
|
|
332
|
-
# extconf.rb
|
|
333
|
-
# REUSE-IgnoreStart
|
|
334
|
-
File.write(File.join(ext_path, "extconf.rb"), <<~RUBY)
|
|
335
|
-
# frozen_string_literal: true
|
|
336
|
-
|
|
337
|
-
#--
|
|
338
|
-
# SPDX-FileCopyrightText: #{@config.year} #{@config.copyright_holder}
|
|
339
|
-
# SPDX-License-Identifier: #{@config.license}
|
|
340
|
-
#++
|
|
341
|
-
|
|
342
|
-
require "mkmf"
|
|
343
|
-
require "rb_sys/mkmf"
|
|
344
|
-
|
|
345
|
-
create_rust_makefile("#{ext_name}/#{ext_name}") do |r|
|
|
346
|
-
# Optional: Force release profile if needed
|
|
347
|
-
# r.profile = ENV.fetch("RB_SYS_CARGO_PROFILE", :release).to_sym
|
|
348
|
-
|
|
349
|
-
# Force static linking on musl to avoid "cdylib" issues
|
|
350
|
-
if RbConfig::CONFIG["target_os"].include?("linux-musl") || RbConfig::CONFIG["host_os"].include?("linux-musl")
|
|
351
|
-
r.extra_rustc_args = ["--crate-type", "staticlib"]
|
|
352
|
-
else
|
|
353
|
-
r.extra_rustc_args = ["--crate-type", "cdylib"]
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
|
-
RUBY
|
|
357
|
-
# REUSE-IgnoreEnd
|
|
358
|
-
puts " Created ext/#{ext_name}/extconf.rb"
|
|
359
|
-
|
|
360
|
-
# .cargo/config.toml
|
|
361
|
-
# REUSE-IgnoreStart
|
|
362
|
-
File.write(File.join(cargo_path, "config.toml"), <<~TOML)
|
|
363
|
-
# SPDX-FileCopyrightText: #{@config.year} #{@config.copyright_holder}
|
|
364
|
-
# SPDX-License-Identifier: #{@config.license}
|
|
365
|
-
|
|
366
|
-
[target.aarch64-apple-darwin]
|
|
367
|
-
rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
|
|
368
|
-
|
|
369
|
-
[target.x86_64-apple-darwin]
|
|
370
|
-
rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
|
|
371
|
-
|
|
372
|
-
# Force dynamic linking on Alpine (musl) to fix "Dynamic loading not supported"
|
|
373
|
-
# errors when bindgen tries to load libclang.
|
|
374
|
-
[target.'cfg(target_env = "musl")']
|
|
375
|
-
rustflags = ["-C", "target-feature=-crt-static"]
|
|
376
|
-
TOML
|
|
377
|
-
# REUSE-IgnoreEnd
|
|
378
|
-
puts " Created ext/#{ext_name}/.cargo/config.toml"
|
|
379
|
-
|
|
380
|
-
# .gitignore
|
|
381
|
-
# REUSE-IgnoreStart
|
|
382
|
-
File.write(File.join(ext_path, ".gitignore"), <<~GITIGNORE)
|
|
383
|
-
# SPDX-FileCopyrightText: #{@config.year} #{@config.copyright_holder}
|
|
384
|
-
# SPDX-License-Identifier: #{@config.license}
|
|
385
|
-
|
|
386
|
-
/target/
|
|
387
|
-
GITIGNORE
|
|
388
|
-
# REUSE-IgnoreEnd
|
|
389
|
-
puts " Created ext/#{ext_name}/.gitignore"
|
|
390
|
-
|
|
391
|
-
# clippy.toml
|
|
392
|
-
# REUSE-IgnoreStart
|
|
393
|
-
File.write(File.join(ext_path, "clippy.toml"), <<~TOML)
|
|
394
|
-
# SPDX-FileCopyrightText: #{@config.year} #{@config.copyright_holder}
|
|
395
|
-
# SPDX-License-Identifier: #{@config.license}
|
|
396
|
-
|
|
397
|
-
disallowed-methods = [
|
|
398
|
-
{ path = "std::boxed::Box::leak", reason = "Use bumpalo arena allocation instead." },
|
|
399
|
-
{ path = "std::vec::Vec::leak", reason = "Use bumpalo arena allocation instead." },
|
|
400
|
-
]
|
|
401
|
-
TOML
|
|
402
|
-
# REUSE-IgnoreEnd
|
|
403
|
-
puts " Created ext/#{ext_name}/clippy.toml"
|
|
404
|
-
|
|
405
|
-
# src/lib.rs stub
|
|
406
|
-
src_path = File.join(ext_path, "src")
|
|
407
|
-
FileUtils.mkdir_p(src_path)
|
|
408
|
-
# REUSE-IgnoreStart
|
|
409
|
-
File.write(File.join(src_path, "lib.rs"), <<~RUST)
|
|
410
|
-
// SPDX-FileCopyrightText: #{@config.year} #{@config.copyright_holder}
|
|
411
|
-
// SPDX-License-Identifier: #{@config.license}
|
|
412
|
-
|
|
413
|
-
use magnus::{define_module, prelude::*, Error};
|
|
414
|
-
|
|
415
|
-
#[magnus::init]
|
|
416
|
-
fn init() -> Result<(), Error> {
|
|
417
|
-
let module = define_module("#{@config.module_name.split('::').first}")?;
|
|
418
|
-
// Add your native methods here
|
|
419
|
-
Ok(())
|
|
420
|
-
}
|
|
421
|
-
RUST
|
|
422
|
-
# REUSE-IgnoreEnd
|
|
423
|
-
puts " Created ext/#{ext_name}/src/lib.rs"
|
|
281
|
+
|
|
282
|
+
render_template("ext/Cargo.toml.erb", "ext/#{ext_name}/Cargo.toml")
|
|
283
|
+
render_template("ext/extconf.rb.erb", "ext/#{ext_name}/extconf.rb")
|
|
284
|
+
render_template("ext/.cargo/config.toml.erb", "ext/#{ext_name}/.cargo/config.toml")
|
|
285
|
+
render_template("ext/.gitignore.erb", "ext/#{ext_name}/.gitignore")
|
|
286
|
+
render_template("ext/clippy.toml.erb", "ext/#{ext_name}/clippy.toml")
|
|
287
|
+
render_template("ext/src/lib.rs.erb", "ext/#{ext_name}/src/lib.rs")
|
|
424
288
|
end
|
|
425
289
|
|
|
426
290
|
private def generate_nested_module(parts, content, indent = 0, is_outermost = true)
|
|
@@ -474,7 +338,7 @@ class TemplateRenderer
|
|
|
474
338
|
#{prefix}module #{parts.first}
|
|
475
339
|
#{prefix} # The version of this gem.
|
|
476
340
|
#{prefix} # See https://semver.org/spec/v2.0.0.html
|
|
477
|
-
#{prefix} VERSION = "0.1.
|
|
341
|
+
#{prefix} VERSION = "0.0.1.pre.1"
|
|
478
342
|
#{prefix}end
|
|
479
343
|
RUBY
|
|
480
344
|
else
|
|
@@ -488,9 +352,13 @@ class TemplateRenderer
|
|
|
488
352
|
end
|
|
489
353
|
|
|
490
354
|
private def binding_for_config
|
|
355
|
+
module_parts = @config.module_name.split("::")
|
|
356
|
+
ext_name = @config.gem_name.tr("-", "_")
|
|
357
|
+
|
|
491
358
|
# Create a binding with all config values available to templates
|
|
492
359
|
b = binding
|
|
493
360
|
b.local_variable_set(:gem_name, @config.gem_name)
|
|
361
|
+
b.local_variable_set(:gem_display_name, @config.module_name)
|
|
494
362
|
b.local_variable_set(:module_name, @config.module_name)
|
|
495
363
|
b.local_variable_set(:require_path, @config.require_path)
|
|
496
364
|
b.local_variable_set(:copyright_holder, @config.copyright_holder)
|
|
@@ -500,9 +368,16 @@ class TemplateRenderer
|
|
|
500
368
|
b.local_variable_set(:year, @config.year)
|
|
501
369
|
b.local_variable_set(:has_rdoc, true)
|
|
502
370
|
b.local_variable_set(:has_rust, @config.has_rust)
|
|
371
|
+
b.local_variable_set(:rubies, @config.ruby_versions)
|
|
372
|
+
b.local_variable_set(:ext_name, ext_name)
|
|
373
|
+
b.local_variable_set(:nested_module, generate_nested_module(module_parts, "# Namespace for library functionality."))
|
|
374
|
+
b.local_variable_set(:nested_module_with_version, generate_nested_module_with_version(module_parts))
|
|
375
|
+
b.local_variable_set(:update_mode, @update_mode)
|
|
503
376
|
b.local_variable_set(:dependencies, [])
|
|
504
377
|
b.local_variable_set(:summary, "Part of the RatatuiRuby ecosystem")
|
|
505
378
|
b.local_variable_set(:description, "#{@config.module_name} - part of the RatatuiRuby TUI framework ecosystem")
|
|
379
|
+
b.local_variable_set(:github_repo, "setdef/#{@config.module_name}")
|
|
380
|
+
b.local_variable_set(:project_name, @config.module_name)
|
|
506
381
|
b.local_variable_set(:version_file_require, "lib/#{@config.require_path}/version")
|
|
507
382
|
b.local_variable_set(:version_file, "lib/#{@config.require_path}/version.rb")
|
|
508
383
|
b
|
|
@@ -513,6 +388,7 @@ end
|
|
|
513
388
|
config = ScaffoldConfig.new
|
|
514
389
|
parent_dir = Dir.pwd
|
|
515
390
|
force = false
|
|
391
|
+
update_mode = false
|
|
516
392
|
|
|
517
393
|
OptionParser.new do |opts|
|
|
518
394
|
opts.banner = "Usage: scaffold GEM_NAME [OPTIONS]"
|
|
@@ -537,6 +413,10 @@ OptionParser.new do |opts|
|
|
|
537
413
|
force = true
|
|
538
414
|
end
|
|
539
415
|
|
|
416
|
+
opts.on("-u", "--update", "Update existing project (diff + prompt for conflicts)") do
|
|
417
|
+
update_mode = true
|
|
418
|
+
end
|
|
419
|
+
|
|
540
420
|
opts.on("--rust", "Include Rust native extension support") do
|
|
541
421
|
config.has_rust = true
|
|
542
422
|
end
|
|
@@ -557,12 +437,14 @@ config.gem_name = ARGV[0]
|
|
|
557
437
|
config.output_dir = File.join(parent_dir, config.gem_name)
|
|
558
438
|
|
|
559
439
|
if File.exist?(config.output_dir)
|
|
560
|
-
if
|
|
440
|
+
if update_mode
|
|
441
|
+
puts "Updating existing project..."
|
|
442
|
+
elsif force
|
|
561
443
|
FileUtils.rm_rf(config.output_dir)
|
|
562
444
|
puts "Removed existing directory: #{config.output_dir}"
|
|
563
445
|
else
|
|
564
446
|
warn "Error: Directory already exists: #{config.output_dir}"
|
|
565
|
-
warn "Use --force to remove it"
|
|
447
|
+
warn "Use --force to remove it, or --update to update in-place"
|
|
566
448
|
exit 1
|
|
567
449
|
end
|
|
568
450
|
end
|
|
@@ -587,76 +469,81 @@ puts " Author: #{config.copyright_holder}"
|
|
|
587
469
|
puts " License: #{config.license}"
|
|
588
470
|
puts ""
|
|
589
471
|
|
|
590
|
-
renderer = TemplateRenderer.new(config, templates_dir, config.output_dir)
|
|
472
|
+
renderer = TemplateRenderer.new(config, templates_dir, config.output_dir, update_mode:)
|
|
591
473
|
renderer.render_all
|
|
592
474
|
|
|
593
|
-
|
|
594
|
-
puts ""
|
|
595
|
-
puts "
|
|
596
|
-
|
|
597
|
-
# Initialize git with
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
puts "
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
475
|
+
if update_mode
|
|
476
|
+
puts ""
|
|
477
|
+
puts "Done! Updated templates at: #{config.output_dir}"
|
|
478
|
+
else
|
|
479
|
+
# Initialize git repo with proper branch structure
|
|
480
|
+
puts ""
|
|
481
|
+
puts "Initializing git repository..."
|
|
482
|
+
Dir.chdir(config.output_dir) do
|
|
483
|
+
# Initialize git with trunk as the default branch (not main/master)
|
|
484
|
+
system("git init --quiet --initial-branch=trunk")
|
|
485
|
+
|
|
486
|
+
# Add remote origin for GitHub
|
|
487
|
+
remote_url = "git@github.com:setdef/#{config.module_name}.git"
|
|
488
|
+
system("git remote add origin #{remote_url}")
|
|
489
|
+
puts " Set remote origin: #{remote_url}"
|
|
490
|
+
|
|
491
|
+
# Initial add (before setup runs)
|
|
492
|
+
system("git add -A")
|
|
493
|
+
end
|
|
494
|
+
puts " Initialized git with trunk branch"
|
|
495
|
+
|
|
496
|
+
# Run setup (installs gems, pre-commit, downloads licenses, etc.)
|
|
497
|
+
puts ""
|
|
498
|
+
puts "Running bin/setup..."
|
|
499
|
+
Dir.chdir(config.output_dir) do
|
|
500
|
+
system("mise trust --yes --raw")
|
|
501
|
+
system("bin/setup")
|
|
502
|
+
end
|
|
617
503
|
|
|
618
|
-
# Copy LICENSE from primary license for gem distribution
|
|
619
|
-
puts ""
|
|
620
|
-
puts "Setting up LICENSE..."
|
|
621
|
-
Dir.chdir(config.output_dir) do
|
|
622
|
-
|
|
623
|
-
if File.exist?(license_file)
|
|
624
|
-
FileUtils.cp(license_file, "LICENSE")
|
|
625
|
-
puts " Copied #{license_file} to LICENSE"
|
|
626
|
-
else
|
|
627
|
-
system("mise x -- reuse download #{config.license}")
|
|
504
|
+
# Copy LICENSE from primary license for gem distribution
|
|
505
|
+
puts ""
|
|
506
|
+
puts "Setting up LICENSE..."
|
|
507
|
+
Dir.chdir(config.output_dir) do
|
|
508
|
+
license_file = "LICENSES/#{config.license}.txt"
|
|
628
509
|
if File.exist?(license_file)
|
|
629
510
|
FileUtils.cp(license_file, "LICENSE")
|
|
630
|
-
puts "
|
|
511
|
+
puts " Copied #{license_file} to LICENSE"
|
|
631
512
|
else
|
|
632
|
-
|
|
513
|
+
system("mise x -- reuse download #{config.license}")
|
|
514
|
+
if File.exist?(license_file)
|
|
515
|
+
FileUtils.cp(license_file, "LICENSE")
|
|
516
|
+
puts " Downloaded and copied #{config.license} to LICENSE"
|
|
517
|
+
else
|
|
518
|
+
warn " WARNING: Could not find or download license: #{config.license}"
|
|
519
|
+
end
|
|
633
520
|
end
|
|
634
521
|
end
|
|
635
|
-
end
|
|
636
522
|
|
|
637
|
-
# Create initial commit and tags
|
|
638
|
-
puts ""
|
|
639
|
-
puts "Creating initial commit and tags..."
|
|
640
|
-
Dir.chdir(config.output_dir) do
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
# Create tag and stable branch
|
|
651
|
-
system("git tag v0.1.0 -m 'Initial scaffold release'")
|
|
652
|
-
system("git branch stable")
|
|
653
|
-
puts " Created branches: trunk (default), stable"
|
|
654
|
-
puts " Created tag: v0.1.0"
|
|
655
|
-
end
|
|
523
|
+
# Create initial commit and tags
|
|
524
|
+
puts ""
|
|
525
|
+
puts "Creating initial commit and tags..."
|
|
526
|
+
Dir.chdir(config.output_dir) do
|
|
527
|
+
# Stage ALL files including those created by bin/setup
|
|
528
|
+
system("git add -A")
|
|
529
|
+
|
|
530
|
+
# Commit (don't use --quiet so we see any errors)
|
|
531
|
+
unless system("git commit -m 'chore: scaffold #{config.gem_name} with ratatui_ruby-devtools'")
|
|
532
|
+
warn " WARNING: Initial commit failed, retrying with --allow-empty"
|
|
533
|
+
system("git commit --allow-empty -m 'chore: scaffold #{config.gem_name} with ratatui_ruby-devtools'")
|
|
534
|
+
end
|
|
656
535
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
puts "
|
|
661
|
-
puts "
|
|
662
|
-
|
|
536
|
+
# Create tag and stable branch
|
|
537
|
+
system("git tag v0.0.1.pre.1 -m 'Initial scaffold release'")
|
|
538
|
+
system("git branch stable")
|
|
539
|
+
puts " Created branches: trunk (default), stable"
|
|
540
|
+
puts " Created tag: v0.0.1.pre.1"
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
puts ""
|
|
544
|
+
puts "Done! Your gem is ready at: #{config.output_dir}"
|
|
545
|
+
puts ""
|
|
546
|
+
puts "Next steps:"
|
|
547
|
+
puts " cd #{config.gem_name}"
|
|
548
|
+
puts " bundle exec rake"
|
|
549
|
+
end
|
|
@@ -34,7 +34,7 @@ namespace :lint do
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Build dynamic task lists based on what's available
|
|
37
|
-
docs_tasks = %w[rubycritic
|
|
37
|
+
docs_tasks = %w[rubycritic]
|
|
38
38
|
docs_tasks.unshift("autodoc") if Rake::Task.task_defined?("autodoc")
|
|
39
39
|
docs_tasks << "safe_rdoc_coverage" if Rake::Task.task_defined?("rdoc:coverage")
|
|
40
40
|
|
|
@@ -43,8 +43,7 @@ namespace :lint do
|
|
|
43
43
|
|
|
44
44
|
task docs: docs_tasks
|
|
45
45
|
task code: code_tasks
|
|
46
|
-
task
|
|
47
|
-
task all: %w[docs code licenses]
|
|
46
|
+
task all: %w[docs code]
|
|
48
47
|
|
|
49
48
|
namespace :fix do
|
|
50
49
|
desc "Auto-fix RuboCop offenses (most aggressive)"
|
|
@@ -62,11 +61,8 @@ namespace :lint do
|
|
|
62
61
|
end
|
|
63
62
|
end
|
|
64
63
|
|
|
65
|
-
desc "Add SPDX headers and normalize Ruby file structure"
|
|
66
|
-
task reuse: %w[reuse:fix reuse:normalize_ruby]
|
|
67
|
-
|
|
68
64
|
# Build dynamic fix:all task
|
|
69
|
-
fix_all_tasks = %w[lint:fix:rubocop
|
|
65
|
+
fix_all_tasks = %w[lint:fix:rubocop]
|
|
70
66
|
fix_all_tasks.insert(1, "lint:fix:clippy") if Dir.exist?("ext")
|
|
71
67
|
|
|
72
68
|
desc "Run all auto-fix tasks"
|