bashly 1.0.0 → 1.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7382bd9a007cc8e3c9b4451e7f3fdbccd62bf740f66cc76f24b03c0e6ef1abdd
4
- data.tar.gz: 32b2ee626d40efd3f60bab256b08e486b2322b3ef4d429c1de090f0d849f5982
3
+ metadata.gz: 67c0cd0da5f37edbad28313553ac30b878edae7d2255158d940af0deb8aa0a05
4
+ data.tar.gz: 6cf9bac796564e7730f3a55e6022d78a962506b418eb00cdecbe1111f3770ffb
5
5
  SHA512:
6
- metadata.gz: 947f2accba281a365338b86580fe9113ca0a4e5286fa206ee3d11e3816514113f9af80417298b24101c63b8fc84cd95fbca4d6d6abf2be32d95c2a122f092050
7
- data.tar.gz: b70e60017a58d0107e44388dfdf228d3089b39d9e1bb3f650c6b4565905b08604f1a400ed4cb4648cc91846298e8e5320d54f45064f5ecc36dfaacb9e609d8bc
6
+ metadata.gz: 16eb355eb439bfc24457b184dd8255c091bb62dc1646c15e6d2842160f8848595de799cb453e54e43cdf5c2605906ed8f8c49ee21f5afda3999c9224b8d6fbc6
7
+ data.tar.gz: 889722479c576c8b30c3be1081911d0a7f61613664fc146fb4a2867b65d7199a5283464cc59eb40530e2c43f8270fbaa11e7f3a3d851cedc87a71515aef235b8
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <div align='center'>
2
- <img src='logo.svg' width=280>
2
+ <img src='support/img/bashly-logo.svg' width=280>
3
3
 
4
4
  # Bashly - Bash CLI Framework and Generator
5
5
 
@@ -13,7 +13,7 @@ Create feature-rich bash scripts using simple YAML configuration
13
13
 
14
14
  ---
15
15
 
16
- ![demo](demo/cast.gif)
16
+ ![demo](support/demo/cast.gif)
17
17
 
18
18
  </div>
19
19
 
