bashly 0.7.6 → 0.7.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/lib/bashly/cli.rb +2 -1
  3. data/lib/bashly/commands/add.rb +1 -0
  4. data/lib/bashly/commands/base.rb +6 -0
  5. data/lib/bashly/commands/generate.rb +13 -1
  6. data/lib/bashly/commands/validate.rb +1 -3
  7. data/lib/bashly/concerns/renderable.rb +8 -1
  8. data/lib/bashly/config.rb +1 -1
  9. data/lib/bashly/config_validator.rb +35 -5
  10. data/lib/bashly/extensions/yaml.rb +10 -0
  11. data/lib/bashly/libraries/completions_function.rb +1 -1
  12. data/lib/bashly/libraries.yml +8 -8
  13. data/lib/bashly/library.rb +3 -2
  14. data/lib/bashly/message_strings.rb +2 -2
  15. data/lib/bashly/refinements/compose_refinements.rb +1 -1
  16. data/lib/bashly/script/argument.rb +13 -1
  17. data/lib/bashly/script/base.rb +7 -30
  18. data/lib/bashly/script/catch_all.rb +4 -0
  19. data/lib/bashly/script/command.rb +14 -8
  20. data/lib/bashly/script/environment_variable.rb +6 -0
  21. data/lib/bashly/script/flag.rb +9 -0
  22. data/lib/bashly/settings.rb +13 -1
  23. data/lib/bashly/version.rb +1 -1
  24. data/lib/bashly/views/argument/usage.erb +2 -2
  25. data/lib/bashly/views/argument/validations.erb +1 -1
  26. data/lib/bashly/views/command/catch_all_filter.erb +1 -1
  27. data/lib/bashly/views/command/command_fallback.erb +1 -1
  28. data/lib/bashly/views/command/command_filter.erb +1 -1
  29. data/lib/bashly/views/command/command_functions.erb +1 -1
  30. data/lib/bashly/views/command/default_assignments.erb +1 -1
  31. data/lib/bashly/views/command/dependencies_filter.erb +1 -1
  32. data/lib/bashly/views/command/environment_variables_filter.erb +1 -1
  33. data/lib/bashly/views/command/fixed_flags_filter.erb +3 -1
  34. data/lib/bashly/views/command/footer.erb +1 -0
  35. data/lib/bashly/views/command/function.erb +1 -1
  36. data/lib/bashly/views/command/initialize.erb +2 -2
  37. data/lib/bashly/views/command/inspect_args.erb +1 -1
  38. data/lib/bashly/views/command/master_script.erb +1 -0
  39. data/lib/bashly/views/command/normalize_input.erb +1 -1
  40. data/lib/bashly/views/command/parse_requirements.erb +1 -1
  41. data/lib/bashly/views/command/parse_requirements_case.erb +15 -3
  42. data/lib/bashly/views/command/parse_requirements_while.erb +1 -1
  43. data/lib/bashly/views/command/required_args_filter.erb +1 -1
  44. data/lib/bashly/views/command/required_flags_filter.erb +1 -1
  45. data/lib/bashly/views/command/root_command.erb +1 -1
  46. data/lib/bashly/views/command/run.erb +1 -1
  47. data/lib/bashly/views/command/usage.erb +1 -1
  48. data/lib/bashly/views/command/usage_args.erb +1 -1
  49. data/lib/bashly/views/command/usage_commands.erb +1 -1
  50. data/lib/bashly/views/command/usage_environment_variables.erb +1 -1
  51. data/lib/bashly/views/command/usage_examples.erb +1 -1
  52. data/lib/bashly/views/command/usage_fixed_flags.erb +1 -1
  53. data/lib/bashly/views/command/usage_flags.erb +1 -1
  54. data/lib/bashly/views/command/user_filter.erb +1 -1
  55. data/lib/bashly/views/command/user_lib.erb +2 -2
  56. data/lib/bashly/views/command/version_command.erb +1 -1
  57. data/lib/bashly/views/command/whitelist_filter.erb +21 -1
  58. data/lib/bashly/views/environment_variable/usage.erb +1 -1
  59. data/lib/bashly/views/flag/case.erb +1 -1
  60. data/lib/bashly/views/flag/conflicts.erb +1 -1
  61. data/lib/bashly/views/flag/usage.erb +1 -1
  62. data/lib/bashly/views/flag/validations.erb +1 -1
  63. data/lib/bashly/views/wrapper/bash3_bouncer.erb +1 -1
  64. data/lib/bashly/views/wrapper/header.erb +1 -1
  65. data/lib/bashly/views/wrapper/wrapper.erb +1 -1
  66. data/lib/bashly.rb +13 -0
  67. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98fc7fa0a703a1bdef30afdd0e4ff4b5a4d5cc6025380e4aa952aeb77c0b9aa8
4
- data.tar.gz: 2d148783f32363862d4039b424ef8ddd5fa050c849bbe742f7195096cdf78830
3
+ metadata.gz: 2ef923a576e9bb9e16ec9f78c96c6008772c77d3bfc9520299fc2dd3cd7f3dd3
4
+ data.tar.gz: 593393d64ca6e63216e0e974c580db50db754fe430d319115aef8610c47d39aa
5
5
  SHA512:
