bashly 1.1.8 → 1.1.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d11d229ff42dc84aa910ee8292e145b63641c5520315583e92d5aa0747e6e578
4
- data.tar.gz: 61a2ddcb970af41f7a086da0a75d0452a637e3fe650569e2768f1a0eefe49849
3
+ metadata.gz: '0857ce9cc52b919c113242568610ed9abbd447221b6d473bbf05f1cd8f29de69'
4
+ data.tar.gz: 3eb8279ffdbf23a69a5c7eb52d98f2f0c3ca1730f54eb6a18b4957038b69810a
5
5
  SHA512:
6
- metadata.gz: b9ffdaa7a7a862b11d932e6533d4b7e1b1e76407292bbaf5ca8afdc192249b0f51ea504adfc459e37b712074291ef612fc5b29965700043676bc94709062e696
7
- data.tar.gz: 629b81b7ef43fa64523a465968828d74192fbd17440e9f20f724a6ef040ebe0c805bed339f899ca6047f53043c29d88537829075bd60be2f752a69e09eaaee55
6
+ metadata.gz: 2aa95822cebdc83d0e3a1af47a2d75886994a7d79976188049b8ea6732ca3d6a83003bfea48c3789e8d3a4db562e16f8f9ed169da414065567c7abf2d2d4c40b
7
+ data.tar.gz: 3cf237064dfe7f9cfb2523c85d838141f9869e246619b9ed2bf9862499a0345ea1da12178802ee6d7a654e1ad8fec674b751442566b8b8ca1e22b77055105fd1
@@ -2,7 +2,10 @@ require 'gtx'
2
2
 
3
3
  module Bashly
4
4
  module Renderable
5
- def render(view)
5
+ attr_reader :render_options
6
+
7
+ def render(view, render_options = {})
8
+ @render_options = render_options
6
9
  GTX.render_file view_path(view), context: binding, filename: "#{views_subfolder}.#{view}"
7
10
  end
8
11
 
@@ -1,6 +1,6 @@
1
1
  class String
2
2
  def sanitize_for_print
3
- gsub("\n", '\\n').gsub('"', '\"').gsub('`', '\\\\`')
3
+ gsub("\n", '\\n').gsub('"', '\"').gsub('`', '\\\\`').gsub('%', '%%')
4
4
  end
5
5
 
6
6
  def for_markdown
@@ -42,6 +42,11 @@ tab_indent: false
42
42
  # `-abc` as if it is `-a -b -c`.
43
43
  compact_short_flags: true
44
44
 
45
+ # When true, the generated script will consider any argument in the form of
46
+ # `--flag=value` and `-f=value` as if it is `--flag value` and `-f value`
47
+ # respectively.
48
+ conjoined_flag_args: true
49
+
45
50
  # Set to 'production' or 'development':
46
51
  # env: production Generate a smaller script, without file markers
47
52
  # env: development Generate with file markers
@@ -1,4 +1,4 @@
1
- # approvals.bash v0.5.0
1
+ # approvals.bash v0.5.1
2
2
  #
3
3
  # Interactive approval testing for Bash.
4
4
  # https://github.com/DannyBen/approvals.bash
@@ -54,6 +54,10 @@ context() {
54
54
  printf "$context_string\n" "$*"
55
55
  }
56
56
 
