bashly 0.7.2 → 0.7.6

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: f0f901e6eab10987e6daf0042b18e097b172f3d54babc286b9fb2f5e83452612
4
- data.tar.gz: 4dc2043674ce9c5d1c3be9bfd8ec72f2cf0f18ac121b909e939dc9dd4ca3ea79
3
+ metadata.gz: 98fc7fa0a703a1bdef30afdd0e4ff4b5a4d5cc6025380e4aa952aeb77c0b9aa8
4
+ data.tar.gz: 2d148783f32363862d4039b424ef8ddd5fa050c849bbe742f7195096cdf78830
5
5
  SHA512:
6
- metadata.gz: f00b5c1afcb6349e7fdcc3e60adc23d2d6aa3d22a31b6375c3a2129adcb4434b9e2885449fdd8ede13dca7d56b0c1b6387681b6428db6028da0208cea82fe0a2
7
- data.tar.gz: 5350aa76e39d2cee1d3ef2eb722e22f10e3d28772917d6b66cd5d11b0fb331a45e6fa2dd006f1096468b6189735ccffbf2650c27cb8a6a8d70b92c9c63803625
6
+ metadata.gz: 309afb5a2c73c170fafacf9ab02066bd1e4d29934c6a94f6518328dbfd246a8679fc0e8ffc51e00a3359adc90cc25d1d9433e8944db7def0cf806ac5dd99d89d
7
+ data.tar.gz: fd16952f25275a54d0aaaf990b00575a19fc5d38cab3206918722240466df9e1ae7f55f439ccfc1129ca5d8733181583fbd4280bf18cb42615119a9122f666d8
@@ -9,6 +9,7 @@ module Bashly
9
9
  usage "bashly add colors [--force]"
10
10
  usage "bashly add yaml [--force]"
11
11
  usage "bashly add validations [--force]"
12
+ usage "bashly add test [--force]"
12
13
  usage "bashly add comp FORMAT [OUTPUT --force]"
13
14
  usage "bashly add (-h|--help)"
14
15
 
@@ -23,6 +24,7 @@ module Bashly
23
24
  command "colors", "Add standard functions for printing colorful and formatted text to the lib directory."
24
25
  command "yaml", "Add standard functions for reading YAML files to the lib directory."
25
26
  command "validations", "Add argument validation functions to the lib directory."
27
+ command "test", "Add approval testing."
26
28
  command "comp", "Generate a bash completions script or function."
27
29
 
28
30
  example "bashly add strings --force"
@@ -55,6 +57,10 @@ module Bashly
55
57
  add_lib 'validations'
56
58
  end
57
59
 
60
+ def test_command
61
+ add_lib 'test'
62
+ end
63
+
58
64
  def comp_command
59
65
  format = args['FORMAT']
60
66
  output = args['OUTPUT']
@@ -1,8 +1,6 @@
1
1
  module Bashly
2
2
  module Commands
3
3
  class Generate < Base
4
- using ComposeRefinements
5
-
6
4
  help "Generate the bash script and required files"
7
5
 
8
6
  usage "bashly generate [--force --quiet --upgrade --wrap FUNCTION]"
@@ -82,7 +80,7 @@ module Bashly
82
80
  end
83
81
 
84
82
  def create_root_command_file
85
- create_file "#{Settings.source_dir}/root_command.sh", command.render(:default_root_script)
83
+ create_file "#{Settings.source_dir}/#{command.filename}", command.render(:default_root_script)
86
84
  end
87
85
 
88
86
  def create_all_command_files
@@ -98,7 +96,7 @@ module Bashly
98
96
  if File.exist? file and !args['--force']
99
97
  quiet_say "!txtblu!skipped!txtrst! #{file} (exists)"
100
98
  else
101
- File.write file, content
99
+ File.deep_write file, content
102
100
  quiet_say "!txtgrn!created!txtrst! #{file}"
103
101
  end
104
102
  end
@@ -118,7 +116,7 @@ module Bashly
118
116
  end
119
117
 
120
118
  def config