6
- metadata.gz: 309afb5a2c73c170fafacf9ab02066bd1e4d29934c6a94f6518328dbfd246a8679fc0e8ffc51e00a3359adc90cc25d1d9433e8944db7def0cf806ac5dd99d89d
7
- data.tar.gz: fd16952f25275a54d0aaaf990b00575a19fc5d38cab3206918722240466df9e1ae7f55f439ccfc1129ca5d8733181583fbd4280bf18cb42615119a9122f666d8
6
+ metadata.gz: 2b0ffb33c0c11b423cb59d594362a0c2c071a685a2ad7d4837cbdc3bb1d6a494bdcce8ff1cb3d19cfb0078ac5f167912a9d2f93c44f173d2821b415fe9cbd92e
7
+ data.tar.gz: c6b6989f3c75226641847feee4e4e8a73326da2a006bbbd52ce7544c7c4ffb3bf1f4a659a15f0b70f14590b1fe0392062099f6ced43134b2a3d81000d5a350f9
data/lib/bashly/cli.rb CHANGED
@@ -6,7 +6,8 @@ module Bashly
6
6
  class CLI
7
7
  def self.runner
8
8
  runner = MisterBin::Runner.new version: Bashly::VERSION,
9
- header: "Bashly - Bash CLI Generator"
9
+ header: "Bashly - Bash CLI Generator",
10
+ footer: "Help: !txtpur!bashly COMMAND --help!txtrst!\nDocs: !undblu!https://bashly.dannyb.co"
10
11
 
11
12
  runner.route 'init', to: Commands::Init
12
13
  runner.route 'preview', to: Commands::Preview
@@ -32,6 +32,7 @@ module Bashly
32
32
  example "bashly add comp script completions.bash"
33
33
 
34
34
  environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
35
+ environment "BASHLY_LIB_DIR", "The path to use for creating the library files, relative to the source dir [default: lib]"
35
36
 
36
37
  def strings_command
37
38
  add_lib 'strings'
@@ -5,6 +5,12 @@ module Bashly
5
5
  module Commands
6
6
  class Base < MisterBin::Command
7
7
  include AssetHelper
8
+
9
+ def validate_config
10
+ config = Config.new "#{Settings.source_dir}/bashly.yml"
11
+ validator = ConfigValidator.new config
12
+ validator.validate
13
+ end
8
14
  end
9
15
  end
10
16
  end
@@ -3,22 +3,34 @@ module Bashly
3
3
  class Generate < Base
4
4
  help "Generate the bash script and required files"
5
5
 
6
- usage "bashly generate [--force --quiet --upgrade --wrap FUNCTION]"
6
+ usage "bashly generate [options]"
7
7
  usage "bashly generate (-h|--help)"
8
8
 
9
9
  option "-f --force", "Overwrite existing files"
10
10
  option "-q --quiet", "Disable on-screen progress report"
11
11
  option "-u --upgrade", "Upgrade all added library functions"
12
12
  option "-w --wrap FUNCTION", "Wrap the entire script in a function so it can also be sourced"
13
+ option "-e --env ENV", "Force the generation environment (see BASHLY_ENV)"
13
14
 
14
15
  environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
15
16
  environment "BASHLY_TARGET_DIR", "The path to use for creating the bash script [default: .]"
17
+ environment "BASHLY_LIB_DIR", "The path to use for upgrading library files, relative to the source dir [default: lib]"
16
18
  environment "BASHLY_STRICT", "When not empty, enable bash strict mode (set -euo pipefail)"
19
+ environment "BASHLY_ENV", <<~EOF
20
+ Set to 'production' or 'development':
21
+ - production generate a smaller script, without file markers
22
+ - development generate with file markers
23
+
24
+ Can be overridden with --env [default: development]
25
+ EOF
17
26
 
18
27
  example "bashly generate --force"
19
28
  example "bashly generate --wrap my_function"
20
29
 
21
30
  def run
31
+ validate_config
32
+ ENV['BASHLY_ENV'] = args['--env'] if args['--env']
33
+ quiet_say "creating !txtgrn!production!txtrst! version" if Bashly.production?
22
34
  create_user_files
23
35
  upgrade_libs if args['--upgrade']
24
36
  create_master_script
@@ -9,9 +9,7 @@ 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
- config = Config.new "#{Settings.source_dir}/bashly.yml"
13
- validator = ConfigValidator.new config
14
- validator.validate
12
+ validate_config
15
13
  say "!txtgrn!OK"
16
14
  end
17
15
  end
@@ -4,13 +4,20 @@ module Bashly
4
4
  module Renderable
5
5
  def render(view)
6
6
  template = File.read view_path(view)
7
- ERB.new(template, trim_mode: '%-').result(binding)
7
+ erb = ERB.new(template, trim_mode: '%-')
8
+ erb.filename = "#{views_subfolder}.#{view}"
9
+ erb.result binding
8
10
  end
9
11
 
10
12
  def strings
11
13
  @strings ||= MessageStrings.new
12
14
  end
13
15
 
16
+ def view_marker(id = nil)
17
+ id ||= ":#{caller_locations.first.path}"
18
+ "# #{id}" unless Bashly.production?
19
+ end
20
+
14
21
  private
15
22
 
16
23
  def view_path(view)
data/lib/bashly/config.rb CHANGED
@@ -10,7 +10,7 @@ module Bashly
10
10
 
