foobar_templates 2.0.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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.beader/.gitignore +6 -0
  3. data/.beader/issues/1-support-flexible-template-topologies.yaml +21 -0
  4. data/.beader/issues/2-add-monorepo-support-for-template-discovery-and-generation.yaml +33 -0
  5. data/.beader/issues/3-implement-monorepo-aware-recursive-template-inventory.yaml +29 -0
  6. data/.beader/issues/4-add-leaf-template-selection-with-ambiguity-and-not-found-handling.yaml +30 -0
  7. data/.beader/issues/5-integrate-leaf-template-resolution-into-generation-flow.yaml +30 -0
  8. data/.beader/issues/6-add-monorepo-discovery-and-generation-test-coverage.yaml +31 -0
  9. data/.beader/issues/7-document-monorepo-template-configuration-and-selection.yaml +30 -0
  10. data/.beader/meta.yaml +1 -0
  11. data/.gitignore +16 -0
  12. data/.rspec +2 -0
  13. data/Gemfile +3 -0
  14. data/LICENSE.txt +22 -0
  15. data/README.md +154 -0
  16. data/Rakefile +55 -0
  17. data/bin/foobar_templates +72 -0
  18. data/changelog +107 -0
  19. data/config/config +8 -0
  20. data/foobar_templates.gemspec +33 -0
  21. data/lib/foobar_templates/cli/cheat_sheet.rb +12 -0
  22. data/lib/foobar_templates/cli/cli.rb +3 -0
  23. data/lib/foobar_templates/cli/dir_to_template.rb +120 -0
  24. data/lib/foobar_templates/cli/setup_personal_templates_repo.rb +135 -0
  25. data/lib/foobar_templates/cli/template_generator.rb +462 -0
  26. data/lib/foobar_templates/configurator.rb +99 -0
  27. data/lib/foobar_templates/core/core.rb +9 -0
  28. data/lib/foobar_templates/core/dir_to_template.rb +114 -0
  29. data/lib/foobar_templates/strings.rb +23 -0
  30. data/lib/foobar_templates/template_manager.rb +119 -0
  31. data/lib/foobar_templates/templates/template-test/.vscode/launch.json +0 -0
  32. data/lib/foobar_templates/templates/template-test/foo-bar/keep +0 -0
  33. data/lib/foobar_templates/templates/template-test/foo-bar.rb +16 -0
  34. data/lib/foobar_templates/templates/template-test/foo_bar/keep +0 -0
  35. data/lib/foobar_templates/templates/template-test/foobar.yml +2 -0
  36. data/lib/foobar_templates/templates/template-test/simple_dir/keep +0 -0
  37. data/lib/foobar_templates/templates/template-test/test_confirmed +0 -0
  38. data/lib/foobar_templates/templates/test_template/foo-bar/keep +0 -0
  39. data/lib/foobar_templates/templates/test_template/foo-bar.rb +21 -0
  40. data/lib/foobar_templates/templates/test_template/foo_bar/keep +0 -0
  41. data/lib/foobar_templates/templates/test_template/foobar.yml +2 -0
  42. data/lib/foobar_templates/templates/test_template/simple_dir/keep +0 -0
  43. data/lib/foobar_templates/version.rb +3 -0
  44. data/lib/foobar_templates.rb +151 -0
  45. data/spec/data/variable_manifest_test.rb +21 -0
  46. data/spec/foobar_templates/cli/dir_to_template_spec.rb +153 -0
  47. data/spec/foobar_templates/core/dir_to_template_spec.rb +104 -0
  48. data/spec/foobar_templates_spec.rb +573 -0
  49. data/spec/spec_helper.rb +106 -0
  50. data/spec/template_manager_spec.rb +68 -0
  51. metadata +157 -0
