bashly 0.6.7 → 0.7.1
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 +55 -9
- 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/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/{models → script}/command.rb +11 -22
- 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} +21 -3
- 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/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 -6
- data/lib/bashly/views/command/parse_requirements.erb +1 -1
- data/lib/bashly/views/command/parse_requirements_case.erb +2 -1
- 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_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/wrapper/bash3_bouncer.erb +5 -0
- data/lib/bashly/views/{script → wrapper}/header.erb +1 -0
- data/lib/bashly/views/{script → wrapper}/wrapper.erb +0 -0
- data/lib/bashly.rb +3 -1
- metadata +28 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53a5744ae1746cdd47de191ea91539ab7336aca894f583384ef0b170c03a746e
|
4
|
+
data.tar.gz: 0e1e9bc417d3947bcce84c3c6f44df87521dfeeae2329cc736a53816cb66cba3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72154aafdc3c3787b4d23a2935192a92c21b94b830d7fb01c22057ed839d30120d8a31a8962b7b075110551d034b2c971b92010e55c88a17d4e9041ecb309ad7
|
7
|
+
data.tar.gz: a02638101b685d5e9de575fe4537991c558c462dc030355b5e8ab6ffe4f32b8018c510afb516f51c02d4412a20c21a3703b5dc5872a0f377baa343345f045605
|
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,30 +1,76 @@
|
|
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 --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"
|
12
|
+
option "-q --quiet", "Disable on-screen progress report"
|
13
|
+
option "-u --upgrade", "Upgrade all added library functions"
|
10
14
|
option "-w --wrap FUNCTION", "Wrap the entire script in a function so it can also be sourced"
|
11
15
|
|
12
16
|
environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
|
13
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)"
|
14
19
|
|
15
20
|
example "bashly generate --force"
|
16
21
|
example "bashly generate --wrap my_function"
|
17
22
|
|
18
23
|
def run
|
19
24
|
create_user_files
|
25
|
+
upgrade_libs if args['--upgrade']
|
20
26
|
create_master_script
|
21
|
-
|
27
|
+
quiet_say "run !txtpur!#{master_script_path} --help!txtrst! to test your bash script"
|
22
28
|
end
|
23
29
|
|
24
30
|
private
|
25
31
|
|
32
|
+
def quiet_say(message)
|
33
|
+
say message unless args['--quiet']
|
34
|
+
end
|
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
|
+
|
26
72
|
def create_user_files
|
27
|
-
|
73
|
+
quiet_say "creating user files in !txtgrn!#{Settings.source_dir}"
|
28
74
|
|
29
75
|
create_file "#{Settings.source_dir}/initialize.sh", command.render(:default_initialize_script)
|
30
76
|
|
@@ -50,21 +96,21 @@ module Bashly
|
|
50
96
|
|
51
97
|
def create_file(file, content)
|
52
98
|
if File.exist? file and !args['--force']
|
53
|
-
|
99
|
+
quiet_say "!txtblu!skipped!txtrst! #{file} (exists)"
|
54
100
|
else
|
55
101
|
File.write file, content
|
56
|
-
|
102
|
+
quiet_say "!txtgrn!created!txtrst! #{file}"
|
57
103
|
end
|
58
104
|
end
|
59
105
|
|
60
106
|
def create_master_script
|
61
107
|
File.write master_script_path, script.code
|
62
108
|
FileUtils.chmod "+x", master_script_path
|
63
|
-
|
109
|
+
quiet_say "!txtgrn!created!txtrst! #{master_script_path}"
|
64
110
|
end
|
65
111
|
|
66
112
|
def script
|
67
|
-
@script ||=
|
113
|
+
@script ||= Script::Wrapper.new(command, args['--wrap'])
|
68
114
|
end
|
69
115
|
|
70
116
|
def master_script_path
|
@@ -72,11 +118,11 @@ module Bashly
|
|
72
118
|
end
|
73
119
|
|
74
120
|
def config
|
75
|
-
@config ||= Config.new
|
121
|
+
@config ||= Config.new("#{Settings.source_dir}/bashly.yml").compose
|
76
122
|
end
|
77
123
|
|
78
124
|
def command
|
79
|
-
@command ||=
|
125
|
+
@command ||= Script::Command.new config
|
80
126
|
end
|
81
127
|
|
82
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
|
@@ -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
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Bashly
|
2
|
+
module Libraries
|
3
|
+
class CompletionsFunction < Completions
|
4
|
+
def files
|
5
|
+
[
|
6
|
+
{
|
7
|
+
path: "#{Settings.source_dir}/lib/#{function_name}.sh",
|
8
|
+
content: completions_function_code(function_name)
|
9
|
+
}
|
10
|
+
]
|
11
|
+
end
|
12
|
+
|
13
|
+
def post_install_message
|
14
|
+
<<~EOF
|
15
|
+
In order to enable completions 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!#{function_name}!txtrst! function.
|
16
|
+
|
17
|
+
Your users can then run something like this to enable completions:
|
18
|
+
|
19
|
+
!txtpur!$ eval \"$(#{command.name} completions)\"
|
20
|
+
EOF
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def function_name
|
26
|
+
@function_name ||= args[0] || 'send_completions'
|
27
|
+
end
|
28
|
+
|
29
|
+
def completions_function_code(function_name)
|
30
|
+
[
|
31
|
+
"## [@bashly-upgrade completions #{function_name}]",
|
32
|
+
command.completion_function(function_name)
|
33
|
+
].join "\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Bashly
|
2
|
+
module Libraries
|
3
|
+
class CompletionsScript < Completions
|
4
|
+
def files
|
5
|
+
[
|
6
|
+
{
|
7
|
+
path: target_path,
|
8
|
+
content: command.completion_script
|
9
|
+
}
|
10
|
+
]
|
11
|
+
end
|
12
|
+
|
13
|
+
def post_install_message
|
14
|
+
<<~EOF
|
15
|
+
In order to enable completions, run:
|
16
|
+
|
17
|
+
!txtpur!$ source #{target_path}
|
18
|
+
EOF
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def target_path
|
24
|
+
@target_path ||= args[0] || "#{Settings.target_dir}/completions.bash"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|