planter-cli 0.0.3
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/.editorconfig +9 -0
- data/.gitignore +44 -0
- data/.irbrc +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +78 -0
- data/.travis.yml +7 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +45 -0
- data/Gemfile +6 -0
- data/Guardfile +25 -0
- data/LICENSE.txt +20 -0
- data/README.md +208 -0
- data/Rakefile +132 -0
- data/bin/plant +106 -0
- data/debug.log +0 -0
- data/docker/Dockerfile +12 -0
- data/docker/Dockerfile-2.6 +12 -0
- data/docker/Dockerfile-2.7 +12 -0
- data/docker/Dockerfile-3.0 +11 -0
- data/docker/bash_profile +15 -0
- data/docker/inputrc +57 -0
- data/lib/.rubocop.yml +1 -0
- data/lib/planter/array.rb +28 -0
- data/lib/planter/color.rb +370 -0
- data/lib/planter/errors.rb +59 -0
- data/lib/planter/file.rb +11 -0
- data/lib/planter/fileentry.rb +87 -0
- data/lib/planter/filelist.rb +144 -0
- data/lib/planter/hash.rb +103 -0
- data/lib/planter/plant.rb +228 -0
- data/lib/planter/prompt.rb +352 -0
- data/lib/planter/script.rb +59 -0
- data/lib/planter/string.rb +383 -0
- data/lib/planter/symbol.rb +28 -0
- data/lib/planter/version.rb +7 -0
- data/lib/planter.rb +222 -0
- data/planter-cli.gemspec +48 -0
- data/scripts/deploy.rb +97 -0
- data/scripts/runtests.sh +5 -0
- data/spec/.rubocop.yml +4 -0
- data/spec/planter/plant_spec.rb +14 -0
- data/spec/planter/string_spec.rb +20 -0
- data/spec/spec_helper.rb +20 -0
- data/src/_README.md +214 -0
- metadata +400 -0
data/lib/planter/file.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Planter
|
4
|
+
# A single file entry in a FileList
|
5
|
+
class FileEntry < Hash
|
6
|
+
# Operation to execute on the file
|
7
|
+
attr_accessor :operation
|
8
|
+
|
9
|
+
# File path and target path
|
10
|
+
attr_reader :file, :target
|
11
|
+
|
12
|
+
##
|
13
|
+
## Initialize a FileEntry object
|
14
|
+
##
|
15
|
+
## @param file [String] The source file path
|
16
|
+
## @param target [String] The target path
|
17
|
+
## @param operation [Symbol] The operation to perform
|
18
|
+
##
|
19
|
+
## @return [FileEntry] a Hash of parameters
|
20
|
+
##
|
21
|
+
def initialize(file, target, operation)
|
22
|
+
@file = file
|
23
|
+
@target = target
|
24
|
+
@operation = operation
|
25
|
+
|
26
|
+
super()
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
## Test if file matches any pattern in config
|
31
|
+
##
|
32
|
+
## @return [Boolean] file matches pattern
|
33
|
+
##
|
34
|
+
def matches_pattern?
|
35
|
+
Planter.patterns.filter { |pattern, _| @file =~ pattern }.count.positive?
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
## Determine operators based on configured filters,
|
40
|
+
## asking for input if necessary
|
41
|
+
##
|
42
|
+
## @return [Symbol] Operator
|
43
|
+
##
|
44
|
+
def test_operator
|
45
|
+
operator = Planter.overwrite ? :overwrite : :copy
|
46
|
+
Planter.patterns.each do |pattern, op|
|
47
|
+
next unless @file =~ pattern
|
48
|
+
|
49
|
+
operator = op == :ask && !Planter.overwrite ? ask_operation : op
|
50
|
+
break
|
51
|
+
end
|
52
|
+
operator
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
## Prompt for file handling. If File exists, offer a merge/overwrite/ignore,
|
57
|
+
## otherwise simply ask whether or not to copy.
|
58
|
+
##
|
59
|
+
def ask_operation
|
60
|
+
if File.exist?(@target)
|
61
|
+
Prompt.file_what?(self)
|
62
|
+
else
|
63
|
+
res = Prompt.yn("Copy #{File.basename(@file)} to #{File.basename(@target)}",
|
64
|
+
default_response: true)
|
65
|
+
res ? :copy : :ignore
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
## Returns a string representation of the object.
|
71
|
+
##
|
72
|
+
## @return [String] String representation of the object.
|
73
|
+
##
|
74
|
+
def inspect
|
75
|
+
"<FileEntry: @file: #{@file}, @target: #{@target}, @operation: #{@operation}>"
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
## Returns a string representation of the object contents.
|
80
|
+
##
|
81
|
+
## @return [String] String representation of the object.
|
82
|
+
##
|
83
|
+
def to_s
|
84
|
+
File.binary?(@file) ? 'Binary file' : IO.read(@file).to_s
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Planter
|
4
|
+
# File listing class
|
5
|
+
class FileList
|
6
|
+
attr_reader :files
|
7
|
+
|
8
|
+
##
|
9
|
+
## Initialize a new FileList object
|
10
|
+
##
|
11
|
+
## @param path [String] The base path for template
|
12
|
+
##
|
13
|
+
def initialize(path)
|
14
|
+
@basedir = File.realdirpath(path)
|
15
|
+
|
16
|
+
search_path = File.join(@basedir, '**/*')
|
17
|
+
files = Dir.glob(search_path, File::FNM_DOTMATCH).reject do |file|
|
18
|
+
file =~ %r{/(_scripts|\.git|_config\.yml$|\.{1,2}$)}
|
19
|
+
end
|
20
|
+
|
21
|
+
files.sort_by!(&:length)
|
22
|
+
|
23
|
+
@files = files.map do |file|
|
24
|
+
new_file = "#{Planter.target}#{file.sub(/^#{@basedir}/, '').apply_variables.apply_regexes}"
|
25
|
+
operation = Planter.overwrite ? :overwrite : :copy
|
26
|
+
FileEntry.new(file, new_file, operation)
|
27
|
+
end
|
28
|
+
|
29
|
+
prepare_copy
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
## Public method for copying files based on their operator
|
34
|
+
##
|
35
|
+
## @return [Boolean] success
|
36
|
+
##
|
37
|
+
def copy
|
38
|
+
@files.each do |file|
|
39
|
+
handle_operator(file)
|
40
|
+
end
|
41
|
+
rescue StandardError => e
|
42
|
+
Planter.notify("#{e}\n#{e.backtrace}", :debug)
|
43
|
+
Planter.notify('Error copying files/directories', :error, exit_code: 128)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
##
|
49
|
+
## Perform operations
|
50
|
+
##
|
51
|
+
## @param entry [FileEntry] The file entry
|
52
|
+
##
|
53
|
+
def handle_operator(entry)
|
54
|
+
case entry.operation
|
55
|
+
when :ignore
|
56
|
+
false
|
57
|
+
when :overwrite
|
58
|
+
copy_file(entry, overwrite: true)
|
59
|
+
when :merge
|
60
|
+
File.exist?(entry.target) ? merge(entry) : copy_file(entry)
|
61
|
+
else
|
62
|
+
copy_file(entry)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
## Copy template files to new directory
|
68
|
+
##
|
69
|
+
## @return [Boolean] success
|
70
|
+
##
|
71
|
+
def prepare_copy
|
72
|
+
@files.each do |entry|
|
73
|
+
if entry.matches_pattern?
|
74
|
+
entry.operation = entry.test_operator
|
75
|
+
propogate_operation(entry)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
## Apply a parent operation to children
|
82
|
+
##
|
83
|
+
## @param entry [FileEntry] The file entry
|
84
|
+
##
|
85
|
+
def propogate_operation(entry)
|
86
|
+
@files.each do |file|
|
87
|
+
file.operation = entry.operation if file.file =~ /^#{entry.file}/
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
## Copy tagged merge sections from source to target
|
93
|
+
##
|
94
|
+
## @param entry [FileEntry] The file entry
|
95
|
+
##
|
96
|
+
def merge(entry)
|
97
|
+
return copy_file(entry) if File.directory?(entry.file)
|
98
|
+
|
99
|
+
type = `file #{entry.file}`
|
100
|
+
case type.sub(/^#{Regexp.escape(entry.file)}: /, '').split(/:/).first
|
101
|
+
when /Apple binary property list/
|
102
|
+
`plutil -convert xml1 #{entry.file}`
|
103
|
+
`plutil -convert xml1 #{entry.target}`
|
104
|
+
content = IO.read(entry.file)
|
105
|
+
when /data/
|
106
|
+
return copy_file(entry)
|
107
|
+
else
|
108
|
+
return copy_file(entry) if File.binary?(entry.file)
|
109
|
+
|
110
|
+
content = IO.read(entry.file)
|
111
|
+
end
|
112
|
+
|
113
|
+
merges = content.scan(%r{(?<=\A|\n).{,4}merge *\n(.*?)\n.{,4}/merge}m)
|
114
|
+
&.map { |m| m[0].strip.apply_variables.apply_regexes }
|
115
|
+
merges = [content] if !merges || merges.empty?
|
116
|
+
new_content = IO.read(entry.target)
|
117
|
+
merges.delete_if { |m| new_content =~ /#{Regexp.escape(m)}/ }
|
118
|
+
if merges.count.positive?
|
119
|
+
File.open(entry.target, 'w') { |f| f.puts "#{new_content.chomp}\n\n#{merges.join("\n\n")}" }
|
120
|
+
Planter.notify("Merged #{entry.file} => #{entry.target} (#{merges.count} merges)", :debug)
|
121
|
+
else
|
122
|
+
copy_file(entry)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
## Perform file copy based on operator
|
128
|
+
##
|
129
|
+
## @param file [FileEntry] The file entry
|
130
|
+
## @param overwrite [Boolean] Force overwrite
|
131
|
+
##
|
132
|
+
def copy_file(file, overwrite: false)
|
133
|
+
if !File.exist?(file.target) || overwrite || Planter.overwrite
|
134
|
+
FileUtils.mkdir_p(File.dirname(file.target))
|
135
|
+
FileUtils.cp(file.file, file.target) unless File.directory?(file.file)
|
136
|
+
Planter.notify("Copied #{file.file} => #{file.target}", :debug)
|
137
|
+
true
|
138
|
+
else
|
139
|
+
Planter.notify("Skipped #{file.file} => #{file.target}", :debug)
|
140
|
+
false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/lib/planter/hash.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Hash helpers
|
4
|
+
class ::Hash
|
5
|
+
## Turn all keys into string
|
6
|
+
##
|
7
|
+
## @return [Hash] copy of the hash where all its keys are strings
|
8
|
+
##
|
9
|
+
def stringify_keys
|
10
|
+
each_with_object({}) do |(k, v), hsh|
|
11
|
+
hsh[k.to_s] = if v.is_a?(Hash)
|
12
|
+
v.stringify_keys
|
13
|
+
elsif v.is_a?(Array)
|
14
|
+
v.map(&:symbolize_keys)
|
15
|
+
else
|
16
|
+
v
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
## Turn all keys into symbols
|
23
|
+
##
|
24
|
+
## @return [Hash] hash with symbolized keys
|
25
|
+
##
|
26
|
+
def symbolize_keys
|
27
|
+
each_with_object({}) do |(k, v), hsh|
|
28
|
+
hsh[k.to_sym] = if v.is_a?(Hash)
|
29
|
+
v.symbolize_keys
|
30
|
+
elsif v.is_a?(Array)
|
31
|
+
v.map(&:symbolize_keys)
|
32
|
+
else
|
33
|
+
v
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
## Deep merge a hash
|
40
|
+
##
|
41
|
+
## @param second [Hash] The hash to merge into self
|
42
|
+
##
|
43
|
+
def deep_merge(second)
|
44
|
+
merger = proc do |_, v1, v2|
|
45
|
+
if v1.is_a?(Hash) && v2.is_a?(Hash)
|
46
|
+
v1.merge(v2, &merger)
|
47
|
+
elsif v1.is_a?(Array) && v2.is_a?(Array)
|
48
|
+
v1 | v2
|
49
|
+
elsif [:undefined, nil, :nil].include?(v2)
|
50
|
+
v1
|
51
|
+
else
|
52
|
+
v2
|
53
|
+
end
|
54
|
+
end
|
55
|
+
merge(second.to_h, &merger)
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
## Freeze all values in a hash
|
60
|
+
##
|
61
|
+
## @return [Hash] Hash with all values frozen
|
62
|
+
##
|
63
|
+
def deep_freeze
|
64
|
+
chilled = {}
|
65
|
+
each do |k, v|
|
66
|
+
chilled[k] = v.is_a?(Hash) ? v.deep_freeze : v.freeze
|
67
|
+
end
|
68
|
+
|
69
|
+
chilled.freeze
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
## Destructive version of #deep_freeze
|
74
|
+
##
|
75
|
+
## @return [Hash] Hash with all values frozen
|
76
|
+
##
|
77
|
+
def deep_freeze!
|
78
|
+
replace deep_thaw.deep_freeze
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
## Unfreeze a hash and all nested values
|
83
|
+
##
|
84
|
+
## @return [Hash] unfrozen hash
|
85
|
+
##
|
86
|
+
def deep_thaw
|
87
|
+
chilled = {}
|
88
|
+
each do |k, v|
|
89
|
+
chilled[k] = v.is_a?(Hash) ? v.deep_thaw : v.dup
|
90
|
+
end
|
91
|
+
|
92
|
+
chilled.dup
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
## Destructive version of #deep_thaw
|
97
|
+
##
|
98
|
+
## @return [Hash] unfrozen hash
|
99
|
+
##
|
100
|
+
def deep_thaw!
|
101
|
+
replace deep_thaw
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Planter
|
4
|
+
# Primary class
|
5
|
+
class Plant
|
6
|
+
##
|
7
|
+
## Initialize a new Plant object
|
8
|
+
##
|
9
|
+
## @param template [String] the template name
|
10
|
+
## @param variables [Hash] Pre-populated variables
|
11
|
+
##
|
12
|
+
def initialize(template = nil, variables = nil)
|
13
|
+
Planter.variables = variables if variables.is_a?(Hash)
|
14
|
+
Planter.config = template if template
|
15
|
+
|
16
|
+
@basedir = File.join(Planter::BASE_DIR, 'templates', Planter.template)
|
17
|
+
@target = Planter.target || Dir.pwd
|
18
|
+
|
19
|
+
@git = Planter.config[:git_init] || false
|
20
|
+
@debug = Planter.debug
|
21
|
+
@repo = Planter.config[:repo] || false
|
22
|
+
|
23
|
+
# Coerce any existing variables (like from the command line) to the types
|
24
|
+
# defined in configuration
|
25
|
+
coerced = {}
|
26
|
+
Planter.variables.each do |k, v|
|
27
|
+
cfg_var = Planter.config[:variables].select { |var| k = var[:key] }
|
28
|
+
next unless cfg_var.count.positive?
|
29
|
+
|
30
|
+
var = cfg_var.first
|
31
|
+
type = var[:type].normalize_type
|
32
|
+
coerced[k] = v.coerce(type)
|
33
|
+
end
|
34
|
+
coerced.each { |k, v| Planter.variables[k] = v }
|
35
|
+
|
36
|
+
# Ask user for any variables not already defined
|
37
|
+
Planter.config[:variables].each do |var|
|
38
|
+
key = var[:key].to_var
|
39
|
+
next if Planter.variables.keys.include?(key)
|
40
|
+
|
41
|
+
q = Planter::Prompt::Question.new(
|
42
|
+
key: key,
|
43
|
+
prompt: var[:prompt] || var[:key],
|
44
|
+
type: var[:type].normalize_type || :string,
|
45
|
+
default: var[:default],
|
46
|
+
value: var[:value],
|
47
|
+
min: var[:min],
|
48
|
+
max: var[:max]
|
49
|
+
)
|
50
|
+
answer = q.ask
|
51
|
+
if answer.nil?
|
52
|
+
Planter.notify("Missing value #{key}", :error, exit_code: 15) unless var[:default]
|
53
|
+
|
54
|
+
answer = var[:default]
|
55
|
+
end
|
56
|
+
|
57
|
+
Planter.variables[key] = answer
|
58
|
+
end
|
59
|
+
|
60
|
+
git_pull if @repo
|
61
|
+
|
62
|
+
@files = FileList.new(@basedir)
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
## Expand GitHub name to full path
|
67
|
+
##
|
68
|
+
## @example Pass a GitHub-style repo path and get full url
|
69
|
+
## expand_repo("ttscoff/planter-cli") #=> https://github.com/ttscoff/planter-cli.git
|
70
|
+
##
|
71
|
+
## @return { description_of_the_return_value }
|
72
|
+
##
|
73
|
+
def expand_repo(repo)
|
74
|
+
repo =~ %r{(?!=http)\w+/\w+} ? "https://github.com/#{repo}.git" : repo
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
## Directory for repo, subdirectory of template
|
79
|
+
##
|
80
|
+
## @return [String] repo path
|
81
|
+
##
|
82
|
+
def repo_dir
|
83
|
+
File.join(@basedir, File.basename(@repo).sub(/\.git$/, ''))
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
## Pull or clone a git repo
|
88
|
+
##
|
89
|
+
## @return [String] new base directory
|
90
|
+
##
|
91
|
+
def git_pull
|
92
|
+
Planter.spinner.update(title: 'Pulling git repo')
|
93
|
+
|
94
|
+
raise Errors::GitError.new('`git` executable not found') unless TTY::Which.exist?('git')
|
95
|
+
|
96
|
+
pwd = Dir.pwd
|
97
|
+
@repo = expand_repo(@repo)
|
98
|
+
|
99
|
+
if File.exist?(repo_dir)
|
100
|
+
Dir.chdir(repo_dir)
|
101
|
+
raise Errors::GitError.new("Directory #{repo_dir} exists but is not git repo") unless File.exist?('.git')
|
102
|
+
|
103
|
+
res = `git pull`
|
104
|
+
raise Errors::GitError.new("Error pulling #{@repo}:\n#{res}") unless $?.success?
|
105
|
+
else
|
106
|
+
Dir.chdir(@basedir)
|
107
|
+
res = `git clone "#{@repo}" "#{repo_dir}"`
|
108
|
+
raise Errors::GitError.new("Error cloning #{@repo}:\n#{res}") unless $?.success?
|
109
|
+
end
|
110
|
+
Dir.chdir(pwd)
|
111
|
+
@basedir = repo_dir
|
112
|
+
rescue StandardError => e
|
113
|
+
raise Errors::GitError.new("Error pulling #{@repo}:\n#{e.message}")
|
114
|
+
end
|
115
|
+
|
116
|
+
##
|
117
|
+
## Plant the template to current directory
|
118
|
+
##
|
119
|
+
def plant
|
120
|
+
Dir.chdir(@target)
|
121
|
+
|
122
|
+
Planter.spinner.auto_spin
|
123
|
+
Planter.spinner.update(title: 'Copying files')
|
124
|
+
res = copy_files
|
125
|
+
if res.is_a?(String)
|
126
|
+
Planter.spinner.error("(#{res})")
|
127
|
+
Process.exit 1
|
128
|
+
end
|
129
|
+
|
130
|
+
Planter.spinner.update(title: 'Applying variables')
|
131
|
+
|
132
|
+
res = update_files
|
133
|
+
if res.is_a?(String)
|
134
|
+
Planter.spinner.error('(Error)')
|
135
|
+
Planter.notify(res, :error, exit_code: 1)
|
136
|
+
end
|
137
|
+
|
138
|
+
if @git
|
139
|
+
raise Errors::GitError.new('`git` executable not found') unless TTY::Which.exist?('git')
|
140
|
+
|
141
|
+
Planter.spinner.update(title: 'Initializing git repo')
|
142
|
+
res = add_git
|
143
|
+
if res.is_a?(String)
|
144
|
+
Planter.spinner.error('(Error)')
|
145
|
+
Planter.notify(res, :error, exit_code: 1)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
if Planter.config[:script]
|
150
|
+
Planter.spinner.update(title: 'Running script')
|
151
|
+
|
152
|
+
scripts = Planter.config[:script]
|
153
|
+
scripts = [scripts] if scripts.is_a?(String)
|
154
|
+
scripts.each do |script|
|
155
|
+
s = Planter::Script.new(@basedir, Dir.pwd, script)
|
156
|
+
s.run
|
157
|
+
end
|
158
|
+
end
|
159
|
+
Planter.spinner.update(title: '😄')
|
160
|
+
Planter.spinner.success(' Planting complete!')
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
## Copy files from template directory, renaming if %%template vars%% exist in title
|
165
|
+
##
|
166
|
+
## @return true if successful, otherwise error description
|
167
|
+
##
|
168
|
+
def copy_files
|
169
|
+
@files.copy
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
## Update content of files in new directory using template variables
|
175
|
+
##
|
176
|
+
def update_files
|
177
|
+
files = Dir.glob('**/*', File::FNM_DOTMATCH).reject { |f| File.directory?(f) || f =~ /^(\.git|config\.yml)/ }
|
178
|
+
|
179
|
+
files.each do |file|
|
180
|
+
type = `file #{file}`
|
181
|
+
case type.sub(/^#{Regexp.escape(file)}: /, '').split(/:/).first
|
182
|
+
when /Apple binary property list/
|
183
|
+
`plutil -convert xml1 #{file}`
|
184
|
+
when /data/
|
185
|
+
next
|
186
|
+
else
|
187
|
+
next if File.binary?(file)
|
188
|
+
end
|
189
|
+
|
190
|
+
content = IO.read(file)
|
191
|
+
new_content = content.apply_variables.apply_regexes
|
192
|
+
|
193
|
+
new_content.gsub!(%r{^.{.4}/?merge *.{,4}\n}, '') if new_content =~ /^.{.4}merge *\n/
|
194
|
+
|
195
|
+
unless content == new_content
|
196
|
+
Planter.notify("Applying variables to #{file}", :debug)
|
197
|
+
File.open(file, 'w') { |f| f.puts new_content }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
true
|
202
|
+
rescue StandardError => e
|
203
|
+
Planter.notify("#{e}\n#{e.backtrace}", :debug)
|
204
|
+
'Error updating files/directories'
|
205
|
+
end
|
206
|
+
|
207
|
+
##
|
208
|
+
## Initialize a git repo and create initial commit/tag
|
209
|
+
##
|
210
|
+
## @return true if successful, otherwise an error description
|
211
|
+
##
|
212
|
+
def add_git
|
213
|
+
return if File.directory?('.git')
|
214
|
+
|
215
|
+
res = pass_fail('git init')
|
216
|
+
res = pass_fail('git add .') if res
|
217
|
+
res = pass_fail('git commit -a -m "initial commit"') if res
|
218
|
+
res = pass_fail('git tag -a 0.0.1 -m "v0.0.1"') if res
|
219
|
+
|
220
|
+
raise StandardError unless res
|
221
|
+
|
222
|
+
true
|
223
|
+
rescue StandardError => e
|
224
|
+
Planter.notify("#{e}\n#{e.backtrace}", :debug)
|
225
|
+
'Error initializing git'
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|