121
- @config ||= Config.new("#{Settings.source_dir}/bashly.yml").compose
119
+ @config ||= Config.new "#{Settings.source_dir}/bashly.yml"
122
120
  end
123
121
 
124
122
  def command
data/lib/bashly/config.rb CHANGED
@@ -4,11 +4,13 @@ module Bashly
4
4
  # A convenience class to use either a hash or a filename as a configuration
5
5
  # source
6
6
  class Config
7
+ using ComposeRefinements
8
+
7
9
  attr_reader :config
8
10
 
9
11
  def self.new(config)
10
12
  if config.is_a? String
11
- YAML.load_file config
13
+ YAML.load_file(config).compose
12
14
  else
13
15
  config
14
16
  end
@@ -79,8 +79,11 @@ module Bashly
79
79
  assert_optional_string "#{key}.default", value['default']
80
80
  assert_optional_string "#{key}.validate", value['validate']
81
81
  assert_boolean "#{key}.required", value['required']
82
+ assert_boolean "#{key}.repeatable", value['repeatable']
82
83
 
83
84
  assert_array "#{key}.allowed", value['allowed'], of: :string
85
+
86
+ refute value['name'].match(/^-/), "#{key}.name must not start with '-'"
84
87
  end
85
88
 
86
89
  def assert_flag(key, value)
@@ -96,6 +99,11 @@ module Bashly
96
99
 
97
100
  assert_boolean "#{key}.required", value['required']
98
101
  assert_array "#{key}.allowed", value['allowed'], of: :string
102
+ assert_array "#{key}.conflicts", value['conflicts'], of: :string
103
+
104
+ assert value['long'].match(/^--[a-zA-Z0-9_\-]+$/), "#{key}.long must be in the form of '--name'" if value['long']
105
+ assert value['short'].match(/^-[a-zA-Z0-9]$/), "#{key}.short must be in the form of '-n'" if value['short']
106
+ refute value['arg'].match(/^-/), "#{key}.arg must not start with '-'" if value['arg']
99
107
  end
100
108
 
101
109
  def assert_env_var(key, value)
@@ -117,6 +125,7 @@ module Bashly
117
125
  assert_optional_string "#{key}.help", value['help']
118
126
  assert_optional_string "#{key}.footer", value['footer']
119
127
  assert_optional_string "#{key}.group", value['group']
128
+ assert_optional_string "#{key}.filename", value['filename']
120
129
 
121
130
  assert_boolean "#{key}.default", value['default']
122
131
  assert_version "#{key}.version", value['version']
@@ -128,6 +137,7 @@ module Bashly
128
137
  assert_array "#{key}.commands", value['commands'], of: :command
129
138
  assert_array "#{key}.completions", value['completions'], of: :string
130
139
  assert_array "#{key}.dependencies", value['dependencies'], of: :string
140
+ assert_array "#{key}.filters", value['filters'], of: :string
131
141
  assert_array "#{key}.environment_variables", value['environment_variables'], of: :env_var
132
142
  assert_array "#{key}.examples", value['examples'], of: :string
133
143
  end
@@ -7,7 +7,7 @@ module Bashly
7
7
  end
8
8
 
9
9
  def config
10
- @config ||= Bashly::Config.new "#{Settings.source_dir}/bashly.yml"
10
+ @config ||= Config.new "#{Settings.source_dir}/bashly.yml"
11
11
  end
12
12
  end
13
13
  end
@@ -23,6 +23,21 @@ strings:
23
23
  - source: "templates/strings.yml"
24
24
  target: "%{user_source_dir}/bashly-strings.yml"
25
25
 
26
+ test:
27
+ files:
28
+ - source: "templates/test/approvals.bash"
29
+ target: "%{user_target_dir}/test/approvals.bash"
30
+ - source: "templates/test/approve"
31
+ target: "%{user_target_dir}/test/approve"
32
+
33
+ post_install_message: |
34
+ Edit your tests in !txtgrn!test/approve!txtrst! and then run:
35
+
36
+ !txtpur!$ ./test/approve!txtrst!"
37
+
38
+ Docs: !undblu!https://github.com/DannyBen/approvals.bash
39
+
40
+
26
41
  validations:
