bashly 0.6.8 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/lib/bashly/cli.rb +1 -0
- data/lib/bashly/commands/add.rb +35 -82
- data/lib/bashly/commands/generate.rb +49 -8
- data/lib/bashly/commands/init.rb +1 -1
- data/lib/bashly/commands/preview.rb +2 -2
- data/lib/bashly/commands/validate.rb +19 -0
- data/lib/bashly/concerns/asset_helper.rb +4 -0
- data/lib/bashly/concerns/command_scopes.rb +68 -0
- data/lib/bashly/concerns/completions.rb +5 -1
- data/lib/bashly/config_validator.rb +135 -0
- data/lib/bashly/extensions/file.rb +13 -0
- data/lib/bashly/extensions/string.rb +5 -1
- data/lib/bashly/libraries/base.rb +19 -0
- data/lib/bashly/libraries/completions.rb +14 -0
- data/lib/bashly/libraries/completions_function.rb +38 -0
- data/lib/bashly/libraries/completions_script.rb +29 -0
- data/lib/bashly/libraries/completions_yaml.rb +27 -0
- data/lib/bashly/libraries.yml +39 -0
- data/lib/bashly/library.rb +63 -0
- data/lib/bashly/refinements/compose_refinements.rb +45 -0
- data/lib/bashly/{models → script}/argument.rb +1 -1
- data/lib/bashly/{models → script}/base.rb +4 -2
- data/lib/bashly/script/catch_all.rb +49 -0
- data/lib/bashly/{models → script}/command.rb +9 -111
- data/lib/bashly/{models → script}/environment_variable.rb +1 -1
- data/lib/bashly/{models → script}/flag.rb +1 -1
- data/lib/bashly/{models/script.rb → script/wrapper.rb} +2 -2
- data/lib/bashly/templates/lib/colors.sh +41 -31
- data/lib/bashly/templates/lib/config.sh +34 -35
- data/lib/bashly/templates/lib/sample_function.sh +10 -10
- data/lib/bashly/templates/lib/validations/validate_dir_exists.sh +4 -0
- data/lib/bashly/templates/lib/validations/validate_file_exists.sh +4 -0
- data/lib/bashly/templates/lib/validations/validate_integer.sh +4 -0
- data/lib/bashly/templates/lib/validations/validate_not_empty.sh +4 -0
- data/lib/bashly/templates/lib/yaml.sh +12 -15
- data/lib/bashly/templates/strings.yml +1 -0
- data/lib/bashly/version.rb +1 -1
- data/lib/bashly/views/argument/validations.erb +8 -0
- data/lib/bashly/views/command/catch_all_filter.erb +2 -2
- data/lib/bashly/views/command/command_filter.erb +1 -1
- data/lib/bashly/views/command/default_assignments.erb +2 -2
- data/lib/bashly/views/command/default_initialize_script.erb +6 -6
- data/lib/bashly/views/command/environment_variables_filter.erb +1 -1
- data/lib/bashly/views/command/fixed_flags_filter.erb +1 -1
- data/lib/bashly/views/command/initialize.erb +1 -1
- data/lib/bashly/views/command/parse_requirements.erb +1 -1
- data/lib/bashly/views/command/parse_requirements_case.erb +4 -3
- data/lib/bashly/views/command/parse_requirements_while.erb +2 -2
- data/lib/bashly/views/command/required_args_filter.erb +1 -5
- data/lib/bashly/views/command/required_flags_filter.erb +1 -4
- data/lib/bashly/views/command/run.erb +4 -4
- data/lib/bashly/views/command/usage.erb +1 -1
- data/lib/bashly/views/command/usage_args.erb +3 -3
- data/lib/bashly/views/command/usage_commands.erb +1 -1
- data/lib/bashly/views/flag/case.erb +2 -1
- data/lib/bashly/views/flag/validations.erb +8 -0
- data/lib/bashly/views/{script → wrapper}/bash3_bouncer.erb +0 -0
- data/lib/bashly/views/{script → wrapper}/header.erb +0 -0
- data/lib/bashly/views/{script → wrapper}/wrapper.erb +0 -0
- data/lib/bashly.rb +3 -1
- metadata +33 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0f901e6eab10987e6daf0042b18e097b172f3d54babc286b9fb2f5e83452612
|
4
|
+
data.tar.gz: 4dc2043674ce9c5d1c3be9bfd8ec72f2cf0f18ac121b909e939dc9dd4ca3ea79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f00b5c1afcb6349e7fdcc3e60adc23d2d6aa3d22a31b6375c3a2129adcb4434b9e2885449fdd8ede13dca7d56b0c1b6387681b6428db6028da0208cea82fe0a2
|
7
|
+
data.tar.gz: 5350aa76e39d2cee1d3ef2eb722e22f10e3d28772917d6b66cd5d11b0fb331a45e6fa2dd006f1096468b6189735ccffbf2650c27cb8a6a8d70b92c9c63803625
|
data/README.md
CHANGED
@@ -40,7 +40,7 @@ a [docker image](https://hub.docker.com/r/dannyben/bashly).
|
|
40
40
|
file for you ([example](https://github.com/DannyBen/bashly/tree/master/examples/minimal#bashlyyml)).
|
41
41
|
2. Bashly then automatically generates a bash script (when you run
|
42
42
|
`bashly generate`) that can parse and validate user input, provide help
|
43
|
-
messages, and run your code for each command
|
43
|
+
messages, and run your code for each command.
|
44
44
|
3. Your code for each command is kept in a separate file, and can be merged
|
45
45
|
again if you change it ([example](https://github.com/DannyBen/bashly/blob/master/examples/minimal/src/root_command.sh)).
|
46
46
|
|
@@ -67,11 +67,13 @@ Bashly is responsible for:
|
|
67
67
|
## Contributing / Support
|
68
68
|
|
69
69
|
If you experience any issue, have a question or a suggestion, or if you wish
|
70
|
-
to contribute, feel free to [open an issue][issues]
|
70
|
+
to contribute, feel free to [open an issue][issues] or
|
71
|
+
[start a discussion][discussions].
|
71
72
|
|
72
73
|
|
73
74
|
|
74
75
|
[issues]: https://github.com/DannyBen/bashly/issues
|
76
|
+
[discussions]: https://github.com/DannyBen/bashly/discussions
|
75
77
|
[docs]: https://bashly.dannyb.co/
|
76
78
|
[examples]: https://github.com/DannyBen/bashly/tree/master/examples#bashly-examples
|
77
79
|
|
data/lib/bashly/cli.rb
CHANGED
data/lib/bashly/commands/add.rb
CHANGED
@@ -8,7 +8,8 @@ module Bashly
|
|
8
8
|
usage "bashly add config [--force]"
|
9
9
|
usage "bashly add colors [--force]"
|
10
10
|
usage "bashly add yaml [--force]"
|
11
|
-
usage "bashly add
|
11
|
+
usage "bashly add validations [--force]"
|
12
|
+
usage "bashly add comp FORMAT [OUTPUT --force]"
|
12
13
|
usage "bashly add (-h|--help)"
|
13
14
|
|
14
15
|
option "-f --force", "Overwrite existing files"
|
@@ -21,6 +22,7 @@ module Bashly
|
|
21
22
|
command "config", "Add standard functions for handling INI files to the lib directory."
|
22
23
|
command "colors", "Add standard functions for printing colorful and formatted text to the lib directory."
|
23
24
|
command "yaml", "Add standard functions for reading YAML files to the lib directory."
|
25
|
+
command "validations", "Add argument validation functions to the lib directory."
|
24
26
|
command "comp", "Generate a bash completions script or function."
|
25
27
|
|
26
28
|
example "bashly add strings --force"
|
@@ -30,119 +32,70 @@ module Bashly
|
|
30
32
|
environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
|
31
33
|
|
32
34
|
def strings_command
|
33
|
-
|
35
|
+
add_lib 'strings'
|
34
36
|
end
|
35
37
|
|
36
38
|
def lib_command
|
37
|
-
|
39
|
+
add_lib 'lib'
|
38
40
|
end
|
39
41
|
|
40
42
|
def config_command
|
41
|
-
|
43
|
+
add_lib 'config'
|
42
44
|
end
|
43
45
|
|
44
46
|
def colors_command
|
45
|
-
|
47
|
+
add_lib 'colors'
|
46
48
|
end
|
47
49
|
|
48
50
|
def yaml_command
|
49
|
-
|
51
|
+
add_lib 'yaml'
|
52
|
+
end
|
53
|
+
|
54
|
+
def validations_command
|
55
|
+
add_lib 'validations'
|
50
56
|
end
|
51
57
|
|
52
58
|
def comp_command
|
53
59
|
format = args['FORMAT']
|
54
60
|
output = args['OUTPUT']
|
55
|
-
|
61
|
+
|
56
62
|
case format
|
57
|
-
when "
|
58
|
-
|
59
|
-
when "yaml"
|
60
|
-
|
61
|
-
when "script"
|
62
|
-
save_comp_script output
|
63
|
-
else
|
64
|
-
raise Error, "Unrecognized format: #{format}"
|
63
|
+
when "script" then add_lib 'completions_script', output
|
64
|
+
when "function" then add_lib 'completions', output
|
65
|
+
when "yaml" then add_lib 'completions_yaml', output
|
66
|
+
else raise Error, "Unrecognized format: #{format}"
|
65
67
|
end
|
66
68
|
|
67
69
|
end
|
68
70
|
|
69
71
|
private
|
70
72
|
|
71
|
-
def
|
72
|
-
|
73
|
+
def add_lib(name, *args)
|
74
|
+
library = Bashly::Library.new name, *args
|
75
|
+
files_created = 0
|
76
|
+
library.files.each do |file|
|
77
|
+
created = safe_write file[:path], file[:content]
|
78
|
+
files_created += 1 if created
|
79
|
+
end
|
80
|
+
message = library.post_install_message
|
81
|
+
say "\n#{message}" if message and files_created > 0
|
73
82
|
end
|
74
83
|
|
75
|
-
def
|
84
|
+
def safe_write(path, content)
|
76
85
|
if !Dir.exist? Settings.source_dir
|
77
86
|
raise InitError, "Directory !txtgrn!#{Settings.source_dir}!txtrst! does not exist\nRun !txtpur!bashly init!txtrst! first"
|
78
87
|
end
|
79
88
|
|
80
|
-
if File.exist?
|
81
|
-
say "skipped
|
89
|
+
if File.exist? path and !args['--force']
|
90
|
+
say "!txtblu!skipped!txtrst! #{path} (exists)"
|
91
|
+
false
|
92
|
+
|
82
93
|
else
|
83
|
-
|
84
|
-
say "created
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
def deep_copy(source, target)
|
89
|
-
target_dir = File.dirname target
|
90
|
-
FileUtils.mkdir_p target_dir unless Dir.exist? target_dir
|
91
|
-
FileUtils.cp source, target
|
92
|
-
end
|
93
|
-
|
94
|
-
def config
|
95
|
-
@config ||= Config.new "#{Settings.source_dir}/bashly.yml"
|
96
|
-
end
|
97
|
-
|
98
|
-
def command
|
99
|
-
@command ||= Models::Command.new config
|
100
|
-
end
|
101
|
-
|
102
|
-
def completions
|
103
|
-
@completions ||= command.completion_data
|
104
|
-
end
|
105
|
-
|
106
|
-
def completions_script
|
107
|
-
@completions_script ||= command.completion_script
|
108
|
-
end
|
109
|
-
|
110
|
-
def completions_function
|
111
|
-
@completions_function ||= command.completion_function
|
112
|
-
end
|
113
|
-
|
114
|
-
def save_comp_yaml(filename = nil)
|
115
|
-
filename ||= "#{Settings.target_dir}/completions.yml"
|
116
|
-
File.write filename, completions.to_yaml
|
117
|
-
say "created !txtgrn!#{filename}"
|
118
|
-
say ""
|
119
|
-
say "This file can be converted to a completions script using the !txtgrn!completely!txtrst! gem."
|
120
|
-
end
|
121
|
-
|
122
|
-
def save_comp_script(filename = nil)
|
123
|
-
filename ||= "#{Settings.target_dir}/completions.bash"
|
124
|
-
File.write filename, completions_script
|
125
|
-
say "created !txtgrn!#{filename}"
|
126
|
-
say ""
|
127
|
-
say "In order to enable completions, run:"
|
128
|
-
say ""
|
129
|
-
say " !txtpur!$ source #{filename}"
|
130
|
-
end
|
131
|
-
|
132
|
-
def save_comp_function(name = nil)
|
133
|
-
name ||= "send_completions"
|
134
|
-
target_dir = "#{Settings.source_dir}/lib"
|
135
|
-
filename = "#{target_dir}/#{name}.sh"
|
94
|
+
File.deep_write path, content
|
95
|
+
say "!txtgrn!created!txtrst! #{path}"
|
96
|
+
true
|
136
97
|
|
137
|
-
|
138
|
-
File.write filename, completions_function
|
139
|
-
|
140
|
-
say "created !txtgrn!#{filename}"
|
141
|
-
say ""
|
142
|
-
say "In order to use it in your script, create a command or a flag (for example: !txtgrn!#{command.name} completions!txtrst! or !txtgrn!#{command.name} --completions!txtrst!) that calls the !txtgrn!#{name}!txtrst! function."
|
143
|
-
say "Your users can then run something like this to enable completions:"
|
144
|
-
say ""
|
145
|
-
say " !txtpur!$ eval \"$(#{command.name} completions)\""
|
98
|
+
end
|
146
99
|
end
|
147
100
|
|
148
101
|
end
|
@@ -1,23 +1,28 @@
|
|
1
1
|
module Bashly
|
2
2
|
module Commands
|
3
3
|
class Generate < Base
|
4
|
+
using ComposeRefinements
|
5
|
+
|
4
6
|
help "Generate the bash script and required files"
|
5
7
|
|
6
|
-
usage "bashly generate [--force --quiet --wrap FUNCTION]"
|
8
|
+
usage "bashly generate [--force --quiet --upgrade --wrap FUNCTION]"
|
7
9
|
usage "bashly generate (-h|--help)"
|
8
10
|
|
9
11
|
option "-f --force", "Overwrite existing files"
|
10
|
-
option "-w --wrap FUNCTION", "Wrap the entire script in a function so it can also be sourced"
|
11
12
|
option "-q --quiet", "Disable on-screen progress report"
|
13
|
+
option "-u --upgrade", "Upgrade all added library functions"
|
14
|
+
option "-w --wrap FUNCTION", "Wrap the entire script in a function so it can also be sourced"
|
12
15
|
|
13
16
|
environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
|
14
17
|
environment "BASHLY_TARGET_DIR", "The path to use for creating the bash script [default: .]"
|
18
|
+
environment "BASHLY_STRICT", "When not empty, enable bash strict mode (set -euo pipefail)"
|
15
19
|
|
16
20
|
example "bashly generate --force"
|
17
21
|
example "bashly generate --wrap my_function"
|
18
22
|
|
19
23
|
def run
|
20
24
|
create_user_files
|
25
|
+
upgrade_libs if args['--upgrade']
|
21
26
|
create_master_script
|
22
27
|
quiet_say "run !txtpur!#{master_script_path} --help!txtrst! to test your bash script"
|
23
28
|
end
|
@@ -28,6 +33,42 @@ module Bashly
|
|
28
33
|
say message unless args['--quiet']
|
29
34
|
end
|
30
35
|
|
36
|
+
def upgrade_libs
|
37
|
+
generated_files.each do |file|
|
38
|
+
content = File.read file
|
39
|
+
|
40
|
+
if content =~ /\[@bashly-upgrade (.+)\]/
|
41
|
+
args = $1.split ' '
|
42
|
+
library_name = args.shift
|
43
|
+
upgrade file, library_name, *args
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def generated_files
|
49
|
+
Dir["#{Settings.source_dir}/**/*.*"].sort
|
50
|
+
end
|
51
|
+
|
52
|
+
def upgrade(existing_file, library_name, *args)
|
53
|
+
if Library.exist? library_name
|
54
|
+
upgrade! existing_file, library_name, *args
|
55
|
+
else
|
56
|
+
quiet_say "!txtred!warning!txtrst! not upgrading !txtcyn!#{existing_file}!txtrst!, unknown library '#{library_name}'"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def upgrade!(existing_file, library_name, *args)
|
61
|
+
library = Bashly::Library.new library_name, *args
|
62
|
+
file = library.find_file existing_file
|
63
|
+
|
64
|
+
if file
|
65
|
+
File.deep_write file[:path], file[:content]
|
66
|
+
quiet_say "!txtcyn!updated!txtrst! #{file[:path]}"
|
67
|
+
else
|
68
|
+
quiet_say "!txtred!warning!txtrst! not upgrading !txtcyn!#{existing_file}!txtrst!, path mismatch"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
31
72
|
def create_user_files
|
32
73
|
quiet_say "creating user files in !txtgrn!#{Settings.source_dir}"
|
33
74
|
|
@@ -55,21 +96,21 @@ module Bashly
|
|
55
96
|
|
56
97
|
def create_file(file, content)
|
57
98
|
if File.exist? file and !args['--force']
|
58
|
-
quiet_say "skipped
|
99
|
+
quiet_say "!txtblu!skipped!txtrst! #{file} (exists)"
|
59
100
|
else
|
60
101
|
File.write file, content
|
61
|
-
quiet_say "created
|
102
|
+
quiet_say "!txtgrn!created!txtrst! #{file}"
|
62
103
|
end
|
63
104
|
end
|
64
105
|
|
65
106
|
def create_master_script
|
66
107
|
File.write master_script_path, script.code
|
67
108
|
FileUtils.chmod "+x", master_script_path
|
68
|
-
quiet_say "created
|
109
|
+
quiet_say "!txtgrn!created!txtrst! #{master_script_path}"
|
69
110
|
end
|
70
111
|
|
71
112
|
def script
|
72
|
-
@script ||=
|
113
|
+
@script ||= Script::Wrapper.new(command, args['--wrap'])
|
73
114
|
end
|
74
115
|
|
75
116
|
def master_script_path
|
@@ -77,11 +118,11 @@ module Bashly
|
|
77
118
|
end
|
78
119
|
|
79
120
|
def config
|
80
|
-
@config ||= Config.new
|
121
|
+
@config ||= Config.new("#{Settings.source_dir}/bashly.yml").compose
|
81
122
|
end
|
82
123
|
|
83
124
|
def command
|
84
|
-
@command ||=
|
125
|
+
@command ||= Script::Command.new config
|
85
126
|
end
|
86
127
|
|
87
128
|
end
|
data/lib/bashly/commands/init.rb
CHANGED
@@ -17,7 +17,7 @@ module Bashly
|
|
17
17
|
end
|
18
18
|
Dir.mkdir target_dir unless Dir.exist? target_dir
|
19
19
|
File.write "#{target_dir}/bashly.yml", yaml_content
|
20
|
-
say "created
|
20
|
+
say "!txtgrn!created!txtrst! #{target_dir}/bashly.yml"
|
21
21
|
say "run !txtpur!bashly generate!txtrst! to create the bash script"
|
22
22
|
end
|
23
23
|
|
@@ -10,8 +10,8 @@ module Bashly
|
|
10
10
|
|
11
11
|
def run
|
12
12
|
config = Config.new "#{Settings.source_dir}/bashly.yml"
|
13
|
-
command =
|
14
|
-
script =
|
13
|
+
command = Script::Command.new(config)
|
14
|
+
script = Script::Wrapper.new command
|
15
15
|
puts script.code
|
16
16
|
end
|
17
17
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Bashly
|
2
|
+
module Commands
|
3
|
+
class Validate < Base
|
4
|
+
help "Scan the configuration file for errors"
|
5
|
+
|
6
|
+
usage "bashly validate"
|
7
|
+
usage "bashly validate (-h|--help)"
|
8
|
+
|
9
|
+
environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
|
10
|
+
|
11
|
+
def run
|
12
|
+
config = Config.new "#{Settings.source_dir}/bashly.yml"
|
13
|
+
validator = ConfigValidator.new config
|
14
|
+
validator.validate
|
15
|
+
say "!txtgrn!OK"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Bashly
|
2
|
+
# This is a `Command` concern responsible for providing additional scopes.
|
3
|
+
module CommandScopes
|
4
|
+
# Returns only the names of the Commands
|
5
|
+
def command_names
|
6
|
+
commands.map &:name
|
7
|
+
end
|
8
|
+
|
9
|
+
# Returns a flat array containing all the commands in this tree.
|
10
|
+
# This includes self + children + grandchildres + ...
|
11
|
+
def deep_commands
|
12
|
+
result = []
|
13
|
+
commands.each do |command|
|
14
|
+
result << command
|
15
|
+
if command.commands.any?
|
16
|
+
result += command.deep_commands
|
17
|
+
end
|
18
|
+
end
|
19
|
+
result
|
20
|
+
end
|
21
|
+
|
22
|
+
# If any of this command's subcommands has the default option set to
|
23
|
+
# true, this default command will be returned, nil otherwise.
|
24
|
+
def default_command
|
25
|
+
commands.find { |c| c.default }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns an array of all the default Args
|
29
|
+
def default_args
|
30
|
+
args.select &:default
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns an array of all the default Environment Variables
|
34
|
+
def default_environment_variables
|
35
|
+
environment_variables.select &:default
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns an array of all the default Flags
|
39
|
+
def default_flags
|
40
|
+
flags.select &:default
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns an array of all the required Arguments
|
44
|
+
def required_args
|
45
|
+
args.select &:required
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns an array of all the required EnvironmentVariables
|
49
|
+
def required_environment_variables
|
50
|
+
environment_variables.select &:required
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns an array of all the required Flags
|
54
|
+
def required_flags
|
55
|
+
flags.select &:required
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns an array of all the args with a whitelist
|
59
|
+
def whitelisted_args
|
60
|
+
args.select &:allowed
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns an array of all the flags with a whitelist arg
|
64
|
+
def whitelisted_flags
|
65
|
+
flags.select &:allowed
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -31,12 +31,16 @@ module Bashly
|
|
31
31
|
flags.map(&:name) + flags.map(&:short)
|
32
32
|
end
|
33
33
|
|
34
|
+
def completion_allowed_args
|
35
|
+
flags.map(&:allowed).flatten + args.map(&:allowed).flatten
|
36
|
+
end
|
37
|
+
|
34
38
|
def completion_words(with_version: false)
|
35
39
|
trivial_flags = %w[--help -h]
|
36
40
|
trivial_flags += %w[--version -v] if with_version
|
37
41
|
all = (
|
38
42
|
command_names + trivial_flags +
|
39
|
-
completion_flag_names
|
43
|
+
completion_flag_names + completion_allowed_args
|
40
44
|
)
|
41
45
|
|
42
46
|
all += completions if completions
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Bashly
|
2
|
+
class ConfigValidator
|
3
|
+
attr_reader :data
|
4
|
+
|
5
|
+
def initialize(data)
|
6
|
+
@data = data
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate
|
10
|
+
assert_command "root", data
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def assert(valid, message)
|
16
|
+
raise ConfigurationError, message unless valid
|
17
|
+
end
|
18
|
+
|
19
|
+
def refute(invalid, message)
|
20
|
+
assert !invalid, message
|
21
|
+
end
|
22
|
+
|
23
|
+
def assert_string(key, value)
|
24
|
+
assert value.is_a?(String), "#{key} must be a string"
|
25
|
+
end
|
26
|
+
|
27
|
+
def assert_optional_string(key, value)
|
28
|
+
assert_string key, value if value
|
29
|
+
end
|
30
|
+
|
31
|
+
def assert_boolean(key, value)
|
32
|
+
assert [true, false, nil].include?(value), "#{key} must be a boolean"
|
33
|
+
end
|
34
|
+
|
35
|
+
def assert_array(key, value, of: nil)
|
36
|
+
return unless value
|
37
|
+
assert value.is_a?(Array), "#{key} must be an array"
|
38
|
+
if of
|
39
|
+
value.each_with_index do |val, i|
|
40
|
+
send "assert_#{of}".to_sym, "#{key}[#{i}]", val
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def assert_hash(key, value)
|
46
|
+
assert value.is_a?(Hash), "#{key} must be a hash"
|
47
|
+
end
|
48
|
+
|
49
|
+
def assert_version(key, value)
|
50
|
+
return unless value
|
51
|
+
assert [String, Integer, Float].include?(value.class),
|
52
|
+
"#{key} must be a string or a number"
|
53
|
+
end
|
54
|
+
|
55
|
+
def assert_catch_all(key, value)
|
56
|
+
return unless value
|
57
|
+
assert [TrueClass, String, Hash].include?(value.class),
|
58
|
+
"#{key} must be a boolean, a string or a hash"
|
59
|
+
|
60
|
+
assert_catch_all_hash key, value if value.is_a? Hash
|
61
|
+
end
|
62
|
+
|
63
|
+
def assert_catch_all_hash(key, value)
|
64
|
+
assert_string "#{key}.label", value['label']
|
65
|
+
assert_optional_string "#{key}.help", value['help']
|
66
|
+
assert_boolean "#{key}.required", value['required']
|
67
|
+
end
|
68
|
+
|
69
|
+
def assert_extensible(key, value)
|
70
|
+
return unless value
|
71
|
+
assert [TrueClass, String].include?(value.class),
|
72
|
+
"#{key} must be a boolean or a string"
|
73
|
+
end
|
74
|
+
|
75
|
+
def assert_arg(key, value)
|
76
|
+
assert_hash key, value
|
77
|
+
assert_string "#{key}.name", value['name']
|
78
|
+
assert_optional_string "#{key}.help", value['help']
|
79
|
+
assert_optional_string "#{key}.default", value['default']
|
80
|
+
assert_optional_string "#{key}.validate", value['validate']
|
81
|
+
assert_boolean "#{key}.required", value['required']
|
82
|
+
|
83
|
+
assert_array "#{key}.allowed", value['allowed'], of: :string
|
84
|
+
end
|
85
|
+
|
86
|
+
def assert_flag(key, value)
|
87
|
+
assert_hash key, value
|
88
|
+
assert value['short'] || value['long'], "#{key} must have at least one of long or short name"
|
89
|
+
|
90
|
+
assert_optional_string "#{key}.long", value['long']
|
91
|
+
assert_optional_string "#{key}.short", value['short']
|
92
|
+
assert_optional_string "#{key}.help", value['help']
|
93
|
+
assert_optional_string "#{key}.arg", value['arg']
|
94
|
+
assert_optional_string "#{key}.default", value['default']
|
95
|
+
assert_optional_string "#{key}.validate", value['validate']
|
96
|
+
|
97
|
+
assert_boolean "#{key}.required", value['required']
|
98
|
+
assert_array "#{key}.allowed", value['allowed'], of: :string
|
99
|
+
end
|
100
|
+
|
101
|
+
def assert_env_var(key, value)
|
102
|
+
assert_hash key, value
|
103
|
+
assert_string "#{key}.name", value['name']
|
104
|
+
assert_optional_string "#{key}.help", value['help']
|
105
|
+
assert_optional_string "#{key}.default", value['default']
|
106
|
+
assert_boolean "#{key}.required", value['required']
|
107
|
+
end
|
108
|
+
|
109
|
+
def assert_command(key, value)
|
110
|
+
assert_hash key, value
|
111
|
+
|
112
|
+
refute value['commands'] && value['args'], "#{key} cannot have both commands and args"
|
113
|
+
refute value['commands'] && value['flags'], "#{key} cannot have both commands and flags"
|
114
|
+
|
115
|
+
assert_string "#{key}.name", value['name']
|
116
|
+
assert_optional_string "#{key}.short", value['short']
|
117
|
+
assert_optional_string "#{key}.help", value['help']
|
118
|
+
assert_optional_string "#{key}.footer", value['footer']
|
119
|
+
assert_optional_string "#{key}.group", value['group']
|
120
|
+
|
121
|
+
assert_boolean "#{key}.default", value['default']
|
122
|
+
assert_version "#{key}.version", value['version']
|
123
|
+
assert_catch_all "#{key}.catch_all", value['catch_all']
|
124
|
+
assert_extensible "#{key}.extensible", value['extensible']
|
125
|
+
|
126
|
+
assert_array "#{key}.args", value['args'], of: :arg
|
127
|
+
assert_array "#{key}.flags", value['flags'] , of: :flag
|
128
|
+
assert_array "#{key}.commands", value['commands'], of: :command
|
129
|
+
assert_array "#{key}.completions", value['completions'], of: :string
|
130
|
+
assert_array "#{key}.dependencies", value['dependencies'], of: :string
|
131
|
+
assert_array "#{key}.environment_variables", value['environment_variables'], of: :env_var
|
132
|
+
assert_array "#{key}.examples", value['examples'], of: :string
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
class File
|
4
|
+
def self.deep_write(file, content)
|
5
|
+
dir = File.dirname file
|
6
|
+
FileUtils.mkdir_p dir unless Dir.exist? dir
|
7
|
+
File.write file, content
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.append(path, content)
|
11
|
+
File.open(path, "a") { |f| f << content }
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Bashly
|
2
|
+
module Libraries
|
3
|
+
class Base
|
4
|
+
attr_reader :args
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@args = args
|
8
|
+
end
|
9
|
+
|
10
|
+
def files
|
11
|
+
raise NotImplementedError, "Please implement #files"
|
12
|
+
end
|
13
|
+
|
14
|
+
def post_install_message
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|