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.
- checksums.yaml +7 -0
- data/.beader/.gitignore +6 -0
- data/.beader/issues/1-support-flexible-template-topologies.yaml +21 -0
- data/.beader/issues/2-add-monorepo-support-for-template-discovery-and-generation.yaml +33 -0
- data/.beader/issues/3-implement-monorepo-aware-recursive-template-inventory.yaml +29 -0
- data/.beader/issues/4-add-leaf-template-selection-with-ambiguity-and-not-found-handling.yaml +30 -0
- data/.beader/issues/5-integrate-leaf-template-resolution-into-generation-flow.yaml +30 -0
- data/.beader/issues/6-add-monorepo-discovery-and-generation-test-coverage.yaml +31 -0
- data/.beader/issues/7-document-monorepo-template-configuration-and-selection.yaml +30 -0
- data/.beader/meta.yaml +1 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +154 -0
- data/Rakefile +55 -0
- data/bin/foobar_templates +72 -0
- data/changelog +107 -0
- data/config/config +8 -0
- data/foobar_templates.gemspec +33 -0
- data/lib/foobar_templates/cli/cheat_sheet.rb +12 -0
- data/lib/foobar_templates/cli/cli.rb +3 -0
- data/lib/foobar_templates/cli/dir_to_template.rb +120 -0
- data/lib/foobar_templates/cli/setup_personal_templates_repo.rb +135 -0
- data/lib/foobar_templates/cli/template_generator.rb +462 -0
- data/lib/foobar_templates/configurator.rb +99 -0
- data/lib/foobar_templates/core/core.rb +9 -0
- data/lib/foobar_templates/core/dir_to_template.rb +114 -0
- data/lib/foobar_templates/strings.rb +23 -0
- data/lib/foobar_templates/template_manager.rb +119 -0
- data/lib/foobar_templates/templates/template-test/.vscode/launch.json +0 -0
- data/lib/foobar_templates/templates/template-test/foo-bar/keep +0 -0
- data/lib/foobar_templates/templates/template-test/foo-bar.rb +16 -0
- data/lib/foobar_templates/templates/template-test/foo_bar/keep +0 -0
- data/lib/foobar_templates/templates/template-test/foobar.yml +2 -0
- data/lib/foobar_templates/templates/template-test/simple_dir/keep +0 -0
- data/lib/foobar_templates/templates/template-test/test_confirmed +0 -0
- data/lib/foobar_templates/templates/test_template/foo-bar/keep +0 -0
- data/lib/foobar_templates/templates/test_template/foo-bar.rb +21 -0
- data/lib/foobar_templates/templates/test_template/foo_bar/keep +0 -0
- data/lib/foobar_templates/templates/test_template/foobar.yml +2 -0
- data/lib/foobar_templates/templates/test_template/simple_dir/keep +0 -0
- data/lib/foobar_templates/version.rb +3 -0
- data/lib/foobar_templates.rb +151 -0
- data/spec/data/variable_manifest_test.rb +21 -0
- data/spec/foobar_templates/cli/dir_to_template_spec.rb +153 -0
- data/spec/foobar_templates/core/dir_to_template_spec.rb +104 -0
- data/spec/foobar_templates_spec.rb +573 -0
- data/spec/spec_helper.rb +106 -0
- data/spec/template_manager_spec.rb +68 -0
- metadata +157 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module FoobarTemplates
|
|
2
|
+
HELP_MSG = <<-HEREDOC
|
|
3
|
+
Foobar Templates version #{FoobarTemplates::VERSION}
|
|
4
|
+
Use foobar_templates to start a new project folder based on a predefined template.
|
|
5
|
+
|
|
6
|
+
Usage Examples:
|
|
7
|
+
|
|
8
|
+
# Download all my template files (configured in ~/.foobar/config)
|
|
9
|
+
$ foobar_templates --install-public-templates
|
|
10
|
+
|
|
11
|
+
# Create a personal mono-repo template for your projects
|
|
12
|
+
$ foobar_templates --setup-personal-templates
|
|
13
|
+
|
|
14
|
+
# Create a ruby gem project using the built in service template
|
|
15
|
+
$ foobar_templates --template ruby-cli-gem your_project_name
|
|
16
|
+
|
|
17
|
+
# Convert the current directory which represents a working project into a
|
|
18
|
+
# template by replacing project name variants with foo-bar placeholders
|
|
19
|
+
$ cd my_recently_built_project
|
|
20
|
+
$ foobar_templates --copy-to-templates
|
|
21
|
+
HEREDOC
|
|
22
|
+
|
|
23
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
|
|
4
|
+
module FoobarTemplates
|
|
5
|
+
|
|
6
|
+
# This class handles the logic for finding templates
|
|
7
|
+
# in the user's dir, the gem's builtin templates
|
|
8
|
+
# (and on the web some day)
|
|
9
|
+
class TemplateManager
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
|
|
13
|
+
def internal_template_location() = File.expand_path("#{File.dirname(__FILE__)}/templates")
|
|
14
|
+
def custom_template_location() = File.expand_path("~/.foobar/templates")
|
|
15
|
+
def default_template_name() = "ruby-cli-gem"
|
|
16
|
+
|
|
17
|
+
def get_template_src(options)
|
|
18
|
+
template_name = options[:template] || default_template_name
|
|
19
|
+
|
|
20
|
+
if template_exists_within_repo?(template_name)
|
|
21
|
+
return resolve_template_path(internal_template_location, template_name)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
custom_src = resolve_template_path(custom_template_location, template_name)
|
|
25
|
+
return custom_src if File.exist?(custom_src)
|
|
26
|
+
|
|
27
|
+
monorepo_match = resolve_monorepo_leaf_template(custom_template_location, template_name)
|
|
28
|
+
return monorepo_match if monorepo_match
|
|
29
|
+
|
|
30
|
+
raise FoobarTemplates::CLIError, "Error: template '#{template_name}' could not be found. Run the below command to list all available templates.\n\n foobar_templates -l"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def template_exists_within_repo?(template_name)
|
|
34
|
+
file_in_source?(template_name) || file_in_source?("template-#{template_name}")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def resolve_template_path(location, name)
|
|
38
|
+
basic = "#{location}/#{name}"
|
|
39
|
+
prefixed = "#{location}/template-#{name}"
|
|
40
|
+
|
|
41
|
+
return basic if File.exist?(basic)
|
|
42
|
+
return prefixed if File.exist?(prefixed)
|
|
43
|
+
|
|
44
|
+
basic # fallback, even if it doesn't exist, will be caught by get_template_src
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def resolve_monorepo_leaf_template(root, requested_name)
|
|
48
|
+
monorepo_roots = immediate_subdirectories(root).select { |dir| monorepo_directory?(dir) }
|
|
49
|
+
return nil if monorepo_roots.empty?
|
|
50
|
+
|
|
51
|
+
requested_leaf_name = requested_name.sub(/^template-/, "")
|
|
52
|
+
leaf_paths = monorepo_roots.flat_map { |dir| collect_leaf_templates(dir) }
|
|
53
|
+
matches = leaf_paths.select do |leaf_path|
|
|
54
|
+
File.basename(leaf_path).sub(/^template-/, "") == requested_leaf_name
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
if matches.length > 1
|
|
58
|
+
readable_paths = matches.map { |path| path.sub(/^#{Regexp.escape(root)}\//, "") }.sort.join(", ")
|
|
59
|
+
raise FoobarTemplates::CLIError, "Ambiguous template name '#{requested_name}'. Matching leaf templates: #{readable_paths}. Rename one of the conflicting templates."
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
if matches.empty?
|
|
63
|
+
readable_leaves = leaf_paths.map { |path| File.basename(path).sub(/^template-/, "") }.uniq.sort
|
|
64
|
+
raise FoobarTemplates::CLIError, "Template '#{requested_name}' not found in monorepo leaf templates. Available leaf templates: #{readable_leaves.join(', ')}. Run 'foobar_templates -l' to list all available templates."
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
matches.first
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def collect_leaf_templates(dir)
|
|
71
|
+
config = load_template_config(dir)
|
|
72
|
+
return [] if config.nil?
|
|
73
|
+
|
|
74
|
+
if config[:monorepo] == true
|
|
75
|
+
immediate_subdirectories(dir).flat_map { |child| collect_leaf_templates(child) }
|
|
76
|
+
else
|
|
77
|
+
[dir]
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def monorepo_directory?(dir)
|
|
82
|
+
config = load_template_config(dir)
|
|
83
|
+
!config.nil? && config[:monorepo] == true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def load_template_config(dir)
|
|
87
|
+
path = File.join(dir, "foobar.yml")
|
|
88
|
+
return nil unless File.exist?(path)
|
|
89
|
+
|
|
90
|
+
raw = YAML.load_file(path, symbolize_names: true)
|
|
91
|
+
raw.is_a?(Hash) ? raw : {}
|
|
92
|
+
rescue StandardError
|
|
93
|
+
{}
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def immediate_subdirectories(dir)
|
|
97
|
+
return [] unless File.exist?(dir)
|
|
98
|
+
|
|
99
|
+
Dir.children(dir).filter_map do |entry|
|
|
100
|
+
full_path = File.join(dir, entry)
|
|
101
|
+
File.directory?(full_path) ? full_path : nil
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def find_in_source_paths(target)
|
|
106
|
+
path = "#{__dir__}/templates/#{target}"
|
|
107
|
+
File.exist?(path) ? path : target
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Get's path to 'target' from within the gem's "templates" folder
|
|
111
|
+
# within the gem's source
|
|
112
|
+
def file_in_source?(target)
|
|
113
|
+
src_in_source_path = "#{File.dirname(__FILE__)}/templates/#{target}"
|
|
114
|
+
File.exist?(src_in_source_path)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
This file shows all the variables in one place!
|
|
2
|
+
See "lib/foobar_templates/templates/test_template/>>> foo-bar.rb"
|
|
3
|
+
|
|
4
|
+
>>> foo-bar: foo-bar
|
|
5
|
+
>>> foo_bar: foo_bar
|
|
6
|
+
>>> FooBar: FooBar
|
|
7
|
+
>>> fooBar: fooBar
|
|
8
|
+
>>> FOO_BAR: FOO_BAR
|
|
9
|
+
>>> foo/bar: foo/bar
|
|
10
|
+
>>> foo_bar/foo_bar: foo_bar/foo_bar
|
|
11
|
+
>>> Foo::Bar: Foo::Bar
|
|
12
|
+
>>> FOO_AUTHOR: FOO_AUTHOR
|
|
13
|
+
>>> FOO_EMAIL: FOO_EMAIL
|
|
14
|
+
>>> FOO_GIT_REPO_DOMAIN: FOO_GIT_REPO_DOMAIN
|
|
15
|
+
>>> FOO_GIT_REPO_URL: FOO_GIT_REPO_URL
|
|
16
|
+
>>> FOO_GIT_REPO_PATH: FOO_GIT_REPO_PATH
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
This file shows all the variables in one place!
|
|
2
|
+
See "lib/foobar_templates/templates/test_template/>>> foo-bar.rb"
|
|
3
|
+
|
|
4
|
+
>>> foo-bar: foo-bar
|
|
5
|
+
>>> Foo Bar: Foo Bar
|
|
6
|
+
>>> foo_bar: foo_bar
|
|
7
|
+
>>> FooBar: FooBar
|
|
8
|
+
>>> fooBar: fooBar
|
|
9
|
+
>>> FOO_BAR: FOO_BAR
|
|
10
|
+
>>> foo/bar: foo/bar
|
|
11
|
+
>>> foo_bar/foo_bar: foo_bar/foo_bar
|
|
12
|
+
>>> Foo::Bar: Foo::Bar
|
|
13
|
+
>>> FOO_AUTHOR: FOO_AUTHOR
|
|
14
|
+
>>> FOO_EMAIL: FOO_EMAIL
|
|
15
|
+
>>> FOO_GIT_REPO_DOMAIN: FOO_GIT_REPO_DOMAIN
|
|
16
|
+
>>> FOO_GIT_REPO_URL: FOO_GIT_REPO_URL
|
|
17
|
+
>>> FOO_GIT_REPO_PATH: FOO_GIT_REPO_PATH
|
|
18
|
+
>>> FOO_IMAGE_PATH: FOO_IMAGE_PATH
|
|
19
|
+
>>> FOO_REGISTRY_DOMAIN: FOO_REGISTRY_DOMAIN
|
|
20
|
+
>>> FOO_REGISTRY_REPO_PATH: FOO_REGISTRY_REPO_PATH
|
|
21
|
+
>>> FOO_K8S_DOMAIN: FOO_K8S_DOMAIN
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
require "foobar_templates/version"
|
|
2
|
+
require "foobar_templates/strings"
|
|
3
|
+
require 'foobar_templates/configurator'
|
|
4
|
+
require 'foobar_templates/template_manager'
|
|
5
|
+
|
|
6
|
+
require 'foobar_templates/core/core'
|
|
7
|
+
require 'foobar_templates/cli/cli'
|
|
8
|
+
|
|
9
|
+
require 'uri'
|
|
10
|
+
|
|
11
|
+
SOURCE_ROOT = File.expand_path("#{File.dirname(__FILE__)}/..")
|
|
12
|
+
|
|
13
|
+
module FoobarTemplates
|
|
14
|
+
class CLIError < StandardError; end
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
|
|
18
|
+
def version
|
|
19
|
+
FoobarTemplates::VERSION
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def cheat_sheet
|
|
23
|
+
CLI::CheatSheet.go
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# lists available templates
|
|
27
|
+
def list
|
|
28
|
+
configurator = Configurator.new
|
|
29
|
+
available_templates = configurator.collect_user_defined_templates
|
|
30
|
+
|
|
31
|
+
available_templates = group_hashes_by_key(available_templates)
|
|
32
|
+
output = convert_grouped_hashes_to_output(available_templates)
|
|
33
|
+
|
|
34
|
+
if output.empty?
|
|
35
|
+
empty_output_msg = "You have no templates. You can install the public example templates with\n"
|
|
36
|
+
empty_output_msg += "the below command:\n\n"
|
|
37
|
+
empty_output_msg += "foobar_templates --install-public-templates"
|
|
38
|
+
return empty_output_msg
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
mark_default_template(output, configurator.default_template)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def install_public_templates
|
|
45
|
+
validate_git_is_installed!
|
|
46
|
+
configurator = Configurator.new
|
|
47
|
+
config_file_data = configurator.config_file_data
|
|
48
|
+
puts "Downloading templates from the following locations: \n #{config_file_data['public_templates'].split(" ").join("\n ")}"
|
|
49
|
+
config_file_data['public_templates'].split.each do |url|
|
|
50
|
+
uri = URI.parse(url)
|
|
51
|
+
template_folder_name = File.basename(uri.path).sub(/\.git$/, "")
|
|
52
|
+
if !File.exist?("#{ENV['HOME']}/.foobar/templates/#{template_folder_name}")
|
|
53
|
+
cmd = "cd #{ENV['HOME']}/.foobar/templates && git clone #{url}"
|
|
54
|
+
cmd += " 2> /dev/null" if $test_env
|
|
55
|
+
`#{cmd}`
|
|
56
|
+
else
|
|
57
|
+
puts "Warning: Skipping, template already exists #{ENV['HOME']}/.foobar/templates/#{template_folder_name}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def validate_git_is_installed!
|
|
63
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
|
64
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
|
65
|
+
exts.each do |ext|
|
|
66
|
+
exe = File.join(path, "git#{ext}")
|
|
67
|
+
return if File.executable?(exe) && !File.directory?(exe)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
raise CLIError, "Error: `git` command not found. Please install Git and ensure it's in your PATH."
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def setup_personal_templates(input: $stdin, output: $stdout)
|
|
75
|
+
CLI::SetupPersonalTemplatesRepo.go(input: input, output: output)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def dir_to_template(input: $stdin, output: $stdout)
|
|
79
|
+
CLI::DirToTemplate.go(input: input, output: output)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def generate_template(options, gem_name)
|
|
83
|
+
require 'foobar_templates/cli/template_generator'
|
|
84
|
+
|
|
85
|
+
template_generator = CLI::TemplateGenerator.new(options, gem_name).run
|
|
86
|
+
rescue FoobarTemplates::CLIError => e
|
|
87
|
+
msg = e.message
|
|
88
|
+
$stderr.puts msg if msg && !msg.empty? && msg != e.class.name
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# input: [ { "predefined" => "default" },
|
|
92
|
+
# { "MISC" => "my_thing" },
|
|
93
|
+
# { "prdefined" => "service" }
|
|
94
|
+
# ]
|
|
95
|
+
#
|
|
96
|
+
# output: [ { "predefined" => ["default", "service"] },
|
|
97
|
+
# { "MISC" => ["my_thing"] }
|
|
98
|
+
# ]
|
|
99
|
+
#
|
|
100
|
+
def group_hashes_by_key(available_templates)
|
|
101
|
+
h = {}
|
|
102
|
+
available_templates.each do |hash|
|
|
103
|
+
k = hash.first.first
|
|
104
|
+
v = hash.first.last
|
|
105
|
+
h[k] = [] unless h.has_key?(k)
|
|
106
|
+
h[k] << v
|
|
107
|
+
end
|
|
108
|
+
h
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# input: [ { "predefined" => ["default", "service"] },
|
|
112
|
+
# { "MISC" => ["my_thing"] }
|
|
113
|
+
# ]
|
|
114
|
+
def convert_grouped_hashes_to_output(available_templates)
|
|
115
|
+
template_list_output = ""
|
|
116
|
+
available_templates.each do |category, template_names|
|
|
117
|
+
|
|
118
|
+
template_list_output << " #{category&.upcase || "UNSPECIFIED"}:\n"
|
|
119
|
+
template_names.each do |el|
|
|
120
|
+
template_list_output << " #{el}\n"
|
|
121
|
+
end
|
|
122
|
+
template_list_output << "\n"
|
|
123
|
+
end
|
|
124
|
+
template_list_output
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def mark_default_template(output_string, default_template)
|
|
128
|
+
output_string.lines.reverse.map do |l|
|
|
129
|
+
if l.strip == default_template
|
|
130
|
+
l[1]= "*"
|
|
131
|
+
"#{l.chomp} (default)\n"
|
|
132
|
+
else
|
|
133
|
+
l
|
|
134
|
+
end
|
|
135
|
+
end.reverse.join
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def which(executable)
|
|
139
|
+
if File.file?(executable) && File.executable?(executable)
|
|
140
|
+
executable
|
|
141
|
+
elsif ENV['PATH']
|
|
142
|
+
path = ENV['PATH'].split(File::PATH_SEPARATOR).find do |p|
|
|
143
|
+
abs_path = File.join(p, executable)
|
|
144
|
+
File.file?(abs_path) && File.executable?(abs_path)
|
|
145
|
+
end
|
|
146
|
+
path && File.expand_path(executable, path)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
This file shows all the variables in one place!
|
|
2
|
+
See "lib/foobar_templates/templates/test_template/foo-bar.rb"
|
|
3
|
+
|
|
4
|
+
foo-bar: good-dog
|
|
5
|
+
Foo Bar: Good Dog
|
|
6
|
+
foo_bar: good_dog
|
|
7
|
+
FooBar: GoodDog
|
|
8
|
+
fooBar: goodDog
|
|
9
|
+
FOO_BAR: GOOD_DOG
|
|
10
|
+
foo/bar: good/dog
|
|
11
|
+
foo_bar/foo_bar: good_dog/good_dog
|
|
12
|
+
Foo::Bar: Good::Dog
|
|
13
|
+
FOO_AUTHOR: Test
|
|
14
|
+
FOO_EMAIL: you@example.com
|
|
15
|
+
FOO_GIT_REPO_DOMAIN: github.com
|
|
16
|
+
FOO_GIT_REPO_URL: https://github.com/Test/good-dog
|
|
17
|
+
FOO_GIT_REPO_PATH: github.com/test/good-dog
|
|
18
|
+
FOO_IMAGE_PATH: test/good-dog
|
|
19
|
+
FOO_REGISTRY_DOMAIN: my-registry.example.com
|
|
20
|
+
FOO_REGISTRY_REPO_PATH: my-registry.example.com/test/good-dog
|
|
21
|
+
FOO_K8S_DOMAIN: my-k8s.example.com
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module FoobarTemplates::CLI
|
|
4
|
+
describe DirToTemplate 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
|
+
@personal_repo = "#{@template_root}/templates-test" # name from reset_test_env
|
|
10
|
+
|
|
11
|
+
reset_test_env
|
|
12
|
+
FileUtils.cd(@dst_dir)
|
|
13
|
+
@initial_dir = FileUtils.pwd
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
after :each do
|
|
17
|
+
FileUtils.cd(@initial_dir)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def init_project(name, files: {})
|
|
21
|
+
project_dir = "#{@dst_dir}/#{name}"
|
|
22
|
+
FileUtils.mkdir_p(project_dir)
|
|
23
|
+
FileUtils.cd(project_dir)
|
|
24
|
+
files.each do |rel, contents|
|
|
25
|
+
FileUtils.mkdir_p(File.dirname(rel)) unless File.dirname(rel) == "."
|
|
26
|
+
File.write(rel, contents)
|
|
27
|
+
end
|
|
28
|
+
`git init -q && git add -A && git commit -q -m init`
|
|
29
|
+
project_dir
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "errors when no personal templates repo exists" do
|
|
33
|
+
init_project("cool-app", files: { "main.rb" => "puts 'hi'\n" })
|
|
34
|
+
|
|
35
|
+
out = StringIO.new
|
|
36
|
+
expect { DirToTemplate.go(input: StringIO.new(""), output: out) }.to raise_error(FoobarTemplates::CLIError)
|
|
37
|
+
expect(out.string).to include "Unable to convert to template, no personal templates repo exists."
|
|
38
|
+
expect(out.string).to include "foobar_templates --setup-personal-templates"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "copies the project into the personal templates repo and runs the rename transform" do
|
|
42
|
+
FileUtils.mkdir_p(@personal_repo)
|
|
43
|
+
project_dir = init_project("cool-app", files: {
|
|
44
|
+
"main.rb" => "class CoolApp; end\nNAME = 'cool-app'\nMOD = 'cool_app'\nENV_VAR = 'COOL_APP_HOME'\n",
|
|
45
|
+
".gitignore" => "ignored.log\n",
|
|
46
|
+
"lib/cool_app.rb" => "module CoolApp; end\n",
|
|
47
|
+
})
|
|
48
|
+
File.write("#{project_dir}/ignored.log", "should not be copied")
|
|
49
|
+
|
|
50
|
+
out = StringIO.new
|
|
51
|
+
DirToTemplate.go(input: StringIO.new(""), output: out)
|
|
52
|
+
|
|
53
|
+
dest = "#{@personal_repo}/cool-app"
|
|
54
|
+
expect(File).to exist("#{dest}/main.rb")
|
|
55
|
+
expect(File).to exist("#{dest}/lib/cool_app.rb")
|
|
56
|
+
expect(File).to exist("#{dest}/.gitignore")
|
|
57
|
+
expect(File).not_to exist("#{dest}/ignored.log")
|
|
58
|
+
expect(File).not_to exist("#{dest}/.git")
|
|
59
|
+
|
|
60
|
+
content = File.read("#{dest}/main.rb")
|
|
61
|
+
expect(content).to include "FooBar"
|
|
62
|
+
expect(content).to include "foo-bar"
|
|
63
|
+
expect(content).to include "foo_bar"
|
|
64
|
+
expect(content).to include "FOO_BAR"
|
|
65
|
+
expect(content).not_to include "cool-app"
|
|
66
|
+
expect(content).not_to include "CoolApp"
|
|
67
|
+
|
|
68
|
+
expect(File.read("#{dest}/foobar.yml")).to include "category: misc"
|
|
69
|
+
|
|
70
|
+
expect(out.string).to include "Template created at: #{dest}"
|
|
71
|
+
expect(out.string).to match(/clean|review|remove/i)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "preserves an existing foobar.yml in the source project" do
|
|
75
|
+
FileUtils.mkdir_p(@personal_repo)
|
|
76
|
+
init_project("cool-app", files: {
|
|
77
|
+
"foobar.yml" => "category: backend\n",
|
|
78
|
+
"main.rb" => "puts 'hi'\n",
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
DirToTemplate.go(input: StringIO.new(""), output: StringIO.new)
|
|
82
|
+
|
|
83
|
+
dest = "#{@personal_repo}/cool-app"
|
|
84
|
+
expect(File.read("#{dest}/foobar.yml")).to include "category: backend"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "prompts for a custom template folder name and category" do
|
|
88
|
+
FileUtils.mkdir_p(@personal_repo)
|
|
89
|
+
init_project("cool-app", files: {
|
|
90
|
+
"main.rb" => "class CoolApp; end\nNAME = 'cool-app'\n",
|
|
91
|
+
"lib/cool_app.rb" => "module CoolApp; end\n",
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
out = StringIO.new
|
|
95
|
+
DirToTemplate.go(input: StringIO.new("my-cool-template\nbackend\n"), output: out)
|
|
96
|
+
|
|
97
|
+
dest = "#{@personal_repo}/my-cool-template"
|
|
98
|
+
expect(File).to exist("#{dest}/main.rb")
|
|
99
|
+
expect(File.read("#{dest}/foobar.yml")).to include "category: backend"
|
|
100
|
+
|
|
101
|
+
content = File.read("#{dest}/main.rb")
|
|
102
|
+
expect(content).to include "FooBar"
|
|
103
|
+
expect(content).to include "foo-bar"
|
|
104
|
+
expect(content).not_to include "cool-app"
|
|
105
|
+
expect(content).not_to include "CoolApp"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "defaults the category prompt to the existing foobar.yml category" do
|
|
109
|
+
FileUtils.mkdir_p(@personal_repo)
|
|
110
|
+
init_project("cool-app", files: {
|
|
111
|
+
"foobar.yml" => "category: backend\n",
|
|
112
|
+
"main.rb" => "puts 'hi'\n",
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
out = StringIO.new
|
|
116
|
+
DirToTemplate.go(input: StringIO.new("\n\n"), output: out)
|
|
117
|
+
|
|
118
|
+
expect(out.string).to include "Category [backend]:"
|
|
119
|
+
expect(File.read("#{@personal_repo}/cool-app/foobar.yml")).to include "category: backend"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it "rejects invalid template folder names" do
|
|
123
|
+
FileUtils.mkdir_p(@personal_repo)
|
|
124
|
+
init_project("cool-app", files: { "main.rb" => "x" })
|
|
125
|
+
|
|
126
|
+
out = StringIO.new
|
|
127
|
+
expect {
|
|
128
|
+
DirToTemplate.go(input: StringIO.new("bad/name\n"), output: out)
|
|
129
|
+
}.to raise_error(FoobarTemplates::CLIError)
|
|
130
|
+
expect(out.string).to include "invalid template folder name"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it "aborts if a template with the same basename already exists" do
|
|
134
|
+
FileUtils.mkdir_p("#{@personal_repo}/cool-app")
|
|
135
|
+
init_project("cool-app", files: { "main.rb" => "x" })
|
|
136
|
+
|
|
137
|
+
out = StringIO.new
|
|
138
|
+
expect { DirToTemplate.go(input: StringIO.new(""), output: out) }.to raise_error(FoobarTemplates::CLIError)
|
|
139
|
+
expect(out.string).to include "already exists at #{@personal_repo}/cool-app"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "errors when not run inside a git repository" do
|
|
143
|
+
FileUtils.mkdir_p(@personal_repo)
|
|
144
|
+
project_dir = "#{@dst_dir}/no-git-app"
|
|
145
|
+
FileUtils.mkdir_p(project_dir)
|
|
146
|
+
FileUtils.cd(project_dir)
|
|
147
|
+
|
|
148
|
+
out = StringIO.new
|
|
149
|
+
expect { DirToTemplate.go(input: StringIO.new(""), output: out) }.to raise_error(FoobarTemplates::CLIError)
|
|
150
|
+
expect(out.string).to include "must be run from within a git repository"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'find'
|
|
3
|
+
|
|
4
|
+
module FoobarTemplates::Core
|
|
5
|
+
describe DirToTemplate do
|
|
6
|
+
before :each do
|
|
7
|
+
@mocked_home = "/tmp/foobar_templates_mock_home"
|
|
8
|
+
@template_root = "#{@mocked_home}/.foobar/templates"
|
|
9
|
+
@dst_dir = "/tmp/foobar_templates_dst_dir"
|
|
10
|
+
|
|
11
|
+
reset_test_env
|
|
12
|
+
FileUtils.chdir(@dst_dir)
|
|
13
|
+
@initial_dir = FileUtils.pwd
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
after :each do
|
|
17
|
+
FileUtils.cd(@initial_dir)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'will process the expected files and ignore the ones that should be ignored' do
|
|
21
|
+
template_dir = create_user_defined_template(category: "wizardly_tools")
|
|
22
|
+
|
|
23
|
+
gitignored_file = "something.toignore"
|
|
24
|
+
|
|
25
|
+
# Setup test template
|
|
26
|
+
FileUtils.cd(template_dir)
|
|
27
|
+
FileUtils.touch("#{template_dir}/README.md")
|
|
28
|
+
FileUtils.touch("#{template_dir}/#{gitignored_file}")
|
|
29
|
+
File.write("#{template_dir}/.gitignore", gitignored_file)
|
|
30
|
+
`git init`
|
|
31
|
+
|
|
32
|
+
files_changed = DirToTemplate.🧙🪄! Find.find("."), dry_run: true
|
|
33
|
+
|
|
34
|
+
expect(files_changed[0]).to eq "Processed: ./README.md"
|
|
35
|
+
expect(File).to exist "#{template_dir}/.gitignore"
|
|
36
|
+
expect(files_changed.count).to eq 1
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'conducts text replacements of package name variants' do
|
|
40
|
+
template_dir = create_user_defined_template(category: "wizardly_tools")
|
|
41
|
+
template_name = "cool-app"
|
|
42
|
+
|
|
43
|
+
FileUtils.cd(template_dir)
|
|
44
|
+
File.write("#{template_dir}/main.go", "package cool_app\nconst Name = \"cool-app\"\nconst ENV = \"COOL_APP_ENV\"\nclass CoolApp\n def coolApp\n end\nend\n")
|
|
45
|
+
File.write("#{template_dir}/.gitignore", "")
|
|
46
|
+
`git init`
|
|
47
|
+
|
|
48
|
+
DirToTemplate.🧙🪄! Find.find("."), template_name: template_name
|
|
49
|
+
|
|
50
|
+
content = File.read("#{template_dir}/main.go")
|
|
51
|
+
expect(content).to include 'foo_bar'
|
|
52
|
+
expect(content).to include 'foo-bar'
|
|
53
|
+
expect(content).to include 'FOO_BAR'
|
|
54
|
+
expect(content).to include 'FooBar'
|
|
55
|
+
expect(content).to include 'fooBar'
|
|
56
|
+
expect(content).not_to include 'cool-app'
|
|
57
|
+
expect(content).not_to include 'cool_app'
|
|
58
|
+
expect(content).not_to include 'COOL_APP'
|
|
59
|
+
expect(content).not_to include 'CoolApp'
|
|
60
|
+
expect(content).not_to include 'coolApp'
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'reverse-substitutes FOO_* placeholder values from git config and foobar_templates config' do
|
|
64
|
+
template_dir = create_user_defined_template(category: "wizardly_tools")
|
|
65
|
+
template_name = "good-dog"
|
|
66
|
+
|
|
67
|
+
FileUtils.cd(template_dir)
|
|
68
|
+
manifest = <<~CONTENT
|
|
69
|
+
author: Test
|
|
70
|
+
email: you@example.com
|
|
71
|
+
repo_domain: github.com
|
|
72
|
+
repo_url: https://github.com/Test/good-dog
|
|
73
|
+
repo_path: github.com/test/good-dog
|
|
74
|
+
image_path: test/good-dog
|
|
75
|
+
registry_domain: my-registry.example.com
|
|
76
|
+
registry_repo_path: my-registry.example.com/test/good-dog
|
|
77
|
+
k8s_domain: my-k8s.example.com
|
|
78
|
+
CONTENT
|
|
79
|
+
File.write("#{template_dir}/manifest.txt", manifest)
|
|
80
|
+
File.write("#{template_dir}/.gitignore", "")
|
|
81
|
+
`git init`
|
|
82
|
+
|
|
83
|
+
DirToTemplate.🧙🪄! Find.find("."), template_name: template_name
|
|
84
|
+
|
|
85
|
+
content = File.read("#{template_dir}/manifest.txt")
|
|
86
|
+
expect(content).to include 'FOO_AUTHOR'
|
|
87
|
+
expect(content).to include 'FOO_EMAIL'
|
|
88
|
+
expect(content).to include 'FOO_GIT_REPO_DOMAIN'
|
|
89
|
+
expect(content).to include 'FOO_GIT_REPO_URL'
|
|
90
|
+
expect(content).to include 'FOO_GIT_REPO_PATH'
|
|
91
|
+
expect(content).to include 'FOO_IMAGE_PATH'
|
|
92
|
+
expect(content).to include 'FOO_REGISTRY_DOMAIN'
|
|
93
|
+
expect(content).to include 'FOO_REGISTRY_REPO_PATH'
|
|
94
|
+
expect(content).to include 'FOO_K8S_DOMAIN'
|
|
95
|
+
|
|
96
|
+
expect(content).not_to include 'you@example.com'
|
|
97
|
+
expect(content).not_to include 'github.com'
|
|
98
|
+
expect(content).not_to include 'my-registry.example.com'
|
|
99
|
+
expect(content).not_to include 'my-k8s.example.com'
|
|
100
|
+
expect(content).not_to include 'good-dog'
|
|
101
|
+
expect(content).not_to include 'good_dog'
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|