11
11
  def self.new(config)
12
12
  if config.is_a? String
13
- YAML.load_file(config).compose
13
+ YAML.properly_load_file(config).compose
14
14
  else
15
15
  config
16
16
  end
@@ -42,8 +42,13 @@ module Bashly
42
42
  end
43
43
  end
44
44
 
45
- def assert_hash(key, value)
45
+ def assert_hash(key, value, whitelist = nil)
46
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
47
52
  end
48
53
 
49
54
  def assert_version(key, value)
@@ -61,6 +66,7 @@ module Bashly
61
66
  end
62
67
 
63
68
  def assert_catch_all_hash(key, value)
69
+ assert_hash key, value, Script::CatchAll.option_keys
64
70
  assert_string "#{key}.label", value['label']
65
71
  assert_optional_string "#{key}.help", value['help']
66
72
  assert_boolean "#{key}.required", value['required']
@@ -73,7 +79,7 @@ module Bashly
73
79
  end
74
80
 
75
81
  def assert_arg(key, value)
76
- assert_hash key, value
82
+ assert_hash key, value, Script::Argument.option_keys
77
83
  assert_string "#{key}.name", value['name']
78
84
  assert_optional_string "#{key}.help", value['help']
79
85
  assert_optional_string "#{key}.default", value['default']
@@ -84,10 +90,12 @@ module Bashly
84
90
  assert_array "#{key}.allowed", value['allowed'], of: :string
85
91
 
86
92
  refute value['name'].match(/^-/), "#{key}.name must not start with '-'"
93
+
94
+ refute value['required'] && value['default'], "#{key} cannot have both required and default"
87
95
  end
88
96
 
89
97
  def assert_flag(key, value)
90
- assert_hash key, value
98
+ assert_hash key, value, Script::Flag.option_keys
91
99
  assert value['short'] || value['long'], "#{key} must have at least one of long or short name"
92
100
 
93
101
  assert_optional_string "#{key}.long", value['long']
@@ -97,6 +105,7 @@ module Bashly
97
105
  assert_optional_string "#{key}.default", value['default']
98
106
  assert_optional_string "#{key}.validate", value['validate']
99
107
 
108
+ assert_boolean "#{key}.repeatable", value['repeatable']
100
109
  assert_boolean "#{key}.required", value['required']
101
110
  assert_array "#{key}.allowed", value['allowed'], of: :string
102
111
  assert_array "#{key}.conflicts", value['conflicts'], of: :string
@@ -104,10 +113,20 @@ module Bashly
104
113
  assert value['long'].match(/^--[a-zA-Z0-9_\-]+$/), "#{key}.long must be in the form of '--name'" if value['long']
105
114
  assert value['short'].match(/^-[a-zA-Z0-9]$/), "#{key}.short must be in the form of '-n'" if value['short']
106
115
  refute value['arg'].match(/^-/), "#{key}.arg must not start with '-'" if value['arg']
116
+
117
+ refute value['required'] && value['default'], "#{key} cannot have both required and default"
118
+
119
+ if value['default']
120
+ assert value['arg'], "#{key}.default does not make sense without arg"
121
+ end
122
+
123
+ if value['allowed']
124
+ assert value['arg'], "#{key}.allowed does not make sense without arg"
125
+ end
107
126
  end
108
127
 
109
128
  def assert_env_var(key, value)
110
- assert_hash key, value
129
+ assert_hash key, value, Script::EnvironmentVariable.option_keys
111
130
  assert_string "#{key}.name", value['name']
112
131
  assert_optional_string "#{key}.help", value['help']
113
132
  assert_optional_string "#{key}.default", value['default']
@@ -115,7 +134,7 @@ module Bashly
115
134
  end
116
135
 
117
136
  def assert_command(key, value)
118
- assert_hash key, value
137
+ assert_hash key, value, Script::Command.option_keys
119
138
 
120
139
  refute value['commands'] && value['args'], "#{key} cannot have both commands and args"
121
140
  refute value['commands'] && value['flags'], "#{key} cannot have both commands and flags"
@@ -127,6 +146,7 @@ module Bashly
127
146
  assert_optional_string "#{key}.group", value['group']
128
147
  assert_optional_string "#{key}.filename", value['filename']
129
148
 
149
+ assert_boolean "#{key}.private", value['private']
130
150
  assert_boolean "#{key}.default", value['default']
131
151
  assert_version "#{key}.version", value['version']
132
152
  assert_catch_all "#{key}.catch_all", value['catch_all']
@@ -140,6 +160,16 @@ module Bashly
140
160
  assert_array "#{key}.filters", value['filters'], of: :string
141
161
  assert_array "#{key}.environment_variables", value['environment_variables'], of: :env_var
142
162
  assert_array "#{key}.examples", value['examples'], of: :string
163
+
164
+ if key == "root"
165
+ refute value['short'], "#{key}.short makes no sense"
166
+ refute value['group'], "#{key}.group makes no sense"
167
+ refute value['default'], "#{key}.default makes no sense"
168
+ refute value['private'], "#{key}.private makes no sense"
169
+ else
170
+ refute value['version'], "#{key}.version makes no sense"
171
+ refute value['extensible'], "#{key}.extensible makes no sense"
172
+ end
143
173
  end
144
174
  end
145
175
  end