27
42
  files:
28
43
  - source: "templates/lib/validations/validate_dir_exists.sh"
@@ -57,7 +57,10 @@ module Bashly
57
57
  end
58
58
 
59
59
  def target_file_args
60
- { user_source_dir: Settings.source_dir }
60
+ {
61
+ user_source_dir: Settings.source_dir,
62
+ user_target_dir: Settings.target_dir
63
+ }
61
64
  end
62
65
  end
63
66
  end
@@ -10,12 +10,14 @@ module Bashly
10
10
  arg
11
11
  catch_all
12
12
  completions
13
+ conflicts
13
14
  default
14
15
  dependencies
15
16
  description
16
17
  environment_variables
17
18
  examples
18
19
  extensible
20
+ filters
19
21
  flags
20
22
  footer
21
23
  group
@@ -24,6 +26,7 @@ module Bashly
24
26
  name
25
27
  parent_name
26
28
  private
29
+ repeatable
27
30
  required
28
31
  short
29
32
  validate
@@ -53,9 +56,9 @@ module Bashly
53
56
  respond_to?(method_name) ? options[key] : super
54
57
  end
55
58
 
56
- def respond_to?(method_name, include_private = false)
59
+ def respond_to_missing?(method_name, include_private = false)
57
60
  OPTION_KEYS.include?(method_name) || super
58
61
  end
59
62
  end
60
63
  end
61
- end
64
+ end
@@ -55,7 +55,7 @@ module Bashly
55
55
  # Returns the bash filename that is expected to hold the user code
56
56
  # for this command
57
57
  def filename
58
- "#{action_name.to_underscore}_command.sh"
58
+ options["filename"] || "#{action_name.to_underscore}_command.sh"
59
59
  end
60
60
 
61
61
  # Returns an array of Flags
@@ -19,6 +19,7 @@ module Bashly
19
19
  result = [aliases.join(", ")]
20
20
  result << arg.upcase if arg
21
21
  result << strings[:required] if required and extended
22
+ result << strings[:repeatable] if repeatable and extended
22
23
  result.join " "
23
24
  end
24
25
  end
@@ -14,6 +14,7 @@ group: "%{group} Commands:"
14
14
  command_shortcut: "Shortcut: %{short}"
15
15
  default_command_summary: "%{summary} (default)"
16
16
  required: "(required)"
17
+ repeatable: "(repeatable)"
17
18
  default: "Default: %{value}"
18
19
  allowed: "Allowed: %{values}"
19
20
 
@@ -25,6 +26,7 @@ version_flag_text: Show version number
25
26
  flag_requires_an_argument: "%{name} requires an argument: %{usage}"
26
27
  invalid_argument: "invalid argument: %s"
27
28
  invalid_flag: "invalid option: %s"
29
+ conflicting_flags: "conflicting options: %s cannot be used with %s"
28
30
  missing_required_argument: "missing required argument: %{arg}\\nusage: %{usage}"
29
31
  missing_required_flag: "missing required flag: %{usage}"
30
32
  missing_required_environment_variable: "missing required environment variable: %{var}"
