diff --git a/examples/README.md b/examples/README.md index f43edfe4..f82d413b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -58,6 +58,7 @@ Each of these examples demonstrates one aspect or feature of bashly. - [command-paths](command-paths#readme) - configuring nested paths for your source scripts - [command-function](command-function#readme) - configuring custom internal function names - [split-config](split-config#readme) - splitting your `bashly.yml` into several smaller files +- [multiple-lib-dirs](multiple-lib-dirs#readme) - loading libraries from multiple locations ## Bashly library features diff --git a/examples/command-paths/README.md b/examples/command-paths/README.md index 75bf632f..064a81f3 100644 --- a/examples/command-paths/README.md +++ b/examples/command-paths/README.md @@ -79,7 +79,7 @@ commands: # The path to use for command files, relative to source_dir # When set to nil (~), command files will be placed directly under source_dir # When set to any other string, command files will be placed under this -# directory, and each command will get its own subdirectory +# directory, and each command will get its own sub-directory # commands_dir: ~ commands_dir: commands diff --git a/examples/command-paths/settings.yml b/examples/command-paths/settings.yml index 25af44de..28ff92b9 100644 --- a/examples/command-paths/settings.yml +++ b/examples/command-paths/settings.yml @@ -1,7 +1,7 @@ # The path to use for command files, relative to source_dir # When set to nil (~), command files will be placed directly under source_dir # When set to any other string, command files will be placed under this -# directory, and each command will get its own subdirectory +# directory, and each command will get its own sub-directory # commands_dir: ~ commands_dir: commands diff --git a/examples/custom-includes/README.md b/examples/custom-includes/README.md index 41b0b7e7..a9c525ee 100644 --- a/examples/custom-includes/README.md +++ b/examples/custom-includes/README.md @@ -56,7 +56,7 @@ echo "After custom code" # Note that code here should be wrapped inside bash functions, and it is # recommended to have a separate file for each function. # -# Subdirectories will also be scanned for *.sh, so you have no reason not +# Sub-directories will also be scanned for *.sh, so you have no reason not # to organize your code neatly. # sample_function() { diff --git a/examples/custom-includes/src/lib/sample_function.sh b/examples/custom-includes/src/lib/sample_function.sh index b975d7a2..a25cdae5 100644 --- a/examples/custom-includes/src/lib/sample_function.sh +++ b/examples/custom-includes/src/lib/sample_function.sh @@ -5,7 +5,7 @@ # Note that code here should be wrapped inside bash functions, and it is # recommended to have a separate file for each function. # -# Subdirectories will also be scanned for *.sh, so you have no reason not +# Sub-directories will also be scanned for *.sh, so you have no reason not # to organize your code neatly. # sample_function() { diff --git a/examples/multiple-lib-dirs/.gitignore b/examples/multiple-lib-dirs/.gitignore new file mode 100644 index 00000000..8a58e1ae --- /dev/null +++ b/examples/multiple-lib-dirs/.gitignore @@ -0,0 +1 @@ +download \ No newline at end of file diff --git a/examples/multiple-lib-dirs/README.md b/examples/multiple-lib-dirs/README.md new file mode 100644 index 00000000..b95f9cd4 --- /dev/null +++ b/examples/multiple-lib-dirs/README.md @@ -0,0 +1,77 @@ +# Multiple Lib Dirs + +Demonstrates how to include more than one lib directories in the generated +script. + +This example was generated with: + +```bash +$ bashly init --minimal +$ bashly add settings +# ... now edit settings.yml to match the example ... +$ bashly generate +# ... now edit src/root_command.sh to match the example ... +$ bashly generate +``` + + + +----- + +## `bashly.yml` + +````yaml +name: download +help: Sample minimal application without commands +version: 0.1.0 + +args: +- name: source + required: true + help: URL to download from +- name: target + help: "Target filename (default: same as source)" + +flags: +- long: --force + short: -f + help: Overwrite existing files + +examples: +- download example.com +- download example.com ./output -f +```` + +## `settings.yml` + +````yaml +# An array or comma delimited string of additional directories to search for +# bash functions. Any bash script found in any of these directories +# (or sub-directories) will be merged into the final script. +# Note that this is relative to the working directory. +extra_lib_dirs: [common_lib, cloud_lib] + +```` + +## `src/root_command.sh` + +````bash +# Calling library functions +common_function +cloud_function +```` + + +## Output + +### `$ ./download some_source` + +````shell +common_function called +cloud_function called + + +```` + + + diff --git a/examples/multiple-lib-dirs/cloud_lib/cloud_function.sh b/examples/multiple-lib-dirs/cloud_lib/cloud_function.sh new file mode 100644 index 00000000..e0d894e7 --- /dev/null +++ b/examples/multiple-lib-dirs/cloud_lib/cloud_function.sh @@ -0,0 +1,3 @@ +cloud_function() { + echo "cloud_function called" +} diff --git a/examples/multiple-lib-dirs/common_lib/common_function.sh b/examples/multiple-lib-dirs/common_lib/common_function.sh new file mode 100644 index 00000000..20baf385 --- /dev/null +++ b/examples/multiple-lib-dirs/common_lib/common_function.sh @@ -0,0 +1,3 @@ +common_function() { + echo "common_function called" +} diff --git a/examples/multiple-lib-dirs/settings.yml b/examples/multiple-lib-dirs/settings.yml new file mode 100644 index 00000000..4e948649 --- /dev/null +++ b/examples/multiple-lib-dirs/settings.yml @@ -0,0 +1,5 @@ +# An array or comma delimited string of additional directories to search for +# bash functions. Any bash script found in any of these directories +# (or sub-directories) will be merged into the final script. +# Note that this is relative to the working directory. +extra_lib_dirs: [common_lib, cloud_lib] diff --git a/examples/multiple-lib-dirs/src/bashly.yml b/examples/multiple-lib-dirs/src/bashly.yml new file mode 100644 index 00000000..58119ca0 --- /dev/null +++ b/examples/multiple-lib-dirs/src/bashly.yml @@ -0,0 +1,19 @@ +name: download +help: Sample minimal application without commands +version: 0.1.0 + +args: +- name: source + required: true + help: URL to download from +- name: target + help: "Target filename (default: same as source)" + +flags: +- long: --force + short: -f + help: Overwrite existing files + +examples: +- download example.com +- download example.com ./output -f diff --git a/examples/multiple-lib-dirs/src/root_command.sh b/examples/multiple-lib-dirs/src/root_command.sh new file mode 100644 index 00000000..68e529c2 --- /dev/null +++ b/examples/multiple-lib-dirs/src/root_command.sh @@ -0,0 +1,3 @@ +# Calling library functions +common_function +cloud_function \ No newline at end of file diff --git a/examples/multiple-lib-dirs/test.sh b/examples/multiple-lib-dirs/test.sh new file mode 100644 index 00000000..0a1a9484 --- /dev/null +++ b/examples/multiple-lib-dirs/test.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -x + +bashly generate + +### Try Me ### + +./download some_source diff --git a/lib/bashly/libraries/lib/sample_function.sh b/lib/bashly/libraries/lib/sample_function.sh index bbe5656f..9588ecd7 100644 --- a/lib/bashly/libraries/lib/sample_function.sh +++ b/lib/bashly/libraries/lib/sample_function.sh @@ -5,7 +5,7 @@ ## Note that code here should be wrapped inside bash functions, and it is ## recommended to have a separate file for each function. ## -## Subdirectories will also be scanned for *.sh, so you have no reason not +## Sub-directories will also be scanned for *.sh, so you have no reason not ## to organize your code neatly. ## sample_function() { diff --git a/lib/bashly/libraries/settings/settings.yml b/lib/bashly/libraries/settings/settings.yml index 194796a8..5f05de2b 100644 --- a/lib/bashly/libraries/settings/settings.yml +++ b/lib/bashly/libraries/settings/settings.yml @@ -53,10 +53,16 @@ target_dir: . # The path to use for common library files, relative to source_dir lib_dir: lib +# An array or comma delimited string of additional directories to search for +# bash functions. Any bash script found in any of these directories +# (or sub-directories) will be merged into the final script. +# Note that this is relative to the working directory. +extra_lib_dirs: ~ + # The path to use for command files, relative to source_dir # When set to nil (~), command files will be placed directly under source_dir # When set to any other string, command files will be placed under this -# directory, and each command will get its own subdirectory +# directory, and each command will get its own sub-directory commands_dir: ~ # The extension to use when reading/writing partial script snippets diff --git a/lib/bashly/script/command.rb b/lib/bashly/script/command.rb index 7b109c0a..33e06e59 100644 --- a/lib/bashly/script/command.rb +++ b/lib/bashly/script/command.rb @@ -153,7 +153,13 @@ def base_usage_pattern # This is meant to provide the user with the ability to add custom # functions def user_lib - @user_lib ||= Dir["#{Settings.full_lib_dir}/**/*.#{Settings.partials_extension}"] + @user_lib ||= begin + result = Settings.all_lib_dirs.map do |dir| + Dir["#{dir}/**/*.#{Settings.partials_extension}"] + end + + result.flatten + end end # Returns a mixed array of Argument and Flag objects that have validations diff --git a/lib/bashly/settings.rb b/lib/bashly/settings.rb index e63e341b..c07d0014 100644 --- a/lib/bashly/settings.rb +++ b/lib/bashly/settings.rb @@ -15,6 +15,7 @@ class << self :enable_inspect_args, :enable_sourcing, :enable_view_markers, + :extra_lib_dirs, :formatter, :function_names, :lib_dir, @@ -107,6 +108,21 @@ def lib_dir @lib_dir ||= get :lib_dir end + def extra_lib_dirs + @extra_lib_dirs ||= begin + dirs = get :extra_lib_dirs + case dirs + when Array then dirs + when String then dirs.split(',').map(&:strip) + else [] + end + end + end + + def all_lib_dirs + @all_lib_dirs = [full_lib_dir] + extra_lib_dirs + end + def partials_extension @partials_extension ||= get :partials_extension end diff --git a/schemas/settings.json b/schemas/settings.json index 5cddac76..057bab36 100644 --- a/schemas/settings.json +++ b/schemas/settings.json @@ -66,6 +66,37 @@ "minLength": 1, "default": "lib" }, + "extra_lib_dirs": { + "title": "extra lib dirs", + "description": "One or more paths to use for common library files, relative to the working directory.\nMay be provided as an array or a comma delimited string.\nhttps://bashly.dev/usage/settings/#extra_lib_dirs", + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "minLength": 1, + "examples": [ + "common, org_lib", + "lib" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "examples": [ + [ + "common", + "org_lib" + ] + ] + } + ], + "default": null + }, "commands_dir": { "title": "commands dir", "description": "The path to use for command files, relative to source_dir\nhttps://bashly.dev/usage/settings/#commands_dir", diff --git a/spec/approvals/examples/multiple-lib-dirs b/spec/approvals/examples/multiple-lib-dirs new file mode 100644 index 00000000..5ed6a7a4 --- /dev/null +++ b/spec/approvals/examples/multiple-lib-dirs @@ -0,0 +1,8 @@ ++ bashly generate +creating user files in src +skipped src/root_command.sh (exists) +created ./download +run ./download --help to test your bash script ++ ./download some_source +common_function called +cloud_function called diff --git a/spec/bashly/settings_spec.rb b/spec/bashly/settings_spec.rb index 19d6607a..e669b072 100644 --- a/spec/bashly/settings_spec.rb +++ b/spec/bashly/settings_spec.rb @@ -103,6 +103,26 @@ end end + describe '::extra_lib_dirs' do + context 'when set using env var as a comma delimited string' do + before { ENV['BASHLY_EXTRA_LIB_DIRS'] = 'one,two' } + after { ENV['BASHLY_EXTRA_LIB_DIRS'] = nil } + + it 'converts comma delimited string to an array' do + expect(subject.extra_lib_dirs).to match_array %w[one two] + end + end + + context 'when provided as an array' do + let(:config) { Config.new({ 'extra_lib_dirs' => %w[item1 item2] }) } + + it 'returns the array as is' do + allow(subject).to receive(:user_settings).and_return(config) + expect(subject.extra_lib_dirs).to match_array %w[item1 item2] + end + end + end + describe '::production?' do it 'returns false by default' do expect(subject.production?).to be false diff --git a/support/schema/settings.yml b/support/schema/settings.yml index 699dbc3b..d8ef047c 100644 --- a/support/schema/settings.yml +++ b/support/schema/settings.yml @@ -63,6 +63,27 @@ properties: type: string minLength: 1 default: lib + extra_lib_dirs: + title: extra lib dirs + description: |- + One or more paths to use for common library files, relative to the working directory. + May be provided as an array or a comma delimited string. + https://bashly.dev/usage/settings/#extra_lib_dirs + oneOf: + - type: "null" + - type: string + minLength: 1 + examples: + - "common, org_lib" + - "lib" + - type: array + items: + type: string + minLength: 1 + examples: + - [common, org_lib] + default: null + commands_dir: title: commands dir description: |-