foobar_templates 2.0.1.rc1 → 2.0.1.rc3
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/bin/foobar_templates +4 -0
- data/changelog +1 -0
- data/config/config +2 -2
- data/lib/foobar_templates/cli/template_generator.rb +120 -61
- data/lib/foobar_templates/version.rb +1 -1
- data/spec/foobar_templates_spec.rb +40 -0
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6f830e123079c5b60a6cd7f799df40f0615b6326565814e837029ea6a8d69e9
|
|
4
|
+
data.tar.gz: 8d19d2bad9ee276d489657f9de11ef0ac8e9929a92d7fa08d95b2f9e609bc9d4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2e2abe9daa3749abab39f10fbb110b8262541fd2ebba6f74d4c6d0bf85fe97d3e05a6745f05cb05fbb27db532c2c2d99945afe028e90dba066335648669ad998
|
|
7
|
+
data.tar.gz: df73ec40e11c12ac8316ab4c6105ac0d9ea895f2ca049b6b676b44660d7656df8041411ef38775d30e98f1d5a6909c2e4cb70e0210769b94d69eebfa6ffa7025
|
data/bin/foobar_templates
CHANGED
|
@@ -29,6 +29,10 @@ parser = OptionParser.new do |opts|
|
|
|
29
29
|
exit
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
opts.on("-p", "--performance", "Show performance metrics for template generation") do
|
|
33
|
+
options[:performance] = true
|
|
34
|
+
end
|
|
35
|
+
|
|
32
36
|
opts.on("--install-public-templates", "Install public templates") do
|
|
33
37
|
FoobarTemplates.install_public_templates
|
|
34
38
|
exit
|
data/changelog
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
** Unreleased **
|
|
2
2
|
- Rename: project renamed from `bundlegem` to `foobar_templates`. Module is now `FoobarTemplates`, executable is `foobar_templates`, per-template config file is `foobar.yml`, and user config/templates live under `~/.foobar/`. No backward-compatibility shims.
|
|
3
|
+
- Bugfix: fixes `Arg list too long` crash when generating from templates containing many gitignored files (e.g. node_modules); ignored directories are now pruned during a breadth-first traversal and path checks are streamed to `git check-ignore` via NUL-delimited stdin instead of argv.
|
|
3
4
|
|
|
4
5
|
** Planned 0.2.x **
|
|
5
6
|
- Feature: templates can declare a `name_validation:` block in bundlegem.yml with two optional keys: `reserved_names` (exact-match denylist) and `regex_validator` (a Ruby regex the project name must match). Validation runs after the built-in regex check and before any files are written. All checks happen in pure Ruby — no shell, no cross-platform concerns.
|
data/config/config
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'pathname'
|
|
2
2
|
require 'yaml'
|
|
3
3
|
require 'open3'
|
|
4
|
-
require 'shellwords'
|
|
5
4
|
require 'set'
|
|
6
5
|
|
|
7
6
|
$TRACE = false
|
|
@@ -24,53 +23,67 @@ module FoobarTemplates::CLI
|
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
def config
|
|
27
|
-
@config ||= build_interpolation_config
|
|
26
|
+
@config ||= time_it("build_interpolation_config") { build_interpolation_config }
|
|
28
27
|
end
|
|
29
28
|
|
|
30
29
|
def run
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
time_it("TOTAL run") do
|
|
31
|
+
puts "Beginning run" if $TRACE
|
|
32
|
+
raise_project_with_that_name_already_exists! if File.exist?(target)
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
puts "ensure_safe_project_name" if $TRACE
|
|
35
|
+
time_it("ensure_safe_project_name") do
|
|
36
|
+
ensure_safe_project_name(name, config[:constant_array])
|
|
37
|
+
end
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
puts "run_name_validation" if $TRACE
|
|
40
|
+
time_it("run_name_validation") { run_name_validation }
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
template_src = time_it("match_template_src") { match_template_src }
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
puts "dynamically_generate_template_directories" if $TRACE
|
|
45
|
+
@template_directories = time_it("dynamically_generate_template_directories") do
|
|
46
|
+
dynamically_generate_template_directories
|
|
47
|
+
end
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
puts "dynamically_generate_templates_files" if $TRACE
|
|
50
|
+
templates = time_it("dynamically_generate_templates_files") do
|
|
51
|
+
dynamically_generate_templates_files
|
|
52
|
+
end
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
puts "Creating new project folder '#{name}'\n\n"
|
|
55
|
+
time_it("create_template_directories") do
|
|
56
|
+
create_template_directories(@template_directories, target)
|
|
57
|
+
end
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
time_it("write_template_files") do
|
|
60
|
+
templates.each do |src, dst|
|
|
61
|
+
template("#{template_src}/#{src}", target.join(dst), config)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
time_it("git_init_and_add") do
|
|
66
|
+
Dir.chdir(target) do
|
|
67
|
+
if @configurator.always_perform_git_init || !inside_git_work_tree?
|
|
68
|
+
`git init`
|
|
69
|
+
end
|
|
70
|
+
`git add .`
|
|
71
|
+
end
|
|
60
72
|
end
|
|
61
|
-
`git add .`
|
|
62
|
-
end
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
if @tconf[:bootstrap_command]
|
|
75
|
+
puts "Executing bootstrap_command"
|
|
76
|
+
cmd = safe_gsub_template_variables(@tconf[:bootstrap_command])
|
|
77
|
+
puts cmd
|
|
78
|
+
time_it("bootstrap_command") do
|
|
79
|
+
Dir.chdir(target) do
|
|
80
|
+
`#{cmd}`
|
|
81
|
+
end
|
|
82
|
+
end
|
|
70
83
|
end
|
|
71
|
-
end
|
|
72
84
|
|
|
73
|
-
|
|
85
|
+
puts "\nComplete."
|
|
86
|
+
end
|
|
74
87
|
end
|
|
75
88
|
|
|
76
89
|
def build_interpolation_config
|
|
@@ -85,7 +98,9 @@ module FoobarTemplates::CLI
|
|
|
85
98
|
git_user_email = `git config user.email`.chomp
|
|
86
99
|
|
|
87
100
|
# Resolve domain values from ~/.foobar/config, prompting if needed
|
|
88
|
-
required_domains = scan_template_for_required_domains
|
|
101
|
+
required_domains = time_it("scan_template_for_required_domains") do
|
|
102
|
+
scan_template_for_required_domains
|
|
103
|
+
end
|
|
89
104
|
prompt_for_missing_domains(required_domains)
|
|
90
105
|
|
|
91
106
|
registry_domain = @configurator.domain('registry_domain')
|
|
@@ -271,39 +286,69 @@ module FoobarTemplates::CLI
|
|
|
271
286
|
|
|
272
287
|
# Returns a hash of source directory names and their destination mappings
|
|
273
288
|
def dynamically_generate_template_directories
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
next if base_path.start_with?(".git" + File::SEPARATOR) || base_path == ".git"
|
|
277
|
-
next if f == "#{@template_src}/." || f == "#{@template_src}/.."
|
|
278
|
-
next unless File.directory?(f)
|
|
279
|
-
# next if ignored_by_git?(@template_src, base_path)
|
|
289
|
+
template_relative_paths.each_with_object({}) do |rel, dirs|
|
|
290
|
+
next unless File.directory?(File.join(@template_src, rel))
|
|
280
291
|
|
|
281
|
-
[
|
|
282
|
-
end
|
|
283
|
-
filter_ignored_files!(@template_src, template_dirs)
|
|
284
|
-
|
|
285
|
-
template_dirs
|
|
292
|
+
dirs[rel] = substitute_template_values(rel)
|
|
293
|
+
end
|
|
286
294
|
end
|
|
287
295
|
|
|
288
296
|
# Figures out the translation between all template files and their
|
|
289
297
|
# destination names
|
|
290
298
|
def dynamically_generate_templates_files
|
|
291
|
-
template_files =
|
|
292
|
-
|
|
293
|
-
next
|
|
294
|
-
next if base_path.start_with?(".git" + File::SEPARATOR) || base_path == ".git"
|
|
295
|
-
next if base_path == "foobar.yml"
|
|
296
|
-
next unless File.file?(f)
|
|
299
|
+
template_files = template_relative_paths.each_with_object({}) do |rel, files|
|
|
300
|
+
next if rel == "foobar.yml"
|
|
301
|
+
next unless File.file?(File.join(@template_src, rel))
|
|
297
302
|
|
|
298
|
-
[
|
|
299
|
-
end
|
|
303
|
+
files[rel] = substitute_template_values(rel)
|
|
304
|
+
end
|
|
300
305
|
|
|
301
306
|
raise_no_files_in_template_error! if template_files.empty?
|
|
302
|
-
filter_ignored_files!(@template_src, template_files)
|
|
303
307
|
|
|
304
308
|
return template_files
|
|
305
309
|
end
|
|
306
310
|
|
|
311
|
+
# Enumerates every relative path under the template source, skipping the
|
|
312
|
+
# .git directory and any gitignored paths. Ignored directories are pruned
|
|
313
|
+
# during traversal so their (potentially huge) contents are never walked.
|
|
314
|
+
def template_relative_paths
|
|
315
|
+
@template_relative_paths ||= time_it("collect_non_ignored_paths") do
|
|
316
|
+
collect_non_ignored_paths(@template_src)
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Breadth-first walk that prunes ignored directories. One batched
|
|
321
|
+
# `git check-ignore` call is made per directory depth level, so we never
|
|
322
|
+
# descend into (or enumerate) an ignored subtree such as node_modules.
|
|
323
|
+
def collect_non_ignored_paths(root)
|
|
324
|
+
results = []
|
|
325
|
+
frontier = [nil] # relative dirs to scan at the current level; nil == root
|
|
326
|
+
|
|
327
|
+
until frontier.empty?
|
|
328
|
+
level_children = []
|
|
329
|
+
frontier.each do |rel_dir|
|
|
330
|
+
abs_dir = rel_dir ? File.join(root, rel_dir) : root
|
|
331
|
+
Dir.children(abs_dir).each do |name|
|
|
332
|
+
next if name == ".git"
|
|
333
|
+
|
|
334
|
+
level_children << (rel_dir ? File.join(rel_dir, name) : name)
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
break if level_children.empty?
|
|
338
|
+
|
|
339
|
+
ignored = ignored_paths(root, level_children)
|
|
340
|
+
next_frontier = []
|
|
341
|
+
level_children.each do |rel|
|
|
342
|
+
next if ignored.include?(rel)
|
|
343
|
+
|
|
344
|
+
results << rel
|
|
345
|
+
next_frontier << rel if File.directory?(File.join(root, rel))
|
|
346
|
+
end
|
|
347
|
+
frontier = next_frontier
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
results
|
|
351
|
+
end
|
|
307
352
|
|
|
308
353
|
# Applies literal foo-bar variant substitutions to path strings
|
|
309
354
|
def substitute_template_values(path_str)
|
|
@@ -351,12 +396,19 @@ module FoobarTemplates::CLI
|
|
|
351
396
|
chunk.nil? || chunk.include?("\x00")
|
|
352
397
|
end
|
|
353
398
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
399
|
+
# Returns the subset of the given relative paths that git considers ignored.
|
|
400
|
+
# Paths are streamed via NUL-delimited stdin rather than argv to avoid the
|
|
401
|
+
# OS ARG_MAX limit ("Arg list too long") and to handle paths containing
|
|
402
|
+
# spaces or newlines. Returns an empty set when root is not a git repo.
|
|
403
|
+
def ignored_paths(root, rel_paths)
|
|
404
|
+
return Set.new if rel_paths.empty?
|
|
405
|
+
|
|
406
|
+
stdin_data = rel_paths.join("\x00")
|
|
407
|
+
stdout, _, _status = Open3.capture3(
|
|
408
|
+
"git", "-C", root.to_s, "check-ignore", "-z", "--stdin",
|
|
409
|
+
stdin_data: stdin_data
|
|
410
|
+
)
|
|
411
|
+
stdout.split("\x00").to_set
|
|
360
412
|
end
|
|
361
413
|
|
|
362
414
|
def create_template_directories(template_directories, target)
|
|
@@ -442,11 +494,18 @@ Exiting...
|
|
|
442
494
|
end
|
|
443
495
|
|
|
444
496
|
def time_it(label = nil)
|
|
497
|
+
return yield unless performance?
|
|
498
|
+
|
|
445
499
|
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
446
|
-
yield
|
|
500
|
+
result = yield
|
|
447
501
|
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
448
502
|
elapsed_ms = ((end_time - start_time) * 1000).round(2)
|
|
449
503
|
puts "#{label || 'Elapsed'}: #{elapsed_ms} ms"
|
|
504
|
+
result
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
def performance?
|
|
508
|
+
@options[:performance]
|
|
450
509
|
end
|
|
451
510
|
|
|
452
511
|
# This checks to see that the gem_name is a valid ruby gem name and will 'work'
|
|
@@ -128,6 +128,26 @@ describe FoobarTemplates do
|
|
|
128
128
|
expect(src_dst_map['simple_dir']).to eq 'simple_dir'
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
+
it "collect_non_ignored_paths walks files but prunes gitignored directories" do
|
|
132
|
+
template_dir = create_user_defined_template("testing", "template-user-supplied")
|
|
133
|
+
options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
|
|
134
|
+
my_gem = FoobarTemplates::CLI::TemplateGenerator.new(options, "good-dog")
|
|
135
|
+
|
|
136
|
+
File.write("#{template_dir}/.gitignore", "node_modules/\n")
|
|
137
|
+
File.write("#{template_dir}/README.md", "Hello")
|
|
138
|
+
FileUtils.mkdir_p("#{template_dir}/src")
|
|
139
|
+
File.write("#{template_dir}/src/main.rb", "puts 1")
|
|
140
|
+
FileUtils.mkdir_p("#{template_dir}/node_modules/big_pkg")
|
|
141
|
+
File.write("#{template_dir}/node_modules/big_pkg/index.js", "ignored")
|
|
142
|
+
`git init #{template_dir}`
|
|
143
|
+
|
|
144
|
+
paths = my_gem.send('collect_non_ignored_paths', template_dir)
|
|
145
|
+
|
|
146
|
+
expect(paths).to include("README.md", "src", "src/main.rb")
|
|
147
|
+
expect(paths).not_to include("node_modules")
|
|
148
|
+
expect(paths.grep(/node_modules/)).to be_empty
|
|
149
|
+
end
|
|
150
|
+
|
|
131
151
|
it "returns the expected interpolated string when substitute_template_values is called" do
|
|
132
152
|
options = { bin: false, ext: false, coc: false, template: "test_template" }
|
|
133
153
|
gem_name = "good-dog"
|
|
@@ -173,6 +193,26 @@ describe FoobarTemplates do
|
|
|
173
193
|
expect(File).not_to exist "#{@dst_dir}/#{gem_name}/node_modules"
|
|
174
194
|
end
|
|
175
195
|
|
|
196
|
+
it "handles templates with a large number of gitignored files without overflowing the arg list" do
|
|
197
|
+
template_dir = create_user_defined_template("testing", "template-user-supplied")
|
|
198
|
+
options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
|
|
199
|
+
gem_name = "good-dog"
|
|
200
|
+
|
|
201
|
+
File.write("#{template_dir}/.gitignore", "node_modules/")
|
|
202
|
+
File.write("#{template_dir}/README.md", "Hello")
|
|
203
|
+
FileUtils.mkdir("#{template_dir}/node_modules")
|
|
204
|
+
# Enough files to blow past ARG_MAX if paths were passed as argv.
|
|
205
|
+
5000.times { |i| File.write("#{template_dir}/node_modules/file_#{i}.rb", "ignored #{i}") }
|
|
206
|
+
`git init #{template_dir}`
|
|
207
|
+
|
|
208
|
+
capture_stdout do
|
|
209
|
+
FoobarTemplates.generate_template(options, gem_name)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
expect(File).to exist "#{@dst_dir}/#{gem_name}/README.md"
|
|
213
|
+
expect(File).not_to exist "#{@dst_dir}/#{gem_name}/node_modules"
|
|
214
|
+
end
|
|
215
|
+
|
|
176
216
|
it "executes the bootstrap_command if supplied" do
|
|
177
217
|
template_dir = create_user_defined_template("testing", "template-user-supplied")
|
|
178
218
|
options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foobar_templates
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.1.
|
|
4
|
+
version: 2.0.1.rc3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- TheNotary
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rake
|
|
@@ -130,9 +130,9 @@ licenses:
|
|
|
130
130
|
- MIT
|
|
131
131
|
metadata:
|
|
132
132
|
bug_tracker_uri: https://github.com/TheNotary/foobar_templates/issues
|
|
133
|
-
changelog_uri: https://github.com/TheNotary/foobar_templates/releases/tag/v2.0.1.
|
|
133
|
+
changelog_uri: https://github.com/TheNotary/foobar_templates/releases/tag/v2.0.1.rc3
|
|
134
134
|
documentation_uri: https://github.com/TheNotary/foobar_templates
|
|
135
|
-
source_code_uri: https://github.com/TheNotary/foobar_templates/tree/v2.0.1.
|
|
135
|
+
source_code_uri: https://github.com/TheNotary/foobar_templates/tree/v2.0.1.rc3
|
|
136
136
|
rdoc_options: []
|
|
137
137
|
require_paths:
|
|
138
138
|
- lib
|
|
@@ -147,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
147
147
|
- !ruby/object:Gem::Version
|
|
148
148
|
version: '0'
|
|
149
149
|
requirements: []
|
|
150
|
-
rubygems_version:
|
|
150
|
+
rubygems_version: 4.0.10
|
|
151
151
|
specification_version: 4
|
|
152
152
|
summary: This gem makes more gems!
|
|
153
153
|
test_files:
|