@@ -0,0 +1,93 @@
1
+ # approvals.bash v0.2.7
2
+ #
3
+ # Interactive approval testing for Bash.
4
+ # https://github.com/DannyBen/approvals.bash
5
+ approve() {
6
+ local expected approval approval_file actual cmd
7
+ approvals_dir=${APPROVALS_DIR:=approvals}
8
+
9
+ cmd=$1
10
+ actual=$(eval "$cmd" 2>&1)
11
+ last_exit_code=$?
12
+ approval=$(printf "%b" "$cmd" | tr -s -c "[:alnum:]" _)
13
+ approval_file="$approvals_dir/${2:-"$approval"}"
14
+
15
+ [[ -d "$approvals_dir" ]] || mkdir "$approvals_dir"
16
+
17
+ if [[ -f "$approval_file" ]]; then
18
+ expected=$(cat "$approval_file")
19
+ else
20
+ echo "--- [$(blue "new: $cmd")] ---"
21
+ printf "%b\n" "$actual"
22
+ echo "--- [$(blue "new: $cmd")] ---"
23
+ expected="$actual"
24
+ user_approval "$cmd" "$actual" "$approval_file"
25
+ return
26
+ fi
27
+
28
+ if [[ "$(printf "%b" "$actual")" = "$(printf "%b" "$expected")" ]]; then
29
+ green "PASS $cmd"
30
+ else
31
+ echo "--- [$(blue "diff: $cmd")] ---"
32
+ $diff_cmd <(printf "%b" "$expected\n") <(printf "%b" "$actual\n" ) | tail -n +4
33
+ echo "--- [$(blue "diff: $cmd")] ---"
34
+ user_approval "$cmd" "$actual" "$approval_file"
35
+ fi
36
+ }
37
+
38
+ describe() {
39
+ cyan "TEST $*"
40
+ }
41
+
42
+ fail() {
43
+ red "FAIL $*"
44
+ exit 1
45
+ }
46
+
47
+ pass() {
48
+ green "PASS $*"
49
+ return 0
50
+ }
51
+
52
+ expect_exit_code() {
53
+ if [[ $last_exit_code == "$1" ]]; then
54
+ pass "exit $last_exit_code"
55
+ else
56
+ fail "Expected exit code $1, got $last_exit_code"
57
+ fi
58
+ }
59
+
60
+ red() { printf "\e[31m%b\e[0m\n" "$*"; }
61
+ green() { printf "\e[32m%b\e[0m\n" "$*"; }
62
+ blue() { printf "\e[34m%b\e[0m\n" "$*"; }
63
+ magenta() { printf "\e[35m%b\e[0m\n" "$*"; }
64
+ cyan() { printf "\e[36m%b\e[0m\n" "$*"; }
65
+
66
+ # Private
67
+
68
+ user_approval() {
69
+ local cmd="$1"
70
+ local actual="$2"
71
+ local approval_file="$3"
72
+
73
+ if [[ -v CI || -v GITHUB_ACTIONS ]]; then
74
+ fail "$cmd"
75
+ fi
76
+
77
+ echo
78
+ printf "[A]pprove? \n"
79
+ response=$(bash -c "read -n 1 key; echo \$key")
80
+ printf "\r"
81
+ if [[ $response =~ [Aa] ]]; then
82
+ printf "%b\n" "$actual" > "$approval_file"
83
+ pass "$cmd"
84
+ else
85
+ fail "$cmd"
86
+ fi
87
+ }
88
+
89
+ if diff --help | grep -- --color > /dev/null 2>&1; then
90
+ diff_cmd="diff --unified --color=always"
91
+ else
92
+ diff_cmd="diff --unified"
93
+ fi
@@ -1,3 +1,3 @@
1
1
  module Bashly
2
- VERSION = "0.7.2"
2
+ VERSION = "0.7.6"
3
3
  end
@@ -1,3 +1,3 @@
1
- echo "# this file is located in '<%= Settings.source_dir %>/root_command.sh'"
1
+ echo "# this file is located in '<%= "#{Settings.source_dir}/#{filename}" %>'"
2
2
  echo "# you can edit it freely and regenerate (it will not be overwritten)"
3
3
  inspect_args
@@ -14,6 +14,7 @@ parse_requirements() {
14
14
  <%= render(:catch_all_filter).indent 2 %>
15
15
  <%= render(:default_assignments).indent 2 %>
16
16
  <%= render(:whitelist_filter).indent 2 %>
17
+ <%= render(:user_filter).indent 2 %>
17
18
  }
18
19
 
19
20
  % commands.each do |command|
@@ -1,4 +1,4 @@
1
1
  # :command.root_command
2
2
  root_command() {
3
- <%= load_user_file("root_command.sh").indent 2 %>
3
+ <%= load_user_file(filename).indent 2 %>
4
4
  }
