planter-cli 0.0.3 → 3.0.1
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/.gitignore +4 -1
- data/.rubocop.yml +5 -7
- data/CHANGELOG.md +21 -0
- data/README.md +28 -1
- data/Rakefile +54 -18
- data/bin/plant +6 -0
- data/docker/Dockerfile-2.6 +5 -5
- data/docker/Dockerfile-2.7 +3 -3
- data/docker/Dockerfile-3.0 +3 -3
- data/lib/planter/array.rb +51 -0
- data/lib/planter/color.rb +1 -1
- data/lib/planter/errors.rb +14 -0
- data/lib/planter/file.rb +87 -4
- data/lib/planter/fileentry.rb +5 -1
- data/lib/planter/filelist.rb +43 -7
- data/lib/planter/hash.rb +81 -84
- data/lib/planter/plant.rb +4 -10
- data/lib/planter/prompt.rb +6 -3
- data/lib/planter/script.rb +24 -12
- data/lib/planter/string.rb +134 -29
- data/lib/planter/tag.rb +54 -0
- data/lib/planter/version.rb +1 -1
- data/lib/planter.rb +60 -34
- data/planter-cli.gemspec +1 -0
- data/spec/config.yml +2 -0
- data/spec/planter/array_spec.rb +28 -0
- data/spec/planter/file_entry_spec.rb +40 -0
- data/spec/planter/file_spec.rb +19 -0
- data/spec/planter/filelist_spec.rb +15 -0
- data/spec/planter/hash_spec.rb +110 -0
- data/spec/planter/plant_spec.rb +1 -0
- data/spec/planter/script_spec.rb +80 -0
- data/spec/planter/string_spec.rb +215 -2
- data/spec/planter/symbol_spec.rb +23 -0
- data/spec/planter.yml +6 -0
- data/spec/planter_spec.rb +82 -0
- data/spec/scripts/test.sh +3 -0
- data/spec/scripts/test_fail.sh +3 -0
- data/spec/spec_helper.rb +8 -2
- data/spec/templates/test/%%project:snake%%.rtf +10 -0
- data/spec/templates/test/Rakefile +6 -0
- data/spec/templates/test/_planter.yml +12 -0
- data/spec/templates/test/_scripts/test.sh +3 -0
- data/spec/templates/test/_scripts/test_fail.sh +3 -0
- data/spec/templates/test/test.rb +5 -0
- data/spec/test_out/image.png +0 -0
- data/spec/test_out/test2.rb +5 -0
- data/src/_README.md +28 -1
- metadata +57 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b4906c83392316be6ac61380bd0d2401a847f4c08f6afc91bdfbd4858980568
|
4
|
+
data.tar.gz: 7253e2ec572be45e7543d82ad2b1dde685fc5b8ba7b256ec914b0575e44ad507
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98fd1c334e3d2efdcdc10171d7a5b5f81898e9a03890bc08cd65e2f3c8a10941cfe46e5e5b23584f28c87aad3b85471ab544c3665049c5abfd911006b40bfbe8
|
7
|
+
data.tar.gz: a7a86edaf7bf49cb65cde877ab4aec8bd92a6f27a90c09f526e50f9f1ddc84c27655e67bfea75b9463c8033601ae1961e4854bce76f4e268bfe87aa3171c7677
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -23,10 +23,6 @@ Style/MutableConstant:
|
|
23
23
|
Style/SpecialGlobalVars:
|
24
24
|
Enabled: false
|
25
25
|
|
26
|
-
Security/YAMLLoad:
|
27
|
-
Exclude:
|
28
|
-
- 'lib/**/*.rb'
|
29
|
-
|
30
26
|
Style/StringLiterals:
|
31
27
|
Enabled: true
|
32
28
|
EnforcedStyle: single_quotes
|
@@ -45,7 +41,7 @@ Metrics/BlockLength:
|
|
45
41
|
Max: 45
|
46
42
|
Exclude:
|
47
43
|
- Rakefile
|
48
|
-
- bin/
|
44
|
+
- bin/untitled
|
49
45
|
- lib/*.rb
|
50
46
|
|
51
47
|
Metrics/ClassLength:
|
@@ -67,8 +63,7 @@ Metrics/ModuleLength:
|
|
67
63
|
Max: 174
|
68
64
|
|
69
65
|
Security/YAMLLoad:
|
70
|
-
|
71
|
-
- '**/*.rb'
|
66
|
+
Enabled: false
|
72
67
|
|
73
68
|
Style/ModuleFunction:
|
74
69
|
Exclude:
|
@@ -76,3 +71,6 @@ Style/ModuleFunction:
|
|
76
71
|
|
77
72
|
Style/RaiseArgs:
|
78
73
|
EnforcedStyle: compact
|
74
|
+
|
75
|
+
Style/SlicingWithRange:
|
76
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
### 3.0.1
|
2
|
+
|
3
|
+
2024-08-31 14:19
|
4
|
+
|
5
|
+
### 3.0.0-alpha
|
6
|
+
|
7
|
+
2024-08-31 14:18
|
8
|
+
|
9
|
+
#### NEW
|
10
|
+
|
11
|
+
- Initial release
|
12
|
+
- Preserve Finder tags when planting (config option `preserve_tags: true`)
|
13
|
+
|
14
|
+
#### IMPROVED
|
15
|
+
|
16
|
+
- More tests
|
17
|
+
|
18
|
+
#### FIXED
|
19
|
+
|
20
|
+
- Code refactoring
|
21
|
+
|
1
22
|
### 0.0.3
|
2
23
|
|
3
24
|
2024-08-28 09:46
|
data/README.md
CHANGED
@@ -14,7 +14,18 @@ If [Gum](https://github.com/charmbracelet/gum) is available it will be used for
|
|
14
14
|
|
15
15
|
## Configuration
|
16
16
|
|
17
|
-
Planter's base configuration is in `~/.config/planter/
|
17
|
+
Planter's base configuration is in `~/.config/planter/planter.yml`. This file can contain any of the keys used in templates (see below) and will serve as a base configuration for all templates. Any key defined in this file will be overridden if it exists in a template.
|
18
|
+
|
19
|
+
Example config (written on first run):
|
20
|
+
|
21
|
+
```yaml
|
22
|
+
files:
|
23
|
+
.DS_Store: ignore
|
24
|
+
"*.tmp": ignore
|
25
|
+
"*.bak": ignore
|
26
|
+
git_init: false
|
27
|
+
preserve_tags: true
|
28
|
+
```
|
18
29
|
|
19
30
|
### Scripts.
|
20
31
|
|
@@ -49,6 +60,10 @@ replacements: # Dictionary of pattern/replacments for regex substitution, see [R
|
|
49
60
|
repo: # If a repository URL is provided, it will be pulled and duplicated instead of copying a file structure
|
50
61
|
```
|
51
62
|
|
63
|
+
#### Default values in template strings
|
64
|
+
|
65
|
+
In a template you can add a default value for a placholder by adding `%default value` to it. For example, `%%project%Default Project%%` will set the placeholder to `Default Project` if the variable value matches the default value in the configuration. This allows you to accept the default on the command line but have a different value inserted in the template. To use another variable in its place, use `$KEY` in the placeholder, e.g. `%%project%$title%%` will replace the `project` key with the value of `title` if the default is selected. Modifiers can be used on either side of the `%`, e.g. `%%project%$title:snake%%`.
|
66
|
+
|
52
67
|
### File-specific handling
|
53
68
|
|
54
69
|
A `files` dictionary can specify how to handle specific files. Options are `copy`, `overwrite`, `merge`, or `ask`. The key for each entry is a filename or glob that matches the source filename (accounting for template variables if applicable):
|
@@ -67,6 +82,14 @@ Merged content
|
|
67
82
|
// /merge
|
68
83
|
```
|
69
84
|
|
85
|
+
Or
|
86
|
+
|
87
|
+
```
|
88
|
+
# merge
|
89
|
+
Merged content
|
90
|
+
# /merge
|
91
|
+
```
|
92
|
+
|
70
93
|
By default files that already exist in the destination directory are not overwritten, and merging allows you to add missing parts to a Rakefile or Makefile, for example.
|
71
94
|
|
72
95
|
If `ask` is specified, a memu will be provided on the command line asking how to handle a file. If the file doesn't already exist, you will be asked only whether to copy the file or not. If it does exist, `overwrite` and `merge` options will be added.
|
@@ -83,6 +106,10 @@ replacements:
|
|
83
106
|
|
84
107
|
Replacements are performed on both file/directory names and file contents.
|
85
108
|
|
109
|
+
### Finder Tags
|
110
|
+
|
111
|
+
If `preserve_tags` is set to `true` in the config (either base or template), then existing Finder tags on the file or folder will be copied to the new file when a template is planted.
|
112
|
+
|
86
113
|
## Usage
|
87
114
|
|
88
115
|
The executable for Planter is `plant`. You can run `plant TEMPLATE` in any directory and TEMPLATE will be planted in the current directory. You can also use `--in PATH` to plant in another directory.
|
data/Rakefile
CHANGED
@@ -38,17 +38,6 @@ end
|
|
38
38
|
desc 'Clobber files'
|
39
39
|
task clobber: :clobber_packages
|
40
40
|
|
41
|
-
desc 'Development version check'
|
42
|
-
task :ver do
|
43
|
-
gver = `git ver`
|
44
|
-
cver = IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
|
45
|
-
res = `grep VERSION lib/planter/version.rb`
|
46
|
-
version = res.match(/VERSION *= *['"](\d+\.\d+\.\d+(\w+)?)/)[1]
|
47
|
-
puts "git tag: #{gver}"
|
48
|
-
puts "version.rb: #{version}"
|
49
|
-
puts "changelog: #{cver}"
|
50
|
-
end
|
51
|
-
|
52
41
|
desc 'Get Script Version'
|
53
42
|
task :sver do
|
54
43
|
res = `grep VERSION lib/planter/version.rb`
|
@@ -56,11 +45,6 @@ task :sver do
|
|
56
45
|
print version
|
57
46
|
end
|
58
47
|
|
59
|
-
desc 'Changelog version check'
|
60
|
-
task :cver do
|
61
|
-
puts IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
|
62
|
-
end
|
63
|
-
|
64
48
|
desc 'Run tests in Docker'
|
65
49
|
task :dockertest, :version, :login, :attempt do |_, args|
|
66
50
|
args.with_defaults(version: 'all', login: false, attempt: 1)
|
@@ -78,6 +62,9 @@ task :dockertest, :version, :login, :attempt do |_, args|
|
|
78
62
|
Rake::Task['dockertest'].invoke(v, false)
|
79
63
|
end
|
80
64
|
Process.exit 0
|
65
|
+
when /^3\.?3/
|
66
|
+
img = 'plantertest33'
|
67
|
+
file = 'docker/Dockerfile-3.3'
|
81
68
|
when /^3/
|
82
69
|
version = '3.0'
|
83
70
|
img = 'plantertest3'
|
@@ -107,10 +94,10 @@ task :dockertest, :version, :login, :attempt do |_, args|
|
|
107
94
|
dir_args = dirs.map { |s, d| " -v '#{s}:#{d}'" }.join(' ')
|
108
95
|
exec "docker run #{dir_args} -it #{img} /bin/bash -l" if args[:login]
|
109
96
|
|
110
|
-
spinner = TTY::Spinner.new("[:spinner] Running tests (#{
|
97
|
+
spinner = TTY::Spinner.new("[:spinner] Running tests (#{version})...", hide_cursor: true)
|
111
98
|
|
112
99
|
spinner.auto_spin
|
113
|
-
|
100
|
+
`docker run --rm #{dir_args} -it #{img}`
|
114
101
|
# raise DockerError.new('Error running docker image') unless $?.success?
|
115
102
|
|
116
103
|
# commit = puts `bash -c "docker commit $(docker ps -a|grep #{img}|awk '{print $1}'|head -n 1) #{img}"`.strip
|
@@ -130,3 +117,52 @@ end
|
|
130
117
|
|
131
118
|
desc 'alias for build'
|
132
119
|
task package: :build
|
120
|
+
|
121
|
+
desc 'Development version check'
|
122
|
+
task :ver do
|
123
|
+
gver = `git ver`
|
124
|
+
cver = IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
|
125
|
+
res = `grep VERSION lib/untitled/version.rb`
|
126
|
+
version = res.match(/VERSION *= *['"](\d+\.\d+\.\d+(\w+)?)/)[1]
|
127
|
+
puts "git tag: #{gver}"
|
128
|
+
puts "version.rb: #{version}"
|
129
|
+
puts "changelog: #{cver}"
|
130
|
+
end
|
131
|
+
|
132
|
+
desc 'Changelog version check'
|
133
|
+
task :cver do
|
134
|
+
puts IO.read(File.join(File.dirname(__FILE__), 'CHANGELOG.md')).match(/^#+ (\d+\.\d+\.\d+(\w+)?)/)[1]
|
135
|
+
end
|
136
|
+
|
137
|
+
desc 'Alias for build'
|
138
|
+
task package: :build
|
139
|
+
|
140
|
+
desc 'Bump incremental version number'
|
141
|
+
task :bump, :type do |_, args|
|
142
|
+
args.with_defaults(type: 'inc')
|
143
|
+
version_file = 'lib/untitled/version.rb'
|
144
|
+
content = IO.read(version_file)
|
145
|
+
content.sub!(/VERSION = '(?<major>\d+)\.(?<minor>\d+)\.(?<inc>\d+)(?<pre>\S+)?'/) do
|
146
|
+
m = Regexp.last_match
|
147
|
+
major = m['major'].to_i
|
148
|
+
minor = m['minor'].to_i
|
149
|
+
inc = m['inc'].to_i
|
150
|
+
pre = m['pre']
|
151
|
+
|
152
|
+
case args[:type]
|
153
|
+
when /^maj/
|
154
|
+
major += 1
|
155
|
+
minor = 0
|
156
|
+
inc = 0
|
157
|
+
when /^min/
|
158
|
+
minor += 1
|
159
|
+
inc = 0
|
160
|
+
else
|
161
|
+
inc += 1
|
162
|
+
end
|
163
|
+
|
164
|
+
$stdout.puts "At version #{major}.#{minor}.#{inc}#{pre}"
|
165
|
+
"VERSION = '#{major}.#{minor}.#{inc}#{pre}'"
|
166
|
+
end
|
167
|
+
File.open(version_file, 'w+') { |f| f.puts content }
|
168
|
+
end
|
data/bin/plant
CHANGED
@@ -22,6 +22,7 @@ options = {
|
|
22
22
|
# min: 1
|
23
23
|
# max: 5
|
24
24
|
Planter.variables = {}
|
25
|
+
Planter.base_dir = ENV['PLANTER_DIR'] || File.expand_path('~/.config/planter')
|
25
26
|
Planter::Color.coloring = $stdout.isatty
|
26
27
|
|
27
28
|
opts = OptionParser.new
|
@@ -53,6 +54,10 @@ opts.on('-o', '--overwrite', 'Overwrite existing files') do
|
|
53
54
|
Planter.overwrite = true
|
54
55
|
end
|
55
56
|
|
57
|
+
opts.on_tail('--base-dir DIRECTORY', 'Use an alternate base directory for config and templates') do |opt|
|
58
|
+
Planter.base_dir = opt
|
59
|
+
end
|
60
|
+
|
56
61
|
opts.on_tail('-d', '--debug', 'Display version number') do
|
57
62
|
Planter.debug = true
|
58
63
|
end
|
@@ -100,6 +105,7 @@ elsif ARGV.count.zero?
|
|
100
105
|
end
|
101
106
|
|
102
107
|
ARGV.each do |template|
|
108
|
+
Planter.spinner.update(title: 'Initializing configuration')
|
103
109
|
Planter.config = template
|
104
110
|
app = Planter::Plant.new
|
105
111
|
app.plant
|
data/docker/Dockerfile-2.6
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
FROM ruby:2.6
|
2
2
|
# RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
3
|
-
RUN mkdir /
|
4
|
-
WORKDIR /
|
5
|
-
# COPY ./ /
|
3
|
+
RUN mkdir /planter
|
4
|
+
WORKDIR /planter
|
5
|
+
# COPY ./ /planter/
|
6
6
|
RUN gem install bundler:2.2.29
|
7
|
-
RUN apt-get update -y
|
8
|
-
RUN apt-get install -y less vim
|
7
|
+
# RUN apt-get update -y
|
8
|
+
# RUN apt-get install -y less vim
|
9
9
|
COPY ./docker/inputrc /root/.inputrc
|
10
10
|
COPY ./docker/bash_profile /root/.bash_profile
|
11
11
|
RUN mkdir -p /root/.config/planter/templates/test
|
data/docker/Dockerfile-2.7
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
FROM ruby:2.7
|
2
2
|
# RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
3
|
-
RUN mkdir /
|
4
|
-
WORKDIR /
|
5
|
-
# COPY ./ /
|
3
|
+
RUN mkdir /planter
|
4
|
+
WORKDIR /planter
|
5
|
+
# COPY ./ /planter/
|
6
6
|
RUN gem install bundler:2.2.29
|
7
7
|
RUN apt-get update -y
|
8
8
|
RUN apt-get install -y less vim
|
data/docker/Dockerfile-3.0
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
FROM ruby:3.0.0
|
2
2
|
# RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
|
3
|
-
RUN mkdir /
|
4
|
-
WORKDIR /
|
5
|
-
# COPY ./ /
|
3
|
+
RUN mkdir /planter
|
4
|
+
WORKDIR /planter
|
5
|
+
# COPY ./ /planter/
|
6
6
|
RUN gem install bundler:2.2.29
|
7
7
|
RUN apt-get update -y
|
8
8
|
RUN apt-get install -y less vim
|
data/lib/planter/array.rb
CHANGED
@@ -24,5 +24,56 @@ module Planter
|
|
24
24
|
end.join('{dw}/')
|
25
25
|
out << '{dw}]{x}'
|
26
26
|
end
|
27
|
+
|
28
|
+
##
|
29
|
+
## Stringify keys in an array of hashes or arrays
|
30
|
+
##
|
31
|
+
## @return [Array] Array with nested hash keys stringified
|
32
|
+
##
|
33
|
+
def stringify_keys
|
34
|
+
each_with_object([]) do |v, arr|
|
35
|
+
arr << if v.is_a?(Hash)
|
36
|
+
v.stringify_keys
|
37
|
+
elsif v.is_a?(Array)
|
38
|
+
v.map { |x| x.is_a?(Hash) || x.is_a?(Array) ? x.stringify_keys : x }
|
39
|
+
else
|
40
|
+
v
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
## Symbolize keys in an array of hashes or arrays
|
47
|
+
##
|
48
|
+
## @return [Array] Array with nested hash keys symbolized
|
49
|
+
##
|
50
|
+
def symbolize_keys
|
51
|
+
each_with_object([]) do |v, arr|
|
52
|
+
arr << if v.is_a?(Hash)
|
53
|
+
v.symbolize_keys
|
54
|
+
elsif v.is_a?(Array)
|
55
|
+
v.map { |x| x.is_a?(Hash) || x.is_a?(Array) ? x.symbolize_keys : x }
|
56
|
+
else
|
57
|
+
v
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Destructive version of #symbolize_keys
|
64
|
+
#
|
65
|
+
# @return [Array] Array with symbolized keys
|
66
|
+
#
|
67
|
+
def symbolize_keys!
|
68
|
+
replace deep_dup.symbolize_keys
|
69
|
+
end
|
70
|
+
|
71
|
+
## Deep duplicate an array of hashes or arrays
|
72
|
+
##
|
73
|
+
## @return [Array] Deep duplicated array
|
74
|
+
##
|
75
|
+
def deep_dup
|
76
|
+
map { |v| v.is_a?(Hash) || v.is_a?(Array) ? v.deep_dup : v.dup }
|
77
|
+
end
|
27
78
|
end
|
28
79
|
end
|
data/lib/planter/color.rb
CHANGED
data/lib/planter/errors.rb
CHANGED
@@ -7,6 +7,7 @@ module Planter
|
|
7
7
|
EXIT_CODES = {
|
8
8
|
argument: 12,
|
9
9
|
canceled: 1,
|
10
|
+
script: 10,
|
10
11
|
config: 127,
|
11
12
|
git: 129
|
12
13
|
}.deep_freeze
|
@@ -55,5 +56,18 @@ module Planter
|
|
55
56
|
super(msg)
|
56
57
|
end
|
57
58
|
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# Script error
|
62
|
+
#
|
63
|
+
class ScriptError < StandardError
|
64
|
+
def initialize(msg = nil)
|
65
|
+
msg = msg ? "Script: #{msg}" : 'Script error'
|
66
|
+
Planter.spinner.error('(Error)')
|
67
|
+
Planter.notify(msg, :error, exit_code: EXIT_CODES[:script])
|
68
|
+
|
69
|
+
super(msg)
|
70
|
+
end
|
71
|
+
end
|
58
72
|
end
|
59
73
|
end
|
data/lib/planter/file.rb
CHANGED
@@ -2,10 +2,93 @@
|
|
2
2
|
|
3
3
|
# Main module
|
4
4
|
module Planter
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
# File helpers
|
6
|
+
class ::File
|
7
|
+
#
|
8
|
+
# Test if file is text
|
9
|
+
#
|
10
|
+
# @param name [String] file path
|
11
|
+
#
|
12
|
+
# @return [Boolean] File is text
|
13
|
+
#
|
14
|
+
def self.text?(name)
|
15
|
+
!binary?(name)
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Test if file is binary
|
20
|
+
#
|
21
|
+
# @param name [String] File path
|
22
|
+
#
|
23
|
+
# @return [Boolean] file is binary
|
24
|
+
#
|
25
|
+
def self.binary?(name)
|
26
|
+
return true if name.nil? || name.empty? || !File.exist?(name)
|
27
|
+
|
28
|
+
ascii = control = binary = 0
|
29
|
+
|
30
|
+
bytes = File.open(name, 'rb') { |io| io.read(1024) }
|
31
|
+
return true if bytes.nil? || bytes.empty?
|
32
|
+
|
33
|
+
bytes.each_byte do |bt|
|
34
|
+
case bt
|
35
|
+
when 0...32
|
36
|
+
control += 1
|
37
|
+
when 32...128
|
38
|
+
ascii += 1
|
39
|
+
else
|
40
|
+
binary += 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
first_test = binary.to_f / ascii > 0.05
|
45
|
+
|
46
|
+
first_test || second_test(name)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Allowable text file types
|
50
|
+
TEXT_TYPES = %w[text ansi xml json yaml csv empty].freeze
|
51
|
+
|
52
|
+
#
|
53
|
+
# Secondary test with file command
|
54
|
+
#
|
55
|
+
# @param name [String] file path
|
56
|
+
#
|
57
|
+
# @return [Boolean] file is binary according to file command
|
58
|
+
#
|
59
|
+
def self.second_test(name)
|
60
|
+
if TTY::Which.exist?('file')
|
61
|
+
file_type, status = Open3.capture2e('file', name)
|
62
|
+
file_type = file_type.split(':')[1..-1].join(':').strip
|
63
|
+
if file_type =~ /Apple binary property list/ && TTY::Which.exist?('plutil')
|
64
|
+
`plutil -convert xml1 "#{name}"`
|
65
|
+
File.binary?(name)
|
66
|
+
else
|
67
|
+
status.success? && !text_type?(file_type)
|
68
|
+
end
|
69
|
+
else
|
70
|
+
false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Tertiary test for binary file
|
76
|
+
#
|
77
|
+
# @param name [String] file path
|
78
|
+
#
|
79
|
+
# @return [Boolean] file is binary according to mdls
|
80
|
+
#
|
81
|
+
def self.third_test(name)
|
82
|
+
if TTY::Which.exist?('mdls')
|
83
|
+
file_type, status = Open3.capture2e('mdls', '-name', 'kMDItemContentTypeTree', name)
|
84
|
+
status.success? && !text_type?(file_type)
|
85
|
+
else
|
86
|
+
false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.text_type?(name)
|
91
|
+
TEXT_TYPES.any? { |type| name.downcase.include?(type) } || name.empty?
|
8
92
|
end
|
9
|
-
false
|
10
93
|
end
|
11
94
|
end
|
data/lib/planter/fileentry.rb
CHANGED
@@ -7,7 +7,7 @@ module Planter
|
|
7
7
|
attr_accessor :operation
|
8
8
|
|
9
9
|
# File path and target path
|
10
|
-
attr_reader :file, :target
|
10
|
+
attr_reader :file, :target, :tags
|
11
11
|
|
12
12
|
##
|
13
13
|
## Initialize a FileEntry object
|
@@ -19,10 +19,14 @@ module Planter
|
|
19
19
|
## @return [FileEntry] a Hash of parameters
|
20
20
|
##
|
21
21
|
def initialize(file, target, operation)
|
22
|
+
return nil unless File.exist?(file)
|
23
|
+
|
22
24
|
@file = file
|
23
25
|
@target = target
|
24
26
|
@operation = operation
|
25
27
|
|
28
|
+
@tags = Tag.get(file)
|
29
|
+
|
26
30
|
super()
|
27
31
|
end
|
28
32
|
|
data/lib/planter/filelist.rb
CHANGED
@@ -10,7 +10,7 @@ module Planter
|
|
10
10
|
##
|
11
11
|
## @param path [String] The base path for template
|
12
12
|
##
|
13
|
-
def initialize(path)
|
13
|
+
def initialize(path = Planter.base_dir)
|
14
14
|
@basedir = File.realdirpath(path)
|
15
15
|
|
16
16
|
search_path = File.join(@basedir, '**/*')
|
@@ -30,9 +30,9 @@ module Planter
|
|
30
30
|
end
|
31
31
|
|
32
32
|
##
|
33
|
-
## Public method for copying files based on their operator
|
33
|
+
## Public method for copying @files to target based on their operator
|
34
34
|
##
|
35
|
-
## @return [Boolean] success
|
35
|
+
## @return [Boolean] success or failure
|
36
36
|
##
|
37
37
|
def copy
|
38
38
|
@files.each do |file|
|
@@ -61,6 +61,14 @@ module Planter
|
|
61
61
|
else
|
62
62
|
copy_file(entry)
|
63
63
|
end
|
64
|
+
|
65
|
+
apply_tags(entry)
|
66
|
+
end
|
67
|
+
|
68
|
+
def apply_tags(entry)
|
69
|
+
return unless Planter.config[:preserve_tags]
|
70
|
+
|
71
|
+
Tag.copy(entry.file, entry.target) if File.exist?(entry.target)
|
64
72
|
end
|
65
73
|
|
66
74
|
##
|
@@ -89,36 +97,53 @@ module Planter
|
|
89
97
|
end
|
90
98
|
|
91
99
|
##
|
92
|
-
## Copy tagged merge sections from source to target
|
100
|
+
## Copy tagged merge sections from source to target. If merge tags do not exist in the file,
|
101
|
+
## append the entire file contents to the target.
|
93
102
|
##
|
94
103
|
## @param entry [FileEntry] The file entry
|
95
104
|
##
|
105
|
+
## @return [Boolean] success
|
106
|
+
##
|
96
107
|
def merge(entry)
|
97
108
|
return copy_file(entry) if File.directory?(entry.file)
|
98
109
|
|
110
|
+
# Get the file type
|
99
111
|
type = `file #{entry.file}`
|
100
112
|
case type.sub(/^#{Regexp.escape(entry.file)}: /, '').split(/:/).first
|
101
113
|
when /Apple binary property list/
|
114
|
+
# Convert to XML1 format
|
102
115
|
`plutil -convert xml1 #{entry.file}`
|
103
116
|
`plutil -convert xml1 #{entry.target}`
|
104
117
|
content = IO.read(entry.file)
|
105
118
|
when /data/
|
119
|
+
# Simply copy the file
|
106
120
|
return copy_file(entry)
|
107
121
|
else
|
122
|
+
# Copy the file if it is binary
|
108
123
|
return copy_file(entry) if File.binary?(entry.file)
|
109
124
|
|
125
|
+
# Read the file content
|
110
126
|
content = IO.read(entry.file)
|
111
127
|
end
|
112
128
|
|
129
|
+
# Get the merge sections from the file, delimited by merge and /merge
|
113
130
|
merges = content.scan(%r{(?<=\A|\n).{,4}merge *\n(.*?)\n.{,4}/merge}m)
|
114
131
|
&.map { |m| m[0].strip.apply_variables.apply_regexes }
|
132
|
+
# If no merge sections are found, use the entire file
|
115
133
|
merges = [content] if !merges || merges.empty?
|
116
|
-
|
117
|
-
|
134
|
+
|
135
|
+
# Get the existing content of the target file
|
136
|
+
target_content = IO.read(entry.target)
|
137
|
+
|
138
|
+
# Remove any merges that already exist in the target file
|
139
|
+
merges.delete_if { |m| target_content =~ /#{Regexp.escape(m)}/ }
|
140
|
+
|
141
|
+
# If there are any merge sections left, merge them with the target file
|
118
142
|
if merges.count.positive?
|
119
|
-
File.open(entry.target, 'w') { |f| f.puts "#{
|
143
|
+
File.open(entry.target, 'w') { |f| f.puts "#{target_content.chomp}\n\n#{merges.join("\n\n")}" }
|
120
144
|
Planter.notify("Merged #{entry.file} => #{entry.target} (#{merges.count} merges)", :debug)
|
121
145
|
else
|
146
|
+
# If there are no merge sections left, copy the file instead
|
122
147
|
copy_file(entry)
|
123
148
|
end
|
124
149
|
end
|
@@ -129,14 +154,25 @@ module Planter
|
|
129
154
|
## @param file [FileEntry] The file entry
|
130
155
|
## @param overwrite [Boolean] Force overwrite
|
131
156
|
##
|
157
|
+
## @return [Boolean] success
|
158
|
+
##
|
132
159
|
def copy_file(file, overwrite: false)
|
160
|
+
# Check if the target file already exists
|
161
|
+
# If it does and overwrite is true, or Planter.overwrite is true,
|
162
|
+
# or if the file doesn't exist, then copy the file
|
133
163
|
if !File.exist?(file.target) || overwrite || Planter.overwrite
|
164
|
+
# Make sure the target directory exists
|
134
165
|
FileUtils.mkdir_p(File.dirname(file.target))
|
166
|
+
# Copy the file if it isn't a directory
|
135
167
|
FileUtils.cp(file.file, file.target) unless File.directory?(file.file)
|
168
|
+
# Log a message to the console
|
136
169
|
Planter.notify("Copied #{file.file} => #{file.target}", :debug)
|
170
|
+
# Return true to indicate success
|
137
171
|
true
|
138
172
|
else
|
173
|
+
# Log a message to the console
|
139
174
|
Planter.notify("Skipped #{file.file} => #{file.target}", :debug)
|
175
|
+
# Return false to indicate that the copy was skipped
|
140
176
|
false
|
141
177
|
end
|
142
178
|
end
|