@@ -27,6 +27,11 @@ usually handled by a framework in any other programming language.
27
27
  It is available both as a [ruby gem](https://rubygems.org/gems/bashly) and as
28
28
  a [docker image](https://hub.docker.com/r/dannyben/bashly).
29
29
 
30
+ ## Bashly is Sponsored By
31
+
32
+ <a href="https://rhodecode.com/"><img src='support/img/RhodeCode-logo.png' width=280></a>
33
+
34
+
30
35
  ## Documentation
31
36
 
32
37
  - [Bashly Homepage][docs]
@@ -64,7 +69,7 @@ Bashly is responsible for:
64
69
  - **Bash completions**.
65
70
  - and more.
66
71
 
67
- ## Contributing / Support
72
+ ## Contributing / Support
68
73
 
69
74
  If you experience any issue, have a question or a suggestion, or if you wish
70
75
  to contribute, feel free to [open an issue][issues] or
@@ -10,6 +10,7 @@ module Bashly
10
10
  @strings ||= MessageStrings.new
11
11
  end
12
12
 
13
+ # Outputs a comment that describes the view unless in production mode
13
14
  def view_marker(id = nil)
14
15
  id ||= ":#{caller_locations(1..1).first.path}"
15
16
  "# #{id}" unless Settings.production?
@@ -18,7 +19,7 @@ module Bashly
18
19
  # Reads a file from the userspace (Settings.source_dir) and returns
19
20
  # its contents. If the file is not found, returns a string with a hint.
20
21
  def load_user_file(file, placeholder: true)
21
- path = "#{Settings.source_dir}/#{file}"
22
+ path = user_file_path file
22
23
 
23
24
  content = if File.exist? path
24
25
  File.read(path).remove_front_matter
@@ -31,6 +32,22 @@ module Bashly
31
32
  Settings.production? ? content : "#{view_marker path}\n#{content}"
32
33
  end
33
34
 
35
+ # Returns a path to a file in the user's source_dir. The file argument
36
+ # should either be without exteneion, or with the user's configured
37
+ # partials_extension.
38
+ def user_file_path(file)
39
+ path = "#{Settings.source_dir}/#{file}"
40
+ ext = ".#{Settings.partials_extension}"
41
+ return path if path.end_with? ext
42
+
43
+ "#{path}#{ext}"
44
+ end
45
+
46
+ # Returns true if the user's source file exists
47
+ def user_file_exist?(file)
48
+ File.exist? user_file_path(file)
49
+ end
50
+
34
51
  private
35
52
 
36
53
  def view_path(view)
@@ -35,19 +35,13 @@ module Bashly
35
35
  end
36
36
  end
37
37
 
38
- def assert_hash(key, value, keys: nil, of: nil)
38
+ def assert_hash(key, value, keys: nil)
39
39
  assert value.is_a?(Hash), "#{key} must be a hash"
40
40
 
41
- if keys
42
- invalid_keys = value.keys.map(&:to_sym) - keys
43
- assert invalid_keys.empty?, "#{key} contains invalid options: #{invalid_keys.join ', '}"
44
- end
45
-
46
- return unless of
41
+ return unless keys
47
42
 
48
- value.each do |k, v|
49
- send "assert_#{of}".to_sym, "#{key}.#{k}", v
50
- end
43
+ invalid_keys = value.keys.map(&:to_sym) - keys
44
+ assert invalid_keys.empty?, "#{key} contains invalid options: #{invalid_keys.join ', '}"
51
45
  end
52
46
 
53
47
  def assert_uniq(key, value, array_keys)
@@ -50,13 +50,30 @@ module Bashly
50
50
  when Array
51
51
  assert_array key, value, of: :string
52
52
  when Hash
53
- assert_hash key, value, of: :string
53
+ assert_dependencies_hash key, value
54
54
  else
55
55
  assert [Array, Hash].include?(value.class),
56
56
  "#{key} must be an array or a hash"
57
57
  end
58
58
  end
59
59
 
60
+ def assert_dependencies_hash(key, value)
61
+ value.each do |k, v|
62
+ assert_dependency "#{key}.#{k}", v
63
+ end
64
+ end
65
+
66
+ def assert_dependency(key, value)
67
+ assert [String, Hash, NilClass].include?(value.class),
68
+ "#{key} must be a string, a hash or nil"
69
+
70
+ return unless value.is_a? Hash
71
+
72
+ assert_hash key, value, keys: Script::Dependency.option_keys
73
+ assert_string_or_array "#{key}.command", value['command']
74
+ assert_optional_string "#{key}.help", value['help']
75
+ end
76
+
60
77
  def assert_extensible(key, value)
61
78
  return unless value
62
79
 
@@ -157,8 +174,8 @@ module Bashly
157
174
  assert_extensible "#{key}.extensible", value['extensible']
158
175
  assert_dependencies "#{key}.dependencies", value['dependencies']
159
176
 
160
- assert value['name'].match(/^[a-z0-9_-]+$/),
161
- "#{key}.name must only contain lowercase alphanumeric characters, hyphens and underscores"
177
+ assert value['name'].match(/^[a-z0-9_\-.]+$/),
178
+ "#{key}.name must only contain lowercase alphanumeric characters, hyphens, dots and underscores"
162
179
 
163
180
  refute value['name'].start_with?('-'), "#{key}.name must not start with a hyphen"
164
181
 
@@ -113,10 +113,18 @@ command.dependencies:
113
113
  - docker
114
114
  - curl
115
115
 
116
- # Hash syntax, to provide additional help message
116
+ # Simple hash syntax, to provide additional (optional) help message
117
117
  dependencies:
118
118
  docker: see https://docker.com for installation instructions
119
119
  git: "install by running: sudo apt install git"
120
+ ruby:
121
+
122
+ # Explicit hash syntax, to provide additional help message and
123
+ # look for "one of" a given list of dependencies
124
+ dependencies:
125
+ http_client:
126
+ command: [curl, wget]
127
+ help: Run 'sudo apt install curl' or 'sudo apt install wget'
120
128
 
121
129
  command.environment_variables:
122
130
  help: Define a list of environment variables. See `environment_variable` for reference.
@@ -0,0 +1,7 @@
1
+ ## after hook
2
+ ##
3
+ ## Any code here will be placed inside an `after_hook()` function and called
4
+ ## after running any command.
5
+ ##
6
+ ## You can safely delete this file if you do not need it.
7
+ echo "==[ After Hook Called ]=="
@@ -0,0 +1,8 @@
1
+ ## before hook
2
+ ##
3
+ ## Any code here will be placed inside a `before_hook()` function and called
4
+ ## before running any command (but after processing its arguments).
5
+ ##
6
+ ## You can safely delete this file if you do not need it.
7
+ echo "==[ Before Hook Called ]=="
8
+ inspect_args
@@ -29,6 +29,14 @@ help:
29
29
  help: Add a help command, in addition to the standard --help flag.
30
30
  handler: Bashly::Libraries::Help
31
31
 
32
+ hooks:
33
+ help: Add placeholders for before/after hooks which are executed before/after any command.
34
+ files:
35
+ - source: "hooks/before.sh"
36
+ target: "%{user_source_dir}/before.%{user_ext}"
37
+ - source: "hooks/after.sh"
38
+ target: "%{user_source_dir}/after.%{user_ext}"
39
+
32
40
  lib:
33
41
  help: |-
34
42
  Create the lib directory for any additional user scripts.
@@ -12,16 +12,20 @@
12
12
  # The path containing the bashly source files
13
13
  source_dir: src
14
14
 
15
- # Tha path to bashly.yml
15
+ # The path to bashly.yml
16
16
  config_path: "%{source_dir}/bashly.yml"
17
17
 
18
18
  # The path to use for creating the bash script
19
19
  target_dir: .
20
20
 
21
- # The path to use for upgrading library files, relative to the source dir
21
+ # The path to use for common library files, relative to the source dir
22
22
  lib_dir: lib
23
23
 
24
- # When true, enable bash strict mode (set -euo pipefail)
24
+ # Configure the bash options that will be added to the initialize function:
25
+ # strict: true Bash strict mode (set -euo pipefail)
26
+ # strict: false Only exit on errors (set -e)
27
+ # strict: '' Do not add any 'set' directive
28
+ # strict: <string> Add any other custom 'set' directive
25
29
  strict: false
26
30
 
27
31
  # When true, the generated script will use tab indentation instead of spaces
@@ -33,11 +37,11 @@ tab_indent: false
33
37
  compact_short_flags: true
34
38
 
35
39
  # Set to 'production' or 'development':
36
- # - production generate a smaller script, without file markers
37
- # - development generate with file markers
40
+ # env: production Generate a smaller script, without file markers
41
+ # env: development Generate with file markers
38
42
  env: development
39
43
 
40
- # The extension to use when reading/writing partial script snippets.
44
+ # The extension to use when reading/writing partial script snippets
41
45
  partials_extension: sh
42
46
 
43
47
  # Display various usage elements in color by providing the name of the color
@@ -53,6 +53,7 @@ module Bashly
53
53
  help.empty? ? full_name : "#{full_name} - #{summary}"
54
54
  end
55
55
 
56
+ # Returns an object representing the catch_all configuration
56
57
  def catch_all
57
58
  @catch_all ||= CatchAll.from_config options['catch_all']
58
59
  end
@@ -133,7 +134,16 @@ module Bashly
133
134
  flags.select(&:default)
134
135
  end
135
136
 
136
- # Returns an array of EnvironmentVariables
137
+ # Returns an array of Dependency objects
138
+ def dependencies
139
+ return [] unless options['dependencies']
140
+
141
+ @dependencies ||= options['dependencies'].map do |key, value|
142
+ Dependency.from_config key, value
143
+ end
144
+ end
145
+
146
+ # Returns an array of EnvironmentVariable objects
137
147
  def environment_variables
138
148
  return [] unless options['environment_variables']
139
149
 
@@ -0,0 +1,42 @@
1
+ module Bashly
2
+ module Script
3
+ class Dependency
4
+ attr_reader :label, :commands, :help
5
+
6
+ class << self
7
+ def option_keys
8
+ @option_keys ||= %i[command help]
9
+ end
10
+
11
+ def from_config(key, value)
12
+ options = case value
13
+ when nil
14
+ { label: key, commands: key }
15
+ when String
16
+ { label: key, commands: key, help: value }
17
+ when Hash
18
+ { label: key, commands: value['command'], help: value['help'] }
19
+ else
20
+ {}
21
+ end
22
+
23
+ new(**options)
24
+ end
25
+ end
26
+
27
+ def initialize(label: nil, commands: nil, help: nil)
28
+ @label = label
29
+ @commands = commands.is_a?(String) ? [commands] : commands
30
+ @help = help&.empty? ? nil : help
31
+ end
32
+
33
+ def multi?
34
+ @multi ||= commands.size > 1
35
+ end
36
+
37
+ def name
38
+ @name ||= multi? ? "#{label} (#{commands.join '/'})" : label
39
+ end
40
+ end
41
+ end
42
+ end
@@ -55,6 +55,16 @@ module Bashly
55
55
  @strict ||= get :strict
56
56
  end
57
57
 
58
+ def strict_string
59
+ if Settings.strict.is_a? String
60
+ Settings.strict
61
+ elsif Settings.strict
62
+ 'set -euo pipefail'
63
+ else
64
+ 'set -e'
65
+ end
66
+ end
67
+
58
68
  def tab_indent
59
69
  @tab_indent ||= get :tab_indent
60
70
  end
@@ -1,3 +1,3 @@
1
1
  module Bashly
2
- VERSION = '1.0.0'
2
+ VERSION = '1.0.2'
3
3
  end
@@ -1,11 +1,13 @@
1
- if dependencies
1
+ if dependencies.any?
2
2
  = view_marker
3
3
 
4
- dependencies.each do |dependency, message|
5
- > if ! command -v {{ dependency }} >/dev/null 2>&1; then
6
- > printf "{{ strings[:missing_dependency] % { dependency: dependency } }}\n" >&2
7
- if message and !message.empty?
8
- > printf "%s\n" "{{ message }}" >&2
4
+ dependencies.each do |dependency|
5
+ > if command -v {{ dependency.commands.join(' ') }} >/dev/null 2>&1; then
6
+ > deps['{{ dependency.label }}']="$(command -v {{ dependency.commands.join(' ') }} | head -n1)"
7
+ > else
8
+ > printf "{{ strings[:missing_dependency] % { dependency: dependency.name } }}\n" >&2
9
+ if dependency.help
10
+ > printf "%s\n" "{{ dependency.help }}" >&2
9
11
  end
10
12
  > exit 1
11
13
  > fi
@@ -3,14 +3,14 @@
3
3
  > initialize() {
4
4
  > version="<%= version %>"
5
5
  > long_usage=''
6
- > {{ Settings.strict ? "set -euo pipefail" : "set -e" }}
6
+ > {{ Settings.strict_string }}
7
7
  >
8
8
 
9
9
  if root_command?
10
10
  = render(:environment_variables_default).indent 2
11
11
  end
12
12
 
13
- = load_user_file("initialize.sh", placeholder: false).indent 2
13
+ = load_user_file("initialize", placeholder: false).indent 2
14
14
 
15
15
  > }
16
16
  >
@@ -1,8 +1,8 @@
1
1
  = view_marker
2
2
 
3
3
  > inspect_args() {
4
- > readarray -t sorted_keys < <(printf '%s\n' "${!args[@]}" | sort)
5
4
  > if ((${#args[@]})); then
5
+ > readarray -t sorted_keys < <(printf '%s\n' "${!args[@]}" | sort)
6
6
  > echo args:
7
7
  > for k in "${sorted_keys[@]}"; do echo "- \${args[$k]} = ${args[$k]}"; done
8
8
  > else
@@ -17,5 +17,13 @@
17
17
  > echo "- \${other_args[$i]} = ${other_args[$i]}"
18
18
  > done
19
19
  > fi
20
+ >
21
+ > if ((${#deps[@]})); then
22
+ > readarray -t sorted_keys < <(printf '%s\n' "${!deps[@]}" | sort)
23
+ > echo
24
+ > echo deps:
25
+ > for k in "${sorted_keys[@]}"; do echo "- \${deps[$k]} = ${deps[$k]}"; done
26
+ > fi
27
+ >
20
28
  > }
21
29
  >
@@ -8,6 +8,7 @@
8
8
  = render :user_lib if user_lib.any?
9
9
  = render :command_functions
10
10
  = render :parse_requirements
11
+ = render :user_hooks
11
12
  = render :initialize
12
13
  = render :run
13
14
 
@@ -2,30 +2,31 @@
2
2
 
3
3
  > run() {
4
4
  > declare -A args=()
5
+ > declare -A deps=()
5
6
  > declare -a other_args=()
6
7
  > declare -a input=()
7
8
  > normalize_input "$@"
8
9
  > parse_requirements "${input[@]}"
10
+ if user_file_exist?('before')
11
+ > before_hook
12
+ end
9
13
  >
10
14
  > case "$action" in
11
15
 
12
16
  deep_commands.each do |command|
13
- > "{{ command.action_name }}")
14
- > if [[ ${args['--help']:-} ]]; then
15
- > long_usage=yes
16
- > {{ command.function_name }}_usage
17
- > else
18
- > {{ command.function_name }}_command
19
- > fi
20
- > ;;
21
- >
17
+ > "{{ command.action_name }}") {{ command.function_name }}_command ;;
22
18
  end
23
19
 
24
20
  if commands.empty?
25
- > "root")
26
- > root_command
27
- > ;;
21
+ > "root") root_command ;;
28
22
  end
29
- >
30
23
  > esac
24
+
25
+ if user_file_exist?('after')
26
+ >
27
+ > after_hook
28
+ end
29
+
31
30
  > }
31
+
32
+
@@ -0,0 +1,17 @@
1
+ if user_file_exist?('before') || user_file_exist?('after')
2
+ = view_marker
3
+ end
4
+
5
+ if user_file_exist?('before')
6
+ > before_hook() {
7
+ = load_user_file("before").indent 2
8
+ > }
9
+ >
10
+ end
11
+
12
+ if user_file_exist?('after')
13
+ > after_hook() {
14
+ = load_user_file("after").indent 2
15
+ > }
16
+ >
17
+ 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: 1.0.0
4
+ version: 1.0.2
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: 2023-02-16 00:00:00.000000000 Z
11
+ date: 2023-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colsole
@@ -155,6 +155,8 @@ files:
155
155
  - lib/bashly/libraries/config/config.sh
156
156
  - lib/bashly/libraries/help/help.rb
157
157
  - lib/bashly/libraries/help/help_command.sh
158
+ - lib/bashly/libraries/hooks/after.sh
159
+ - lib/bashly/libraries/hooks/before.sh
158
160
  - lib/bashly/libraries/lib/sample_function.sh
159
161
  - lib/bashly/libraries/libraries.yml
160
162
  - lib/bashly/libraries/settings/settings.yml
@@ -174,6 +176,7 @@ files:
174
176
  - lib/bashly/script/base.rb
175
177
  - lib/bashly/script/catch_all.rb
176
178
  - lib/bashly/script/command.rb
179
+ - lib/bashly/script/dependency.rb
177
180
  - lib/bashly/script/environment_variable.rb
178
181
  - lib/bashly/script/flag.rb
179
182
  - lib/bashly/script/wrapper.rb
@@ -221,6 +224,7 @@ files:
221
224
  - lib/bashly/views/command/usage_fixed_flags.gtx
222
225
  - lib/bashly/views/command/usage_flags.gtx
223
226
  - lib/bashly/views/command/user_filter.gtx
227
+ - lib/bashly/views/command/user_hooks.gtx
224
228
  - lib/bashly/views/command/user_lib.gtx
225
229
  - lib/bashly/views/command/version_command.gtx
226
230
  - lib/bashly/views/command/whitelist_filter.gtx
@@ -258,7 +262,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
258
262
  - !ruby/object:Gem::Version
259
263
  version: '0'
260
264
  requirements: []
261
- rubygems_version: 3.4.6
265
+ rubygems_version: 3.4.8
262
266
  signing_key:
263
267
  specification_version: 4
264
268
  summary: Bash Command Line Tool Generator