bashly 0.7.7 → 0.7.10
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/lib/bashly/cli.rb +2 -1
- data/lib/bashly/commands/add.rb +41 -33
- data/lib/bashly/commands/generate.rb +13 -2
- data/lib/bashly/concerns/renderable.rb +8 -1
- data/lib/bashly/config_validator.rb +19 -6
- data/lib/bashly/extensions/string.rb +6 -0
- data/lib/bashly/extensions/yaml.rb +2 -0
- data/lib/bashly/libraries.yml +13 -9
- data/lib/bashly/script/argument.rb +13 -1
- data/lib/bashly/script/base.rb +7 -29
- data/lib/bashly/script/catch_all.rb +4 -0
- data/lib/bashly/script/command.rb +24 -6
- data/lib/bashly/script/environment_variable.rb +6 -0
- data/lib/bashly/script/flag.rb +9 -0
- data/lib/bashly/script/wrapper.rb +11 -7
- data/lib/bashly/settings.rb +34 -5
- data/lib/bashly/templates/settings.yml +24 -0
- data/lib/bashly/version.rb +1 -1
- data/lib/bashly/views/argument/usage.erb +2 -2
- data/lib/bashly/views/argument/validations.erb +1 -1
- data/lib/bashly/views/command/catch_all_filter.erb +1 -1
- data/lib/bashly/views/command/command_fallback.erb +1 -1
- data/lib/bashly/views/command/command_filter.erb +1 -1
- data/lib/bashly/views/command/command_functions.erb +1 -1
- data/lib/bashly/views/command/default_assignments.erb +1 -1
- data/lib/bashly/views/command/dependencies_filter.erb +1 -1
- data/lib/bashly/views/command/environment_variables_filter.erb +1 -1
- data/lib/bashly/views/command/fixed_flags_filter.erb +3 -1
- data/lib/bashly/views/command/footer.erb +1 -0
- data/lib/bashly/views/command/function.erb +1 -1
- data/lib/bashly/views/command/initialize.erb +1 -1
- data/lib/bashly/views/command/inspect_args.erb +1 -1
- data/lib/bashly/views/command/master_script.erb +1 -0
- data/lib/bashly/views/command/normalize_input.erb +1 -1
- data/lib/bashly/views/command/parse_requirements.erb +1 -1
- data/lib/bashly/views/command/parse_requirements_case.erb +5 -23
- data/lib/bashly/views/command/parse_requirements_case_catch_all.erb +18 -0
- data/lib/bashly/views/command/parse_requirements_case_repeatable.erb +18 -0
- data/lib/bashly/views/command/parse_requirements_case_simple.erb +18 -0
- data/lib/bashly/views/command/parse_requirements_while.erb +1 -1
- data/lib/bashly/views/command/required_args_filter.erb +1 -1
- data/lib/bashly/views/command/required_flags_filter.erb +1 -1
- data/lib/bashly/views/command/root_command.erb +1 -1
- data/lib/bashly/views/command/run.erb +1 -1
- data/lib/bashly/views/command/usage.erb +1 -1
- data/lib/bashly/views/command/usage_args.erb +1 -1
- data/lib/bashly/views/command/usage_commands.erb +1 -1
- data/lib/bashly/views/command/usage_environment_variables.erb +1 -1
- data/lib/bashly/views/command/usage_examples.erb +1 -1
- data/lib/bashly/views/command/usage_fixed_flags.erb +1 -1
- data/lib/bashly/views/command/usage_flags.erb +1 -1
- data/lib/bashly/views/command/user_filter.erb +1 -1
- data/lib/bashly/views/command/user_lib.erb +2 -2
- data/lib/bashly/views/command/version_command.erb +1 -1
- data/lib/bashly/views/command/whitelist_filter.erb +11 -1
- data/lib/bashly/views/environment_variable/usage.erb +1 -1
- data/lib/bashly/views/flag/case.erb +2 -28
- data/lib/bashly/views/flag/case_arg.erb +19 -0
- data/lib/bashly/views/flag/case_no_arg.erb +8 -0
- data/lib/bashly/views/flag/conflicts.erb +1 -1
- data/lib/bashly/views/flag/usage.erb +1 -1
- data/lib/bashly/views/flag/validations.erb +1 -1
- data/lib/bashly/views/wrapper/bash3_bouncer.erb +1 -1
- data/lib/bashly/views/wrapper/wrapper.erb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 471a0ae8e462a056c196ebf128f02f3f30ff3a100c10aff2d2c010757d6d942e
|
|
4
|
+
data.tar.gz: d78cf8473f40b241ee412a00d819be124a244acda1f487f20e105dee1cba9842
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 77617bc6668796730c67d33621d497a8a454a6208d7f42d7c28ecb26e940da2e4edee6b3aefd8fc98b301d8480a13f14c1f6b292ff7f4d29b6030e601cdc1b08
|
|
7
|
+
data.tar.gz: d7b851826c9dde19201f2fb48b5f4b5ff0cd9a2421f4770ed3fdbc1c340943b9d0c3b3703b26646e46dfb012dca0c2a10728c568786ac4c381ab1431dc191695
|
data/lib/bashly/cli.rb
CHANGED
|
@@ -6,7 +6,8 @@ module Bashly
|
|
|
6
6
|
class CLI
|
|
7
7
|
def self.runner
|
|
8
8
|
runner = MisterBin::Runner.new version: Bashly::VERSION,
|
|
9
|
-
header: "Bashly - Bash CLI Generator"
|
|
9
|
+
header: "Bashly - Bash CLI Generator",
|
|
10
|
+
footer: "Help: !txtpur!bashly COMMAND --help!txtrst!\nDocs: !undblu!https://bashly.dannyb.co"
|
|
10
11
|
|
|
11
12
|
runner.route 'init', to: Commands::Init
|
|
12
13
|
runner.route 'preview', to: Commands::Preview
|
data/lib/bashly/commands/add.rb
CHANGED
|
@@ -3,14 +3,15 @@ module Bashly
|
|
|
3
3
|
class Add < Base
|
|
4
4
|
help "Add extra features and customization to your script"
|
|
5
5
|
|
|
6
|
-
usage "bashly add strings [--force]"
|
|
7
|
-
usage "bashly add lib [--force]"
|
|
8
|
-
usage "bashly add config [--force]"
|
|
9
6
|
usage "bashly add colors [--force]"
|
|
10
|
-
usage "bashly add yaml [--force]"
|
|
11
|
-
usage "bashly add validations [--force]"
|
|
12
|
-
usage "bashly add test [--force]"
|
|
13
7
|
usage "bashly add comp FORMAT [OUTPUT --force]"
|
|
8
|
+
usage "bashly add config [--force]"
|
|
9
|
+
usage "bashly add lib [--force]"
|
|
10
|
+
usage "bashly add settings [--force]"
|
|
11
|
+
usage "bashly add strings [--force]"
|
|
12
|
+
usage "bashly add test [--force]"
|
|
13
|
+
usage "bashly add validations [--force]"
|
|
14
|
+
usage "bashly add yaml [--force]"
|
|
14
15
|
usage "bashly add (-h|--help)"
|
|
15
16
|
|
|
16
17
|
option "-f --force", "Overwrite existing files"
|
|
@@ -18,14 +19,15 @@ module Bashly
|
|
|
18
19
|
param "FORMAT", "Output format, can be one of:\n function : generate a function file to be included in your script.\n script : generate a standalone bash completions script.\n yaml : generate a yaml compatible with completely."
|
|
19
20
|
param "OUTPUT", "For the 'comp function' command: Name of the generated function.\nFor the 'comp script' or 'comp yaml' commands: path to output file.\nIn all cases, this is optional and will have sensible defaults."
|
|
20
21
|
|
|
21
|
-
command "strings", "Copy an additional configuration file to your project, allowing you to customize all the tips and error strings."
|
|
22
|
-
command "lib", "Create the additional lib directory for additional user scripts. All *.sh scripts in this folder will be included in the final bash script."
|
|
23
|
-
command "config", "Add standard functions for handling INI files to the lib directory."
|
|
24
22
|
command "colors", "Add standard functions for printing colorful and formatted text to the lib directory."
|
|
25
|
-
command "yaml", "Add standard functions for reading YAML files to the lib directory."
|
|
26
|
-
command "validations", "Add argument validation functions to the lib directory."
|
|
27
|
-
command "test", "Add approval testing."
|
|
28
23
|
command "comp", "Generate a bash completions script or function."
|
|
24
|
+
command "config", "Add standard functions for handling INI files to the lib directory."
|
|
25
|
+
command "lib", "Create the additional lib directory for additional user scripts. All *.sh scripts in this folder will be included in the final bash script."
|
|
26
|
+
command "settings", "Copy a sample settings.yml file to your project, allowing you to customize some bashly options."
|
|
27
|
+
command "strings", "Copy an additional configuration file to your project, allowing you to customize all the tips and error strings."
|
|
28
|
+
command "test", "Add approval testing."
|
|
29
|
+
command "validations", "Add argument validation functions to the lib directory."
|
|
30
|
+
command "yaml", "Add standard functions for reading YAML files to the lib directory."
|
|
29
31
|
|
|
30
32
|
example "bashly add strings --force"
|
|
31
33
|
example "bashly add comp function"
|
|
@@ -34,45 +36,51 @@ module Bashly
|
|
|
34
36
|
environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
|
|
35
37
|
environment "BASHLY_LIB_DIR", "The path to use for creating the library files, relative to the source dir [default: lib]"
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
attr_reader :skip_src_check
|
|
40
|
+
|
|
41
|
+
def colors_command
|
|
42
|
+
add_lib 'colors'
|
|
39
43
|
end
|
|
40
44
|
|
|
41
|
-
def
|
|
42
|
-
|
|
45
|
+
def comp_command
|
|
46
|
+
format = args['FORMAT']
|
|
47
|
+
output = args['OUTPUT']
|
|
48
|
+
|
|
49
|
+
case format
|
|
50
|
+
when "script" then add_lib 'completions_script', output
|
|
51
|
+
when "function" then add_lib 'completions', output
|
|
52
|
+
when "yaml" then add_lib 'completions_yaml', output
|
|
53
|
+
else raise Error, "Unrecognized format: #{format}"
|
|
54
|
+
end
|
|
43
55
|
end
|
|
44
56
|
|
|
45
57
|
def config_command
|
|
46
58
|
add_lib 'config'
|
|
47
59
|
end
|
|
48
60
|
|
|
49
|
-
def
|
|
50
|
-
|
|
61
|
+
def settings_command
|
|
62
|
+
@skip_src_check = true
|
|
63
|
+
add_lib 'settings'
|
|
51
64
|
end
|
|
52
65
|
|
|
53
|
-
def
|
|
54
|
-
add_lib '
|
|
66
|
+
def strings_command
|
|
67
|
+
add_lib 'strings'
|
|
55
68
|
end
|
|
56
69
|
|
|
57
|
-
def
|
|
58
|
-
add_lib '
|
|
70
|
+
def lib_command
|
|
71
|
+
add_lib 'lib'
|
|
59
72
|
end
|
|
60
73
|
|
|
61
74
|
def test_command
|
|
62
75
|
add_lib 'test'
|
|
63
76
|
end
|
|
64
77
|
|
|
65
|
-
def
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
case format
|
|
70
|
-
when "script" then add_lib 'completions_script', output
|
|
71
|
-
when "function" then add_lib 'completions', output
|
|
72
|
-
when "yaml" then add_lib 'completions_yaml', output
|
|
73
|
-
else raise Error, "Unrecognized format: #{format}"
|
|
74
|
-
end
|
|
78
|
+
def yaml_command
|
|
79
|
+
add_lib 'yaml'
|
|
80
|
+
end
|
|
75
81
|
|
|
82
|
+
def validations_command
|
|
83
|
+
add_lib 'validations'
|
|
76
84
|
end
|
|
77
85
|
|
|
78
86
|
private
|
|
@@ -89,7 +97,7 @@ module Bashly
|
|
|
89
97
|
end
|
|
90
98
|
|
|
91
99
|
def safe_write(path, content)
|
|
92
|
-
if !Dir.exist? Settings.source_dir
|
|
100
|
+
if !skip_src_check and !Dir.exist? Settings.source_dir
|
|
93
101
|
raise InitError, "Directory !txtgrn!#{Settings.source_dir}!txtrst! does not exist\nRun !txtpur!bashly init!txtrst! first"
|
|
94
102
|
end
|
|
95
103
|
|
|
@@ -3,24 +3,35 @@ module Bashly
|
|
|
3
3
|
class Generate < Base
|
|
4
4
|
help "Generate the bash script and required files"
|
|
5
5
|
|
|
6
|
-
usage "bashly generate [
|
|
6
|
+
usage "bashly generate [options]"
|
|
7
7
|
usage "bashly generate (-h|--help)"
|
|
8
8
|
|
|
9
9
|
option "-f --force", "Overwrite existing files"
|
|
10
10
|
option "-q --quiet", "Disable on-screen progress report"
|
|
11
11
|
option "-u --upgrade", "Upgrade all added library functions"
|
|
12
12
|
option "-w --wrap FUNCTION", "Wrap the entire script in a function so it can also be sourced"
|
|
13
|
+
option "-e --env ENV", "Force the generation environment (see BASHLY_ENV)"
|
|
13
14
|
|
|
14
15
|
environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
|
|
15
16
|
environment "BASHLY_TARGET_DIR", "The path to use for creating the bash script [default: .]"
|
|
16
17
|
environment "BASHLY_LIB_DIR", "The path to use for upgrading library files, relative to the source dir [default: lib]"
|
|
17
18
|
environment "BASHLY_STRICT", "When not empty, enable bash strict mode (set -euo pipefail)"
|
|
19
|
+
environment "BASHLY_TAB_INDENT", "When not empty, the generated script will use tab indentation instead of spaces (every 2 leading spaces will be converted to a tab character)"
|
|
20
|
+
environment "BASHLY_ENV", <<~EOF
|
|
21
|
+
Set to 'production' or 'development':
|
|
22
|
+
- production generate a smaller script, without file markers
|
|
23
|
+
- development generate with file markers
|
|
24
|
+
|
|
25
|
+
Can be overridden with --env [default: development]
|
|
26
|
+
EOF
|
|
18
27
|
|
|
19
28
|
example "bashly generate --force"
|
|
20
29
|
example "bashly generate --wrap my_function"
|
|
21
30
|
|
|
22
31
|
def run
|
|
23
32
|
validate_config
|
|
33
|
+
Settings.env = args['--env'] if args['--env']
|
|
34
|
+
quiet_say "creating !txtgrn!production!txtrst! version" if Settings.production?
|
|
24
35
|
create_user_files
|
|
25
36
|
upgrade_libs if args['--upgrade']
|
|
26
37
|
create_master_script
|
|
@@ -104,7 +115,7 @@ module Bashly
|
|
|
104
115
|
end
|
|
105
116
|
|
|
106
117
|
def create_master_script
|
|
107
|
-
File.write master_script_path, script.code
|
|
118
|
+
File.write master_script_path, script.code(tab_indent: Settings.tab_indent)
|
|
108
119
|
FileUtils.chmod "+x", master_script_path
|
|
109
120
|
quiet_say "!txtgrn!created!txtrst! #{master_script_path}"
|
|
110
121
|
end
|
|
@@ -4,13 +4,20 @@ module Bashly
|
|
|
4
4
|
module Renderable
|
|
5
5
|
def render(view)
|
|
6
6
|
template = File.read view_path(view)
|
|
7
|
-
ERB.new(template, trim_mode: '%-')
|
|
7
|
+
erb = ERB.new(template, trim_mode: '%-')
|
|
8
|
+
erb.filename = "#{views_subfolder}.#{view}"
|
|
9
|
+
erb.result binding
|
|
8
10
|
end
|
|
9
11
|
|
|
10
12
|
def strings
|
|
11
13
|
@strings ||= MessageStrings.new
|
|
12
14
|
end
|
|
13
15
|
|
|
16
|
+
def view_marker(id = nil)
|
|
17
|
+
id ||= ":#{caller_locations.first.path}"
|
|
18
|
+
"# #{id}" unless Settings.production?
|
|
19
|
+
end
|
|
20
|
+
|
|
14
21
|
private
|
|
15
22
|
|
|
16
23
|
def view_path(view)
|
|
@@ -42,8 +42,13 @@ module Bashly
|
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
def assert_hash(key, value)
|
|
45
|
+
def assert_hash(key, value, whitelist = nil)
|
|
46
46
|
assert value.is_a?(Hash), "#{key} must be a hash"
|
|
47
|
+
|
|
48
|
+
if whitelist
|
|
49
|
+
invalid_keys = value.keys.map(&:to_sym) - whitelist
|
|
50
|
+
assert invalid_keys.empty?, "#{key} contains invalid options: #{invalid_keys.join(', ')}"
|
|
51
|
+
end
|
|
47
52
|
end
|
|
48
53
|
|
|
49
54
|
def assert_version(key, value)
|
|
@@ -61,6 +66,7 @@ module Bashly
|
|
|
61
66
|
end
|
|
62
67
|
|
|
63
68
|
def assert_catch_all_hash(key, value)
|
|
69
|
+
assert_hash key, value, Script::CatchAll.option_keys
|
|
64
70
|
assert_string "#{key}.label", value['label']
|
|
65
71
|
assert_optional_string "#{key}.help", value['help']
|
|
66
72
|
assert_boolean "#{key}.required", value['required']
|
|
@@ -73,7 +79,7 @@ module Bashly
|
|
|
73
79
|
end
|
|
74
80
|
|
|
75
81
|
def assert_arg(key, value)
|
|
76
|
-
assert_hash key, value
|
|
82
|
+
assert_hash key, value, Script::Argument.option_keys
|
|
77
83
|
assert_string "#{key}.name", value['name']
|
|
78
84
|
assert_optional_string "#{key}.help", value['help']
|
|
79
85
|
assert_optional_string "#{key}.default", value['default']
|
|
@@ -89,7 +95,7 @@ module Bashly
|
|
|
89
95
|
end
|
|
90
96
|
|
|
91
97
|
def assert_flag(key, value)
|
|
92
|
-
assert_hash key, value
|
|
98
|
+
assert_hash key, value, Script::Flag.option_keys
|
|
93
99
|
assert value['short'] || value['long'], "#{key} must have at least one of long or short name"
|
|
94
100
|
|
|
95
101
|
assert_optional_string "#{key}.long", value['long']
|
|
@@ -99,6 +105,7 @@ module Bashly
|
|
|
99
105
|
assert_optional_string "#{key}.default", value['default']
|
|
100
106
|
assert_optional_string "#{key}.validate", value['validate']
|
|
101
107
|
|
|
108
|
+
assert_boolean "#{key}.repeatable", value['repeatable']
|
|
102
109
|
assert_boolean "#{key}.required", value['required']
|
|
103
110
|
assert_array "#{key}.allowed", value['allowed'], of: :string
|
|
104
111
|
assert_array "#{key}.conflicts", value['conflicts'], of: :string
|
|
@@ -119,7 +126,7 @@ module Bashly
|
|
|
119
126
|
end
|
|
120
127
|
|
|
121
128
|
def assert_env_var(key, value)
|
|
122
|
-
assert_hash key, value
|
|
129
|
+
assert_hash key, value, Script::EnvironmentVariable.option_keys
|
|
123
130
|
assert_string "#{key}.name", value['name']
|
|
124
131
|
assert_optional_string "#{key}.help", value['help']
|
|
125
132
|
assert_optional_string "#{key}.default", value['default']
|
|
@@ -127,11 +134,11 @@ module Bashly
|
|
|
127
134
|
end
|
|
128
135
|
|
|
129
136
|
def assert_command(key, value)
|
|
130
|
-
assert_hash key, value
|
|
137
|
+
assert_hash key, value, Script::Command.option_keys
|
|
131
138
|
|
|
132
139
|
refute value['commands'] && value['args'], "#{key} cannot have both commands and args"
|
|
133
140
|
refute value['commands'] && value['flags'], "#{key} cannot have both commands and flags"
|
|
134
|
-
|
|
141
|
+
|
|
135
142
|
assert_string "#{key}.name", value['name']
|
|
136
143
|
assert_optional_string "#{key}.short", value['short']
|
|
137
144
|
assert_optional_string "#{key}.help", value['help']
|
|
@@ -139,6 +146,7 @@ module Bashly
|
|
|
139
146
|
assert_optional_string "#{key}.group", value['group']
|
|
140
147
|
assert_optional_string "#{key}.filename", value['filename']
|
|
141
148
|
|
|
149
|
+
assert_boolean "#{key}.private", value['private']
|
|
142
150
|
assert_boolean "#{key}.default", value['default']
|
|
143
151
|
assert_version "#{key}.version", value['version']
|
|
144
152
|
assert_catch_all "#{key}.catch_all", value['catch_all']
|
|
@@ -153,6 +161,11 @@ module Bashly
|
|
|
153
161
|
assert_array "#{key}.environment_variables", value['environment_variables'], of: :env_var
|
|
154
162
|
assert_array "#{key}.examples", value['examples'], of: :string
|
|
155
163
|
|
|
164
|
+
if value['catch_all'] and value['args']
|
|
165
|
+
repeatable_arg = value['args'].select { |a| a['repeatable'] }.first&.dig 'name'
|
|
166
|
+
refute repeatable_arg, "#{key}.catch_all makes no sense with repeatable arg (#{repeatable_arg})"
|
|
167
|
+
end
|
|
168
|
+
|
|
156
169
|
if key == "root"
|
|
157
170
|
refute value['short'], "#{key}.short makes no sense"
|
|
158
171
|
refute value['group'], "#{key}.group makes no sense"
|
data/lib/bashly/libraries.yml
CHANGED
|
@@ -3,21 +3,25 @@ colors:
|
|
|
3
3
|
- source: "templates/lib/colors.sh"
|
|
4
4
|
target: "%{user_lib_dir}/colors.sh"
|
|
5
5
|
|
|
6
|
+
completions: :CompletionsFunction
|
|
7
|
+
completions_script: :CompletionsScript
|
|
8
|
+
completions_yaml: :CompletionsYAML
|
|
9
|
+
|
|
6
10
|
config:
|
|
7
11
|
files:
|
|
8
12
|
- source: "templates/lib/config.sh"
|
|
9
13
|
target: "%{user_lib_dir}/config.sh"
|
|
10
14
|
|
|
11
|
-
yaml:
|
|
12
|
-
files:
|
|
13
|
-
- source: "templates/lib/yaml.sh"
|
|
14
|
-
target: "%{user_lib_dir}/yaml.sh"
|
|
15
|
-
|
|
16
15
|
lib:
|
|
17
16
|
files:
|
|
18
17
|
- source: "templates/lib/sample_function.sh"
|
|
19
18
|
target: "%{user_lib_dir}/sample_function.sh"
|
|
20
19
|
|
|
20
|
+
settings:
|
|
21
|
+
files:
|
|
22
|
+
- source: "templates/settings.yml"
|
|
23
|
+
target: "settings.yml"
|
|
24
|
+
|
|
21
25
|
strings:
|
|
22
26
|
files:
|
|
23
27
|
- source: "templates/strings.yml"
|
|
@@ -37,7 +41,6 @@ test:
|
|
|
37
41
|
|
|
38
42
|
Docs: !undblu!https://github.com/DannyBen/approvals.bash
|
|
39
43
|
|
|
40
|
-
|
|
41
44
|
validations:
|
|
42
45
|
files:
|
|
43
46
|
- source: "templates/lib/validations/validate_dir_exists.sh"
|
|
@@ -49,6 +52,7 @@ validations:
|
|
|
49
52
|
- source: "templates/lib/validations/validate_not_empty.sh"
|
|
50
53
|
target: "%{user_lib_dir}/validations/validate_not_empty.sh"
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
yaml:
|
|
56
|
+
files:
|
|
57
|
+
- source: "templates/lib/yaml.sh"
|
|
58
|
+
target: "%{user_lib_dir}/yaml.sh"
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
module Bashly
|
|
2
2
|
module Script
|
|
3
3
|
class Argument < Base
|
|
4
|
+
class << self
|
|
5
|
+
def option_keys
|
|
6
|
+
@option_keys ||= %i[
|
|
7
|
+
allowed default help name repeatable required validate
|
|
8
|
+
]
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
4
12
|
def usage_string
|
|
5
|
-
required ?
|
|
13
|
+
required ? label : "[#{label}]"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def label
|
|
17
|
+
repeatable ? "#{name.upcase}..." : name.upcase
|
|
6
18
|
end
|
|
7
19
|
end
|
|
8
20
|
end
|
data/lib/bashly/script/base.rb
CHANGED
|
@@ -5,33 +5,11 @@ module Bashly
|
|
|
5
5
|
|
|
6
6
|
attr_reader :options
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
conflicts
|
|
14
|
-
default
|
|
15
|
-
dependencies
|
|
16
|
-
description
|
|
17
|
-
environment_variables
|
|
18
|
-
examples
|
|
19
|
-
extensible
|
|
20
|
-
filters
|
|
21
|
-
flags
|
|
22
|
-
footer
|
|
23
|
-
group
|
|
24
|
-
help
|
|
25
|
-
long
|
|
26
|
-
name
|
|
27
|
-
parent_name
|
|
28
|
-
private
|
|
29
|
-
repeatable
|
|
30
|
-
required
|
|
31
|
-
short
|
|
32
|
-
validate
|
|
33
|
-
version
|
|
34
|
-
]
|
|
8
|
+
class << self
|
|
9
|
+
def option_keys
|
|
10
|
+
@option_keys ||= []
|
|
11
|
+
end
|
|
12
|
+
end
|
|
35
13
|
|
|
36
14
|
def initialize(options)
|
|
37
15
|
raise Error, "Invalid options provided" unless options.respond_to? :keys
|
|
@@ -39,7 +17,7 @@ module Bashly
|
|
|
39
17
|
end
|
|
40
18
|
|
|
41
19
|
def optional
|
|
42
|
-
!required
|
|
20
|
+
!options['required']
|
|
43
21
|
end
|
|
44
22
|
|
|
45
23
|
def summary
|
|
@@ -56,7 +34,7 @@ module Bashly
|
|
|
56
34
|
end
|
|
57
35
|
|
|
58
36
|
def respond_to_missing?(method_name, include_private = false)
|
|
59
|
-
|
|
37
|
+
self.class.option_keys.include?(method_name) || super
|
|
60
38
|
end
|
|
61
39
|
end
|
|
62
40
|
end
|
|
@@ -4,6 +4,18 @@ module Bashly
|
|
|
4
4
|
include Completions
|
|
5
5
|
include CommandScopes
|
|
6
6
|
|
|
7
|
+
class << self
|
|
8
|
+
def option_keys
|
|
9
|
+
@option_keys ||= %i[
|
|
10
|
+
args catch_all commands completions
|
|
11
|
+
default dependencies environment_variables examples
|
|
12
|
+
extensible filename filters flags
|
|
13
|
+
footer group help name
|
|
14
|
+
private short version
|
|
15
|
+
]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
7
19
|
# Returns the name to be used as an action.
|
|
8
20
|
# - If it is the root command, the action is "root"
|
|
9
21
|
# - Else, it is all the parents, except the first one (root) joined
|
|
@@ -28,7 +40,7 @@ module Bashly
|
|
|
28
40
|
|
|
29
41
|
# Returns a string suitable to be a headline
|
|
30
42
|
def caption_string
|
|
31
|
-
help ? "#{full_name} - #{summary}"
|
|
43
|
+
help.empty? ? full_name : "#{full_name} - #{summary}"
|
|
32
44
|
end
|
|
33
45
|
|
|
34
46
|
def catch_all
|
|
@@ -82,15 +94,16 @@ module Bashly
|
|
|
82
94
|
# If the file is not found, returns a string with a hint.
|
|
83
95
|
def load_user_file(file, placeholder: true)
|
|
84
96
|
path = "#{Settings.source_dir}/#{file}"
|
|
85
|
-
default_content = placeholder ? "echo \"error: cannot load file\"" : ''
|
|
86
97
|
|
|
87
98
|
content = if File.exist? path
|
|
88
99
|
File.read(path).remove_front_matter
|
|
89
|
-
|
|
90
|
-
|
|
100
|
+
elsif placeholder
|
|
101
|
+
%q[echo "error: cannot load file"]
|
|
102
|
+
else
|
|
103
|
+
''
|
|
91
104
|
end
|
|
92
105
|
|
|
93
|
-
"#
|
|
106
|
+
Settings.production? ? content : "#{view_marker path}\n#{content}"
|
|
94
107
|
end
|
|
95
108
|
|
|
96
109
|
# Returns an array of all parents. For example, the command
|
|
@@ -99,7 +112,12 @@ module Bashly
|
|
|
99
112
|
options['parents'] || []
|
|
100
113
|
end
|
|
101
114
|
|
|
102
|
-
# Returns
|
|
115
|
+
# Returns true if one of the args is repeatable
|
|
116
|
+
def repeatable_arg_exist?
|
|
117
|
+
args.select(&:repeatable).any?
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Returns true if this is the root command (no parents)
|
|
103
121
|
def root_command?
|
|
104
122
|
parents.empty?
|
|
105
123
|
end
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
module Bashly
|
|
2
2
|
module Script
|
|
3
3
|
class EnvironmentVariable < Base
|
|
4
|
+
class << self
|
|
5
|
+
def option_keys
|
|
6
|
+
@option_keys ||= %i[default help name required]
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
4
10
|
def usage_string(extended: false)
|
|
5
11
|
result = [name.upcase]
|
|
6
12
|
result << strings[:required] if required and extended
|
data/lib/bashly/script/flag.rb
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
module Bashly
|
|
2
2
|
module Script
|
|
3
3
|
class Flag < Base
|
|
4
|
+
class << self
|
|
5
|
+
def option_keys
|
|
6
|
+
@option_keys ||= %i[
|
|
7
|
+
allowed arg conflicts default help long repeatable required
|
|
8
|
+
short validate
|
|
9
|
+
]
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
4
13
|
def aliases
|
|
5
14
|
if long and short
|
|
6
15
|
[long, short]
|
|
@@ -9,18 +9,22 @@ module Bashly
|
|
|
9
9
|
@command, @function_name = command, function_name
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def code
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
def code(tab_indent: false)
|
|
13
|
+
tab_indent ? base_code.expand_tabs : base_code
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def base_code
|
|
19
|
+
result = if function_name
|
|
20
|
+
[header, render('wrapper')]
|
|
15
21
|
else
|
|
16
|
-
|
|
22
|
+
[header, body]
|
|
17
23
|
end
|
|
18
24
|
|
|
19
|
-
result.lint
|
|
25
|
+
result.join("\n").lint
|
|
20
26
|
end
|
|
21
27
|
|
|
22
|
-
private
|
|
23
|
-
|
|
24
28
|
def header
|
|
25
29
|
@header ||= header!
|
|
26
30
|
end
|
data/lib/bashly/settings.rb
CHANGED
|
@@ -1,27 +1,56 @@
|
|
|
1
1
|
module Bashly
|
|
2
2
|
class Settings
|
|
3
3
|
class << self
|
|
4
|
-
attr_writer :source_dir, :target_dir, :lib_dir, :strict
|
|
4
|
+
attr_writer :source_dir, :target_dir, :lib_dir, :strict, :tab_indent
|
|
5
5
|
|
|
6
6
|
def source_dir
|
|
7
|
-
@source_dir ||=
|
|
7
|
+
@source_dir ||= get :source_dir, 'src'
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def target_dir
|
|
11
|
-
@target_dir ||=
|
|
11
|
+
@target_dir ||= get :target_dir, '.'
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def lib_dir
|
|
15
|
-
@lib_dir ||=
|
|
15
|
+
@lib_dir ||= get :lib_dir, 'lib'
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def strict
|
|
19
|
-
@strict ||=
|
|
19
|
+
@strict ||= get :strict
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def tab_indent
|
|
23
|
+
@tab_indent ||= get :tab_indent
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def env
|
|
27
|
+
@env ||= get(:env, :development)&.to_sym
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def env=(value)
|
|
31
|
+
@env = value&.to_sym
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def production?
|
|
35
|
+
env == :production
|
|
20
36
|
end
|
|
21
37
|
|
|
22
38
|
def full_lib_dir
|
|
23
39
|
"#{source_dir}/#{lib_dir}"
|
|
24
40
|
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def get(key, default = nil)
|
|
45
|
+
ENV["BASHLY_#{key.upcase}"] || user_settings[key.to_s] || default
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def user_settings
|
|
49
|
+
@user_settings ||= begin
|
|
50
|
+
File.exist?('settings.yml') ? Config.new('settings.yml') : {}
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
25
54
|
end
|
|
26
55
|
end
|
|
27
56
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# All settings are optional (with their default values provided below), and
|
|
2
|
+
# can also be set with an environment variable with the same name, capitalized
|
|
3
|
+
# and prefixed by `BASHLY_` - for example: BASHLY_SOURCE_DIR
|
|
4
|
+
|
|
5
|
+
# The path containing the bashly configuration and source files
|
|
6
|
+
source_dir: src
|
|
7
|
+
|
|
8
|
+
# The path to use for creating the bash script
|
|
9
|
+
target_dir: .
|
|
10
|
+
|
|
11
|
+
# The path to use for upgrading library files, relative to the source dir
|
|
12
|
+
lib_dir: lib
|
|
13
|
+
|
|
14
|
+
# When true, enable bash strict mode (set -euo pipefail)
|
|
15
|
+
strict: false
|
|
16
|
+
|
|
17
|
+
# When true, the generated script will use tab indentation instead of spaces
|
|
18
|
+
# (every 2 leading spaces will be converted to a tab character)
|
|
19
|
+
tab_indent: false
|
|
20
|
+
|
|
21
|
+
# Set to 'production' or 'development':
|
|
22
|
+
# - production generate a smaller script, without file markers
|
|
23
|
+
# - development generate with file markers
|
|
24
|
+
env: development
|
data/lib/bashly/version.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
<%= view_marker %>
|
|
2
2
|
case "${1:-}" in
|
|
3
|
+
% if root_command?
|
|
3
4
|
% if short_flag_exist? "-v"
|
|
4
5
|
--version )
|
|
5
6
|
% else
|
|
@@ -9,6 +10,7 @@ case "${1:-}" in
|
|
|
9
10
|
exit
|
|
10
11
|
;;
|
|
11
12
|
|
|
13
|
+
% end
|
|
12
14
|
% if short_flag_exist? "-h"
|
|
13
15
|
--help )
|
|
14
16
|
% else
|
|
@@ -1,26 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
% if args.any?
|
|
3
|
-
% condition = "if"
|
|
4
|
-
% args.each do |arg|
|
|
5
|
-
<%= condition %> [[ -z ${args[<%= arg.name %>]+x} ]]; then
|
|
6
|
-
<%= arg.render(:validations).indent 2 %>
|
|
7
|
-
args[<%= arg.name %>]=$1
|
|
8
|
-
shift
|
|
9
|
-
% condition = "elif"
|
|
10
|
-
% end
|
|
11
|
-
else
|
|
1
|
+
<%= view_marker %>
|
|
12
2
|
% if catch_all.enabled?
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
printf "<%= strings[:invalid_argument] %>\n" "$key"
|
|
17
|
-
exit 1
|
|
18
|
-
% end
|
|
19
|
-
fi
|
|
20
|
-
% elsif catch_all.enabled?
|
|
21
|
-
other_args+=("$1")
|
|
22
|
-
shift
|
|
3
|
+
<%= render(:parse_requirements_case_catch_all) %>
|
|
4
|
+
% elsif repeatable_arg_exist?
|
|
5
|
+
<%= render(:parse_requirements_case_repeatable) %>
|
|
23
6
|
% else
|
|
24
|
-
|
|
25
|
-
exit 1
|
|
7
|
+
<%= render(:parse_requirements_case_simple) %>
|
|
26
8
|
% end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<%= view_marker %>
|
|
2
|
+
% if args.any?
|
|
3
|
+
% condition = "if"
|
|
4
|
+
% args.each do |arg|
|
|
5
|
+
<%= condition %> [[ -z ${args[<%= arg.name %>]+x} ]]; then
|
|
6
|
+
<%= arg.render(:validations).indent 2 %>
|
|
7
|
+
args[<%= arg.name %>]=$1
|
|
8
|
+
shift
|
|
9
|
+
% condition = "elif"
|
|
10
|
+
% end
|
|
11
|
+
else
|
|
12
|
+
other_args+=("$1")
|
|
13
|
+
shift
|
|
14
|
+
fi
|
|
15
|
+
% else
|
|
16
|
+
other_args+=("$1")
|
|
17
|
+
shift
|
|
18
|
+
% end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<%= view_marker %>
|
|
2
|
+
% condition = "if"
|
|
3
|
+
% args.each do |arg|
|
|
4
|
+
<%= condition %> [[ -z ${args[<%= arg.name %>]+x} ]]; then
|
|
5
|
+
<%= arg.render(:validations).indent 2 %>
|
|
6
|
+
% if arg.repeatable
|
|
7
|
+
args[<%= arg.name %>]="\"$1\""
|
|
8
|
+
shift
|
|
9
|
+
else
|
|
10
|
+
args[<%= arg.name %>]="${args[<%= arg.name %>]} \"$1\""
|
|
11
|
+
shift
|
|
12
|
+
% else
|
|
13
|
+
args[<%= arg.name %>]=$1
|
|
14
|
+
shift
|
|
15
|
+
% end
|
|
16
|
+
% condition = "elif"
|
|
17
|
+
% end
|
|
18
|
+
fi
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<%= view_marker %>
|
|
2
|
+
% if args.any?
|
|
3
|
+
% condition = "if"
|
|
4
|
+
% args.each do |arg|
|
|
5
|
+
<%= condition %> [[ -z ${args[<%= arg.name %>]+x} ]]; then
|
|
6
|
+
<%= arg.render(:validations).indent 2 %>
|
|
7
|
+
args[<%= arg.name %>]=$1
|
|
8
|
+
shift
|
|
9
|
+
% condition = "elif"
|
|
10
|
+
% end
|
|
11
|
+
else
|
|
12
|
+
printf "<%= strings[:invalid_argument] %>\n" "$key"
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
% else
|
|
16
|
+
printf "<%= strings[:invalid_argument] %>\n" "$key"
|
|
17
|
+
exit 1
|
|
18
|
+
% end
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
<%= view_marker %>
|
|
2
2
|
% whitelisted_args.each do |arg|
|
|
3
|
+
% if arg.repeatable
|
|
4
|
+
eval "input_array=(${args[<%= arg.name %>]})"
|
|
5
|
+
for i in "${input_array[@]}"; do
|
|
6
|
+
if [[ ! $i =~ ^(<%= arg.allowed.join '|' %>)$ ]]; then
|
|
7
|
+
printf "%s\n" "<%= strings[:disallowed_argument] % { name: arg.name, allowed: arg.allowed.join(', ') } %>"
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
done
|
|
11
|
+
% else
|
|
3
12
|
if [[ ! ${args[<%= arg.name %>]} =~ ^(<%= arg.allowed.join '|' %>)$ ]]; then
|
|
4
13
|
printf "%s\n" "<%= strings[:disallowed_argument] % { name: arg.name, allowed: arg.allowed.join(', ') } %>"
|
|
5
14
|
exit 1
|
|
6
15
|
fi
|
|
7
16
|
% end
|
|
17
|
+
% end
|
|
8
18
|
% whitelisted_flags.each do |flag|
|
|
9
19
|
% if flag.repeatable
|
|
10
20
|
eval "input_array=(${args[<%= flag.name %>]})"
|
|
@@ -1,30 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
<%= view_marker %>
|
|
2
2
|
<%= aliases.join " | " %> )
|
|
3
3
|
<%= render(:conflicts).indent 2 %>
|
|
4
|
-
|
|
5
|
-
if [[ -n ${2+x} ]]; then
|
|
6
|
-
<%= render(:validations).indent 4 %>
|
|
7
|
-
% if repeatable
|
|
8
|
-
if [[ -z ${args[<%= name %>]+x} ]]; then
|
|
9
|
-
args[<%= name %>]="\"$2\""
|
|
10
|
-
else
|
|
11
|
-
args[<%= name %>]="${args[<%= name %>]} \"$2\""
|
|
12
|
-
fi
|
|
13
|
-
% else
|
|
14
|
-
args[<%= name %>]="$2"
|
|
15
|
-
% end
|
|
16
|
-
shift
|
|
17
|
-
shift
|
|
18
|
-
else
|
|
19
|
-
printf "%s\n" "<%= strings[:flag_requires_an_argument] % { name: name, usage: usage_string } %>"
|
|
20
|
-
exit 1
|
|
21
|
-
fi
|
|
22
|
-
% else
|
|
23
|
-
% if repeatable
|
|
24
|
-
(( args[<%= name %>]+=1 ))
|
|
25
|
-
% else
|
|
26
|
-
args[<%= name %>]=1
|
|
27
|
-
% end
|
|
28
|
-
shift
|
|
29
|
-
% end
|
|
30
|
-
;;
|
|
4
|
+
<%= render(arg ? :case_arg : :case_no_arg).indent 2 %>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%= view_marker %>
|
|
2
|
+
if [[ -n ${2+x} ]]; then
|
|
3
|
+
<%= render(:validations).indent 2 %>
|
|
4
|
+
% if repeatable
|
|
5
|
+
if [[ -z ${args[<%= name %>]+x} ]]; then
|
|
6
|
+
args[<%= name %>]="\"$2\""
|
|
7
|
+
else
|
|
8
|
+
args[<%= name %>]="${args[<%= name %>]} \"$2\""
|
|
9
|
+
fi
|
|
10
|
+
% else
|
|
11
|
+
args[<%= name %>]="$2"
|
|
12
|
+
% end
|
|
13
|
+
shift
|
|
14
|
+
shift
|
|
15
|
+
else
|
|
16
|
+
printf "%s\n" "<%= strings[:flag_requires_an_argument] % { name: name, usage: usage_string } %>"
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
;;
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bashly
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Danny Ben Shitrit
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-04-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: colsole
|
|
@@ -121,6 +121,7 @@ files:
|
|
|
121
121
|
- lib/bashly/templates/lib/validations/validate_not_empty.sh
|
|
122
122
|
- lib/bashly/templates/lib/yaml.sh
|
|
123
123
|
- lib/bashly/templates/minimal.yml
|
|
124
|
+
- lib/bashly/templates/settings.yml
|
|
124
125
|
- lib/bashly/templates/strings.yml
|
|
125
126
|
- lib/bashly/templates/test/approvals.bash
|
|
126
127
|
- lib/bashly/version.rb
|
|
@@ -145,6 +146,9 @@ files:
|
|
|
145
146
|
- lib/bashly/views/command/normalize_input.erb
|
|
146
147
|
- lib/bashly/views/command/parse_requirements.erb
|
|
147
148
|
- lib/bashly/views/command/parse_requirements_case.erb
|
|
149
|
+
- lib/bashly/views/command/parse_requirements_case_catch_all.erb
|
|
150
|
+
- lib/bashly/views/command/parse_requirements_case_repeatable.erb
|
|
151
|
+
- lib/bashly/views/command/parse_requirements_case_simple.erb
|
|
148
152
|
- lib/bashly/views/command/parse_requirements_while.erb
|
|
149
153
|
- lib/bashly/views/command/required_args_filter.erb
|
|
150
154
|
- lib/bashly/views/command/required_flags_filter.erb
|
|
@@ -163,6 +167,8 @@ files:
|
|
|
163
167
|
- lib/bashly/views/command/whitelist_filter.erb
|
|
164
168
|
- lib/bashly/views/environment_variable/usage.erb
|
|
165
169
|
- lib/bashly/views/flag/case.erb
|
|
170
|
+
- lib/bashly/views/flag/case_arg.erb
|
|
171
|
+
- lib/bashly/views/flag/case_no_arg.erb
|
|
166
172
|
- lib/bashly/views/flag/conflicts.erb
|
|
167
173
|
- lib/bashly/views/flag/usage.erb
|
|
168
174
|
- lib/bashly/views/flag/validations.erb
|
|
@@ -192,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
192
198
|
- !ruby/object:Gem::Version
|
|
193
199
|
version: '0'
|
|
194
200
|
requirements: []
|
|
195
|
-
rubygems_version: 3.
|
|
201
|
+
rubygems_version: 3.3.3
|
|
196
202
|
signing_key:
|
|
197
203
|
specification_version: 4
|
|
198
204
|
summary: Bash Command Line Tool Generator
|