bashly 0.7.10 → 0.8.2
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/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
|