bashly 0.4.1 → 0.5.0

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: 7e2d8e44019822a048ede2fadd9da870df078afad2931470748912b25acfb93f
4
- data.tar.gz: 84738b8835be3af778b0df351154c2a73c966ee906317f61534c8100a17da9f8
3
+ metadata.gz: 6b573ae52d9c93016675adc209eeb011d3f6897791b0e159e25af5a76e7290d6
4
+ data.tar.gz: 69b99fb9b6729dd21d748688c11041130f3e35852212d148afe8af715b13804f
5
5
  SHA512:
6
- metadata.gz: 653e9a5a685d321837472cb2522f93aeabc0ed0de479f97ca35dcbb1d08e2bd997d50e17653c464ce201388f6b21ac59fa4b9d28a01beed5d5119beb8b278e1b
7
- data.tar.gz: 79c111478814fb30246a214511946331c7a581196187c41ef6bc249be3858123786bba3596484b27d493288714fb9d257fc18e7b4432b0f77effe8ed7baa740b
6
+ metadata.gz: f624d54df411325e3b4b6c8446a56d6e6bbd22e9caa2310c13d4c9d254920f84db1f19684355255374cbfa41653020a9f9d848acfa97afb88d76e448928f00d7
7
+ data.tar.gz: 1a98f13669d2de4a9b092afba7fdaebff16c0647a41ccf1696b6abdcda114661e6fb9995cfba1e0da348e30c79a9ae0777078bff3fc8a82cdcfd650c152c528b
data/README.md CHANGED
@@ -22,15 +22,16 @@ Create beautiful bash scripts from simple YAML configuration
22
22
  - [Prerequisites](#prerequisites)
23
23
  - [What is Bashly](#what-is-bashly)
24
24
  - [Usage](#usage)
25
- - [Using the input arguemnts in your code](#using-the-input-arguemnts-in-your-code)
25
+ - [Using the input arguments in your code](#using-the-input-arguments-in-your-code)
26
26
  - [Examples](#examples)
27
- - [Sample configuraiton for a script without commands](#sample-configuraiton-for-a-script-without-commands)
28
- - [Sample configuraiton for a script with commands](#sample-configuraiton-for-a-script-with-commands)
27
+ - [Sample configuration for a script without commands](#sample-configuration-for-a-script-without-commands)
28
+ - [Sample configuration for a script with commands](#sample-configuration-for-a-script-with-commands)
29
29
  - [Configuration Reference](#configuration-reference)
30
30
  - [Command options](#command-options)
31
31
  - [Argument options](#argument-options)
32
32
  - [Flag options](#flag-options)
33
33
  - [Environment Variable options](#environment-variable-options)
34
+ - [Extensible Commands](#extensible-commands)
34
35
  - [Real World Examples](#real-world-examples)
35
36
  - [Contributing / Support](#contributing--support)
36
37
 
@@ -113,7 +114,7 @@ Finally, edit the files in the `src` folder. Each of your script's commands
113
114
  get their own file. Once you edit, run `bashly generate` again to merge the
114
115
  content from your functions back into the script.
115
116
 
116
- ### Using the input arguemnts in your code
117
+ ### Using the input arguments in your code
117
118
 
118
119
  In order to access the parsed arguments in any of your partial scripts, you
119
120
  may simply access the `$args` associative array.
@@ -163,13 +164,13 @@ This is detected automatically by the contents of the configuration: If it
163
164
  contains a `commands` definition, it will generate a script with commands.
164
165
 
165
166
 
166
- ### Sample configuraiton for a script without commands
167
+ ### Sample configuration for a script without commands
167
168
 
168
169
  - Generate this script by running `bashly generate --minimal`
169
170
  - [See the initial sample bashly.yml file](examples/minimal/src/bashly.yml)
170
171
  - [See the generated bash script](examples/minimal/download)
171
172
 
172
- ### Sample configuraiton for a script with commands
173
+ ### Sample configuration for a script with commands
173
174
 
174
175
  - Generate this script by running `bashly generate`
175
176
  - [See the initial sample bashly.yml file](examples/commands/src/bashly.yml)
@@ -193,22 +194,23 @@ The `bashly.yml` configuration file consists of these types:
193
194
 
194
195
  ### Command options
195
196
 
196
- Unless otherwise specified, these definitiona can be used for both the root
197
+ Unless otherwise specified, these definitions can be used for both the root
197
198
  command and subcommands (under the `commands` definition).
198
199
 
199
-
200
200
  Option | Description
201
201
  -----------|-------------
202
202
  `name` | The name of the script or subcommand.
203
203
  `short` | An additional, optional pattern - usually used to denote a one letter variation of the command name. You can add `*` as a suffix, to denote a "starts with" pattern - for example `short: m*`. *Applicable only in subcommands*.
204
204
  `help` | The header text to display when using `--help`. This option can have multiple lines. In this case, the first line will be used as summary wherever appropriate.
205
205
  `version` | The string to display when using `--version`. *Applicable only in the main command*.
206
- `default` | Setting this to `yes` on any command, will make unrecognized command line arguments to be passed to this command. *Applicable only in subcommands*.
206
+ `default` | Setting this to `true` on any command, will cause any unrecognized command line to be passed to this command. *Applicable only in subcommands*.
207
+ `extensible` | Specify that this command can be [externally extended](#extensible-commands).
207
208
  `examples` | Specify an array of examples to show when using `--help`. Each example can have multiple lines.
208
- `environment_variables` | Specify an array of environment variables needed by your script.
209
- `commands` | Specify the array of commands. Each command will have its own args and flags. Note: if `commands` is provided, you cannot specify flags or args at the same level.
210
- `args` | Specify the array of positional arguments this script needs.
211
- `flags` | Specify the array of option flags this script needs.
209
+ `environment_variables` | Specify an array of [environment variables](#environment-variable-options) needed by your script.
210
+ `commands` | Specify the array of [commands](#command-options). Each command will have its own args and flags. Note: if `commands` is provided, you cannot specify flags or args at the same level.
211
+ `args` | Specify the array of [positional arguments](#argument-options) this script needs.
212
+ `flags` | Specify the array of option [flags](#flag-options) this script needs.
213
+ `catch_all` | Specify that this command should allow for additional arbitrary arguments or flags. It can be set in one of three ways:<br>- Set to `true` to just enable it.<br>- Set to a string, to use this string in the usage help text.<br>- Set to a hash containing `label` and `help` keys, to show a detailed help for it when running with `--help`.
212
214
  `dependencies` | Specify an array of any required external dependencies (commands). The script execution will be halted with a friendly error unless all dependency commands exist.
213
215
  `group` | In case you have many commands, use this option to specify a caption to display before this command. This option is purely for display purposes, and needs to be specified only for the first command in each group.
214
216
 
@@ -223,6 +225,7 @@ bash function.
223
225
  `help` | The message to display when using `--help`. Can have multiple lines.
224
226
  `required` | Specify if this argument is required. Note that once you define an optional argument (without required: true) then you cannot define required arguments after it.
225
227
  `default` | The value to use in case it is not provided by the user. Implies that this argument is optional.
228
+ `allowed` | Limit the allowed values by providing an array.
226
229
 
227
230
  ### Flag options
228
231
 
@@ -238,6 +241,7 @@ short form).
238
241
  `arg` | If the flag requires an argument, specify its name here.
239
242
  `required` | Specify if this flag is required.
240
243
  `default` | The value to use in case it is not provided by the user. Implies that this flag is optional, and only makes sense when the flag has an argument.
244
+ `allowed` | For flags with an argument, you can limit the allowed values by providing an array.
241
245
 
242
246
  #### Special handling for -v and -h
243
247
 
@@ -260,6 +264,82 @@ set.
260
264
  `required` | Specify if this variable is required.
261
265
 
262
266
 
267
+ ## Extensible Commands
268
+
269
+ You may configure your generated bash script to delegate any unknown command
270
+ to an external executable, by setting the `extensible` option to either `true`,
271
+ or to a different external command.
272
+
273
+ This is similar to how `git` works. When you execute `git whatever`, the `git`
274
+ command will look for a file named `git-whatever` in the path, and execute it.
275
+
276
+ Note that this option cannot be specified together with the `default` option,
277
+ since both specify a handler for unknown commands.
278
+
279
+ Bashly supports two operation modes.
280
+
281
+ ### Extension Mode (`extensible: true`)
282
+
283
+ By setting `extensible` to `true`, a specially named executable will be called
284
+ when an unknown command is called by the user.
285
+
286
+ Given this `bashly.yml` configuration:
287
+
288
+ ```yaml
289
+ name: myscript
290
+ help: Example
291
+ version: 0.1.0
292
+ extensible: true
293
+
294
+ commands:
295
+ - name: upload
296
+ help: Upload a file
297
+ ```
298
+
299
+ And this user command:
300
+
301
+ ```
302
+ $ myscript something
303
+
304
+ ```
305
+
306
+ The generated script will look for an executable named `myscript-something`
307
+ in the path. If found, it will be called.
308
+
309
+ See the [extensible example](examples/extensible).
310
+
311
+
312
+ ### Delegate Mode (`extensible: <executable name>`)
313
+
314
+ By setting `extensible` to any string, unknown command calls by the user will
315
+ be delegated to the executable with that name.
316
+
317
+ Given this `bashly.yml` configuration:
318
+
319
+ ```yaml
320
+ name: mygit
321
+ help: Example
322
+ version: 0.1.0
323
+ extensible: git
324
+
325
+ commands:
326
+ - name: push
327
+ help: Push to my repository
328
+ ```
329
+
330
+ And this user command:
331
+
332
+ ```
333
+ $ mygit status
334
+
335
+ ```
336
+
337
+ The generated script will execute `git status`.
338
+
339
+ See the [extensible-delegate example](examples/extensible-delegate).
340
+
341
+
342
+
263
343
  ## Real World Examples
264
344
 
265
345
  - [Rush][rush] - a Personal Package Manager
@@ -272,9 +352,9 @@ set.
272
352
  If you experience any issue, have a question or a suggestion, or if you wish
273
353
  to contribute, feel free to [open an issue][issues].
274
354
 
355
+
356
+
275
357
  [issues]: https://github.com/DannyBen/bashly/issues
276
358
  [rush]: https://github.com/DannyBen/rush-cli
277
359
  [alf]: https://github.com/DannyBen/alf
278
360
  [git-changelog]: https://github.com/DannyBen/git-changelog
279
-
280
-
@@ -16,7 +16,6 @@ class String
16
16
  strip!
17
17
  split("\n").collect! do |line|
18
18
  if line.length > length
19
- line.gsub!(/([^\s]{#{length}})([^\s$])/, "\\1 \\2")
20
19
  line.gsub(/(.{1,#{length}})(\s+|$)/, "\\1\n").rstrip
21
20
  else
22
21
  line
@@ -6,12 +6,15 @@ module Bashly
6
6
  attr_reader :options
7
7
 
8
8
  OPTION_KEYS = %i[
9
+ allowed
9
10
  arg
11
+ catch_all
12
+ default
10
13
  dependencies
11
14
  description
12
- default
13
15
  environment_variables
14
16
  examples
17
+ extensible
15
18
  flags
16
19
  group
17
20
  help
@@ -28,6 +28,30 @@ module Bashly
28
28
  help ? "#{full_name} - #{summary}" : full_name
29
29
  end
30
30
 
31
+ # Returns a label for the catch_all directive
32
+ def catch_all_label
33
+ return nil unless catch_all
34
+
35
+ if catch_all.is_a? String
36
+ "#{catch_all.upcase}..."
37
+ elsif catch_all.is_a?(Hash) and catch_all['label'].is_a?(String)
38
+ "#{catch_all['label'].upcase}..."
39
+ else
40
+ "..."
41
+ end
42
+ end
43
+
44
+ # Returns a used defined help string for the catch_all directive
45
+ def catch_all_help
46
+ return nil unless catch_all
47
+
48
+ if catch_all.is_a?(Hash) and catch_all['help'].is_a?(String)
49
+ catch_all['help']
50
+ else
51
+ nil
52
+ end
53
+ end
54
+
31
55
  # Returns only the names of the Commands
32
56
  def command_names
33
57
  commands.map &:name
@@ -159,6 +183,7 @@ module Bashly
159
183
  result << arg.usage_string
160
184
  end
161
185
  result << "[options]" unless flags.empty?
186
+ result << "[#{catch_all_label}]" if catch_all
162
187
  result.join " "
163
188
  end
164
189
 
@@ -175,6 +200,16 @@ module Bashly
175
200
  verify_commands if commands.any?
176
201
  end
177
202
 
203
+ # Returns an array of all the args with a whitelist
204
+ def whitelisted_args
205
+ args.select &:allowed
206
+ end
207
+
208
+ # Returns an array of all the flags with a whitelist arg
209
+ def whitelisted_flags
210
+ flags.select &:allowed
211
+ end
212
+
178
213
  private
179
214
 
180
215
  def verify_commands
@@ -15,6 +15,7 @@ command_shortcut: "Shortcut: %{short}"
15
15
  default_command_summary: "%{summary} (default)"
16
16
  required: "(required)"
17
17
  default: "Default: %{value}"
18
+ allowed: "Allowed: %{values}"
18
19
 
19
20
  # Fixed flags help text
20
21
  help_flag_text: Show this help
@@ -28,3 +29,5 @@ missing_required_argument: "missing required argument: %{arg}\\nusage: %{usage}"
28
29
  missing_required_flag: "missing required flag: %{usage}"
29
30
  missing_required_environment_variable: "missing required environment variable: %{var}"
30
31
  missing_dependency: "missing dependency: %{dependency}"
32
+ disallowed_flag: "%{name} must be one of: %{allowed}"
33
+ disallowed_argument: "%{name} must be one of: %{allowed}"
@@ -1,3 +1,3 @@
1
1
  module Bashly
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -1,6 +1,9 @@
1
1
  # :argument.usage
2
2
  echo " <%= name.upcase %>"
3
3
  printf "<%= help.wrap(76).indent(4).sanitize_for_print %>\n"
4
+ <%- if allowed -%>
5
+ printf " <%= strings[:allowed] % { values: allowed.join(', ') } -%>\n"
6
+ <%- end -%>
4
7
  <%- if default -%>
5
8
  printf " <%= strings[:default] % { value: default } -%>\n"
6
9
  <%- end -%>
@@ -0,0 +1,45 @@
1
+ # :command.command_fallback
2
+ <%- if default_command -%>
3
+ "" )
4
+ <%= function_name %>_usage
5
+ exit 1
6
+ ;;
7
+
8
+ * )
9
+ action="<%= default_command.name %>"
10
+ <%= default_command.function_name %>_parse_requirements "$@"
11
+ shift $#
12
+ ;;
13
+ <%- elsif extensible.is_a? String -%>
14
+ "" )
15
+ <%= function_name %>_usage
16
+ exit 1
17
+ ;;
18
+
19
+ * )
20
+ if [[ -x "$(command -v "<%= extensible %>")" ]]; then
21
+ exec <%= extensible %> "$@"
22
+ else
23
+ <%= function_name %>_usage
24
+ exit 1
25
+ fi
26
+ <%- elsif extensible -%>
27
+ "" )
28
+ <%= function_name %>_usage
29
+ exit 1
30
+ ;;
31
+
32
+ * )
33
+ if [[ -x "$(command -v "<%= function_name %>-$action")" ]]; then
34
+ shift
35
+ exec "<%= function_name %>-$action" "$@"
36
+ else
37
+ <%= function_name %>_usage
38
+ exit 1
39
+ fi
40
+ <%- else -%>
41
+ * )
42
+ <%= function_name %>_usage
43
+ exit 1
44
+ ;;
45
+ <%- end -%>
@@ -15,25 +15,7 @@ case $action in
15
15
  ;;
16
16
 
17
17
  <%- end -%>
18
- <%- if default_command -%>
19
- "" )
20
- <%= function_name %>_usage
21
- exit 1
22
- ;;
23
-
24
- * )
25
- action="<%= default_command.name %>"
26
- <%= default_command.function_name %>_parse_requirements "$@"
27
- shift $#
28
- ;;
29
-
30
- <%- else -%>
31
- * )
32
- <%= function_name %>_usage
33
- exit 1
34
- ;;
35
-
36
- <%- end -%>
18
+ <%= render :command_fallback %>
37
19
  esac
38
20
  <%- else -%>
39
21
  action="<%= action_name %>"
@@ -7,4 +7,13 @@ inspect_args() {
7
7
  else
8
8
  echo args: none
9
9
  fi
10
+
11
+ if (( ${#other_args[@]} )); then
12
+ echo
13
+ echo other_args:
14
+ echo "- \${other_args[*]} = ${other_args[*]}"
15
+ for i in "${!other_args[@]}"; do
16
+ echo "- \${other_args[$i]} = ${other_args[$i]}"
17
+ done
18
+ fi
10
19
  }
@@ -12,6 +12,7 @@ parse_requirements() {
12
12
  <%= render(:required_flags_filter).indent 2 %>
13
13
  <%= render(:parse_requirements_while).indent 2 %>
14
14
  <%= render(:default_assignments).indent 2 %>
15
+ <%= render(:whitelist_filter).indent 2 %>
15
16
  }
16
17
 
17
18
  <%- commands.each do |command| %>
@@ -8,9 +8,17 @@
8
8
  <%- condition = "elif" -%>
9
9
  <%- end -%>
10
10
  else
11
+ <%- if catch_all -%>
12
+ other_args+=("$1")
13
+ shift
14
+ <%- else -%>
11
15
  printf "<%= strings[:invalid_argument] %>\n" "$key"
12
16
  exit 1
17
+ <%- end -%>
13
18
  fi
19
+ <%- elsif catch_all -%>
20
+ other_args+=("$1")
21
+ shift
14
22
  <%- else -%>
15
23
  printf "<%= strings[:invalid_argument] %>\n" "$key"
16
24
  exit 1
@@ -8,9 +8,15 @@ while [[ $# -gt 0 ]]; do
8
8
  <%- end -%>
9
9
 
10
10
  -* )
11
+ <%- if catch_all -%>
12
+ other_args+=("$1")
13
+ shift
14
+ ;;
15
+ <%- else -%>
11
16
  printf "<%= strings[:invalid_flag] %>\n" "$key"
12
17
  exit 1
13
18
  ;;
19
+ <%- end -%>
14
20
 
15
21
  * )
16
22
  <%= render(:parse_requirements_case).indent 4 %>
@@ -1,6 +1,7 @@
1
1
  # :command.run
2
2
  run() {
3
3
  declare -A args
4
+ declare -a other_args
4
5
  parse_requirements "$@"
5
6
 
6
7
  <%- condition = "if" -%>
@@ -14,12 +15,7 @@ run() {
14
15
  fi
15
16
  <% condition = "elif" %>
16
17
  <%- end -%>
17
- <%= condition %> [[ ${args[--version]} ]]; then
18
- version_command
19
- elif [[ ${args[--help]} ]]; then
20
- long_usage=yes
21
- <%= name %>_usage
22
- elif [[ $action == "root" ]]; then
18
+ <%= condition %> [[ $action == "root" ]]; then
23
19
  root_command
24
20
  fi
25
21
  }
@@ -37,7 +37,7 @@
37
37
  printf "<%= strings[:options] %>\n"
38
38
  <%= render(:usage_fixed_flags).indent 4 %>
39
39
  <%= render(:usage_flags).indent 4 if flags.any? %>
40
- <%= render(:usage_args).indent 4 if args.any? %>
40
+ <%= render(:usage_args).indent 4 if args.any? or catch_all_help %>
41
41
  <%= render(:usage_environment_variables).indent 4 if environment_variables.any? %>
42
42
  <%= render(:usage_examples).indent 4 if examples %>
43
43
 
@@ -1,6 +1,14 @@
1
1
  # :command.usage_args
2
2
  printf "<%= strings[:arguments] %>\n"
3
+ <%- if args.any? -%>
3
4
 
4
5
  <%- args.each do |arg| -%>
5
6
  <%= arg.render(:usage) %>
6
7
  <%- end -%>
8
+ <%- end -%>
9
+ <%- if catch_all_help -%>
10
+
11
+ echo " <%= catch_all_label %>"
12
+ printf "<%= catch_all_help.wrap(76).indent(4).sanitize_for_print %>\n"
13
+ echo
14
+ <%- end -%>
@@ -0,0 +1,13 @@
1
+ # :command.whitelist_filter
2
+ <%- whitelisted_args.each do |arg| -%>
3
+ if [[ ! ${args[<%= arg.name %>]} =~ ^(<%= arg.allowed.join '|' %>)$ ]]; then
4
+ printf "%s\n" "<%= strings[:disallowed_argument] % { name: arg.name, allowed: arg.allowed.join(', ') } %>"
5
+ exit 1
6
+ fi
7
+ <%- end -%>
8
+ <%- whitelisted_flags.each do |flag| -%>
9
+ if [[ ! ${args[<%= flag.name %>]} =~ ^(<%= flag.allowed.join '|' %>)$ ]]; then
10
+ printf "%s\n" "<%= strings[:disallowed_flag] % { name: flag.name, allowed: flag.allowed.join(', ') } %>"
11
+ exit 1
12
+ fi
13
+ <%- end -%>
@@ -1,6 +1,9 @@
1
1
  # :flag.usage
2
2
  echo " <%= usage_string extended: true %>"
3
3
  printf "<%= help.wrap(76).indent(4).sanitize_for_print %>\n"
4
+ <%- if allowed -%>
5
+ printf " <%= strings[:allowed] % { values: allowed.join(', ') } -%>\n"
6
+ <%- end -%>
4
7
  <%- if default -%>
5
8
  printf " <%= strings[:default] % { value: default } -%>\n"
6
9
  <%- 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.4.1
4
+ version: 0.5.0
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: 2021-02-20 00:00:00.000000000 Z
11
+ date: 2021-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colsole
@@ -92,6 +92,7 @@ files:
92
92
  - lib/bashly/templates/strings.yml
93
93
  - lib/bashly/version.rb
94
94
  - lib/bashly/views/argument/usage.erb
95
+ - lib/bashly/views/command/command_fallback.erb
95
96
  - lib/bashly/views/command/command_filter.erb
96
97
  - lib/bashly/views/command/command_functions.erb
97
98
  - lib/bashly/views/command/default_assignments.erb
@@ -121,6 +122,7 @@ files:
121
122
  - lib/bashly/views/command/usage_flags.erb
122
123
  - lib/bashly/views/command/user_lib.erb
123
124
  - lib/bashly/views/command/version_command.erb
125
+ - lib/bashly/views/command/whitelist_filter.erb
124
126
  - lib/bashly/views/environment_variable/usage.erb
125
127
  - lib/bashly/views/flag/case.erb
126
128
  - lib/bashly/views/flag/usage.erb
@@ -145,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
147
  - !ruby/object:Gem::Version
146
148
  version: '0'
147
149
  requirements: []
148
- rubygems_version: 3.2.3
150
+ rubygems_version: 3.2.16
149
151
  signing_key:
150
152
  specification_version: 4
151
153
  summary: Bash Command Line Tool Generator