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 +4 -4
- data/README.md +8 -3
- data/lib/bashly/concerns/renderable.rb +18 -1
- data/lib/bashly/concerns/validation_helpers.rb +4 -10
- data/lib/bashly/config_validator.rb +20 -3
- data/lib/bashly/docs/command.yml +9 -1
- data/lib/bashly/libraries/hooks/after.sh +7 -0
- data/lib/bashly/libraries/hooks/before.sh +8 -0
- data/lib/bashly/libraries/libraries.yml +8 -0
- data/lib/bashly/libraries/settings/settings.yml +10 -6
- data/lib/bashly/script/command.rb +11 -1
- data/lib/bashly/script/dependency.rb +42 -0
- data/lib/bashly/settings.rb +10 -0
- data/lib/bashly/version.rb +1 -1
- data/lib/bashly/views/command/dependencies_filter.gtx +8 -6
- data/lib/bashly/views/command/initialize.gtx +2 -2
- data/lib/bashly/views/command/inspect_args.gtx +9 -1
- data/lib/bashly/views/command/master_script.gtx +1 -0
- data/lib/bashly/views/command/run.gtx +14 -13
- data/lib/bashly/views/command/user_hooks.gtx +17 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67c0cd0da5f37edbad28313553ac30b878edae7d2255158d940af0deb8aa0a05
|
4
|
+
data.tar.gz: 6cf9bac796564e7730f3a55e6022d78a962506b418eb00cdecbe1111f3770ffb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-

|
16
|
+

|
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 =
|
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
|
38
|
+
def assert_hash(key, value, keys: nil)
|
39
39
|
assert value.is_a?(Hash), "#{key} must be a hash"
|
40
40
|
|
41
|
-
|
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.
|
49
|
-
|
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
|
-
|
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
|
|
data/lib/bashly/docs/command.yml
CHANGED
@@ -113,10 +113,18 @@ command.dependencies:
|
|
113
113
|
- docker
|
114
114
|
- curl
|
115
115
|
|
116
|
-
#
|
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,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
|
-
#
|
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
|
21
|
+
# The path to use for common library files, relative to the source dir
|
22
22
|
lib_dir: lib
|
23
23
|
|
24
|
-
#
|
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
|
-
#
|
37
|
-
#
|
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
|
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
|
data/lib/bashly/settings.rb
CHANGED
@@ -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
|
data/lib/bashly/version.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
if dependencies
|
1
|
+
if dependencies.any?
|
2
2
|
= view_marker
|
3
3
|
|
4
|
-
dependencies.each do |dependency
|
5
|
-
> if
|
6
|
-
>
|
7
|
-
|
8
|
-
|
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.
|
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
|
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
|
>
|
@@ -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.
|
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-
|
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.
|
265
|
+
rubygems_version: 3.4.8
|
262
266
|
signing_key:
|
263
267
|
specification_version: 4
|
264
268
|
summary: Bash Command Line Tool Generator
|