bashly 1.1.7 → 1.1.9

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: 0a7e677e8b16a4711ece6638e16e585b1bf74fca12cc6c3e9ea4810c209d4693
4
- data.tar.gz: 630667bb3c3d36055a0160f05719a9561d8ec7cdbd02af80fd9c2a7a5edabd17
3
+ metadata.gz: 57e204b6791653f135c34bdf18a2b882d180087bbec57044db09540e342d81f0
4
+ data.tar.gz: ef1bc1015e325d227b3bc32d537a51fc369ea7833972f6f9a15e592d79ddc131
5
5
  SHA512:
6
- metadata.gz: 7dd08f97c66d81979c68b43d356b650ec50d30b0c2d0d8663388f0fec56f881bd5b140a55719143e00d155b2f0ae321d6207bf63c7e248d324489cf7dc3b7cee
7
- data.tar.gz: 754c2b4aaa9e289b04e83e10b861933b26ef3168620315a7a9655c8dc991c095d0e42037a43d379b2d1f86d6f4eb5a6e3d6ea70009642d873e7dafb1627cfffa
6
+ metadata.gz: 4947a104aa165d0e216a53a07908b72c290ff8e905e96acde044025bccefbab783ed5c87f3c217b674bdfb57db21c5d149827a9d8383065fef78eaac701826c0
7
+ data.tar.gz: 99eb5313b587980c540f22596cc8e99794cf7ed24cb0f62e06c4179a64399e80099c6cbc7d2803fe96827cdbf5734bbb9fc7364533979da45cd44973d0511748
@@ -0,0 +1,45 @@
1
+ module Bashly
2
+ # A helper class, used by the `Array#indent` extension.
3
+ # It will return the array of strings with all strings prefixed by `indentation`
4
+ # unless the line is within a heredoc block.
5
+ class IndentationHelper
6
+ attr_reader :marker, :indentation
7
+
8
+ def initialize(indentation)
9
+ @indentation = indentation
10
+ @marker = nil
11
+ end
12
+
13
+ def indent(line)
14
+ if inside_heredoc?
15
+ reset_marker if heredoc_closed?(line)
16
+ line
17
+ else
18
+ set_heredoc_state(line)
19
+ "#{indentation}#{line}"
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def reset_marker
26
+ @marker = nil
27
+ end
28
+
29
+ def inside_heredoc?
30
+ !!marker
31
+ end
32
+
33
+ def set_heredoc_state(line)
34
+ @marker = extract_heredoc_marker(line)
35
+ end
36
+
37
+ def extract_heredoc_marker(line)
38
+ line =~ /<<-?\s*(\w+)/ ? $1 : nil
39
+ end
40
+
41
+ def heredoc_closed?(line)
42
+ inside_heredoc? && /^#{marker}\n?$/.match?(line)
43
+ end
44
+ end
45
+ end
@@ -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,9 +1,13 @@
1
+ require 'bashly/concerns/indentation_helper'
2
+
1
3
  class Array
2
4
  def indent(offset)
3
5
  return self unless offset.positive?
4
6
 
5
7
  indentation = ' ' * offset
6
- map { |line| "#{indentation}#{line}" }
8
+ indenter = Bashly::IndentationHelper.new indentation
9
+
10
+ map { |line| indenter.indent line }
7
11
  end
8
12
 
9
13
  def nonuniq
@@ -1,7 +1,10 @@
1
- # approvals.bash v0.4.2
1
+ # approvals.bash v0.5.1
2
2
  #
3
3
  # Interactive approval testing for Bash.
4
4
  # https://github.com/DannyBen/approvals.bash