@@ -0,0 +1,10 @@
1
+ module YAML
2
+ # This awkward patch is due to https://bugs.ruby-lang.org/issues/17866
3
+ def self.properly_load_file(path)
4
+ YAML.load_file path, aliases: true
5
+ rescue ArgumentError
6
+ # :nocov:
7
+ YAML.load_file path
8
+ # :nocov:
9
+ end
10
+ end
@@ -4,7 +4,7 @@ module Bashly
4
4
  def files
5
5
  [
6
6
  {
7
- path: "#{Settings.source_dir}/lib/#{function_name}.sh",
7
+ path: "#{Settings.full_lib_dir}/#{function_name}.sh",
8
8
  content: completions_function_code(function_name)
9
9
  }
10
10
  ]
@@ -1,22 +1,22 @@
1
1
  colors:
2
2
  files:
3
3
  - source: "templates/lib/colors.sh"
4
- target: "%{user_source_dir}/lib/colors.sh"
4
+ target: "%{user_lib_dir}/colors.sh"
5
5
 
6
6
  config:
7
7
  files:
8
8
  - source: "templates/lib/config.sh"
9
- target: "%{user_source_dir}/lib/config.sh"
9
+ target: "%{user_lib_dir}/config.sh"
10
10
 
11
11
  yaml:
12
12
  files:
13
13
  - source: "templates/lib/yaml.sh"
14
- target: "%{user_source_dir}/lib/yaml.sh"
14
+ target: "%{user_lib_dir}/yaml.sh"
15
15
 
16
16
  lib:
17
17
  files:
18
18
  - source: "templates/lib/sample_function.sh"
19
- target: "%{user_source_dir}/lib/sample_function.sh"
19
+ target: "%{user_lib_dir}/sample_function.sh"
20
20
 
21
21
  strings:
22
22
  files:
@@ -41,13 +41,13 @@ test:
41
41
  validations:
42
42
  files:
43
43
  - source: "templates/lib/validations/validate_dir_exists.sh"
44
- target: "%{user_source_dir}/lib/validations/validate_dir_exists.sh"
44
+ target: "%{user_lib_dir}/validations/validate_dir_exists.sh"
45
45
  - source: "templates/lib/validations/validate_file_exists.sh"
46
- target: "%{user_source_dir}/lib/validations/validate_file_exists.sh"
46
+ target: "%{user_lib_dir}/validations/validate_file_exists.sh"
47
47
  - source: "templates/lib/validations/validate_integer.sh"
48
- target: "%{user_source_dir}/lib/validations/validate_integer.sh"
48
+ target: "%{user_lib_dir}/validations/validate_integer.sh"
49
49
  - source: "templates/lib/validations/validate_not_empty.sh"
50
- target: "%{user_source_dir}/lib/validations/validate_not_empty.sh"
50
+ target: "%{user_lib_dir}/validations/validate_not_empty.sh"
51
51
 
52
52
  completions: :CompletionsFunction
53
53
  completions_script: :CompletionsScript
@@ -6,7 +6,7 @@ module Bashly
6
6
  end
7
7
 
8
8
  def config
9
- @config ||= YAML.load_file(config_path)
9
+ @config ||= YAML.properly_load_file(config_path)
10
10
  end
11
11
 
12
12
  def config_path
@@ -59,7 +59,8 @@ module Bashly
59
59
  def target_file_args
60
60
  {
61
61
  user_source_dir: Settings.source_dir,
62
- user_target_dir: Settings.target_dir
62
+ user_target_dir: Settings.target_dir,
63
+ user_lib_dir: Settings.full_lib_dir,
63
64
  }
64
65
  end
65
66
  end
@@ -13,7 +13,7 @@ module Bashly
13
13
  private
14
14
 
15
15
  def values!
16
- defaults = YAML.load_file asset("templates/strings.yml")
16
+ defaults = YAML.properly_load_file asset("templates/strings.yml")
17
17
  defaults.merge project_strings
18
18
  end
19
19
 
@@ -23,7 +23,7 @@ module Bashly
23
23
 
24
24
  def project_strings!
25
25
  if File.exist? project_strings_path
26
- YAML.load_file project_strings_path
26
+ YAML.properly_load_file project_strings_path
27
27
  else
28
28
  {}
29
29
  end
@@ -22,7 +22,7 @@ module ComposeRefinements
22
22
  end
23
23
 
24
24
  def safe_load_yaml(path)
25
- loaded = YAML.load_file path
25
+ loaded = YAML.properly_load_file path
26
26
  return loaded if loaded.is_a? Array or loaded.is_a? Hash
27
27
  raise Bashly::ConfigurationError, "Cannot find a valid YAML in !txtgrn!#{path}"
28
28
 
@@ -1,8 +1,20 @@
1
1
  module Bashly
2
2
  module Script
3
3
  class Argument < Base
4
+ class << self
5
+ def option_keys
6
+ @option_keys ||= %i[
7
+ allowed default help name repeatable required validate
8
+ ]
9
+ end
10
+ end
11
+
4
12
  def usage_string
5
- required ? name.upcase : "[#{name.upcase}]"
13
+ required ? label : "[#{label}]"
14
+ end
15
+
16
+ def label
17
+ repeatable ? "#{name.upcase}..." : name.upcase
6
18
  end
7
19
  end
8
20
  end
