bashly 0.8.9 → 0.9.0
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/README.md +7 -0
- data/bin/bashly +2 -2
- data/lib/bashly/cli.rb +2 -3
- data/lib/bashly/commands/add.rb +74 -50
- data/lib/bashly/commands/base.rb +4 -3
- data/lib/bashly/commands/generate.rb +41 -35
- data/lib/bashly/commands/init.rb +10 -9
- data/lib/bashly/commands/preview.rb +4 -4
- data/lib/bashly/commands/validate.rb +8 -7
- data/lib/bashly/concerns/asset_helper.rb +1 -1
- data/lib/bashly/concerns/completions.rb +24 -15
- data/lib/bashly/concerns/renderable.rb +5 -5
- data/lib/bashly/concerns/validation_helpers.rb +20 -12
- data/lib/bashly/config.rb +1 -1
- data/lib/bashly/config_validator.rb +47 -23
- data/lib/bashly/deprecation.rb +9 -7
- data/lib/bashly/exceptions.rb +1 -1
- data/lib/bashly/extensions/array.rb +4 -4
- data/lib/bashly/extensions/file.rb +3 -3
- data/lib/bashly/extensions/string.rb +6 -6
- data/lib/bashly/libraries/base.rb +11 -1
- data/lib/bashly/libraries/completions_function.rb +9 -10
- data/lib/bashly/libraries/completions_script.rb +7 -8
- data/lib/bashly/libraries/completions_yaml.rb +7 -8
- data/lib/bashly/libraries/help.rb +36 -0
- data/lib/bashly/libraries.yml +3 -1
- data/lib/bashly/library.rb +7 -5
- data/lib/bashly/message_strings.rb +1 -1
- data/lib/bashly/refinements/compose_refinements.rb +2 -2
- data/lib/bashly/script/argument.rb +1 -1
- data/lib/bashly/script/base.rb +3 -2
- data/lib/bashly/script/catch_all.rb +6 -4
- data/lib/bashly/script/command.rb +51 -51
- data/lib/bashly/script/environment_variable.rb +5 -5
- data/lib/bashly/script/flag.rb +7 -7
- data/lib/bashly/script/wrapper.rb +5 -4
- data/lib/bashly/settings.rb +4 -5
- data/lib/bashly/templates/help/help_command.sh +30 -0
- data/lib/bashly/templates/lib/colors.sh +2 -2
- data/lib/bashly/templates/lib/config.sh +10 -10
- data/lib/bashly/templates/lib/yaml.sh +9 -9
- data/lib/bashly/templates/test/approvals.bash +36 -12
- data/lib/bashly/templates/test/approve +14 -9
- data/lib/bashly/version.rb +2 -2
- data/lib/bashly/views/command/command_fallback.gtx +2 -2
- data/lib/bashly/views/command/command_filter.gtx +9 -9
- data/lib/bashly/views/command/dependencies_filter.gtx +7 -2
- data/lib/bashly/views/command/fixed_flags_filter.gtx +18 -12
- data/lib/bashly/views/command/inspect_args.gtx +2 -2
- data/lib/bashly/views/command/normalize_input.gtx +1 -1
- data/lib/bashly/views/command/parse_requirements_while.gtx +11 -11
- data/lib/bashly/views/command/run.gtx +14 -13
- data/lib/bashly/views/command/usage_environment_variables.gtx +1 -1
- data/lib/bashly/views/flag/case.gtx +1 -1
- data/lib/bashly/views/flag/case_no_arg.gtx +1 -1
- metadata +10 -8
- data/lib/bashly/libraries/completions.rb +0 -14
data/lib/bashly/script/base.rb
CHANGED
@@ -12,7 +12,8 @@ module Bashly
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def initialize(options)
|
15
|
-
raise Error,
|
15
|
+
raise Error, 'Invalid options provided' unless options.respond_to? :keys
|
16
|
+
|
16
17
|
@options = options
|
17
18
|
end
|
18
19
|
|
@@ -21,7 +22,7 @@ module Bashly
|
|
21
22
|
end
|
22
23
|
|
23
24
|
def summary
|
24
|
-
help.empty? ?
|
25
|
+
help.empty? ? '' : help.split("\n").first
|
25
26
|
end
|
26
27
|
|
27
28
|
def help
|
@@ -18,12 +18,15 @@ module Bashly
|
|
18
18
|
{}
|
19
19
|
end
|
20
20
|
|
21
|
-
new
|
21
|
+
new(**options)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
def initialize(label: nil, help: nil, required: false, enabled: true)
|
26
|
-
@label
|
26
|
+
@label = label
|
27
|
+
@help = help
|
28
|
+
@required = required
|
29
|
+
@enabled = enabled
|
27
30
|
end
|
28
31
|
|
29
32
|
def enabled?
|
@@ -44,10 +47,9 @@ module Bashly
|
|
44
47
|
|
45
48
|
def usage_string
|
46
49
|
return nil unless enabled?
|
50
|
+
|
47
51
|
required? ? label : "[#{label}]"
|
48
52
|
end
|
49
|
-
|
50
53
|
end
|
51
54
|
end
|
52
55
|
end
|
53
|
-
|
@@ -26,7 +26,7 @@ module Bashly
|
|
26
26
|
# by space. For example, for a command like "docker container run"
|
27
27
|
# the action name is "container run".
|
28
28
|
def action_name
|
29
|
-
parents.any? ? (parents[1
|
29
|
+
parents.any? ? (parents[1..] + [name]).join(' ') : 'root'
|
30
30
|
end
|
31
31
|
|
32
32
|
# Returns all the possible aliases for this command
|
@@ -34,28 +34,20 @@ module Bashly
|
|
34
34
|
[name] + alt
|
35
35
|
end
|
36
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
37
|
# Returns an array of alternative aliases if any
|
48
38
|
def alt
|
49
39
|
# DEPRECATION 0.8.0
|
50
40
|
options['alias'] ||= options['short']
|
51
|
-
return [] unless options[
|
41
|
+
return [] unless options['alias']
|
42
|
+
|
52
43
|
options['alias'].is_a?(String) ? [options['alias']] : options['alias']
|
53
44
|
end
|
54
45
|
|
55
46
|
# Returns an array of Arguments
|
56
47
|
def args
|
57
|
-
return [] unless options[
|
58
|
-
|
48
|
+
return [] unless options['args']
|
49
|
+
|
50
|
+
options['args'].map do |options|
|
59
51
|
Argument.new options
|
60
52
|
end
|
61
53
|
end
|
@@ -85,8 +77,8 @@ module Bashly
|
|
85
77
|
|
86
78
|
command.public_commands.each do |subcommand|
|
87
79
|
result[command.group_string]["#{command.name} #{subcommand.name}"] = {
|
88
|
-
summary:
|
89
|
-
help_only: command.expose != 'always'
|
80
|
+
summary: subcommand.summary_string,
|
81
|
+
help_only: command.expose != 'always',
|
90
82
|
}
|
91
83
|
end
|
92
84
|
end
|
@@ -96,13 +88,14 @@ module Bashly
|
|
96
88
|
|
97
89
|
# Returns only the names of the Commands
|
98
90
|
def command_names
|
99
|
-
commands.map
|
91
|
+
commands.map(&:name)
|
100
92
|
end
|
101
93
|
|
102
94
|
# Returns an array of the Commands
|
103
95
|
def commands
|
104
|
-
return [] unless options[
|
105
|
-
|
96
|
+
return [] unless options['commands']
|
97
|
+
|
98
|
+
options['commands'].map do |options|
|
106
99
|
result = Command.new options
|
107
100
|
result.parents = parents + [name]
|
108
101
|
result.parent_command = self
|
@@ -126,42 +119,51 @@ module Bashly
|
|
126
119
|
# If any of this command's subcommands has the default option set to
|
127
120
|
# true, this default command will be returned, nil otherwise.
|
128
121
|
def default_command
|
129
|
-
commands.find
|
122
|
+
commands.find(&:default)
|
130
123
|
end
|
131
124
|
|
132
125
|
# Returns an array of all the default Args
|
133
126
|
def default_args
|
134
|
-
args.select
|
127
|
+
args.select(&:default)
|
135
128
|
end
|
136
129
|
|
137
130
|
# Returns an array of all the default Environment Variables
|
138
131
|
def default_environment_variables
|
139
|
-
environment_variables.select
|
132
|
+
environment_variables.select(&:default)
|
140
133
|
end
|
141
134
|
|
142
135
|
# Returns an array of all the default Flags
|
143
136
|
def default_flags
|
144
|
-
flags.select
|
137
|
+
flags.select(&:default)
|
145
138
|
end
|
146
139
|
|
147
140
|
# Returns an array of EnvironmentVariables
|
148
141
|
def environment_variables
|
149
|
-
return [] unless options[
|
150
|
-
|
142
|
+
return [] unless options['environment_variables']
|
143
|
+
|
144
|
+
options['environment_variables'].map do |options|
|
151
145
|
EnvironmentVariable.new options
|
152
146
|
end
|
153
147
|
end
|
154
148
|
|
149
|
+
# Returns an array of examples
|
150
|
+
def examples
|
151
|
+
return nil unless options['examples']
|
152
|
+
|
153
|
+
options['examples'].is_a?(Array) ? options['examples'] : [options['examples']]
|
154
|
+
end
|
155
|
+
|
155
156
|
# Returns the bash filename that is expected to hold the user code
|
156
157
|
# for this command
|
157
158
|
def filename
|
158
|
-
options[
|
159
|
+
options['filename'] || "#{action_name.to_underscore}_command.sh"
|
159
160
|
end
|
160
161
|
|
161
162
|
# Returns an array of Flags
|
162
163
|
def flags
|
163
|
-
return [] unless options[
|
164
|
-
|
164
|
+
return [] unless options['flags']
|
165
|
+
|
166
|
+
options['flags'].map do |options|
|
165
167
|
Flag.new options
|
166
168
|
end
|
167
169
|
end
|
@@ -186,7 +188,7 @@ module Bashly
|
|
186
188
|
# Returns the string for the group caption
|
187
189
|
def group_string
|
188
190
|
if group
|
189
|
-
strings[:group] % { group: group }
|
191
|
+
strings[:group] % { group: group }
|
190
192
|
else
|
191
193
|
strings[:commands]
|
192
194
|
end
|
@@ -194,18 +196,17 @@ module Bashly
|
|
194
196
|
|
195
197
|
# Returns a mode identifier
|
196
198
|
def mode
|
197
|
-
@mode ||=
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
end
|
199
|
+
@mode ||= if global_flags? then :global_flags
|
200
|
+
elsif commands.any? then :commands
|
201
|
+
elsif args.any? && flags.any? then :args_and_flags
|
202
|
+
elsif args.any? then :args
|
203
|
+
elsif flags.any? then :flags
|
204
|
+
else
|
205
|
+
:empty
|
205
206
|
end
|
206
207
|
end
|
207
208
|
|
208
|
-
# Returns an array of all parents. For example, the command
|
209
|
+
# Returns an array of all parents. For example, the command
|
209
210
|
# "docker container run" will have [docker, container] as its parents
|
210
211
|
def parents
|
211
212
|
@parents ||= []
|
@@ -213,7 +214,7 @@ module Bashly
|
|
213
214
|
|
214
215
|
# Returns only commands that are not private
|
215
216
|
def public_commands
|
216
|
-
commands.reject
|
217
|
+
commands.reject(&:private)
|
217
218
|
end
|
218
219
|
|
219
220
|
# Returns true if one of the args is repeatable
|
@@ -223,17 +224,17 @@ module Bashly
|
|
223
224
|
|
224
225
|
# Returns an array of all the required Arguments
|
225
226
|
def required_args
|
226
|
-
args.select
|
227
|
+
args.select(&:required)
|
227
228
|
end
|
228
229
|
|
229
230
|
# Returns an array of all the required EnvironmentVariables
|
230
231
|
def required_environment_variables
|
231
|
-
environment_variables.select
|
232
|
+
environment_variables.select(&:required)
|
232
233
|
end
|
233
234
|
|
234
235
|
# Returns an array of all the required Flags
|
235
236
|
def required_flags
|
236
|
-
flags.select
|
237
|
+
flags.select(&:required)
|
237
238
|
end
|
238
239
|
|
239
240
|
# Returns true if this is the root command (no parents)
|
@@ -260,21 +261,20 @@ module Bashly
|
|
260
261
|
result = [full_name]
|
261
262
|
|
262
263
|
result.push case mode
|
263
|
-
when :global_flags then [
|
264
|
-
when :commands then [
|
265
|
-
when :args_and_flags then usage_string_args + [
|
264
|
+
when :global_flags then ['[OPTIONS]', 'COMMAND']
|
265
|
+
when :commands then ['COMMAND']
|
266
|
+
when :args_and_flags then usage_string_args + ['[OPTIONS]']
|
266
267
|
when :args then usage_string_args
|
267
|
-
when :flags then [
|
268
|
-
else nil
|
268
|
+
when :flags then ['[OPTIONS]']
|
269
269
|
end
|
270
270
|
|
271
271
|
result.push catch_all.usage_string if catch_all.enabled? && commands.empty?
|
272
|
-
result.compact.join
|
272
|
+
result.compact.join ' '
|
273
273
|
end
|
274
274
|
|
275
275
|
# Returns an array of args usage_string for the command's usage_string
|
276
276
|
def usage_string_args
|
277
|
-
args.map
|
277
|
+
args.map(&:usage_string)
|
278
278
|
end
|
279
279
|
|
280
280
|
# Returns an array of files to include as is inside the script
|
@@ -286,12 +286,12 @@ module Bashly
|
|
286
286
|
|
287
287
|
# Returns an array of all the args with a whitelist
|
288
288
|
def whitelisted_args
|
289
|
-
args.select
|
289
|
+
args.select(&:allowed)
|
290
290
|
end
|
291
291
|
|
292
292
|
# Returns an array of all the flags with a whitelist arg
|
293
293
|
def whitelisted_flags
|
294
|
-
flags.select
|
294
|
+
flags.select(&:allowed)
|
295
295
|
end
|
296
296
|
end
|
297
297
|
end
|
@@ -3,15 +3,15 @@ module Bashly
|
|
3
3
|
class EnvironmentVariable < Base
|
4
4
|
class << self
|
5
5
|
def option_keys
|
6
|
-
@option_keys ||= %i[default help name required]
|
6
|
+
@option_keys ||= %i[default help name required private]
|
7
7
|
end
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def usage_string(extended: false)
|
11
11
|
result = [name.upcase]
|
12
|
-
result << strings[:required] if required
|
13
|
-
result.join
|
12
|
+
result << strings[:required] if required && extended
|
13
|
+
result.join ' '
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
17
|
-
end
|
17
|
+
end
|
data/lib/bashly/script/flag.rb
CHANGED
@@ -11,9 +11,9 @@ module Bashly
|
|
11
11
|
]
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def aliases
|
16
|
-
if long
|
16
|
+
if long && short
|
17
17
|
[long, short]
|
18
18
|
elsif long
|
19
19
|
[long]
|
@@ -27,12 +27,12 @@ module Bashly
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def usage_string(extended: false)
|
30
|
-
result = [aliases.join(
|
30
|
+
result = [aliases.join(', ')]
|
31
31
|
result << arg.upcase if arg
|
32
|
-
result << strings[:required] if required
|
33
|
-
result << strings[:repeatable] if repeatable
|
34
|
-
result.join
|
32
|
+
result << strings[:required] if required && extended
|
33
|
+
result << strings[:repeatable] if repeatable && extended
|
34
|
+
result.join ' '
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
38
|
-
end
|
38
|
+
end
|
@@ -6,7 +6,8 @@ module Bashly
|
|
6
6
|
attr_reader :command, :function_name
|
7
7
|
|
8
8
|
def initialize(command, function_name = nil)
|
9
|
-
@command
|
9
|
+
@command = command
|
10
|
+
@function_name = function_name
|
10
11
|
end
|
11
12
|
|
12
13
|
def code(tab_indent: false)
|
@@ -38,13 +39,13 @@ module Bashly
|
|
38
39
|
end
|
39
40
|
|
40
41
|
def default_header
|
41
|
-
result = render
|
42
|
+
result = render 'header'
|
42
43
|
result += render('bash3_bouncer') unless function_name
|
43
44
|
result
|
44
45
|
end
|
45
46
|
|
46
47
|
def body
|
47
|
-
@body ||= command.render
|
48
|
+
@body ||= command.render 'master_script'
|
48
49
|
end
|
49
50
|
|
50
51
|
def custom_header_path
|
@@ -52,4 +53,4 @@ module Bashly
|
|
52
53
|
end
|
53
54
|
end
|
54
55
|
end
|
55
|
-
end
|
56
|
+
end
|
data/lib/bashly/settings.rb
CHANGED
@@ -51,8 +51,8 @@ module Bashly
|
|
51
51
|
def get(key)
|
52
52
|
case env_value key
|
53
53
|
when nil then config[key.to_s]
|
54
|
-
when
|
55
|
-
when
|
54
|
+
when '0', 'false', 'no' then false
|
55
|
+
when '1', 'true', 'yes' then true
|
56
56
|
else env_value key
|
57
57
|
end
|
58
58
|
end
|
@@ -70,13 +70,12 @@ module Bashly
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def defsult_settings
|
73
|
-
@defsult_settings ||= Config.new
|
73
|
+
@defsult_settings ||= Config.new default_settings_path
|
74
74
|
end
|
75
75
|
|
76
76
|
def default_settings_path
|
77
|
-
asset
|
77
|
+
asset 'templates/settings.yml'
|
78
78
|
end
|
79
|
-
|
80
79
|
end
|
81
80
|
end
|
82
81
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
## Help command [@bashly-upgrade help]
|
2
|
+
## This file is a part of Bashly standard library
|
3
|
+
##
|
4
|
+
## Add this as a command to your bashly.yml:
|
5
|
+
##
|
6
|
+
## commands:
|
7
|
+
## - name: help
|
8
|
+
## help: Show help about a command
|
9
|
+
## args:
|
10
|
+
## - name: command
|
11
|
+
## help: Help subject
|
12
|
+
##
|
13
|
+
command="${args[command]}"
|
14
|
+
long_usage=yes
|
15
|
+
|
16
|
+
if [[ -z "$command" ]]; then
|
17
|
+
# No command argument, show the global help
|
18
|
+
help_function=%{name}_usage
|
19
|
+
else
|
20
|
+
# Show the help for the requested command
|
21
|
+
help_function="%{name}_${command}_usage"
|
22
|
+
fi
|
23
|
+
|
24
|
+
# Call the help function if it exists
|
25
|
+
if [[ $(type -t "$help_function") ]]; then
|
26
|
+
"$help_function"
|
27
|
+
else
|
28
|
+
echo "No help available for this command"
|
29
|
+
exit 1
|
30
|
+
fi
|
@@ -24,13 +24,13 @@ config_get() {
|
|
24
24
|
local value=""
|
25
25
|
|
26
26
|
config_init
|
27
|
-
|
27
|
+
|
28
28
|
while IFS= read -r line || [ -n "$line" ]; do
|
29
29
|
if [[ $line =~ $regex ]]; then
|
30
30
|
value="${BASH_REMATCH[1]}"
|
31
31
|
break
|
32
32
|
fi
|
33
|
-
done <
|
33
|
+
done <"$CONFIG_FILE"
|
34
34
|
|
35
35
|
echo "$value"
|
36
36
|
}
|
@@ -48,7 +48,7 @@ config_set() {
|
|
48
48
|
local output=""
|
49
49
|
local found_key=""
|
50
50
|
local newline
|
51
|
-
|
51
|
+
|
52
52
|
while IFS= read -r line || [ -n "$line" ]; do
|
53
53
|
newline=$line
|
54
54
|
if [[ $line =~ $regex ]]; then
|
@@ -58,13 +58,13 @@ config_set() {
|
|
58
58
|
elif [[ $line ]]; then
|
59
59
|
output="$output$line\n"
|
60
60
|
fi
|
61
|
-
done <
|
61
|
+
done <"$CONFIG_FILE"
|
62
62
|
|
63
63
|
if [[ -z $found_key ]]; then
|
64
64
|
output="$output$key = $value\n"
|
65
65
|
fi
|
66
66
|
|
67
|
-
printf "%b\n" "$output" >
|
67
|
+
printf "%b\n" "$output" >"$CONFIG_FILE"
|
68
68
|
}
|
69
69
|
|
70
70
|
## Delete a key from the config.
|
@@ -81,9 +81,9 @@ config_del() {
|
|
81
81
|
if [[ $line ]] && [[ ! $line =~ $regex ]]; then
|
82
82
|
output="$output$line\n"
|
83
83
|
fi
|
84
|
-
done <
|
84
|
+
done <"$CONFIG_FILE"
|
85
85
|
|
86
|
-
printf "%b\n" "$output" >
|
86
|
+
printf "%b\n" "$output" >"$CONFIG_FILE"
|
87
87
|
}
|
88
88
|
|
89
89
|
## Show the config file
|
@@ -106,20 +106,20 @@ config_keys() {
|
|
106
106
|
|
107
107
|
local keys=()
|
108
108
|
local key
|
109
|
-
|
109
|
+
|
110
110
|
while IFS= read -r line || [ -n "$line" ]; do
|
111
111
|
if [[ $line =~ $regex ]]; then
|
112
112
|
key="${BASH_REMATCH[1]}"
|
113
113
|
keys+=("$key")
|
114
114
|
fi
|
115
|
-
done <
|
115
|
+
done <"$CONFIG_FILE"
|
116
116
|
echo "${keys[@]}"
|
117
117
|
}
|
118
118
|
|
119
119
|
## Returns true if the specified key exists in the config file.
|
120
120
|
## Usage:
|
121
121
|
##
|
122
|
-
## if config_has_key "key"
|
122
|
+
## if config_has_key "key"; then
|
123
123
|
## echo "key exists"
|
124
124
|
## fi
|
125
125
|
##
|
@@ -11,16 +11,16 @@
|
|
11
11
|
## eval $(yaml_load "settings.yml") # create variables in scope
|
12
12
|
##
|
13
13
|
yaml_load() {
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
local prefix=$2
|
15
|
+
local s='[[:space:]]*' w='[a-zA-Z0-9_]*'
|
16
|
+
local fs
|
17
17
|
|
18
|
-
|
18
|
+
fs=$(echo @ | tr @ '\034')
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
sed -ne "s|^\($s\):|\1|" \
|
21
|
+
-e "s|^\($s\)\($w\)$s:${s}[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \
|
22
|
+
-e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" "$1" |
|
23
|
+
awk -F"$fs" '{
|
24
24
|
indent = length($1)/2;
|
25
25
|
vname[indent] = $2;
|
26
26
|
for (i in vname) {if (i > indent) {delete vname[i]}}
|
@@ -28,5 +28,5 @@ yaml_load() {
|
|
28
28
|
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
|
29
29
|
printf("%s%s%s=\"%s\"\n", "'"$prefix"'",vn, $2, $3);
|
30
30
|
}
|
31
|
-
|
31
|
+
}'
|
32
32
|
}
|
@@ -1,14 +1,14 @@
|
|
1
|
-
# approvals.bash v0.
|
1
|
+
# approvals.bash v0.3.3
|
2
2
|
#
|
3
3
|
# Interactive approval testing for Bash.
|
4
4
|
# https://github.com/DannyBen/approvals.bash
|
5
5
|
approve() {
|
6
6
|
local expected approval approval_file actual cmd
|
7
7
|
approvals_dir=${APPROVALS_DIR:=approvals}
|
8
|
-
|
8
|
+
|
9
9
|
cmd=$1
|
10
|
-
|
11
|
-
last_exit_code=$?
|
10
|
+
last_exit_code=0
|
11
|
+
actual=$(eval "$cmd" 2>&1) || last_exit_code=$?
|
12
12
|
approval=$(printf "%b" "$cmd" | tr -s -c "[:alnum:]" _)
|
13
13
|
approval_file="$approvals_dir/${2:-"$approval"}"
|
14
14
|
|
@@ -26,26 +26,32 @@ approve() {
|
|
26
26
|
fi
|
27
27
|
|
28
28
|
if [[ "$(printf "%b" "$actual")" = "$(printf "%b" "$expected")" ]]; then
|
29
|
-
|
29
|
+
pass "$cmd"
|
30
30
|
else
|
31
31
|
echo "--- [$(blue "diff: $cmd")] ---"
|
32
|
-
$diff_cmd <(printf "%b" "$expected\n") <(printf "%b" "$actual\n"
|
32
|
+
$diff_cmd <(printf "%b" "$expected\n") <(printf "%b" "$actual\n") | tail -n +4
|
33
33
|
echo "--- [$(blue "diff: $cmd")] ---"
|
34
34
|
user_approval "$cmd" "$actual" "$approval_file"
|
35
35
|
fi
|
36
36
|
}
|
37
37
|
|
38
38
|
describe() {
|
39
|
-
|
39
|
+
echo
|
40
|
+
blue "= $*"
|
41
|
+
}
|
42
|
+
|
43
|
+
context() {
|
44
|
+
echo
|
45
|
+
magenta "= $*"
|
40
46
|
}
|
41
47
|
|
42
48
|
fail() {
|
43
|
-
red "
|
49
|
+
red " FAILED: $*"
|
44
50
|
exit 1
|
45
51
|
}
|
46
52
|
|
47
53
|
pass() {
|
48
|
-
green "
|
54
|
+
green " approved: $*"
|
49
55
|
return 0
|
50
56
|
}
|
51
57
|
|
@@ -74,19 +80,37 @@ user_approval() {
|
|
74
80
|
fail "$cmd"
|
75
81
|
fi
|
76
82
|
|
77
|
-
echo
|
83
|
+
echo
|
78
84
|
printf "[A]pprove? \n"
|
79
85
|
response=$(bash -c "read -n 1 key; echo \$key")
|
80
86
|
printf "\r"
|
81
87
|
if [[ $response =~ [Aa] ]]; then
|
82
|
-
printf "%b\n" "$actual" >
|
88
|
+
printf "%b\n" "$actual" >"$approval_file"
|
83
89
|
pass "$cmd"
|
84
90
|
else
|
85
91
|
fail "$cmd"
|
86
92
|
fi
|
87
93
|
}
|
88
94
|
|
89
|
-
|
95
|
+
onexit() {
|
96
|
+
exitcode=$?
|
97
|
+
if [[ "$exitcode" == 0 ]]; then
|
98
|
+
green "\nFinished successfully"
|
99
|
+
else
|
100
|
+
red "\nFinished with failures"
|
101
|
+
fi
|
102
|
+
exit $exitcode
|
103
|
+
}
|
104
|
+
|
105
|
+
onerror() {
|
106
|
+
fail "Caller: $(caller)"
|
107
|
+
}
|
108
|
+
|
109
|
+
set -e
|
110
|
+
trap 'onexit' EXIT
|
111
|
+
trap 'onerror' ERR
|
112
|
+
|
113
|
+
if diff --help | grep -- --color >/dev/null 2>&1; then
|
90
114
|
diff_cmd="diff --unified --color=always"
|
91
115
|
else
|
92
116
|
diff_cmd="diff --unified"
|
@@ -3,18 +3,23 @@
|
|
3
3
|
|
4
4
|
cd ./test || exit
|
5
5
|
source approvals.bash
|
6
|
-
PATH="$PATH:../"
|
7
6
|
|
8
7
|
# Update me
|
9
|
-
cli
|
8
|
+
cli=./download
|
10
9
|
|
11
|
-
# Tests
|
12
|
-
|
13
|
-
|
14
|
-
approve "$cli --help"
|
10
|
+
# Tests (context, describe and indentation are optional)
|
11
|
+
context "when DEBUG is on"
|
12
|
+
export DEBUG=1
|
15
13
|
|
16
|
-
describe "
|
17
|
-
|
18
|
-
|
14
|
+
describe "root command"
|
15
|
+
approve "$cli"
|
16
|
+
approve "$cli --help"
|
17
|
+
|
18
|
+
context "when DEBUG is off"
|
19
|
+
unset DEBUG
|
20
|
+
|
21
|
+
describe "some other command"
|
22
|
+
approve "$cli other"
|
23
|
+
approve "$cli other --help"
|
19
24
|
|
20
25
|
# ...more tests...
|