5
+ #
6
+ # shellcheck disable=SC2059
7
+ # Disabling SC2059 (quoted format string) because we use dynamic format strings
5
8
  approve() {
6
9
  local expected approval approval_file actual cmd
7
10
  approvals_dir=${APPROVALS_DIR:=approvals}
@@ -21,9 +24,9 @@ approve() {
21
24
  if [[ -f "$approval_file" ]]; then
22
25
  expected=$(cat "$approval_file")
23
26
  else
24
- echo "--- [$(blue "new: $cmd")] ---"
27
+ printf -- "$new_diff_string\n" "$cmd"
25
28
  printf "%b\n" "$actual"
26
- echo "--- [$(blue "new: $cmd")] ---"
29
+ printf -- "$new_diff_string\n" "$cmd"
27
30
  expected="$actual"
28
31
  user_approval "$cmd" "$actual" "$approval_file"
29
32
  return
@@ -32,9 +35,9 @@ approve() {
32
35
  if [[ "$(printf "%b" "$actual")" = "$(printf "%b" "$expected")" ]]; then
33
36
  pass "$cmd"
34
37
  else
35
- echo "--- [$(blue "diff: $cmd")] ---"
38
+ printf -- "$changed_diff_string\n" "$cmd"
36
39
  $diff_cmd <(printf "%b" "$expected\n") <(printf "%b" "$actual\n") | tail -n +4
37
- echo "--- [$(blue "diff: $cmd")] ---"
40
+ printf -- "$changed_diff_string\n" "$cmd"
38
41
  user_approval "$cmd" "$actual" "$approval_file"
39
42
  fi
40
43
  }
@@ -44,22 +47,24 @@ allow_diff() {
44
47
  }
45
48
 
46
49
  describe() {
47
- echo
48
- blue "= $*"
50
+ printf "$describe_string\n" "$*"
49
51
  }
50
52
 
51
53
  context() {
52
- echo
53
- magenta "= $*"
54
+ printf "$context_string\n" "$*"
55
+ }
56
+
57
+ it() {
58
+ printf "$it_string\n" "$*"
54
59
  }
55
60
 
56
61
  fail() {
57
- red " FAILED: $*"
62
+ printf "$fail_string\n" "$*"
58
63
  exit 1
59
64
  }
60
65
 
61
66
  pass() {
62
- green " approved: $*"
67
+ printf "$pass_string\n" "$*"
63
68
  return 0
64
69
  }
65
70
 
@@ -67,31 +72,61 @@ expect_exit_code() {
67
72
  if [[ $last_exit_code == "$1" ]]; then
68
73
  pass "exit $last_exit_code"
69
74
  else
70
- fail "Expected exit code $1, got $last_exit_code"
75
+ fail "expected exit code $1, got $last_exit_code"
71
76
  fi
72
77
  }
73
78
 
74
- red() { printf "\e[31m%b\e[0m\n" "$*"; }
75
- green() { printf "\e[32m%b\e[0m\n" "$*"; }
76
- blue() { printf "\e[34m%b\e[0m\n" "$*"; }
77
- magenta() { printf "\e[35m%b\e[0m\n" "$*"; }
78
- cyan() { printf "\e[36m%b\e[0m\n" "$*"; }
79
-
80
79
  # Private
81
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
+
82
112
  user_approval() {
83
113
  local cmd="$1"
84
114
  local actual="$2"
85
115
  local approval_file="$3"
86
116
 
87
- if [[ -v CI || -v GITHUB_ACTIONS ]]; then
117
+ if [[ -v CI || -v GITHUB_ACTIONS ]] && [[ -z "${AUTO_APPROVE+x}" ]]; then
88
118
  fail "$cmd"
89
119
  fi
90
120
 
91
- echo
92
- printf "[A]pprove? \n"
93
- response=$(bash -c "read -n 1 key; echo \$key")
94
- printf "\r"
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
+
95
130
  if [[ $response =~ [Aa] ]]; then
96
131
  printf "%b\n" "$actual" >"$approval_file"
97
132
  pass "$cmd"
@@ -103,21 +138,33 @@ user_approval() {
103
138
  onexit() {
104
139
  exitcode=$?
105
140
  if [[ "$exitcode" == 0 ]]; then
106
- green "\nFinished successfully"
141
+ printf "$exit_success_string\n" "$0"
107
142
  else
108
- red "\nFinished with failures"
143
+ printf "$exit_failed_string\n" "$0"
109
144
  fi
145
+ echo
110
146
  exit $exitcode
111
147
  }
112
148
 
113
149
  onerror() {
114
- fail "Caller: $(caller)"
150
+ fail "caller: $(caller)"
115
151
  }
116
152
 
117
153
  set -e
118
154
  trap 'onexit' EXIT
119
155
  trap 'onerror' ERR
120
156
 
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"
161
+ pass_string=" $(green approved) %s"
162
+ exit_success_string="$(green ▌ exit) $(bold %s finished successfully)"
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) ├────"
166
+ approval_string="[A]pprove? "
167
+
121
168
  if diff --help | grep -- --color >/dev/null 2>&1; then
122
169
  diff_cmd="diff --unified --color=always"
123
170
  else
@@ -1,41 +1,43 @@
1
- module ComposeRefinements
2
- refine Hash do
3
- def compose(keyword = 'import')
4
- result = {}
5
- each do |k, v|
6
- if k.to_s == keyword
7
- sub = safe_load_yaml(v).compose keyword
8
- if sub.is_a? Array
9
- result = sub
1
+ module Bashly
2
+ module ComposeRefinements
3
+ refine Hash do
4
+ def compose(keyword = 'import')
5
+ result = {}
6
+ each do |k, v|
7
+ if k.to_s == keyword
8
+ sub = safe_load_yaml(v).compose keyword
9
+ if sub.is_a? Array
10
+ result = sub
11
+ else
12
+ result.merge! sub
13
+ end
14
+ elsif v.respond_to? :compose
15
+ result[k] = v.compose keyword
10
16
  else
11
- result.merge! sub
17
+ result[k] = v
12
18
  end
13
- elsif v.respond_to? :compose
14
- result[k] = v.compose keyword
15
- else
16
- result[k] = v
17
19
  end
20
+ result
18
21
  end
19
- result
20
- end
21
22
 
22
- def safe_load_yaml(path)
23
- loaded = YAML.load_erb_file path
24
- return loaded if loaded.is_a?(Array) || loaded.is_a?(Hash)
23
+ def safe_load_yaml(path)
24
+ loaded = YAML.load_erb_file path
25
+ return loaded if loaded.is_a?(Array) || loaded.is_a?(Hash)
25
26
 
26
- raise Bashly::ConfigurationError, "Cannot find a valid YAML in g`#{path}`"
27
- rescue Errno::ENOENT
28
- raise Bashly::ConfigurationError, "Cannot find import file g`#{path}`"
27
+ raise Bashly::ConfigurationError, "Cannot find a valid YAML in g`#{path}`"
28
+ rescue Errno::ENOENT
29
+ raise Bashly::ConfigurationError, "Cannot find import file g`#{path}`"
30
+ end
29
31
  end
30
- end
31
32
 
32
- refine Array do
33
- def compose(keyword = 'import')
34
- map do |x|
35
- if x.respond_to? :compose
36
- x.compose keyword
37
- else
38
- x
33
+ refine Array do
34
+ def compose(keyword = 'import')
35
+ map do |x|
36
+ if x.respond_to? :compose
37
+ x.compose keyword
38
+ else
39
+ x
40
+ end
39
41
  end
40
42
  end
41
43
  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
@@ -332,7 +343,7 @@ module Bashly
332
343
  args.select(&:allowed)
333
344
  end
334
345
 
335
- # Returns an array of all the environemnt_variables with a whitelist arg
346
+ # Returns an array of all the environment_variables with a whitelist arg
336
347
  def whitelisted_environment_variables
337
348
  environment_variables.select(&:allowed)
338
349
  end
@@ -1,3 +1,3 @@
1
1
  module Bashly
2
- VERSION = '1.1.7'
2
+ VERSION = '1.1.9'
3
3
  end
@@ -1,4 +1,4 @@
1
- # View Tempaltes
1
+ # View Templates
2
2
 
3
3
  These are [GTX](https://github.com/dannyben/gtx) templates.
4
4
 
@@ -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
@@ -27,3 +27,4 @@ end
27
27
  > shift
28
28
  > done
29
29
  > }
30
+ >
@@ -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.7
4
+ version: 1.1.9
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-02-17 00:00:00.000000000 Z
11
+ date: 2024-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colsole
@@ -161,6 +161,7 @@ files:
161
161
  - lib/bashly/completions/completely.yaml.gtx
162
162
  - lib/bashly/concerns/asset_helper.rb
163
163
  - lib/bashly/concerns/completions.rb
164
+ - lib/bashly/concerns/indentation_helper.rb
164
165
  - lib/bashly/concerns/renderable.rb
165
166
  - lib/bashly/concerns/validation_helpers.rb
166
167
  - lib/bashly/config.rb
@@ -225,6 +226,8 @@ files:
225
226
  - lib/bashly/templates/minimal.yml
226
227
  - lib/bashly/version.rb
227
228
  - lib/bashly/views/README.md
229
+ - lib/bashly/views/argument/case.gtx
230
+ - lib/bashly/views/argument/case_repeatable.gtx
228
231
  - lib/bashly/views/argument/usage.gtx
229
232
  - lib/bashly/views/argument/validations.gtx
230
233
  - lib/bashly/views/command/catch_all_filter.gtx