@@ -5,42 +5,19 @@ module Bashly
5
5
 
6
6
  attr_reader :options
7
7
 
8
- OPTION_KEYS = %i[
9
- allowed
10
- arg
11
- catch_all
12
- completions
13
- conflicts
14
- default
15
- dependencies
16
- description
17
- environment_variables
18
- examples
19
- extensible
20
- filters
21
- flags
22
- footer
23
- group
24
- help
25
- long
26
- name
27
- parent_name
28
- private
29
- repeatable
30
- required
31
- short
32
- validate
33
- version
34
- ]
8
+ class << self
9
+ def option_keys
10
+ @option_keys ||= []
11
+ end
12
+ end
35
13
 
36
14
  def initialize(options)
37
15
  raise Error, "Invalid options provided" unless options.respond_to? :keys
38
16
  @options = options
39
- validate_options if respond_to? :validate_options
40
17
  end
41
18
 
42
19
  def optional
43
- !required
20
+ !options['required']
44
21
  end
45
22
 
46
23
  def summary
@@ -57,7 +34,7 @@ module Bashly
57
34
  end
58
35
 
59
36
  def respond_to_missing?(method_name, include_private = false)
60
- OPTION_KEYS.include?(method_name) || super
37
+ self.class.option_keys.include?(method_name) || super
61
38
  end
62
39
  end
63
40
  end
@@ -2,6 +2,10 @@ module Bashly
2
2
  module Script
3
3
  class CatchAll
4
4
  class << self
5
+ def option_keys
6
+ @option_keys ||= %i[label help required]
7
+ end
8
+
5
9
  def from_config(config)
6
10
  options = case config
7
11
  when nil
@@ -4,6 +4,18 @@ module Bashly
4
4
  include Completions
5
5
  include CommandScopes
6
6
 
7
+ class << self
8
+ def option_keys
9
+ @option_keys ||= %i[
10
+ args catch_all commands completions
11
+ default dependencies environment_variables examples
12
+ extensible filename filters flags
13
+ footer group help name
14
+ private short version
15
+ ]
16
+ end
17
+ end
18
+
7
19
  # Returns the name to be used as an action.
8
20
  # - If it is the root command, the action is "root"
9
21
  # - Else, it is all the parents, except the first one (root) joined
@@ -90,7 +102,7 @@ module Bashly
90
102
  default_content
91
103
  end
92
104
 
93
- "# :#{path}\n#{content}"
105
+ Bashly.production? ? content : "#{view_marker path}\n#{content}"
94
106
  end
95
107
 
96
108
  # Returns an array of all parents. For example, the command
@@ -125,13 +137,7 @@ module Bashly
125
137
  # This is meant to provide the user with the ability to add custom
126
138
  # functions
127
139
  def user_lib
128
- @user_lib ||= Dir["#{Settings.source_dir}/lib/**/*.sh"].sort
129
- end
130
-
131
- # Raise an exception if there are some serious issues with the command
132
- # definition. This is called by Base#initialize.
133
- def validate_options
134
- Bashly::ConfigValidator.new(options).validate
140
+ @user_lib ||= Dir["#{Settings.full_lib_dir}/**/*.sh"].sort
135
141
  end
136
142
 
137
143
  end
@@ -1,6 +1,12 @@
1
1
  module Bashly
2
2
  module Script
3
3
  class EnvironmentVariable < Base
4
+ class << self
5
+ def option_keys
6
+ @option_keys ||= %i[default help name required]
7
+ end
8
+ end
9
+
4
10
  def usage_string(extended: false)
5
11
  result = [name.upcase]
6
12
  result << strings[:required] if required and extended
@@ -1,6 +1,15 @@
1
1
  module Bashly
2
2
  module Script
3
3
  class Flag < Base
4
+ class << self
5
+ def option_keys
6
+ @option_keys ||= %i[
7
+ allowed arg conflicts default help long repeatable required
8
+ short validate
9
+ ]
10
+ end
11
+ end
12
+
4
13
  def aliases
5
14
  if long and short
6
15
  [long, short]
@@ -1,7 +1,7 @@
1
1
  module Bashly
2
2
  class Settings
3
3
  class << self
4
- attr_writer :source_dir, :target_dir
4
+ attr_writer :source_dir, :target_dir, :lib_dir, :strict
5
5
 
6
6
  def source_dir
7
7
  @source_dir ||= ENV['BASHLY_SOURCE_DIR'] || 'src'
@@ -10,6 +10,18 @@ module Bashly
10
10
  def target_dir
11
11
  @target_dir ||= ENV['BASHLY_TARGET_DIR'] || '.'
12
12
  end
13
+
14
+ def lib_dir
15
+ @lib_dir ||= ENV['BASHLY_LIB_DIR'] || 'lib'
16
+ end
17
+
18
+ def strict
19
+ @strict ||= ENV['BASHLY_STRICT']
20
+ end
21
+
22
+ def full_lib_dir
23
+ "#{source_dir}/#{lib_dir}"
24
+ end
13
25
  end
14
26
  end
15
27
  end
@@ -1,3 +1,3 @@
1
1
  module Bashly
2
- VERSION = "0.7.6"
2
+ VERSION = "0.7.9"
3
3
  end