@@ -0,0 +1,11 @@
1
+ # :command.user_filter
2
+ % if filters
3
+ % filters.each do |filter|
4
+ filter_error=$(filter_<%= filter %>)
5
+ if [[ -n $filter_error ]]; then
6
+ echo "$filter_error"
7
+ exit 1
8
+ fi
9
+
10
+ % end
11
+ % end
@@ -1,17 +1,30 @@
1
1
  # :flag.case
2
2
  <%= aliases.join " | " %> )
3
+ <%= render(:conflicts).indent 2 %>
3
4
  % if arg
4
5
  if [[ -n ${2+x} ]]; then
5
6
  <%= render(:validations).indent 4 %>
7
+ % if repeatable
8
+ if [[ -z ${args[<%= name %>]+x} ]]; then
9
+ args[<%= name %>]="\"$2\""
10
+ else
11
+ args[<%= name %>]="${args[<%= name %>]} \"$2\""
12
+ fi
13
+ % else
6
14
  args[<%= name %>]="$2"
15
+ % end
7
16
  shift
8
17
  shift
9
18
  else
10
19
  printf "%s\n" "<%= strings[:flag_requires_an_argument] % { name: name, usage: usage_string } %>"
11
20
  exit 1
12
21
  fi
22
+ % else
23
+ % if repeatable
24
+ (( args[<%= name %>]+=1 ))
13
25
  % else
14
26
  args[<%= name %>]=1
27
+ % end
15
28
  shift
16
29
  % end
17
30
  ;;
@@ -0,0 +1,18 @@
1
+ # :flag.conflicts
2
+ % if conflicts
3
+ % if conflicts.count == 1
4
+ if [[ -n "${args[<%= conflicts.first %>]:-}" ]]; then
5
+ printf "<%= strings[:conflicting_flags] %>\n" "$key" "<%= conflicts.first %>"
6
+ exit 1
7
+ fi
8
+
9
+ % else
10
+ for conflict in <%= conflicts.join ' ' %>; do
11
+ if [[ -n "${args[$conflict]:-}" ]]; then
12
+ printf "<%= strings[:conflicting_flags] %>\n" "$key" "$conflict"
13
+ exit 1
14
+ fi
15
+ done
16
+
17
+ % end
18
+ % 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.7.2
4
+ version: 0.7.6
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-01-28 00:00:00.000000000 Z
11
+ date: 2022-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colsole
@@ -121,6 +121,7 @@ files:
121
121
  - lib/bashly/templates/lib/yaml.sh
122
122
  - lib/bashly/templates/minimal.yml
123
123
  - lib/bashly/templates/strings.yml
124
+ - lib/bashly/templates/test/approvals.bash
124
125
  - lib/bashly/version.rb
125
126
  - lib/bashly/views/argument/usage.erb
126
127
  - lib/bashly/views/argument/validations.erb
@@ -155,11 +156,13 @@ files:
155
156
  - lib/bashly/views/command/usage_examples.erb
156
157
  - lib/bashly/views/command/usage_fixed_flags.erb
157
158
  - lib/bashly/views/command/usage_flags.erb
159
+ - lib/bashly/views/command/user_filter.erb
158
160
  - lib/bashly/views/command/user_lib.erb
159
161
  - lib/bashly/views/command/version_command.erb
160
162
  - lib/bashly/views/command/whitelist_filter.erb
161
163
  - lib/bashly/views/environment_variable/usage.erb
162
164
  - lib/bashly/views/flag/case.erb
165
+ - lib/bashly/views/flag/conflicts.erb
163
166
  - lib/bashly/views/flag/usage.erb
164
167
  - lib/bashly/views/flag/validations.erb
165
168
  - lib/bashly/views/wrapper/bash3_bouncer.erb
@@ -188,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
191
  - !ruby/object:Gem::Version
189
192
  version: '0'
190
193
  requirements: []
191
- rubygems_version: 3.2.15
194
+ rubygems_version: 3.3.7
192
195
  signing_key:
193
196
  specification_version: 4
194
197
  summary: Bash Command Line Tool Generator