bashly 0.7.10 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/bashly/commands/base.rb +21 -3
- data/lib/bashly/commands/generate.rb +48 -11
- data/lib/bashly/commands/preview.rb +5 -4
- data/lib/bashly/commands/validate.rb +7 -1
- data/lib/bashly/concerns/completions.rb +60 -35
- data/lib/bashly/concerns/renderable.rb +16 -0
- data/lib/bashly/concerns/validation_helpers.rb +75 -0
- data/lib/bashly/config_validator.rb +32 -41
- data/lib/bashly/deprecation.rb +25 -0
- data/lib/bashly/extensions/array.rb +5 -0
- data/lib/bashly/script/command.rb +140 -22
- data/lib/bashly/script/flag.rb +4 -2
- data/lib/bashly/templates/bashly.yml +2 -2
- data/lib/bashly/templates/strings.yml +1 -1
- data/lib/bashly/version.rb +1 -1
- data/lib/bashly/views/command/usage.erb +2 -2
- data/lib/bashly/views/command/usage_commands.erb +8 -13
- metadata +25 -10
- data/lib/bashly/concerns/command_scopes.rb +0 -68
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc4a83c90c751c1df692c55db4f7335619c927cb86a30de4fcb7eef5115710a1
|
4
|
+
data.tar.gz: 343ede4e31e7a0833b6335a9690c9ecbe060da1983d829cfbdf197bbfe5dc640
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b5873adb5c173a4676ae071f6d83b11975053addd8b39e50613375032d441b227d6f27c4ad860719bdf22e37f7ce8882217e5c622c607dcff048fe7dfdaab18
|
7
|
+
data.tar.gz: c01a6a3c20aee19338c3cddfbac0254d7316c5b9aa8f2423bbd6ba65746b439e1ce7e3eec2f2654087c3a6b55388fdeca645dfe786e4adcac97997f1a0f778e4
|
data/lib/bashly/commands/base.rb
CHANGED
@@ -6,10 +6,28 @@ module Bashly
|
|
6
6
|
class Base < MisterBin::Command
|
7
7
|
include AssetHelper
|
8
8
|
|
9
|
+
def config
|
10
|
+
@config ||= Config.new "#{Settings.source_dir}/bashly.yml"
|
11
|
+
end
|
12
|
+
|
13
|
+
def config_validator
|
14
|
+
@config_validator ||= ConfigValidator.new config
|
15
|
+
end
|
16
|
+
|
9
17
|
def validate_config
|
10
|
-
|
11
|
-
|
12
|
-
|
18
|
+
config_validator.validate
|
19
|
+
end
|
20
|
+
|
21
|
+
def with_valid_config
|
22
|
+
validate_config
|
23
|
+
yield
|
24
|
+
show_deprecations
|
25
|
+
end
|
26
|
+
|
27
|
+
def show_deprecations
|
28
|
+
return if config_validator.deprecations.empty? or ENV['BASHLY_HIDE_DEPRECATIONS']
|
29
|
+
messages = "\n" + config_validator.deprecations.map(&:message).join("\n\n") + "\n\n"
|
30
|
+
say! messages
|
13
31
|
end
|
14
32
|
end
|
15
33
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'filewatcher'
|
2
|
+
|
1
3
|
module Bashly
|
2
4
|
module Commands
|
3
5
|
class Generate < Base
|
@@ -9,7 +11,8 @@ module Bashly
|
|
9
11
|
option "-f --force", "Overwrite existing files"
|
10
12
|
option "-q --quiet", "Disable on-screen progress report"
|
11
13
|
option "-u --upgrade", "Upgrade all added library functions"
|
12
|
-
option "-w --
|
14
|
+
option "-w --watch", "Watch the source directory for changes and regenerate on change"
|
15
|
+
option "-r --wrap FUNCTION", "Wrap the entire script in a function so it can also be sourced"
|
13
16
|
option "-e --env ENV", "Force the generation environment (see BASHLY_ENV)"
|
14
17
|
|
15
18
|
environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
|
@@ -27,23 +30,61 @@ module Bashly
|
|
27
30
|
|
28
31
|
example "bashly generate --force"
|
29
32
|
example "bashly generate --wrap my_function"
|
33
|
+
example "bashly g -uw"
|
34
|
+
|
35
|
+
attr_reader :watching
|
30
36
|
|
31
37
|
def run
|
32
|
-
validate_config
|
33
38
|
Settings.env = args['--env'] if args['--env']
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
quiet_say "run !txtpur!#{master_script_path} --help!txtrst! to test your bash script"
|
39
|
+
@watching = args['--watch']
|
40
|
+
|
41
|
+
generate
|
42
|
+
watch if watching
|
39
43
|
end
|
40
44
|
|
41
45
|
private
|
42
46
|
|
47
|
+
def watch
|
48
|
+
quiet_say "!txtgrn!watching!txtrst! #{Settings.source_dir}\n"
|
49
|
+
|
50
|
+
Filewatcher.new([Settings.source_dir]).watch do
|
51
|
+
reset
|
52
|
+
generate
|
53
|
+
|
54
|
+
rescue Bashly::ConfigurationError => e
|
55
|
+
say! "!undred!#{e.class}!txtrst!\n#{e.message}"
|
56
|
+
|
57
|
+
ensure
|
58
|
+
quiet_say "!txtgrn!waiting\n"
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate
|
64
|
+
with_valid_config do
|
65
|
+
quiet_say "creating !txtgrn!production!txtrst! version" if Settings.production?
|
66
|
+
generate_all_files
|
67
|
+
quiet_say "run !txtpur!#{master_script_path} --help!txtrst! to test your bash script" unless watching
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def reset
|
72
|
+
@config = nil
|
73
|
+
@config_validator = nil
|
74
|
+
@command = nil
|
75
|
+
@script = nil
|
76
|
+
end
|
77
|
+
|
43
78
|
def quiet_say(message)
|
44
79
|
say message unless args['--quiet']
|
45
80
|
end
|
46
81
|
|
82
|
+
def generate_all_files
|
83
|
+
create_user_files
|
84
|
+
upgrade_libs if args['--upgrade']
|
85
|
+
create_master_script
|
86
|
+
end
|
87
|
+
|
47
88
|
def upgrade_libs
|
48
89
|
generated_files.each do |file|
|
49
90
|
content = File.read file
|
@@ -128,10 +169,6 @@ module Bashly
|
|
128
169
|
"#{Settings.target_dir}/#{command.name}"
|
129
170
|
end
|
130
171
|
|
131
|
-
def config
|
132
|
-
@config ||= Config.new "#{Settings.source_dir}/bashly.yml"
|
133
|
-
end
|
134
|
-
|
135
172
|
def command
|
136
173
|
@command ||= Script::Command.new config
|
137
174
|
end
|
@@ -9,10 +9,11 @@ module Bashly
|
|
9
9
|
environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
|
10
10
|
|
11
11
|
def run
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
with_valid_config do
|
13
|
+
command = Script::Command.new config
|
14
|
+
script = Script::Wrapper.new command
|
15
|
+
puts script.code
|
16
|
+
end
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
@@ -10,7 +10,13 @@ module Bashly
|
|
10
10
|
|
11
11
|
def run
|
12
12
|
validate_config
|
13
|
-
|
13
|
+
show_deprecations
|
14
|
+
deprecations = config_validator.deprecations
|
15
|
+
if deprecations.empty?
|
16
|
+
say "!txtgrn!OK"
|
17
|
+
else
|
18
|
+
say "!txtred!WARNING!txtrst! Found #{deprecations.count} deprecations"
|
19
|
+
end
|
14
20
|
end
|
15
21
|
end
|
16
22
|
end
|
@@ -1,51 +1,76 @@
|
|
1
1
|
require 'completely'
|
2
2
|
|
3
3
|
module Bashly
|
4
|
-
# This is a `Command` concern responsible for providing bash
|
4
|
+
# This is a `Command` and `Flag` concern responsible for providing bash
|
5
|
+
# completion data
|
5
6
|
module Completions
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
result.merge! command.completion_data(with_version: false)
|
11
|
-
end
|
7
|
+
module Flag
|
8
|
+
def completion_data(command_full_name)
|
9
|
+
result = {}
|
10
|
+
comps = allowed || completions
|
12
11
|
|
13
|
-
|
14
|
-
|
12
|
+
if comps
|
13
|
+
aliases.each do |name|
|
14
|
+
result["#{command_full_name}*#{name}"] = comps
|
15
|
+
end
|
16
|
+
end
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
+
result
|
19
|
+
end
|
18
20
|
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
module Command
|
23
|
+
def completion_data(with_version: true)
|
24
|
+
result = {}
|
23
25
|
|
24
|
-
|
26
|
+
all_full_names.each do |name|
|
27
|
+
result[name] = completion_words(with_version: with_version)
|
28
|
+
flags.each do |flag|
|
29
|
+
result.merge! flag.completion_data(name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
commands.each do |command|
|
34
|
+
result.merge! command.completion_data(with_version: false)
|
35
|
+
end
|
25
36
|
|
26
|
-
|
27
|
-
|
28
|
-
end
|
37
|
+
result
|
38
|
+
end
|
29
39
|
|
30
|
-
|
31
|
-
|
32
|
-
|
40
|
+
def completion_script
|
41
|
+
completion_generator.script
|
42
|
+
end
|
33
43
|
|
34
|
-
|
35
|
-
|
36
|
-
|
44
|
+
def completion_function(name = nil)
|
45
|
+
completion_generator.wrapper_function(name)
|
46
|
+
end
|
37
47
|
|
38
|
-
|
39
|
-
trivial_flags = %w[--help -h]
|
40
|
-
trivial_flags += %w[--version -v] if with_version
|
41
|
-
all = (
|
42
|
-
command_names + trivial_flags +
|
43
|
-
completion_flag_names + completion_allowed_args
|
44
|
-
)
|
48
|
+
private
|
45
49
|
|
46
|
-
|
47
|
-
|
48
|
-
|
50
|
+
def completion_generator
|
51
|
+
Completely::Completions.new(completion_data)
|
52
|
+
end
|
53
|
+
|
54
|
+
def completion_flag_names
|
55
|
+
flags.map(&:name) + flags.map(&:short)
|
56
|
+
end
|
57
|
+
|
58
|
+
def completion_allowed_args
|
59
|
+
args.map(&:allowed).flatten
|
60
|
+
end
|
61
|
+
|
62
|
+
def completion_words(with_version: false)
|
63
|
+
trivial_flags = %w[--help -h]
|
64
|
+
trivial_flags += %w[--version -v] if with_version
|
65
|
+
all = (
|
66
|
+
command_aliases + trivial_flags +
|
67
|
+
completion_flag_names + completion_allowed_args
|
68
|
+
)
|
49
69
|
|
70
|
+
all += completions if completions
|
71
|
+
all.compact.uniq.sort
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
50
75
|
end
|
51
|
-
end
|
76
|
+
end
|
@@ -18,6 +18,22 @@ module Bashly
|
|
18
18
|
"# #{id}" unless Settings.production?
|
19
19
|
end
|
20
20
|
|
21
|
+
# Reads a file from the userspace (Settings.source_dir) and returns
|
22
|
+
# its contents. If the file is not found, returns a string with a hint.
|
23
|
+
def load_user_file(file, placeholder: true)
|
24
|
+
path = "#{Settings.source_dir}/#{file}"
|
25
|
+
|
26
|
+
content = if File.exist? path
|
27
|
+
File.read(path).remove_front_matter
|
28
|
+
elsif placeholder
|
29
|
+
%q[echo "error: cannot load file"]
|
30
|
+
else
|
31
|
+
''
|
32
|
+
end
|
33
|
+
|
34
|
+
Settings.production? ? content : "#{view_marker path}\n#{content}"
|
35
|
+
end
|
36
|
+
|
21
37
|
private
|
22
38
|
|
23
39
|
def view_path(view)
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Bashly
|
2
|
+
# This is a `ConfigValidator` concern responsible for providing basic
|
3
|
+
# assertion methods.
|
4
|
+
module ValidationHelpers
|
5
|
+
|
6
|
+
def deprecations
|
7
|
+
@deprecations ||= []
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def assert(valid, message)
|
13
|
+
raise ConfigurationError, message unless valid
|
14
|
+
end
|
15
|
+
|
16
|
+
def refute(invalid, message)
|
17
|
+
assert !invalid, message
|
18
|
+
end
|
19
|
+
|
20
|
+
def deprecate(key, **options)
|
21
|
+
deprecations.push Deprecation.new(key, **options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def assert_string(key, value)
|
25
|
+
assert value.is_a?(String), "#{key} must be a string"
|
26
|
+
end
|
27
|
+
|
28
|
+
def assert_optional_string(key, value)
|
29
|
+
assert_string key, value if value
|
30
|
+
end
|
31
|
+
|
32
|
+
def assert_boolean(key, value)
|
33
|
+
assert [true, false, nil].include?(value), "#{key} must be a boolean"
|
34
|
+
end
|
35
|
+
|
36
|
+
def assert_array(key, value, of: nil)
|
37
|
+
return unless value
|
38
|
+
assert value.is_a?(Array), "#{key} must be an array"
|
39
|
+
if of
|
40
|
+
value.each_with_index do |val, i|
|
41
|
+
send "assert_#{of}".to_sym, "#{key}[#{i}]", val
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def assert_hash(key, value, whitelist = nil)
|
47
|
+
assert value.is_a?(Hash), "#{key} must be a hash"
|
48
|
+
|
49
|
+
if whitelist
|
50
|
+
invalid_keys = value.keys.map(&:to_sym) - whitelist
|
51
|
+
assert invalid_keys.empty?, "#{key} contains invalid options: #{invalid_keys.join(', ')}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def assert_uniq(key, value, array_keys)
|
56
|
+
return unless value
|
57
|
+
array_keys = [array_keys] unless array_keys.is_a? Array
|
58
|
+
list = []
|
59
|
+
array_keys.each do |array_key|
|
60
|
+
list += value.map { |c| c[array_key] }.compact.flatten
|
61
|
+
end
|
62
|
+
|
63
|
+
nonuniqs = list.nonuniq
|
64
|
+
assert nonuniqs.empty?, "#{key} contains non-unique elements (#{nonuniqs.join ', '}) in #{array_keys.join ' or '}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def assert_string_or_array(key, value)
|
68
|
+
return unless value
|
69
|
+
assert [Array, String].include?(value.class),
|
70
|
+
"#{key} must be a string or an array"
|
71
|
+
|
72
|
+
assert_array key, value, of: :string if value.is_a? Array
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module Bashly
|
2
2
|
class ConfigValidator
|
3
|
+
include ValidationHelpers
|
4
|
+
|
3
5
|
attr_reader :data
|
4
6
|
|
5
7
|
def initialize(data)
|
@@ -12,45 +14,6 @@ module Bashly
|
|
12
14
|
|
13
15
|
private
|
14
16
|
|
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, whitelist = nil)
|
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
|
52
|
-
end
|
53
|
-
|
54
17
|
def assert_version(key, value)
|
55
18
|
return unless value
|
56
19
|
assert [String, Integer, Float].include?(value.class),
|
@@ -78,6 +41,11 @@ module Bashly
|
|
78
41
|
"#{key} must be a boolean or a string"
|
79
42
|
end
|
80
43
|
|
44
|
+
def assert_expose(key, value)
|
45
|
+
return unless value
|
46
|
+
assert [true, false, nil, 'always'].include?(value), "#{key} must be a boolean, or the string 'always'"
|
47
|
+
end
|
48
|
+
|
81
49
|
def assert_arg(key, value)
|
82
50
|
assert_hash key, value, Script::Argument.option_keys
|
83
51
|
assert_string "#{key}.name", value['name']
|
@@ -98,6 +66,8 @@ module Bashly
|
|
98
66
|
assert_hash key, value, Script::Flag.option_keys
|
99
67
|
assert value['short'] || value['long'], "#{key} must have at least one of long or short name"
|
100
68
|
|
69
|
+
refute value['allowed'] && value['completions'], "#{key} cannot have both allowed and completions"
|
70
|
+
|
101
71
|
assert_optional_string "#{key}.long", value['long']
|
102
72
|
assert_optional_string "#{key}.short", value['short']
|
103
73
|
assert_optional_string "#{key}.help", value['help']
|
@@ -109,6 +79,7 @@ module Bashly
|
|
109
79
|
assert_boolean "#{key}.required", value['required']
|
110
80
|
assert_array "#{key}.allowed", value['allowed'], of: :string
|
111
81
|
assert_array "#{key}.conflicts", value['conflicts'], of: :string
|
82
|
+
assert_array "#{key}.completions", value['completions'], of: :string
|
112
83
|
|
113
84
|
assert value['long'].match(/^--[a-zA-Z0-9_\-]+$/), "#{key}.long must be in the form of '--name'" if value['long']
|
114
85
|
assert value['short'].match(/^-[a-zA-Z0-9]$/), "#{key}.short must be in the form of '-n'" if value['short']
|
@@ -123,6 +94,10 @@ module Bashly
|
|
123
94
|
if value['allowed']
|
124
95
|
assert value['arg'], "#{key}.allowed does not make sense without arg"
|
125
96
|
end
|
97
|
+
|
98
|
+
if value['completions']
|
99
|
+
assert value['arg'], "#{key}.completions does not make sense without arg"
|
100
|
+
end
|
126
101
|
end
|
127
102
|
|
128
103
|
def assert_env_var(key, value)
|
@@ -140,7 +115,6 @@ module Bashly
|
|
140
115
|
refute value['commands'] && value['flags'], "#{key} cannot have both commands and flags"
|
141
116
|
|
142
117
|
assert_string "#{key}.name", value['name']
|
143
|
-
assert_optional_string "#{key}.short", value['short']
|
144
118
|
assert_optional_string "#{key}.help", value['help']
|
145
119
|
assert_optional_string "#{key}.footer", value['footer']
|
146
120
|
assert_optional_string "#{key}.group", value['group']
|
@@ -148,8 +122,10 @@ module Bashly
|
|
148
122
|
|
149
123
|
assert_boolean "#{key}.private", value['private']
|
150
124
|
assert_boolean "#{key}.default", value['default']
|
125
|
+
assert_expose "#{key}.expose", value['expose']
|
151
126
|
assert_version "#{key}.version", value['version']
|
152
127
|
assert_catch_all "#{key}.catch_all", value['catch_all']
|
128
|
+
assert_string_or_array "#{key}.alias", value['alias']
|
153
129
|
assert_extensible "#{key}.extensible", value['extensible']
|
154
130
|
|
155
131
|
assert_array "#{key}.args", value['args'], of: :arg
|
@@ -161,20 +137,35 @@ module Bashly
|
|
161
137
|
assert_array "#{key}.environment_variables", value['environment_variables'], of: :env_var
|
162
138
|
assert_array "#{key}.examples", value['examples'], of: :string
|
163
139
|
|
140
|
+
assert_uniq "#{key}.commands", value['commands'], ['name', 'alias']
|
141
|
+
assert_uniq "#{key}.flags", value['flags'], 'long'
|
142
|
+
assert_uniq "#{key}.flags", value['flags'], 'short'
|
143
|
+
assert_uniq "#{key}.args", value['args'], 'name'
|
144
|
+
|
164
145
|
if value['catch_all'] and value['args']
|
165
146
|
repeatable_arg = value['args'].select { |a| a['repeatable'] }.first&.dig 'name'
|
166
147
|
refute repeatable_arg, "#{key}.catch_all makes no sense with repeatable arg (#{repeatable_arg})"
|
167
148
|
end
|
168
149
|
|
150
|
+
if value['expose']
|
151
|
+
assert value['commands'], "#{key}.expose makes no sense without commands"
|
152
|
+
end
|
153
|
+
|
169
154
|
if key == "root"
|
170
|
-
refute value['
|
155
|
+
refute value['alias'], "#{key}.alias makes no sense"
|
171
156
|
refute value['group'], "#{key}.group makes no sense"
|
172
157
|
refute value['default'], "#{key}.default makes no sense"
|
173
158
|
refute value['private'], "#{key}.private makes no sense"
|
159
|
+
refute value['expose'], "#{key}.expose makes no sense"
|
174
160
|
else
|
175
161
|
refute value['version'], "#{key}.version makes no sense"
|
176
162
|
refute value['extensible'], "#{key}.extensible makes no sense"
|
177
163
|
end
|
164
|
+
|
165
|
+
# DEPRECATION 0.8.0
|
166
|
+
if value['short']
|
167
|
+
deprecate "#{key}.short", replacement: "alias", reference: "https://github.com/DannyBen/bashly/pull/220"
|
168
|
+
end
|
178
169
|
end
|
179
170
|
end
|
180
171
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Bashly
|
2
|
+
class Deprecation
|
3
|
+
attr_reader :old, :replacement, :reference
|
4
|
+
|
5
|
+
def initialize(old, replacement: nil, reference: nil)
|
6
|
+
@old, @replacement, @reference = old, replacement, reference
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
result = ["Deprecation Warning:", "!txtred!#{old}!txtrst! is deprecated"]
|
11
|
+
result.push "use !txtgrn!#{replacement}!txtrst! instead" if replacement
|
12
|
+
result.push "see !undblu!#{reference}!txtrst!" if reference
|
13
|
+
|
14
|
+
result.map { |line| "!txtred!▐!txtrst! #{line}"}.join("\n")
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_h
|
18
|
+
{
|
19
|
+
old: old,
|
20
|
+
replacement: replacement,
|
21
|
+
reference: reference
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,21 +1,25 @@
|
|
1
1
|
module Bashly
|
2
2
|
module Script
|
3
3
|
class Command < Base
|
4
|
-
include Completions
|
5
|
-
include CommandScopes
|
4
|
+
include Completions::Command
|
6
5
|
|
7
6
|
class << self
|
8
7
|
def option_keys
|
9
8
|
@option_keys ||= %i[
|
10
|
-
args catch_all commands completions
|
9
|
+
alias args catch_all commands completions
|
11
10
|
default dependencies environment_variables examples
|
12
|
-
extensible filename filters flags
|
11
|
+
extensible expose filename filters flags
|
13
12
|
footer group help name
|
14
|
-
private
|
13
|
+
private version
|
14
|
+
short
|
15
15
|
]
|
16
|
+
# DEPRECATION 0.8.0
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
20
|
+
attr_accessor :parent_command
|
21
|
+
attr_writer :parents
|
22
|
+
|
19
23
|
# Returns the name to be used as an action.
|
20
24
|
# - If it is the root command, the action is "root"
|
21
25
|
# - Else, it is all the parents, except the first one (root) joined
|
@@ -27,7 +31,25 @@ module Bashly
|
|
27
31
|
|
28
32
|
# Returns all the possible aliases for this command
|
29
33
|
def aliases
|
30
|
-
|
34
|
+
[name] + alt
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns an array of all full names (including aliases and aliases of
|
38
|
+
# parents)
|
39
|
+
def all_full_names
|
40
|
+
if parent_command
|
41
|
+
parent_command.all_full_names.product(aliases).map { |a| a.join ' ' }
|
42
|
+
else
|
43
|
+
aliases
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns an array of alternative aliases if any
|
48
|
+
def alt
|
49
|
+
# DEPRECATION 0.8.0
|
50
|
+
options['alias'] ||= options['short']
|
51
|
+
return [] unless options["alias"]
|
52
|
+
options['alias'].is_a?(String) ? [options['alias']] : options['alias']
|
31
53
|
end
|
32
54
|
|
33
55
|
# Returns an array of Arguments
|
@@ -47,15 +69,81 @@ module Bashly
|
|
47
69
|
@catch_all ||= CatchAll.from_config options['catch_all']
|
48
70
|
end
|
49
71
|
|
72
|
+
# Returns a full list of the Command names and aliases combined
|
73
|
+
def command_aliases
|
74
|
+
commands.map(&:aliases).flatten
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns a data structure for displaying subcommands help
|
78
|
+
def command_help_data
|
79
|
+
result = {}
|
80
|
+
|
81
|
+
public_commands.each do |command|
|
82
|
+
result[command.group_string] ||= {}
|
83
|
+
result[command.group_string][command.name] = { summary: command.summary_string }
|
84
|
+
next unless command.expose
|
85
|
+
|
86
|
+
command.public_commands.each do |subcommand|
|
87
|
+
result[command.group_string]["#{command.name} #{subcommand.name}"] = {
|
88
|
+
summary: subcommand.summary_string,
|
89
|
+
help_only: command.expose != 'always'
|
90
|
+
}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
result
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns only the names of the Commands
|
98
|
+
def command_names
|
99
|
+
commands.map &:name
|
100
|
+
end
|
101
|
+
|
50
102
|
# Returns an array of the Commands
|
51
103
|
def commands
|
52
104
|
return [] unless options["commands"]
|
53
105
|
options["commands"].map do |options|
|
54
|
-
|
55
|
-
|
106
|
+
result = Command.new options
|
107
|
+
result.parents = parents + [name]
|
108
|
+
result.parent_command = self
|
109
|
+
result
|
56
110
|
end
|
57
111
|
end
|
58
112
|
|
113
|
+
# Returns a flat array containing all the commands in this tree.
|
114
|
+
# This includes self + children + grandchildres + ...
|
115
|
+
def deep_commands
|
116
|
+
result = []
|
117
|
+
commands.each do |command|
|
118
|
+
result << command
|
119
|
+
if command.commands.any?
|
120
|
+
result += command.deep_commands
|
121
|
+
end
|
122
|
+
end
|
123
|
+
result
|
124
|
+
end
|
125
|
+
|
126
|
+
# If any of this command's subcommands has the default option set to
|
127
|
+
# true, this default command will be returned, nil otherwise.
|
128
|
+
def default_command
|
129
|
+
commands.find { |c| c.default }
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns an array of all the default Args
|
133
|
+
def default_args
|
134
|
+
args.select &:default
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns an array of all the default Environment Variables
|
138
|
+
def default_environment_variables
|
139
|
+
environment_variables.select &:default
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns an array of all the default Flags
|
143
|
+
def default_flags
|
144
|
+
flags.select &:default
|
145
|
+
end
|
146
|
+
|
59
147
|
# Returns an array of EnvironmentVariables
|
60
148
|
def environment_variables
|
61
149
|
return [] unless options["environment_variables"]
|
@@ -89,27 +177,24 @@ module Bashly
|
|
89
177
|
parents.any? ? (parents + [name]).join(' ') : name
|
90
178
|
end
|
91
179
|
|
92
|
-
#
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
path = "#{Settings.source_dir}/#{file}"
|
97
|
-
|
98
|
-
content = if File.exist? path
|
99
|
-
File.read(path).remove_front_matter
|
100
|
-
elsif placeholder
|
101
|
-
%q[echo "error: cannot load file"]
|
180
|
+
# Returns the string for the group caption
|
181
|
+
def group_string
|
182
|
+
if group
|
183
|
+
strings[:group] % { group: group }
|
102
184
|
else
|
103
|
-
|
185
|
+
strings[:commands]
|
104
186
|
end
|
105
|
-
|
106
|
-
Settings.production? ? content : "#{view_marker path}\n#{content}"
|
107
187
|
end
|
108
188
|
|
109
189
|
# Returns an array of all parents. For example, the command
|
110
190
|
# "docker container run" will have [docker, container] as its parents
|
111
191
|
def parents
|
112
|
-
|
192
|
+
@parents ||= []
|
193
|
+
end
|
194
|
+
|
195
|
+
# Returns only commands that are not private
|
196
|
+
def public_commands
|
197
|
+
commands.reject &:private
|
113
198
|
end
|
114
199
|
|
115
200
|
# Returns true if one of the args is repeatable
|
@@ -117,6 +202,21 @@ module Bashly
|
|
117
202
|
args.select(&:repeatable).any?
|
118
203
|
end
|
119
204
|
|
205
|
+
# Returns an array of all the required Arguments
|
206
|
+
def required_args
|
207
|
+
args.select &:required
|
208
|
+
end
|
209
|
+
|
210
|
+
# Returns an array of all the required EnvironmentVariables
|
211
|
+
def required_environment_variables
|
212
|
+
environment_variables.select &:required
|
213
|
+
end
|
214
|
+
|
215
|
+
# Returns an array of all the required Flags
|
216
|
+
def required_flags
|
217
|
+
flags.select &:required
|
218
|
+
end
|
219
|
+
|
120
220
|
# Returns true if this is the root command (no parents)
|
121
221
|
def root_command?
|
122
222
|
parents.empty?
|
@@ -127,6 +227,15 @@ module Bashly
|
|
127
227
|
flags.select { |f| f.short == flag }.any?
|
128
228
|
end
|
129
229
|
|
230
|
+
# Returns the summary string
|
231
|
+
def summary_string
|
232
|
+
if default
|
233
|
+
strings[:default_command_summary] % { summary: summary }
|
234
|
+
else
|
235
|
+
summary
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
130
239
|
# Returns a constructed string suitable for Usage pattern
|
131
240
|
def usage_string
|
132
241
|
result = [full_name]
|
@@ -146,6 +255,15 @@ module Bashly
|
|
146
255
|
@user_lib ||= Dir["#{Settings.full_lib_dir}/**/*.sh"].sort
|
147
256
|
end
|
148
257
|
|
258
|
+
# Returns an array of all the args with a whitelist
|
259
|
+
def whitelisted_args
|
260
|
+
args.select &:allowed
|
261
|
+
end
|
262
|
+
|
263
|
+
# Returns an array of all the flags with a whitelist arg
|
264
|
+
def whitelisted_flags
|
265
|
+
flags.select &:allowed
|
266
|
+
end
|
149
267
|
end
|
150
268
|
end
|
151
269
|
end
|
data/lib/bashly/script/flag.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
module Bashly
|
2
2
|
module Script
|
3
3
|
class Flag < Base
|
4
|
+
include Completions::Flag
|
5
|
+
|
4
6
|
class << self
|
5
7
|
def option_keys
|
6
8
|
@option_keys ||= %i[
|
7
|
-
allowed arg conflicts default help long repeatable
|
8
|
-
short validate
|
9
|
+
allowed arg completions conflicts default help long repeatable
|
10
|
+
required short validate
|
9
11
|
]
|
10
12
|
end
|
11
13
|
end
|
@@ -8,7 +8,7 @@ environment_variables:
|
|
8
8
|
|
9
9
|
commands:
|
10
10
|
- name: download
|
11
|
-
|
11
|
+
alias: d
|
12
12
|
help: Download a file
|
13
13
|
|
14
14
|
args:
|
@@ -32,7 +32,7 @@ commands:
|
|
32
32
|
help: Set the default location to download to
|
33
33
|
|
34
34
|
- name: upload
|
35
|
-
|
35
|
+
alias: u
|
36
36
|
help: Upload a file
|
37
37
|
args:
|
38
38
|
- name: source
|
@@ -11,7 +11,7 @@ environment_variables: "Environment Variables:"
|
|
11
11
|
group: "%{group} Commands:"
|
12
12
|
|
13
13
|
# Usage helpers
|
14
|
-
|
14
|
+
command_alias: "Alias: %{alias}"
|
15
15
|
default_command_summary: "%{summary} (default)"
|
16
16
|
required: "(required)"
|
17
17
|
repeatable: "(repeatable)"
|
data/lib/bashly/version.rb
CHANGED
@@ -1,18 +1,13 @@
|
|
1
1
|
<%= view_marker %>
|
2
|
-
%
|
3
|
-
|
4
|
-
|
5
|
-
%
|
6
|
-
%
|
7
|
-
|
8
|
-
% summary = strings[:default_command_summary] % { summary: summary } if command.default
|
9
|
-
% if command.group
|
10
|
-
% if command.name == commands.first.name
|
11
|
-
printf "<%= strings[:group] % { group: command.group } %>\n"
|
2
|
+
% maxlen = command_help_data.values.map(&:keys).flatten.map(&:size).max
|
3
|
+
% command_help_data.each do |group, commands|
|
4
|
+
printf "<%= group %>\n"
|
5
|
+
% commands.each do |command, info|
|
6
|
+
% if info[:help_only]
|
7
|
+
[[ -n $long_usage ]] && echo " <%= command.ljust maxlen %> <%= info[:summary] %>"
|
12
8
|
% else
|
13
|
-
|
14
|
-
% end
|
9
|
+
echo " <%= command.ljust maxlen %> <%= info[:summary] %>"
|
15
10
|
% end
|
16
|
-
echo " <%= command.name.ljust maxlen %> <%= summary %>"
|
17
11
|
% end
|
18
12
|
echo
|
13
|
+
% end
|
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.
|
4
|
+
version: 0.8.2
|
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-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colsole
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.7'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.7'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: completely
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 0.4.2
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 0.4.2
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: mister_bin
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,28 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0.
|
61
|
+
version: '0.2'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: filewatcher
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.1'
|
62
76
|
type: :runtime
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
82
|
+
version: '1.1'
|
69
83
|
description: Generate bash command line tools using YAML configuration
|
70
84
|
email: db@dannyben.com
|
71
85
|
executables:
|
@@ -84,11 +98,12 @@ files:
|
|
84
98
|
- lib/bashly/commands/preview.rb
|
85
99
|
- lib/bashly/commands/validate.rb
|
86
100
|
- lib/bashly/concerns/asset_helper.rb
|
87
|
-
- lib/bashly/concerns/command_scopes.rb
|
88
101
|
- lib/bashly/concerns/completions.rb
|
89
102
|
- lib/bashly/concerns/renderable.rb
|
103
|
+
- lib/bashly/concerns/validation_helpers.rb
|
90
104
|
- lib/bashly/config.rb
|
91
105
|
- lib/bashly/config_validator.rb
|
106
|
+
- lib/bashly/deprecation.rb
|
92
107
|
- lib/bashly/exceptions.rb
|
93
108
|
- lib/bashly/extensions/array.rb
|
94
109
|
- lib/bashly/extensions/file.rb
|
@@ -198,7 +213,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
213
|
- !ruby/object:Gem::Version
|
199
214
|
version: '0'
|
200
215
|
requirements: []
|
201
|
-
rubygems_version: 3.3.
|
216
|
+
rubygems_version: 3.3.14
|
202
217
|
signing_key:
|
203
218
|
specification_version: 4
|
204
219
|
summary: Bash Command Line Tool Generator
|
@@ -1,68 +0,0 @@
|
|
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
|