@@ -1,5 +1,5 @@
1
- # :argument.usage
2
- echo " <%= name.upcase %>"
1
+ <%= view_marker %>
2
+ echo " <%= label %>"
3
3
  printf "<%= help.wrap(76).indent(4).sanitize_for_print %>\n"
4
4
  % if allowed
5
5
  printf " <%= strings[:allowed] % { values: allowed.join(', ') } -%>\n"
@@ -1,4 +1,4 @@
1
- # :argument.validations
1
+ <%= view_marker %>
2
2
  % if validate
3
3
  if [[ -n $(validate_<%= validate %> "$1") ]]; then
4
4
  printf "<%= strings[:validation_error] %>\n" "<%= name.upcase %>" "$(validate_<%= validate %> "$1")"
@@ -1,4 +1,4 @@
1
- # :command.catch_all_filter
1
+ <%= view_marker %>
2
2
  % if catch_all.required?
3
3
  if [[ ${#other_args[@]} -eq 0 ]]; then
4
4
  printf "<%= strings[:missing_required_argument] % { arg: catch_all.label, usage: usage_string } %>\n"
@@ -1,4 +1,4 @@
1
- # :command.command_fallback
1
+ <%= view_marker %>
2
2
  % if default_command
3
3
  "" )
4
4
  <%= function_name %>_usage
@@ -1,4 +1,4 @@
1
- # :command.command_filter
1
+ <%= view_marker %>
2
2
  % if commands.any?
3
3
  action=${1:-}
4
4
 
@@ -1,4 +1,4 @@
1
- # :command.command_functions
1
+ <%= view_marker %>
2
2
  % deep_commands.each do |command|
3
3
  <%= command.render :function unless command.commands.any? %>
4
4
  % end
@@ -1,4 +1,4 @@
1
- # :command.default_assignments
1
+ <%= view_marker %>
2
2
  % default_args.each do |arg|
3
3
  [[ -n ${args[<%= arg.name %>]:-} ]] || args[<%= arg.name %>]="<%= arg.default %>"
4
4
  % end
@@ -1,4 +1,4 @@
1
- # :command.dependencies_filter
1
+ <%= view_marker %>
2
2
  % if dependencies
3
3
  % dependencies.each do |dependency|
4
4
  if ! [[ -x "$(command -v <%= dependency %>)" ]]; then
@@ -1,4 +1,4 @@
1
- # :command.environment_variables_filter
1
+ <%= view_marker %>
2
2
  % if default_environment_variables.any?
3
3
  % default_environment_variables.each do |env_var|
4
4
  export <%= env_var.name.upcase %>="${<%= env_var.name.upcase %>:-<%= env_var.default %>}"
@@ -1,5 +1,6 @@
1
- # :command.fixed_flag_filter
1
+ <%= view_marker %>
2
2
  case "${1:-}" in
3
+ % if root_command?
3
4
  % if short_flag_exist? "-v"
4
5
  --version )
5
6
  % else
@@ -9,6 +10,7 @@ case "${1:-}" in
9
10
  exit
10
11
  ;;
11
12
 
13
+ % end
12
14
  % if short_flag_exist? "-h"
13
15
  --help )
14
16
  % else
@@ -1,2 +1,3 @@
1
+ <%= view_marker %>
1
2
  printf "<%= footer.gsub("\n", '\n').gsub('"', '\"') %>\n"
2
3
  echo
@@ -1,4 +1,4 @@
1
- # :command.function
1
+ <%= view_marker %>
2
2
  <%= function_name %>_command() {
3
3
  <%= load_user_file(filename).indent 2 %>
4
4
  }
@@ -1,8 +1,8 @@
1
- # :command.initialize
1
+ <%= view_marker %>
2
2
  initialize() {
3
3
  version="<%= version %>"
4
4
  long_usage=''
5
- <%= ENV['BASHLY_STRICT'] ? "set -euo pipefail" : "set -e" %>
5
+ <%= Settings.strict ? "set -euo pipefail" : "set -e" %>
6
6
 
7
7
  <%= load_user_file("initialize.sh", placeholder: false).indent 2 %>
8
8
  }
