bashly 1.0.0 → 1.0.2

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: 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