@@ -0,0 +1,573 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe FoobarTemplates do
5
+ before :each do
6
+ @mocked_home = "/tmp/foobar_templates_mock_home"
7
+ @template_root = "#{@mocked_home}/.foobar/templates"
8
+ @dst_dir = "/tmp/foobar_templates_dst_dir"
9
+
10
+ reset_test_env
11
+ FileUtils.chdir(@dst_dir)
12
+ end
13
+
14
+ it 'has a version number' do
15
+ expect(FoobarTemplates::VERSION).not_to be nil
16
+ end
17
+
18
+ it 'has a cheat sheet it will share' do
19
+ output = FoobarTemplates.cheat_sheet
20
+
21
+ expect(output).to match(/foo-bar: \W* good-dog/x)
22
+ expect(output).to match(/FOO_BAR: \W* GOOD_DOG/x)
23
+ expect(output).to match(/FOO_IMAGE_PATH: \W* test\/good-dog/x)
24
+ end
25
+
26
+ # List
27
+
28
+ it 'gives the user a helpful output when there are no templates installed' do
29
+ list_output = FoobarTemplates.list
30
+
31
+ expect(list_output).to start_with "You have no templates."
32
+ expect(File).to exist("#{ENV['HOME']}/.foobar")
33
+ end
34
+
35
+ it 'creates a config file if needed and lists properly' do
36
+ create_user_defined_template
37
+
38
+ list_output = FoobarTemplates.list
39
+
40
+ expect(list_output).to eq " MISC:\n empty_template\n\n"
41
+ expect(File).to exist("#{ENV['HOME']}/.foobar")
42
+ end
43
+
44
+ it "lists with good categories" do
45
+ category = "ARDUINO"
46
+ create_user_defined_template(category)
47
+
48
+ list_output = FoobarTemplates.list
49
+ expect(list_output).to include category
50
+ end
51
+
52
+ it "lists omit the prefix 'template-' if present in repo" do
53
+ category = "ANYTHING"
54
+ full_template_name = "template-happy-burger"
55
+ create_user_defined_template(category, "template-happy-burger")
56
+
57
+ list_output = FoobarTemplates.list
58
+ # expect(list_output.include?(full_template_name)).to be false
59
+ expect(list_output).not_to include full_template_name
60
+ expect(list_output).to include "happy-burger"
61
+ end
62
+
63
+ it "lists leaf templates inside monorepo containers" do
64
+ create_monorepo_template(["template-platform"], monorepo: true)
65
+ create_monorepo_template(["template-platform", "template-api"], monorepo: false, category: "services")
66
+ create_monorepo_template(["template-platform", "template-ui"], monorepo: false, category: "frontend")
67
+
68
+ list_output = FoobarTemplates.list
69
+
70
+ expect(list_output).to include "api"
71
+ expect(list_output).to include "ui"
72
+ expect(list_output).not_to include "platform"
73
+ expect(list_output).to include "SERVICES"
74
+ expect(list_output).to include "FRONTEND"
75
+ end
76
+
77
+ it "recursively traverses nested monorepo containers" do
78
+ create_monorepo_template(["template-org"], monorepo: true)
79
+ create_monorepo_template(["template-org", "template-team"], monorepo: true)
80
+ create_monorepo_template(["template-org", "template-team", "template-console"], monorepo: false, category: "cli")
81
+
82
+ list_output = FoobarTemplates.list
83
+
84
+ expect(list_output).to include "console"
85
+ expect(list_output).to include "CLI"
86
+ expect(list_output).not_to include "org"
87
+ expect(list_output).not_to include "team"
88
+ end
89
+
90
+ # Generate
91
+
92
+ it "generates from a monorepo leaf template and uses leaf bootstrap config" do
93
+ root_dir = create_monorepo_template(["template-platform"], monorepo: true)
94
+ leaf_dir = create_monorepo_template(["template-platform", "template-api"], monorepo: false, category: "services")
95
+
96
+ File.write("#{root_dir}/foobar.yml", "monorepo: true\nbootstrap_command: echo root-config\n")
97
+ File.write("#{leaf_dir}/foobar.yml", "bootstrap_command: echo leaf-config\n")
98
+ File.write("#{leaf_dir}/foo-bar.rb", "puts 'foo-bar'\n")
99
+
100
+ options = { bin: false, ext: false, coc: false, template: "api" }
101
+ gem_name = "tool-go-good-dog"
102
+
103
+ output = capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
104
+
105
+ expect(output).to include "leaf-config"
106
+ expect(output).not_to include "root-config"
107
+ expect(File).to exist("#{@dst_dir}/#{gem_name}/#{gem_name}.rb")
108
+ end
109
+
110
+ it "finds the template-test template even if the template- prefix was omitted" do
111
+ options = {bin: false, ext: false, coc: false, template: "test"}
112
+ gem_name = "tmp_gem"
113
+
114
+ capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
115
+ expect(File).to exist("#{@dst_dir}/#{gem_name}/test_confirmed")
116
+ expect(File).to exist("#{@dst_dir}/#{gem_name}/.vscode/launch.json")
117
+ end
118
+
119
+ it "has a useful dynamically_generate_template_directories method" do
120
+ options = { bin: false, ext: false, coc: false, template: "test_template" }
121
+ gem_name = "good-dog"
122
+ my_gem = FoobarTemplates::CLI::TemplateGenerator.new(options, gem_name)
123
+
124
+ src_dst_map = my_gem.send('dynamically_generate_template_directories')
125
+
126
+ expect(src_dst_map['foo-bar']).to eq "good-dog"
127
+ expect(src_dst_map['foo_bar']).to eq "good_dog"
128
+ expect(src_dst_map['simple_dir']).to eq 'simple_dir'
129
+ end
130
+
131
+ it "returns the expected interpolated string when substitute_template_values is called" do
132
+ options = { bin: false, ext: false, coc: false, template: "test_template" }
133
+ gem_name = "good-dog"
134
+ my_gem = FoobarTemplates::CLI::TemplateGenerator.new(options, gem_name)
135
+
136
+ short_path = 'foo-bar'
137
+ long_path = 'hello/foo-bar/blah/foo-bar'
138
+
139
+ short_interpolated_string = my_gem.send('substitute_template_values', short_path)
140
+ expect(short_interpolated_string).to eq gem_name
141
+
142
+ long_interpolated_string = my_gem.send('substitute_template_values', long_path)
143
+ expect(long_interpolated_string).to eq "hello/good-dog/blah/good-dog"
144
+ end
145
+
146
+ it "has a useful dynamically_generate_templates_files method" do
147
+ options = { bin: false, ext: false, coc: false, template: "test_template" }
148
+ gem_name = "good-dog"
149
+ my_gem = FoobarTemplates::CLI::TemplateGenerator.new(options, gem_name)
150
+
151
+ src_dst_map = my_gem.send('dynamically_generate_templates_files')
152
+
153
+ expect(src_dst_map['foo-bar/keep']).to eq "good-dog/keep"
154
+ expect(src_dst_map['foo-bar.rb']).to eq 'good-dog.rb'
155
+ expect(src_dst_map['foo_bar/keep']).to eq 'good_dog/keep'
156
+ expect(src_dst_map['simple_dir/keep']).to eq 'simple_dir/keep'
157
+ end
158
+
159
+ it "won't generate template files that are listed under the gitignore" do
160
+ template_dir = create_user_defined_template("testing", "template-user-supplied")
161
+ options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
162
+ gem_name = "good-dog"
163
+
164
+ File.write("#{template_dir}/.gitignore", "node_modules/")
165
+ File.write("#{template_dir}/README.md", "Hello")
166
+ FileUtils.mkdir("#{template_dir}/node_modules")
167
+ File.write("#{template_dir}/node_modules/dont_template.rb", "I must not be interpretted")
168
+ `git init #{template_dir}`
169
+
170
+ capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
171
+
172
+ expect(File).not_to exist "#{@dst_dir}/#{gem_name}/node_modules/dont_template.rb"
173
+ expect(File).not_to exist "#{@dst_dir}/#{gem_name}/node_modules"
174
+ end
175
+
176
+ it "executes the bootstrap_command if supplied" do
177
+ template_dir = create_user_defined_template("testing", "template-user-supplied")
178
+ options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
179
+ gem_name = "good-dog"
180
+
181
+ File.write("#{template_dir}/foobar.yml", "bootstrap_command: echo hihihi")
182
+ File.write("#{template_dir}/README.md", "# Readme...")
183
+ `git init #{template_dir}`
184
+
185
+ output = capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
186
+
187
+ expect(output).to include "hihihi"
188
+ end
189
+
190
+ it "interpolates variables into the bootstrap_command" do
191
+ # this is quite pointless, we're literally allowing the user to execute a command on their shell...
192
+ template_dir = create_user_defined_template("testing", "template-user-supplied")
193
+ options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
194
+ gem_name = "good-dog"
195
+
196
+ File.write("#{template_dir}/foobar.yml", 'bootstrap_command: "echo foo-bar"')
197
+ File.write("#{template_dir}/README.md", "# Readme...")
198
+ `git init #{template_dir}`
199
+
200
+ output = capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
201
+
202
+ expect(output).to include "echo #{gem_name}"
203
+ end
204
+
205
+ describe "name_validation" do
206
+ it "rejects names listed in reserved_names and skips bootstrap_command" do
207
+ template_dir = create_user_defined_template("testing", "template-user-supplied")
208
+ options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
209
+ gem_name = "good-dog"
210
+
211
+ File.write(
212
+ "#{template_dir}/foobar.yml",
213
+ "name_validation:\n reserved_names: [bad-dog, good-dog, ugly-dog]\nbootstrap_command: echo BOOTSTRAP_RAN\n"
214
+ )
215
+ File.write("#{template_dir}/README.md", "# Readme")
216
+ `git init #{template_dir}`
217
+
218
+ expect {
219
+ capture_stdout { capture_stderr { FoobarTemplates.generate_template(options, gem_name) } }
220
+ }.to raise_error(RuntimeError)
221
+
222
+ expect($captured_stderr).to include "reserved by template"
223
+ expect(File).not_to exist("#{@dst_dir}/#{gem_name}")
224
+ end
225
+
226
+ it "allows names that are not in reserved_names" do
227
+ template_dir = create_user_defined_template("testing", "template-user-supplied")
228
+ options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
229
+ gem_name = "good-dog"
230
+
231
+ File.write(
232
+ "#{template_dir}/foobar.yml",
233
+ "name_validation:\n reserved_names: [bad-dog, ugly-dog]\n"
234
+ )
235
+ File.write("#{template_dir}/README.md", "# Readme")
236
+ `git init #{template_dir}`
237
+
238
+ capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
239
+
240
+ expect(File).to exist("#{@dst_dir}/#{gem_name}/README.md")
241
+ end
242
+
243
+ it "rejects names that don't match regex_validator" do
244
+ template_dir = create_user_defined_template("testing", "template-user-supplied")
245
+ options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
246
+ gem_name = "good-dog"
247
+
248
+ File.write(
249
+ "#{template_dir}/foobar.yml",
250
+ "name_validation:\n regex_validator: \"^[a-z][a-z0-9_]*$\"\n"
251
+ )
252
+ File.write("#{template_dir}/README.md", "# Readme")
253
+ `git init #{template_dir}`
254
+
255
+ expect {
256
+ capture_stdout { capture_stderr { FoobarTemplates.generate_template(options, gem_name) } }
257
+ }.to raise_error(RuntimeError)
258
+
259
+ expect($captured_stderr).to include "does not match"
260
+ expect(File).not_to exist("#{@dst_dir}/#{gem_name}")
261
+ end
262
+
263
+ it "accepts names that match regex_validator" do
264
+ template_dir = create_user_defined_template("testing", "template-user-supplied")
265
+ options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
266
+ gem_name = "good-dog"
267
+
268
+ File.write(
269
+ "#{template_dir}/foobar.yml",
270
+ "name_validation:\n regex_validator: \"^[a-z][a-z0-9-]*$\"\n"
271
+ )
272
+ File.write("#{template_dir}/README.md", "# Readme")
273
+ `git init #{template_dir}`
274
+
275
+ capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
276
+
277
+ expect(File).to exist("#{@dst_dir}/#{gem_name}/README.md")
278
+ end
279
+
280
+ it "applies both reserved_names and regex_validator together" do
281
+ template_dir = create_user_defined_template("testing", "template-user-supplied")
282
+ options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
283
+ gem_name = "fmt"
284
+
285
+ File.write(
286
+ "#{template_dir}/foobar.yml",
287
+ "name_validation:\n reserved_names: [fmt, std]\n regex_validator: \"^[a-z][a-z0-9-]*$\"\n"
288
+ )
289
+ File.write("#{template_dir}/README.md", "# Readme")
290
+ `git init #{template_dir}`
291
+
292
+ expect {
293
+ capture_stdout { capture_stderr { FoobarTemplates.generate_template(options, gem_name) } }
294
+ }.to raise_error(RuntimeError)
295
+
296
+ expect($captured_stderr).to include "reserved"
297
+ end
298
+
299
+ it "raises a clear error when regex_validator is malformed" do
300
+ template_dir = create_user_defined_template("testing", "template-user-supplied")
301
+ options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
302
+ gem_name = "good-dog"
303
+
304
+ File.write(
305
+ "#{template_dir}/foobar.yml",
306
+ "name_validation:\n regex_validator: \"[unclosed\"\n"
307
+ )
308
+ File.write("#{template_dir}/README.md", "# Readme")
309
+ `git init #{template_dir}`
310
+
311
+ expect {
312
+ capture_stdout { capture_stderr { FoobarTemplates.generate_template(options, gem_name) } }
313
+ }.to raise_error(RuntimeError)
314
+
315
+ expect($captured_stderr).to include "invalid name_validation.regex_validator"
316
+ end
317
+
318
+ it "is a no-op when name_validation is omitted" do
319
+ template_dir = create_user_defined_template("testing", "template-user-supplied")
320
+ options = { bin: false, ext: false, coc: false, template: "template-user-supplied" }
321
+ gem_name = "good-dog"
322
+
323
+ File.write("#{template_dir}/foobar.yml", "category: testing\n")
324
+ File.write("#{template_dir}/README.md", "# Readme")
325
+ `git init #{template_dir}`
326
+
327
+ capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
328
+
329
+ expect(File).to exist("#{@dst_dir}/#{gem_name}/README.md")
330
+ end
331
+ end
332
+
333
+ it "has a test proving every interpolation in one file" do
334
+ expected_manifest = File.read("#{ENV['SPEC_DATA_DIR']}/variable_manifest_test.rb")
335
+ options = { bin: false, ext: false, coc: false, template: "test_template" }
336
+ gem_name = "good-dog"
337
+
338
+ capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
339
+
340
+ resulting_manifest = File.read("#{@dst_dir}/#{gem_name}/#{gem_name}.rb")
341
+ expect(resulting_manifest).to eq expected_manifest
342
+ end
343
+
344
+ it "has config[:unprefixed_name] removing purpose-tool- from name" do
345
+ options = { bin: false, ext: false, coc: false, template: "test_template" }
346
+ gem_name = "tool-go-good-dog"
347
+ my_gem = FoobarTemplates::CLI::TemplateGenerator.new(options, gem_name)
348
+
349
+ config = my_gem.build_interpolation_config
350
+
351
+ expect(config[:unprefixed_name]).to eq "good-dog"
352
+ end
353
+
354
+ describe "domain config prompting" do
355
+ it "prompts for missing domain values when template requires them" do
356
+ template_dir = create_user_defined_template("testing", "template-needs-registry")
357
+ File.write("#{template_dir}/README.md", "Deploy to FOO_REGISTRY_DOMAIN please")
358
+ `git init #{template_dir}`
359
+
360
+ # Remove registry_domain from config so it triggers a prompt
361
+ config_path = "#{@mocked_home}/.foobar/config"
362
+ config_data = YAML.load_file(config_path)
363
+ config_data.delete('registry_domain')
364
+ File.write(config_path, "# Comments made to this file will not be preserved\n#{YAML.dump(config_data)}")
365
+
366
+ options = { bin: false, ext: false, coc: false, template: "template-needs-registry" }
367
+ gem_name = "prompted-app"
368
+
369
+ # Simulate user typing "my-prompted-registry.io"
370
+ allow($stdin).to receive(:gets).and_return("my-prompted-registry.io\n")
371
+
372
+ output = capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
373
+
374
+ expect(output).to include "registry-domain"
375
+ expect(output).to include "~/.foobar/config"
376
+
377
+ # Verify value was saved to config
378
+ saved_config = YAML.load_file(config_path)
379
+ expect(saved_config['registry_domain']).to eq "my-prompted-registry.io"
380
+ end
381
+
382
+ it "does not prompt when domain values are already configured" do
383
+ template_dir = create_user_defined_template("testing", "template-has-domains")
384
+ File.write("#{template_dir}/README.md", "FOO_REGISTRY_DOMAIN and FOO_K8S_DOMAIN")
385
+ `git init #{template_dir}`
386
+
387
+ options = { bin: false, ext: false, coc: false, template: "template-has-domains" }
388
+ gem_name = "no-prompt-app"
389
+
390
+ # $stdin.gets should NOT be called
391
+ expect($stdin).not_to receive(:gets)
392
+
393
+ capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
394
+ end
395
+
396
+ it "does not prompt for templates without domain placeholders" do
397
+ template_dir = create_user_defined_template("testing", "template-simple")
398
+ File.write("#{template_dir}/README.md", "Just a simple template with foo-bar name")
399
+ `git init #{template_dir}`
400
+
401
+ options = { bin: false, ext: false, coc: false, template: "template-simple" }
402
+ gem_name = "simple-app"
403
+
404
+ expect($stdin).not_to receive(:gets)
405
+
406
+ capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
407
+ end
408
+
409
+ it "defaults repo_domain to github.com when user enters empty string" do
410
+ template_dir = create_user_defined_template("testing", "template-needs-repo")
411
+ File.write("#{template_dir}/README.md", "Clone from FOO_GIT_REPO_URL")
412
+ `git init #{template_dir}`
413
+
414
+ config_path = "#{@mocked_home}/.foobar/config"
415
+ config_data = YAML.load_file(config_path)
416
+ config_data.delete('repo_domain')
417
+ File.write(config_path, "# Comments made to this file will not be preserved\n#{YAML.dump(config_data)}")
418
+
419
+ options = { bin: false, ext: false, coc: false, template: "template-needs-repo" }
420
+ gem_name = "default-repo-app"
421
+
422
+ allow($stdin).to receive(:gets).and_return("\n")
423
+
424
+ capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
425
+
426
+ saved_config = YAML.load_file(config_path)
427
+ expect(saved_config['repo_domain']).to eq "github.com"
428
+ end
429
+ end
430
+
431
+ describe "git init behavior" do
432
+ it "skips git init when generating inside an existing git repo" do
433
+ # Initialize a git repo in the destination directory
434
+ `git init #{@dst_dir}`
435
+
436
+ template_dir = create_user_defined_template("testing", "template-git-test")
437
+ File.write("#{template_dir}/README.md", "Hello foo-bar")
438
+ `git init #{template_dir}`
439
+
440
+ options = { bin: false, ext: false, coc: false, template: "template-git-test" }
441
+ gem_name = "git-skip-app"
442
+
443
+ capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
444
+
445
+ # The generated project should NOT have its own .git directory
446
+ expect(File).not_to exist("#{@dst_dir}/#{gem_name}/.git")
447
+ # But the files should still exist
448
+ expect(File).to exist("#{@dst_dir}/#{gem_name}/README.md")
449
+ end
450
+
451
+ it "runs git init when generating outside a git repo" do
452
+ template_dir = create_user_defined_template("testing", "template-git-test2")
453
+ File.write("#{template_dir}/README.md", "Hello foo-bar")
454
+ `git init #{template_dir}`
455
+
456
+ options = { bin: false, ext: false, coc: false, template: "template-git-test2" }
457
+ gem_name = "git-init-app"
458
+
459
+ capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
460
+
461
+ # The generated project SHOULD have its own .git directory
462
+ expect(File).to exist("#{@dst_dir}/#{gem_name}/.git")
463
+ end
464
+
465
+ it "always runs git init when always_perform_git_init is true" do
466
+ # Initialize a git repo in the destination directory
467
+ `git init #{@dst_dir}`
468
+
469
+ # Set always_perform_git_init to true in config
470
+ config_path = "#{@mocked_home}/.foobar/config"
471
+ config_data = YAML.load_file(config_path)
472
+ config_data['always_perform_git_init'] = true
473
+ File.write(config_path, "# Comments made to this file will not be preserved\n#{YAML.dump(config_data)}")
474
+
475
+ template_dir = create_user_defined_template("testing", "template-git-test3")
476
+ File.write("#{template_dir}/README.md", "Hello foo-bar")
477
+ `git init #{template_dir}`
478
+
479
+ options = { bin: false, ext: false, coc: false, template: "template-git-test3" }
480
+ gem_name = "git-force-app"
481
+
482
+ capture_stdout { FoobarTemplates.generate_template(options, gem_name) }
483
+
484
+ # The generated project SHOULD have its own .git directory even though parent is a repo
485
+ expect(File).to exist("#{@dst_dir}/#{gem_name}/.git")
486
+ end
487
+ end
488
+
489
+ describe "install public templates" do
490
+ before :each do
491
+ setup_mock_web_template
492
+ end
493
+ after :each do
494
+ remove_mock_web_template
495
+ end
496
+
497
+ it "can download public templates from the web" do
498
+ capture_stdout { FoobarTemplates.install_public_templates }
499
+ expect(File).to exist("#{ENV['HOME']}/.foobar/templates/template-arduino/README.md")
500
+ end
501
+ end
502
+
503
+ describe "create personal templates" do
504
+ let(:github_name) { "Test" } # set by reset_test_env via `git config --global user.name "Test"`
505
+ let(:local_dir) { "#{ENV['HOME']}/.foobar/templates/templates-#{github_name}" }
506
+
507
+ before :each do
508
+ # Default: no remote — skip network calls.
509
+ allow(FoobarTemplates).to receive(:remote_repo_exists?).and_return(false)
510
+ end
511
+
512
+ it "errors when repo_domain is not configured" do
513
+ config_path = "#{ENV['HOME']}/.foobar/config"
514
+ data = YAML.load_file(config_path)
515
+ data['repo_domain'] = nil
516
+ File.write(config_path, "# Comments made to this file will not be preserved\n#{YAML.dump(data)}")
517
+
518
+ out = StringIO.new
519
+ FoobarTemplates.setup_personal_templates(input: StringIO.new(""), output: out)
520
+
521
+ expect(out.string).to include("`repo_domain` is not set")
522
+ expect(File).not_to exist(local_dir)
523
+ end
524
+
525
+ it "creates the mono-repo with foobar.yml and README when no remote exists" do
526
+ out = StringIO.new
527
+ FoobarTemplates.setup_personal_templates(input: StringIO.new(""), output: out)
528
+
529
+ expect(File).to exist("#{local_dir}/foobar.yml")
530
+ expect(File.read("#{local_dir}/foobar.yml")).to include("monorepo: true")
531
+
532
+ expect(File).to exist("#{local_dir}/README.md")
533
+ expect(File.read("#{local_dir}/README.md")).to include("https://github.com/thenotary/foobar_templates")
534
+
535
+ expect(File).to exist("#{local_dir}/.git")
536
+ expect(out.string).to include("Created personal templates mono-repo")
537
+ end
538
+
539
+ it "refuses to overwrite an existing local templates directory" do
540
+ FileUtils.mkdir_p(local_dir)
541
+
542
+ out = StringIO.new
543
+ FoobarTemplates.setup_personal_templates(input: StringIO.new(""), output: out)
544
+
545
+ expect(out.string).to include("The template directory already exists, #{local_dir}")
546
+ expect(File).not_to exist("#{local_dir}/foobar.yml")
547
+ end
548
+
549
+ it "prompts to clone when the remote repo exists and aborts on 'n'" do
550
+ allow(FoobarTemplates::CLI::SetupPersonalTemplatesRepo).to receive(:remote_repo_exists?).and_return(true)
551
+
552
+ out = StringIO.new
553
+ FoobarTemplates.setup_personal_templates(input: StringIO.new("n\n"), output: out)
554
+
555
+ expect(out.string).to include("clone it down? [Y/n]")
556
+ expect(out.string).to include("Aborted")
557
+ expect(File).not_to exist(local_dir)
558
+ end
559
+
560
+ it "prompts for github name when git user.name is unset" do
561
+ `git config --global --unset user.name`
562
+
563
+ out = StringIO.new
564
+ FoobarTemplates.setup_personal_templates(input: StringIO.new("octocat\n"), output: out)
565
+
566
+ expect(out.string).to include("Enter your GitHub user name")
567
+ expect(File).to exist("#{ENV['HOME']}/.foobar/templates/templates-octocat/foobar.yml")
568
+ ensure
569
+ `git config --global user.name "Test"`
570
+ end
571
+ end
572
+
573
+ end
@@ -0,0 +1,106 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ $test_env = true
3
+ require 'foobar_templates'
4
+ require 'foobar_templates/cli/template_generator'
5
+ require 'fileutils'
6
+ require 'yaml'
7
+ require 'pry'
8
+
9
+ # Mock our home directory
10
+ ENV['HOME'] = "/tmp/foobar_templates_mock_home"
11
+ ENV['SPEC_DATA_DIR'] = File.expand_path("./spec/data") # I fell into this from sloppy chdir handling, bad CLI/ API isolation.
12
+
13
+
14
+ def setup_mock_web_template
15
+ mock_repo = "#{ENV['HOME']}/template-arduino.git"
16
+ FileUtils.mkdir(mock_repo)
17
+ FileUtils.touch("#{mock_repo}/README.md")
18
+
19
+ `cd "#{mock_repo}" && git init && git add . && git commit -m "haxing"`
20
+
21
+ # Update the config file so install_public_templates finds our mock repo
22
+ config_path = "#{ENV['HOME']}/.foobar/config"
23
+ config_data = YAML.load_file(config_path)
24
+ config_data['public_templates'] = mock_repo
25
+ File.write(config_path, "# Comments made to this file will not be preserved\n#{YAML.dump(config_data)}")
26
+ end
27
+
28
+ def remove_mock_web_template
29
+ FileUtils.rm_rf("#{ENV['HOME']}/template-arduino.git")
30
+ end
31
+
32
+ def create_user_defined_template(category = nil, template_name = "empty_template")
33
+ new_template_dir = "#{@template_root}/#{template_name}"
34
+
35
+ # Creates the gem template (empty folder)
36
+ FileUtils.mkdir_p new_template_dir
37
+
38
+ # Writes the category
39
+ File.open("#{new_template_dir}/foobar.yml", "w+") do |f|
40
+ f.puts "category: #{category}" unless category.nil?
41
+ end
42
+
43
+ new_template_dir
44
+ end
45
+
46
+ def create_monorepo_template(path_segments, monorepo: true, category: nil)
47
+ template_dir = File.join(@template_root, *path_segments)
48
+ FileUtils.mkdir_p(template_dir)
49
+
50
+ config = []
51
+ config << "monorepo: true" if monorepo
52
+ config << "category: #{category}" unless category.nil?
53
+ File.write("#{template_dir}/foobar.yml", config.join("\n"))
54
+
55
+ template_dir
56
+ end
57
+
58
+
59
+ def reset_test_env
60
+ FileUtils.rm_rf @mocked_home
61
+ FileUtils.rm_rf @dst_dir
62
+
63
+ FileUtils.mkdir_p @dst_dir
64
+ FileUtils.mkdir_p @template_root
65
+ FileUtils.cd @dst_dir
66
+ auth_settings = ' git config --global user.email "you@example.com"'
67
+ auth_settings += ' && git config --global user.name "Test"'
68
+
69
+ `git config --global init.defaultBranch main && #{auth_settings}`
70
+
71
+ # Write domain config to ~/.foobar/config instead of git config
72
+ config_data = {
73
+ 'default_template' => 'cli_gem',
74
+ 'public_templates' => '',
75
+ 'registry_domain' => 'my-registry.example.com',
76
+ 'k8s_domain' => 'my-k8s.example.com',
77
+ 'repo_domain' => 'github.com',
78
+ }
79
+ FileUtils.mkdir_p "#{@mocked_home}/.foobar"
80
+ File.write("#{@mocked_home}/.foobar/config", "# Comments made to this file will not be preserved\n#{YAML.dump(config_data)}")
81
+ end
82
+
83
+ # squelch stdout
84
+ # usage capture_stdout { a_method(a_signal.new, a_model, a_helper) }
85
+ def capture_stdout(&block)
86
+ original_stdout = $stdout
87
+ $stdout = fake = StringIO.new
88
+ begin
89
+ yield
90
+ ensure
91
+ $stdout = original_stdout
92
+ end
93
+ fake.string
94
+ end
95
+
96
+ def capture_stderr(&block)
97
+ original_stderr = $stderr
98
+ $stderr = fake = StringIO.new
99
+ begin
100
+ yield
101
+ ensure
102
+ $stderr = original_stderr
103
+ $captured_stderr = fake.string
104
+ end
105
+ fake.string
106
+ end