@@ -1,4 +1,4 @@
1
- # :command.inspect_args
1
+ <%= view_marker %>
2
2
  inspect_args() {
3
3
  readarray -t sorted_keys < <(printf '%s\n' "${!args[@]}" | sort)
4
4
  if (( ${#args[@]} )); then
@@ -1,3 +1,4 @@
1
+ <%= view_marker %>
1
2
  <%= render :root_command if commands.empty? %>
2
3
  <%= render :version_command %>
3
4
  <%= render :usage %>
@@ -1,4 +1,4 @@
1
- # :command.normalize_input
1
+ <%= view_marker %>
2
2
  normalize_input() {
3
3
  local arg flags
4
4
 
@@ -1,4 +1,4 @@
1
- # :command.parse_requirements
1
+ <%= view_marker %>
2
2
  % if root_command?
3
3
  parse_requirements() {
4
4
  % else
@@ -1,18 +1,30 @@
1
- # :command.parse_requirements_case
1
+ <%= view_marker %>
2
+ % repeatable_arg = false
2
3
  % if args.any?
3
4
  % condition = "if"
4
5
  % args.each do |arg|
5
6
  <%= condition %> [[ -z ${args[<%= arg.name %>]+x} ]]; then
6
7
  <%= arg.render(:validations).indent 2 %>
8
+ % if arg.repeatable
9
+ % repeatable_arg = true
10
+ args[<%= arg.name %>]="\"$1\""
11
+ shift
12
+ else
13
+ args[<%= arg.name %>]="${args[<%= arg.name %>]} \"$1\""
14
+ shift
15
+ % else
7
16
  args[<%= arg.name %>]=$1
8
17
  shift
18
+ % end
9
19
  % condition = "elif"
10
20
  % end
21
+ % if !repeatable_arg
11
22
  else
23
+ % end
12
24
  % if catch_all.enabled?
13
25
  other_args+=("$1")
14
26
  shift
15
- % else
27
+ % elsif !repeatable_arg
16
28
  printf "<%= strings[:invalid_argument] %>\n" "$key"
17
29
  exit 1
18
30
  % end
@@ -20,7 +32,7 @@ fi
20
32
  % elsif catch_all.enabled?
21
33
  other_args+=("$1")
22
34
  shift
23
- % else
35
+ % elsif !repeatable_arg
24
36
  printf "<%= strings[:invalid_argument] %>\n" "$key"
25
37
  exit 1
26
38
  % end
@@ -1,4 +1,4 @@
1
- # :command.parse_requirements_while
1
+ <%= view_marker %>
2
2
  while [[ $# -gt 0 ]]; do
3
3
  key="$1"
4
4
  case "$key" in
@@ -1,4 +1,4 @@
1
- # :command.required_args_filter
1
+ <%= view_marker %>
2
2
  % required_args.each do |arg|
3
3
  if [[ -z ${args[<%= arg.name %>]+x} ]]; then
4
4
  printf "<%= strings[:missing_required_argument] % { arg: arg.name.upcase, usage: usage_string } %>\n"
@@ -1,4 +1,4 @@
1
- # :command.required_flags_filter
1
+ <%= view_marker %>
2
2
  % required_flags.each do |flag|
3
3
  if [[ -z ${args[<%= flag.long %>]+x} ]]; then
4
4
  printf "<%= strings[:missing_required_flag] % { usage: flag.usage_string } %>\n"
@@ -1,4 +1,4 @@
1
- # :command.root_command
1
+ <%= view_marker %>
2
2
  root_command() {
3
3
  <%= load_user_file(filename).indent 2 %>
4
4
  }
@@ -1,4 +1,4 @@
1
- # :command.run
1
+ <%= view_marker %>
2
2
  run() {
3
3
  declare -A args=()
4
4
  declare -a other_args=()
@@ -1,4 +1,4 @@
1
- # :command.usage
1
+ <%= view_marker %>
2
2
  <%= function_name %>_usage() {
3
3
  if [[ -n $long_usage ]]; then
4
4
  <%- if summary == help -%>
@@ -1,4 +1,4 @@
1
- # :command.usage_args
1
+ <%= view_marker %>
2
2
  printf "<%= strings[:arguments] %>\n"
3
3
  % if args.any?
4
4
 
@@ -1,4 +1,4 @@
1
- # :command.usage_commands
1
+ <%= view_marker %>
2
2
  % unless commands.first.group
3
3
  printf "<%= strings[:commands] %>\n"
4
4
  % end
@@ -1,4 +1,4 @@
1
- # :command.usage_environment_variables
1
+ <%= view_marker %>
2
2
  printf "<%= strings[:environment_variables] %>\n"
3
3
 
4
4
  % environment_variables.each do |env_var|
@@ -1,4 +1,4 @@
1
- # :command.usage_examples
1
+ <%= view_marker %>
2
2
  printf "<%= strings[:examples] %>\n"
3
3
 
4
4
  % examples.each do |example|
@@ -1,4 +1,4 @@
1
- # :command.usage_fixed_flags
1
+ <%= view_marker %>
2
2
  % if short_flag_exist? "-h"
3
3
  echo " --help"
4
4
  % else
@@ -1,4 +1,4 @@
1
- # :command.usage_flags
1
+ <%= view_marker %>
2
2
  % flags.each do |flag|
3
3
  <%= flag.render(:usage) %>
4
4
  % end
@@ -1,4 +1,4 @@
1
- # :command.user_filter
1
+ <%= view_marker %>
2
2
  % if filters
3
3
  % filters.each do |filter|
4
4
  filter_error=$(filter_<%= filter %>)
@@ -1,6 +1,6 @@
1
- # :command.user_lib
1
+ <%= view_marker %>
2
2
  % user_lib.each do |file|
3
- # <%= ":#{file}" %>
3
+ <%= view_marker file %>
4
4
  <%= File.read file %>
5
5
 
6
6
  % end
@@ -1,4 +1,4 @@
1
- # :command.version_command
1
+ <%= view_marker %>
2
2
  version_command() {
3
3
  echo "$version"
4
4
  }
@@ -1,13 +1,33 @@
1
- # :command.whitelist_filter
1
+ <%= view_marker %>
2
2
  % whitelisted_args.each do |arg|
3
+ % if arg.repeatable
4
+ eval "input_array=(${args[<%= arg.name %>]})"
5
+ for i in "${input_array[@]}"; do
6
+ if [[ ! $i =~ ^(<%= arg.allowed.join '|' %>)$ ]]; then
7
+ printf "%s\n" "<%= strings[:disallowed_argument] % { name: arg.name, allowed: arg.allowed.join(', ') } %>"
8
+ exit 1
9
+ fi
10
+ done
11
+ % else
3
12
  if [[ ! ${args[<%= arg.name %>]} =~ ^(<%= arg.allowed.join '|' %>)$ ]]; then
4
13
  printf "%s\n" "<%= strings[:disallowed_argument] % { name: arg.name, allowed: arg.allowed.join(', ') } %>"
5
14
  exit 1
6
15
  fi
7
16
  % end
17
+ % end
8
18
  % whitelisted_flags.each do |flag|
19
+ % if flag.repeatable
20
+ eval "input_array=(${args[<%= flag.name %>]})"
21
+ for i in "${input_array[@]}"; do
22
+ if [[ ! $i =~ ^(<%= flag.allowed.join '|' %>)$ ]]; then
23
+ printf "%s\n" "<%= strings[:disallowed_flag] % { name: flag.name, allowed: flag.allowed.join(', ') } %>"
24
+ exit 1
25
+ fi
26
+ done
27
+ % else
9
28
  if [[ ! ${args[<%= flag.name %>]} =~ ^(<%= flag.allowed.join '|' %>)$ ]]; then
10
29
  printf "%s\n" "<%= strings[:disallowed_flag] % { name: flag.name, allowed: flag.allowed.join(', ') } %>"
11
30
  exit 1
12
31
  fi
32
+ % end
13
33
  % end
@@ -1,4 +1,4 @@
1
- # :environment_variable.usage
1
+ <%= view_marker %>
2
2
  echo " <%= usage_string extended: true %>"
3
3
  printf "<%= help.wrap(76).indent(4).sanitize_for_print %>\n"
4
4
  % if default
@@ -1,4 +1,4 @@
1
- # :flag.case
1
+ <%= view_marker %>
2
2
  <%= aliases.join " | " %> )
3
3
  <%= render(:conflicts).indent 2 %>
4
4
  % if arg
@@ -1,4 +1,4 @@
1
- # :flag.conflicts
1
+ <%= view_marker %>
2
2
  % if conflicts
3
3
  % if conflicts.count == 1
4
4
  if [[ -n "${args[<%= conflicts.first %>]:-}" ]]; then
@@ -1,4 +1,4 @@
1
- # :flag.usage
1
+ <%= view_marker %>
2
2
  echo " <%= usage_string extended: true %>"
3
3
  printf "<%= help.wrap(76).indent(4).sanitize_for_print %>\n"
4
4
  % if allowed
@@ -1,4 +1,4 @@
1
- # :flag.validations
1
+ <%= view_marker %>
2
2
  % if validate
3
3
  if [[ -n $(validate_<%= validate %> "$2") ]]; then
4
4
  printf "<%= strings[:validation_error] %>\n" "<%= usage_string %>" "$(validate_<%= validate %> "$2")"
@@ -1,4 +1,4 @@
1
- # :script.bash3_bouncer
1
+ <%= view_marker %>
2
2
  if [[ "${BASH_VERSINFO:-0}" -lt 4 ]]; then
3
3
  printf "<%= strings[:unsupported_bash_version] -%>\n"
4
4
  exit 1
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env bash
2
- # This script was generated by bashly (https://github.com/DannyBen/bashly)
2
+ # This script was generated by bashly <%= Bashly::VERSION %> (https://bashly.dannyb.co)
3
3
  # Modifying it manually is not recommended
4
4
 
@@ -1,4 +1,4 @@
1
- # :script.wrapper
1
+ <%= view_marker %>
2
2
  <%= function_name %>() {
3
3
  <%= body.indent 2 %>
4
4
  }
data/lib/bashly.rb CHANGED
@@ -7,6 +7,7 @@ end
7
7
 
8
8
  requires 'bashly/concerns'
9
9
 
10
+ requires 'bashly/extensions'
10
11
  requires 'bashly/settings'
11
12
  requires 'bashly/exceptions'
12
13
  requires 'bashly/refinements'
@@ -14,3 +15,15 @@ requires 'bashly/script/base'
14
15
  requires 'bashly/commands/base'
15
16
  requires 'bashly/libraries/base'
16
17
  requires 'bashly'
18
+
19
+ module Bashly
20
+ class << self
21
+ def env
22
+ ENV['BASHLY_ENV']&.to_sym
23
+ end
24
+
25
+ def production?
26
+ env == :production
27
+ end
28
+ end
29
+ 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.6
4
+ version: 0.7.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: 2022-02-18 00:00:00.000000000 Z
11
+ date: 2022-04-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colsole
@@ -93,6 +93,7 @@ files:
93
93
  - lib/bashly/extensions/array.rb
94
94
  - lib/bashly/extensions/file.rb
95
95
  - lib/bashly/extensions/string.rb
96
+ - lib/bashly/extensions/yaml.rb
96
97
  - lib/bashly/libraries.yml
97
98
  - lib/bashly/libraries/base.rb
98
99
  - lib/bashly/libraries/completions.rb
@@ -191,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
191
192
  - !ruby/object:Gem::Version
192
193
  version: '0'
193
194
  requirements: []
194
- rubygems_version: 3.3.7
195
+ rubygems_version: 3.3.3
195
196
  signing_key:
196
197
  specification_version: 4
197
198
  summary: Bash Command Line Tool Generator