57
+ it() {
58
+ printf "$it_string\n" "$*"
59
+ }
60
+
57
61
  fail() {
58
62
  printf "$fail_string\n" "$*"
59
63
  exit 1
@@ -72,29 +76,57 @@ expect_exit_code() {
72
76
  fi
73
77
  }
74
78
 
75
- bold() { printf "\e[1m%b\e[0m\n" "$*"; }
76
- blue() { printf "\e[34m%b\e[0m\n" "$*"; }
77
- cyan() { printf "\e[36m%b\e[0m\n" "$*"; }
78
- green() { printf "\e[32m%b\e[0m\n" "$*"; }
79
- magenta() { printf "\e[35m%b\e[0m\n" "$*"; }
80
- red() { printf "\e[31m%b\e[0m\n" "$*"; }
81
- yellow() { printf "\e[33m%b\e[0m\n" "$*"; }
82
-
83
79
  # Private
84
80
 
81
+ print_in_color() {
82
+ local color="$1"
83
+ shift
84
+ if [[ -z ${NO_COLOR+x} ]]; then
85
+ printf "$color%b\e[0m\n" "$*"
86
+ else
87
+ printf "%b\n" "$*"
88
+ fi
89
+ }
90
+
91
+ red() { print_in_color "\e[31m" "$*"; }
92
+ green() { print_in_color "\e[32m" "$*"; }
93
+ yellow() { print_in_color "\e[33m" "$*"; }
94
+ blue() { print_in_color "\e[34m" "$*"; }
95
+ magenta() { print_in_color "\e[35m" "$*"; }
96
+ cyan() { print_in_color "\e[36m" "$*"; }
97
+ bold() { print_in_color "\e[1m" "$*"; }
98
+ underlined() { print_in_color "\e[4m" "$*"; }
99
+ red_bold() { print_in_color "\e[1;31m" "$*"; }
100
+ green_bold() { print_in_color "\e[1;32m" "$*"; }
101
+ yellow_bold() { print_in_color "\e[1;33m" "$*"; }
102
+ blue_bold() { print_in_color "\e[1;34m" "$*"; }
103
+ magenta_bold() { print_in_color "\e[1;35m" "$*"; }
104
+ cyan_bold() { print_in_color "\e[1;36m" "$*"; }
105
+ red_underlined() { print_in_color "\e[4;31m" "$*"; }
106
+ green_underlined() { print_in_color "\e[4;32m" "$*"; }
107
+ yellow_underlined() { print_in_color "\e[4;33m" "$*"; }
108
+ blue_underlined() { print_in_color "\e[4;34m" "$*"; }
109
+ magenta_underlined() { print_in_color "\e[4;35m" "$*"; }
110
+ cyan_underlined() { print_in_color "\e[4;36m" "$*"; }
111
+
85
112
  user_approval() {
86
113
  local cmd="$1"
87
114
  local actual="$2"
88
115
  local approval_file="$3"
89
116
 
90
- if [[ -v CI || -v GITHUB_ACTIONS ]]; then
117
+ if [[ -v CI || -v GITHUB_ACTIONS ]] && [[ -z "${AUTO_APPROVE+x}" ]]; then
91
118
  fail "$cmd"
92
119
  fi
93
120
 
94
- echo
95
- printf "$approval_string"
96
- response=$(bash -c "read -n 1 key; echo \$key")
97
- printf "\b%.s" $(seq 1 $((${#approval_string} + 1)))
121
+ if [[ -v AUTO_APPROVE ]]; then
122
+ response=a
123
+ else
124
+ echo
125
+ printf "$approval_string"
126
+ response=$(bash -c "read -n 1 key; echo \$key")
127
+ printf "\b%.s" $(seq 1 $((${#approval_string} + 1)))
128
+ fi
129
+
98
130
  if [[ $response =~ [Aa] ]]; then
99
131
  printf "%b\n" "$actual" >"$approval_file"
100
132
  pass "$cmd"
@@ -122,14 +154,15 @@ set -e
122
154
  trap 'onexit' EXIT
123
155
  trap 'onerror' ERR
124
156
 
125
- describe_string="$(blue ▌ describe) %s"
126
- context_string="$(magenta ▌ context) %s"
127
- fail_string=" $(red FAILED) %s"
157
+ describe_string="$(bold ▌ describe) %s"
158
+ context_string="$(bold ▌ context) %s"
159
+ it_string="$(bold ▌ it) %s"
160
+ fail_string=" $(red_bold failed) %s"
128
161
  pass_string=" $(green approved) %s"
129
162
  exit_success_string="$(green ▌ exit) $(bold %s finished successfully)"
130
- exit_failed_string="$(red ▌ exit) $(bold %s finished with errors)"
131
- new_diff_string="────┤ $(yellow new): $(bold %s)) ├────"
132
- changed_diff_string="────┤ $(cyan changed): $(bold %s)) ├────"
163
+ exit_failed_string="$(red_bold ▌ exit) $(bold %s finished with errors)"
164
+ new_diff_string="────┤ $(yellow new): $(bold %s) ├────"
165
+ changed_diff_string="────┤ $(blue changed): $(bold %s) ├────"
133
166
  approval_string="[A]pprove? "
134
167
 
135
168
  if diff --help | grep -- --color >/dev/null 2>&1; then
@@ -29,7 +29,7 @@ module Bashly
29
29
  options['help'] ||= ''
30
30
  end
31
31
 
32
- def method_missing(method_name, *arguments, &block)
32
+ def method_missing(method_name, *arguments, &)
33
33
  key = method_name.to_s
34
34
  respond_to?(method_name) ? options[key] : super
35
35
  end
@@ -48,7 +48,7 @@ module Bashly
48
48
  def usage_string
49
49
  return nil unless enabled?
50
50
 
51
- required? ? label : "[#{label}]"
51
+ required? ? "[--] #{label}" : "[--] [#{label}]"
52
52
  end
53
53
  end
54
54
  end
@@ -101,9 +101,10 @@ module Bashly
101
101
  end
102
102
 
103
103
  # Returns a flat array containing all the commands in this tree.
104
- # This includes self + children + grandchildres + ...
105
- def deep_commands
104
+ # This includes children + grandchildren (recursive), and may include self
105
+ def deep_commands(include_self: false)
106
106
  result = []
107
+ result << self if include_self
107
108
  commands.each do |command|
108
109
  result << command
109
110
  if command.commands.any?
@@ -217,6 +218,16 @@ module Bashly
217
218
  result
218
219
  end
219
220
 
221
+ # Returns true if this command, or any subcommand (deep) as any arg or
222
+ # flag with arg that is defined as unique
223
+ def has_unique_args_or_flags?
224
+ deep_commands(include_self: true).each do |command|
225
+ return true if command.args.count(&:unique).positive? ||
226
+ command.flags.count(&:unique).positive?
227
+ end
228
+ false
229
+ end
230
+
220
231
  # Returns a mode identifier
221
232
  def mode
222
233
  @mode ||= if global_flags? then :global_flags
@@ -6,6 +6,7 @@ module Bashly
6
6
  attr_writer(
7
7
  :commands_dir,
8
8
  :compact_short_flags,
9
+ :conjoined_flag_args,
9
10
  :config_path,
10
11
  :lib_dir,
11
12
  :partials_extension,
@@ -24,6 +25,10 @@ module Bashly
24
25
  @compact_short_flags ||= get :compact_short_flags
25
26
  end
26
27
 
28
+ def conjoined_flag_args
29
+ @conjoined_flag_args ||= get :conjoined_flag_args
30
+ end
31
+
27
32
  def config_path
28
33
  @config_path ||= get(:config_path) % { source_dir: source_dir }
29
34
  end
@@ -1,3 +1,3 @@
1
1
  module Bashly
2
- VERSION = '1.1.8'
2
+ VERSION = '1.1.10'
3
3
  end
@@ -0,0 +1,6 @@
1
+ = view_marker
2
+
3
+ condition = render_options[:index].zero? ? 'if' : 'elif'
4
+ > {{ condition }} [[ -z ${args['{{ name }}']+x} ]]; then
5
+ > args['{{ name }}']=$1
6
+ > shift
@@ -0,0 +1,25 @@
1
+ = view_marker
2
+
3
+ condition = render_options[:index].zero? ? 'if' : 'elif'
4
+
5
+ if render_options[:index] == 0
6
+ > escaped="$(printf '%q' "$1")"
7
+ end
8
+
9
+ > {{ condition }} [[ -z ${args['{{ name }}']+x} ]]; then
10
+ if repeatable
11
+ > args['{{ name }}']="$escaped"
12
+ if unique
13
+ > unique_lookup["{{ name }}:$escaped"]=1
14
+ > elif [[ -z "${unique_lookup["{{ name }}:$escaped"]:-}" ]]; then
15
+ > args['{{ name }}']="${args['{{ name }}']} $escaped"
16
+ > unique_lookup["{{ name }}:$escaped"]=1
17
+ else
18
+ > else
19
+ > args['{{ name }}']="${args['{{ name }}']} $escaped"
20
+ end
21
+
22
+ else
23
+ > args['{{ name }}']="$1"
24
+
25
+ end
@@ -1,30 +1,7 @@
1
1
  = view_marker
2
2
 
3
- > normalize_input() {
4
- > local arg flags
5
- >
6
- > while [[ $# -gt 0 ]]; do
7
- > arg="$1"
8
- > if [[ $arg =~ ^(--[a-zA-Z0-9_\-]+)=(.+)$ ]]; then
9
- > input+=("${BASH_REMATCH[1]}")
10
- > input+=("${BASH_REMATCH[2]}")
11
- > elif [[ $arg =~ ^(-[a-zA-Z0-9])=(.+)$ ]]; then
12
- > input+=("${BASH_REMATCH[1]}")
13
- > input+=("${BASH_REMATCH[2]}")
14
-
15
- if Settings.compact_short_flags
16
- > elif [[ $arg =~ ^-([a-zA-Z0-9][a-zA-Z0-9]+)$ ]]; then
17
- > flags="${BASH_REMATCH[1]}"
18
- > for ((i = 0; i < ${#flags}; i++)); do
19
- > input+=("-${flags:i:1}")
20
- > done
3
+ if Settings.compact_short_flags || Settings.conjoined_flag_args
4
+ = render :normalize_input_function
5
+ else
6
+ = render :normalize_input_simple
21
7
  end
22
-
23
- > else
24
- > input+=("$arg")
25
- > fi
26
- >
27
- > shift
28
- > done
29
- > }
30
- >
@@ -0,0 +1,39 @@
1
+ = view_marker
2
+
3
+ > normalize_input() {
4
+ > local arg flags passthru
5
+ > passthru=false
6
+ >
7
+ > while [[ $# -gt 0 ]]; do
8
+ > arg="$1"
9
+ > if [[ $passthru == true ]]; then
10
+ > input+=("$arg")
11
+
12
+ if Settings.conjoined_flag_args
13
+ > elif [[ $arg =~ ^(--[a-zA-Z0-9_\-]+)=(.+)$ ]]; then
14
+ > input+=("${BASH_REMATCH[1]}")
15
+ > input+=("${BASH_REMATCH[2]}")
16
+ > elif [[ $arg =~ ^(-[a-zA-Z0-9])=(.+)$ ]]; then
17
+ > input+=("${BASH_REMATCH[1]}")
18
+ > input+=("${BASH_REMATCH[2]}")
19
+ end
20
+
21
+ if Settings.compact_short_flags
22
+ > elif [[ $arg =~ ^-([a-zA-Z0-9][a-zA-Z0-9]+)$ ]]; then
23
+ > flags="${BASH_REMATCH[1]}"
24
+ > for ((i = 0; i < ${#flags}; i++)); do
25
+ > input+=("-${flags:i:1}")
26
+ > done
27
+ end
28
+
29
+ > elif [[ "$arg" == "--" ]]; then
30
+ > passthru=true
31
+ > input+=("$arg")
32
+ > else
33
+ > input+=("$arg")
34
+ > fi
35
+ >
36
+ > shift
37
+ > done
38
+ > }
39
+ >
@@ -0,0 +1,6 @@
1
+ = view_marker
2
+
3
+ > normalize_input() {
4
+ > input=("$@")
5
+ > }
6
+ >
@@ -1,13 +1,8 @@
1
1
  = view_marker
2
2
 
3
3
  if args.any?
4
- condition = "if"
5
- args.each do |arg|
6
- > {{ condition }} [[ -z ${args['{{ arg.name }}']+x} ]]; then
7
- > args['{{ arg.name }}']=$1
8
- > shift
9
-
10
- condition = "elif"
4
+ args.each_with_index do |arg, index|
5
+ = arg.render :case, index: index
11
6
  end
12
7
 
13
8
  > else
@@ -1,30 +1,9 @@
1
1
  = view_marker
2
2
 
3
- condition = "if"
4
- args.each do |arg|
5
- > {{ condition }} [[ -z ${args['{{ arg.name }}']+x} ]]; then
6
- if arg.repeatable
7
- > args['{{ arg.name }}']="\"$1\""
8
- > shift
9
- if arg.unique
10
- > elif [[ ! "${args['{{ arg.name }}']}" =~ \"$1\" ]]; then
11
- > args['{{ arg.name }}']="${args[{{ arg.name }}]} \"$1\""
12
- > shift
13
- > else
14
- > shift
15
- else
16
- > else
17
- > args['{{ arg.name }}']="${args[{{ arg.name }}]} \"$1\""
18
- > shift
19
- end
20
-
21
- else
22
- > args['{{ arg.name }}']=$1
23
- > shift
24
-
25
- end
26
- condition = "elif"
3
+ args.each_with_index do |arg, index|
4
+ = arg.render :case_repeatable, index: index
27
5
  end
28
6
 
29
7
  > fi
30
- >
8
+ > shift
9
+ >
@@ -1,13 +1,8 @@
1
1
  = view_marker
2
2
 
3
3
  if args.any?
4
- condition = "if"
5
- args.each do |arg|
6
- > {{ condition }} [[ -z ${args['{{ arg.name }}']+x} ]]; then
7
- > args['{{ arg.name }}']=$1
8
- > shift
9
-
10
- condition = "elif"
4
+ args.each_with_index do |arg, index|
5
+ = arg.render :case, index: index
11
6
  end
12
7
 
13
8
  > else
@@ -6,6 +6,9 @@
6
6
  > declare -a other_args=()
7
7
  > declare -a env_var_names=()
8
8
  > declare -a input=()
9
+ if has_unique_args_or_flags?
10
+ > declare -A unique_lookup=()
11
+ end
9
12
  > normalize_input "$@"
10
13
  > parse_requirements "${input[@]}"
11
14
  if user_file_exist?('before')
@@ -29,5 +32,3 @@ if user_file_exist?('after')
29
32
  end
30
33
 
31
34
  > }
32
-
33
-
@@ -3,15 +3,19 @@
3
3
  > if [[ -n ${2+x} ]]; then
4
4
 
5
5
  if repeatable
6
+ > escaped="$(printf '%q' "$2")"
6
7
  > if [[ -z ${args['{{ name }}']+x} ]]; then
7
- > args['{{ name }}']="\"$2\""
8
+ > args['{{ name }}']="$escaped"
8
9
  if unique
9
- > elif [[ ! "${args['{{ name }}']}" =~ \"$2\" ]]; then
10
+ > elif [[ -z "${unique_lookup["{{ name }}:${escaped}"]:-}" ]]; then
10
11
  else
11
12
  > else
12
13
  end
13
- > args['{{ name }}']="${args['{{ name }}']} \"$2\""
14
+ > args['{{ name }}']="${args['{{ name }}']} $escaped"
14
15
  > fi
16
+ if unique
17
+ > unique_lookup["{{ name }}:${escaped}"]=1
18
+ end
15
19
 
16
20
  else
17
21
  > args['{{ name }}']="$2"
@@ -25,4 +29,4 @@ end
25
29
  > exit 1
26
30
  > fi
27
31
  > ;;
28
- >
32
+ >
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: 1.1.8
4
+ version: 1.1.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: 2024-03-02 00:00:00.000000000 Z
11
+ date: 2024-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colsole
@@ -226,6 +226,8 @@ files:
226
226
  - lib/bashly/templates/minimal.yml
227
227
  - lib/bashly/version.rb
228
228
  - lib/bashly/views/README.md
229
+ - lib/bashly/views/argument/case.gtx
230
+ - lib/bashly/views/argument/case_repeatable.gtx
229
231
  - lib/bashly/views/argument/usage.gtx
230
232
  - lib/bashly/views/argument/validations.gtx
231
233
  - lib/bashly/views/command/catch_all_filter.gtx
@@ -246,6 +248,8 @@ files:
246
248
  - lib/bashly/views/command/long_usage.gtx
247
249
  - lib/bashly/views/command/master_script.gtx
248
250
  - lib/bashly/views/command/normalize_input.gtx
251
+ - lib/bashly/views/command/normalize_input_function.gtx
252
+ - lib/bashly/views/command/normalize_input_simple.gtx
249
253
  - lib/bashly/views/command/parse_requirements.gtx
250
254
  - lib/bashly/views/command/parse_requirements_case.gtx
251
255
  - lib/bashly/views/command/parse_requirements_case_catch_all.gtx
@@ -296,7 +300,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
296
300
  requirements:
297
301
  - - ">="
298
302
  - !ruby/object:Gem::Version
299
- version: '3.0'
303
+ version: '3.1'
300
304
  required_rubygems_version: !ruby/object:Gem::Requirement
301
305
  requirements:
302
306
  - - ">="