bashly 0.1.0
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 +7 -0
- data/README.md +48 -0
- data/bin/bashly +17 -0
- data/lib/bashly.rb +12 -0
- data/lib/bashly/cli.rb +19 -0
- data/lib/bashly/commands/generate.rb +67 -0
- data/lib/bashly/commands/init.rb +53 -0
- data/lib/bashly/commands/preview.rb +20 -0
- data/lib/bashly/concerns/renderable.rb +26 -0
- data/lib/bashly/config.rb +17 -0
- data/lib/bashly/exceptions.rb +6 -0
- data/lib/bashly/extensions/array.rb +7 -0
- data/lib/bashly/extensions/string.rb +20 -0
- data/lib/bashly/models/argument.rb +18 -0
- data/lib/bashly/models/base.rb +38 -0
- data/lib/bashly/models/command.rb +104 -0
- data/lib/bashly/models/flag.rb +31 -0
- data/lib/bashly/settings.rb +15 -0
- data/lib/bashly/templates/bashly.yml +39 -0
- data/lib/bashly/templates/minimal.yml +16 -0
- data/lib/bashly/version.rb +3 -0
- data/lib/bashly/views/argument/usage.erb +4 -0
- data/lib/bashly/views/command/command_filter.erb +25 -0
- data/lib/bashly/views/command/command_functions.erb +4 -0
- data/lib/bashly/views/command/default_root_script.erb +3 -0
- data/lib/bashly/views/command/default_script.erb +4 -0
- data/lib/bashly/views/command/fixed_flags_filter.erb +14 -0
- data/lib/bashly/views/command/function.erb +4 -0
- data/lib/bashly/views/command/initialize.erb +6 -0
- data/lib/bashly/views/command/inspect_args.erb +5 -0
- data/lib/bashly/views/command/master_script.erb +13 -0
- data/lib/bashly/views/command/parse_args.erb +12 -0
- data/lib/bashly/views/command/parse_args_case.erb +17 -0
- data/lib/bashly/views/command/parse_args_secondary.erb +7 -0
- data/lib/bashly/views/command/parse_args_while.erb +20 -0
- data/lib/bashly/views/command/required_args_filter.erb +12 -0
- data/lib/bashly/views/command/required_flags_filter.erb +7 -0
- data/lib/bashly/views/command/root_command.erb +4 -0
- data/lib/bashly/views/command/run.erb +25 -0
- data/lib/bashly/views/command/usage.erb +21 -0
- data/lib/bashly/views/command/usage_args.erb +6 -0
- data/lib/bashly/views/command/usage_commands.erb +7 -0
- data/lib/bashly/views/command/usage_fixed_flags.erb +7 -0
- data/lib/bashly/views/command/usage_flags.erb +4 -0
- data/lib/bashly/views/command/version_command.erb +4 -0
- data/lib/bashly/views/flag/case.erb +16 -0
- data/lib/bashly/views/flag/usage.erb +4 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a4a6b6397239e1fc2b30d3f7cace59aa6bff9b305cfad97b43567dd49cd9eeb8
|
4
|
+
data.tar.gz: 27eb14cc3d2f833dec9e218b3affc0e5d90fc0a4cb258e769127590b501a50ac
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0d907d581476cd66f71078319e1d1290f824d2fbbeac6315cd54988ce1068daf59c31e8ab383b498e7aae4955b7d3b2c225e3a3c701aead077527266d7e7dbe5
|
7
|
+
data.tar.gz: 8ca10f7db1dd3f2fb03909e60b32f858220b85fd3a961d5c8c2b19e19026238fbce5a1c99ac66b8f3949e217ebdd0cd107cb430d74ff060f4e960f511ab9c6a5
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
Bashly - Bbash CLI Generator
|
2
|
+
==================================================
|
3
|
+
|
4
|
+
Create beautiful bash scripts from simple YAML configuration.
|
5
|
+
|
6
|
+

|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
Installation
|
11
|
+
--------------------------------------------------
|
12
|
+
|
13
|
+
$ gem install bashly
|
14
|
+
|
15
|
+
|
16
|
+
Usage
|
17
|
+
--------------------------------------------------
|
18
|
+
|
19
|
+
In an empty directory, create a sample configuration file by running
|
20
|
+
|
21
|
+
$ bashly init
|
22
|
+
# or, to generate a simpler configuration:
|
23
|
+
$ bashly init --minimal
|
24
|
+
|
25
|
+
This will create a sample `src/bashly.yml` file.
|
26
|
+
You can edit this file to specify which arguments, flags and subcommands you
|
27
|
+
need in your bash script.
|
28
|
+
|
29
|
+
Then, generate an initial bash script and function placeholder scripts by
|
30
|
+
running
|
31
|
+
|
32
|
+
$ bashly generate
|
33
|
+
|
34
|
+
This will:
|
35
|
+
|
36
|
+
1. Create the bash executable script.
|
37
|
+
2. Create files for you to edit in the `src` folder.
|
38
|
+
|
39
|
+
Finally, edit the files in the `src` folder. Each of your script's commands
|
40
|
+
get their own file. Once you edit, run `bashly generate` again to merge the
|
41
|
+
content from your functions back into the script.
|
42
|
+
|
43
|
+
|
44
|
+
Contributing / Support
|
45
|
+
--------------------------------------------------
|
46
|
+
|
47
|
+
If you experience any issue, have a question or a suggestion, or if you wish
|
48
|
+
to contribute, feel free to [open an issue][issues].
|
data/bin/bashly
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'bashly'
|
3
|
+
require 'colsole'
|
4
|
+
include Colsole
|
5
|
+
|
6
|
+
runner = Bashly::CLI.runner
|
7
|
+
|
8
|
+
begin
|
9
|
+
exit runner.run ARGV
|
10
|
+
rescue Bashly::Interrupt
|
11
|
+
say! "\nGoodbye"
|
12
|
+
exit 1
|
13
|
+
rescue => e
|
14
|
+
puts e.backtrace.reverse if ENV['DEBUG']
|
15
|
+
say! "!undred!#{e.class}!txtrst!\n#{e.message}"
|
16
|
+
exit 1
|
17
|
+
end
|
data/lib/bashly.rb
ADDED
data/lib/bashly/cli.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'mister_bin'
|
2
|
+
|
3
|
+
module Bashly
|
4
|
+
# The CLI class is used by the bashly binary and forwards incoming CLI
|
5
|
+
# commands to the relevant Bashly::Commands class
|
6
|
+
class CLI
|
7
|
+
def self.runner
|
8
|
+
runner = MisterBin::Runner.new version: Bashly::VERSION,
|
9
|
+
header: "Bashly - Bash CLI Generator"
|
10
|
+
|
11
|
+
runner.route 'init', to: Commands::Init
|
12
|
+
runner.route 'preview', to: Commands::Preview
|
13
|
+
runner.route 'generate', to: Commands::Generate
|
14
|
+
|
15
|
+
runner
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'mister_bin'
|
2
|
+
|
3
|
+
module Bashly
|
4
|
+
module Commands
|
5
|
+
class Generate < MisterBin::Command
|
6
|
+
help "Generate the bash script and required files"
|
7
|
+
|
8
|
+
usage "bashly generate [--force]"
|
9
|
+
usage "bashly generate (-h|--help)"
|
10
|
+
|
11
|
+
option "-f --force", "Overwrite existing files"
|
12
|
+
|
13
|
+
environment "BASHLY_SOURCE_DIR", "The path to use for creating the configuration file [default: src]"
|
14
|
+
environment "BASHLY_TARGET_DIR", "The path to use for creating the bash script [default: .]"
|
15
|
+
|
16
|
+
def run
|
17
|
+
create_user_files
|
18
|
+
create_master_script
|
19
|
+
say "run !txtpur!#{master_script_path} --help!txtrst! to test your bash script"
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def create_user_files
|
25
|
+
say "creating user files in !txtgrn!#{Settings.source_dir}"
|
26
|
+
|
27
|
+
if command.commands.empty?
|
28
|
+
create_file "#{Settings.source_dir}/root_command.sh", command.render(:default_root_script)
|
29
|
+
end
|
30
|
+
|
31
|
+
command.commands.each do |subcommand|
|
32
|
+
file = "#{Settings.source_dir}/#{subcommand.full_name.to_underscore}_command.sh"
|
33
|
+
content = subcommand.render :default_script
|
34
|
+
create_file file, content
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_file(file, content)
|
39
|
+
if File.exist? file and !args['--force']
|
40
|
+
say "skipped !txtgrn!#{file}!txtrst! (exists)"
|
41
|
+
else
|
42
|
+
File.write file, content
|
43
|
+
say "created !txtgrn!#{file}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_master_script
|
48
|
+
master_script = command.render 'master_script'
|
49
|
+
File.write master_script_path, master_script
|
50
|
+
say "created !txtgrn!#{master_script_path}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def master_script_path
|
54
|
+
"#{Settings.target_dir}/#{command.name}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def config
|
58
|
+
@config ||= Config.new "#{Settings.source_dir}/bashly.yml"
|
59
|
+
end
|
60
|
+
|
61
|
+
def command
|
62
|
+
@command ||= Models::Command.new config
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'mister_bin'
|
2
|
+
|
3
|
+
module Bashly
|
4
|
+
module Commands
|
5
|
+
class Init < MisterBin::Command
|
6
|
+
summary "Initialize a new workspace"
|
7
|
+
help "This command will create the source folder, and place a template configuration file in it."
|
8
|
+
|
9
|
+
usage "bashly init [--minimal]"
|
10
|
+
usage "bashly init (-h|--help)"
|
11
|
+
|
12
|
+
option "-m --minimal", "Use a minimal configuration file (without subcommands)"
|
13
|
+
|
14
|
+
environment "BASHLY_SOURCE_DIR", "The path to use for creating the configuration file [default: src]"
|
15
|
+
|
16
|
+
def run
|
17
|
+
if Dir.exist? target_dir and !Dir.empty? target_dir
|
18
|
+
raise InitError, "Directory !txtgrn!#{target_dir}!txtrst! already exists and is not empty"
|
19
|
+
end
|
20
|
+
Dir.mkdir target_dir unless Dir.exist? target_dir
|
21
|
+
File.write "#{target_dir}/bashly.yml", yaml_content
|
22
|
+
say "created !txtgrn!#{target_dir}/bashly.yml"
|
23
|
+
say "run !txtpur!bashly generate!txtrst! to create the bash script"
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def yaml_content
|
29
|
+
@yaml_content ||= yaml_content!
|
30
|
+
end
|
31
|
+
|
32
|
+
def yaml_content!
|
33
|
+
if args['--minimal']
|
34
|
+
File.read minimal_template_file
|
35
|
+
else
|
36
|
+
File.read template_file
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def template_file
|
41
|
+
File.expand_path '../templates/bashly.yml', __dir__
|
42
|
+
end
|
43
|
+
|
44
|
+
def minimal_template_file
|
45
|
+
File.expand_path '../templates/minimal.yml', __dir__
|
46
|
+
end
|
47
|
+
|
48
|
+
def target_dir
|
49
|
+
@target_dir ||= Settings.source_dir
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'mister_bin'
|
2
|
+
|
3
|
+
module Bashly
|
4
|
+
module Commands
|
5
|
+
class Preview < MisterBin::Command
|
6
|
+
help "Generate the bash script to STDOUT"
|
7
|
+
|
8
|
+
usage "bashly preview"
|
9
|
+
usage "bashly preview (-h|--help)"
|
10
|
+
|
11
|
+
environment "BASHLY_SOURCE_DIR", "The path containing the bashly configuration and source files [default: src]"
|
12
|
+
|
13
|
+
def run
|
14
|
+
config = Config.new "#{Settings.source_dir}/bashly.yml"
|
15
|
+
command = Models::Command.new(config)
|
16
|
+
puts command.render 'master_script'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Bashly
|
2
|
+
module Renderable
|
3
|
+
def render(view)
|
4
|
+
template = File.read view_path(view)
|
5
|
+
ERB.new(template, nil, '%-').result(binding)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def view_path(view)
|
11
|
+
"#{self_views_path}/#{view}.erb"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self_views_path
|
15
|
+
@self_view_path ||= "#{base_views_path}/#{views_subfolder}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def base_views_path
|
19
|
+
@base_views_path ||= File.expand_path("../views/", __dir__)
|
20
|
+
end
|
21
|
+
|
22
|
+
def views_subfolder
|
23
|
+
@views_subfolder ||= self.class.name.split('::').last.to_underscore
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Bashly
|
4
|
+
# A convenience class to use either a hash or a filename as a configuration
|
5
|
+
# source
|
6
|
+
class Config
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def self.new(config)
|
10
|
+
if config.is_a? String
|
11
|
+
YAML.load_file config
|
12
|
+
else
|
13
|
+
config
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
class String
|
5
|
+
def indent(offset)
|
6
|
+
return self unless offset > 0
|
7
|
+
split("\n").indent(offset).join("\n")
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_underscore!
|
11
|
+
gsub!(/(.)([A-Z])/,'\1_\2')
|
12
|
+
gsub!(' ', '_')
|
13
|
+
downcase!
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_underscore
|
17
|
+
dup.tap { |s| s.to_underscore! }
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Bashly
|
2
|
+
module Models
|
3
|
+
class Argument < Base
|
4
|
+
def optional
|
5
|
+
!required
|
6
|
+
end
|
7
|
+
|
8
|
+
def usage_string
|
9
|
+
required ? name.upcase : "[#{name.upcase}]"
|
10
|
+
end
|
11
|
+
|
12
|
+
def summary
|
13
|
+
return "" unless help
|
14
|
+
help.split("\n").first
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Bashly
|
2
|
+
module Models
|
3
|
+
class Base
|
4
|
+
include Renderable
|
5
|
+
|
6
|
+
attr_reader :options
|
7
|
+
|
8
|
+
OPTION_KEYS = %i[
|
9
|
+
arg
|
10
|
+
description
|
11
|
+
flags
|
12
|
+
help
|
13
|
+
long
|
14
|
+
name
|
15
|
+
parent_name
|
16
|
+
required
|
17
|
+
short
|
18
|
+
version
|
19
|
+
]
|
20
|
+
|
21
|
+
def initialize(options)
|
22
|
+
raise Error, "Invalid options provided" unless options.respond_to? :keys
|
23
|
+
@options = options
|
24
|
+
verify if respond_to? :verify
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing(method_name, *arguments, &block)
|
28
|
+
key = method_name.to_s
|
29
|
+
respond_to?(method_name) ? options[key] : super
|
30
|
+
end
|
31
|
+
|
32
|
+
def respond_to?(method_name, include_private = false)
|
33
|
+
# options.has_key?(method_name.to_s) || super
|
34
|
+
OPTION_KEYS.include?(method_name) || super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Bashly
|
2
|
+
module Models
|
3
|
+
class Command < Base
|
4
|
+
# Returns all the possible aliases for this command
|
5
|
+
def aliases
|
6
|
+
short ? [name, short] : [name]
|
7
|
+
end
|
8
|
+
|
9
|
+
# Returns an array of Arguments
|
10
|
+
def args
|
11
|
+
return [] unless options["args"]
|
12
|
+
options["args"].map do |options|
|
13
|
+
Argument.new options
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns a string suitable to be a headline
|
18
|
+
def caption_string
|
19
|
+
help ? "#{full_name} - #{summary}" : full_name
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns only the names of the subcommands (Commands)
|
23
|
+
def command_names
|
24
|
+
commands.map &:name
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns an array of the subcommands (Commands)
|
28
|
+
def commands
|
29
|
+
return [] unless options["commands"]
|
30
|
+
options["commands"].map do |options|
|
31
|
+
options['parent_name'] = full_name
|
32
|
+
command = Command.new options
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns an array of Flags
|
37
|
+
def flags
|
38
|
+
return [] unless options["flags"]
|
39
|
+
options["flags"].map do |options|
|
40
|
+
Flag.new options
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the name of the command, including its parent name (in case
|
45
|
+
# this is a subcommand)
|
46
|
+
def full_name
|
47
|
+
parent_name ? "#{parent_name} #{name}" : name
|
48
|
+
end
|
49
|
+
|
50
|
+
# Reads a file from the userspace (Settings.source_dir) and returns
|
51
|
+
# its contents.
|
52
|
+
# If the file is not found, returns a string with a hint.
|
53
|
+
def load_user_file(file)
|
54
|
+
path = "#{Settings.source_dir}/#{file}"
|
55
|
+
content = if File.exist? path
|
56
|
+
File.read path
|
57
|
+
else
|
58
|
+
"# error: cannot load file"
|
59
|
+
end
|
60
|
+
"# :#{path}\n#{content}"
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns an array of all the required Arguments
|
64
|
+
def required_args
|
65
|
+
args.select &:required
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns an array of all the required Flags
|
69
|
+
def required_flags
|
70
|
+
flags.select &:required
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the first line of the help message
|
74
|
+
def summary
|
75
|
+
return "" unless help
|
76
|
+
help.split("\n").first
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns a constructed string suitable for Usage pattern
|
80
|
+
def usage_string
|
81
|
+
result = [full_name]
|
82
|
+
result << "[command]" if commands.any?
|
83
|
+
args.each do |arg|
|
84
|
+
result << arg.usage_string
|
85
|
+
end
|
86
|
+
result << "[options]"
|
87
|
+
result.join " "
|
88
|
+
end
|
89
|
+
|
90
|
+
def verify
|
91
|
+
if commands.any?
|
92
|
+
if args.any? or flags.any?
|
93
|
+
raise ConfigurationError, "Error in the !txtgrn!#{full_name}!txtrst! command.\nThe !txtgrn!commands!txtrst! key cannot be at the same level as the !txtgrn!args!txtrst! or !txtgrn!flags!txtrst! keys."
|
94
|
+
end
|
95
|
+
|
96
|
+
if parent_name
|
97
|
+
raise ConfigurationError, "Error in the !txtgrn!#{full_name}!txtrst! command.\nNested commands are not supported."
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Bashly
|
2
|
+
module Models
|
3
|
+
class Flag < Base
|
4
|
+
def aliases
|
5
|
+
if long and short
|
6
|
+
[long, short]
|
7
|
+
elsif long
|
8
|
+
[long]
|
9
|
+
else
|
10
|
+
[short]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def optional
|
15
|
+
!required
|
16
|
+
end
|
17
|
+
|
18
|
+
def summary
|
19
|
+
return "" unless help
|
20
|
+
help.split("\n").first
|
21
|
+
end
|
22
|
+
|
23
|
+
def usage_string(extended: false)
|
24
|
+
result = [aliases.join(", ")]
|
25
|
+
result << arg.upcase if arg
|
26
|
+
result << "(required)" if required and extended
|
27
|
+
result.join " "
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Bashly
|
2
|
+
class Settings
|
3
|
+
class << self
|
4
|
+
attr_writer :source_dir, :target_dir
|
5
|
+
|
6
|
+
def source_dir
|
7
|
+
@source_dir ||= ENV['BASHLY_SOURCE_DIR'] || 'src'
|
8
|
+
end
|
9
|
+
|
10
|
+
def target_dir
|
11
|
+
@target_dir ||= ENV['BASHLY_TARGET_DIR'] || '.'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
name: cli
|
2
|
+
help: Sample application
|
3
|
+
version: 0.1.0
|
4
|
+
|
5
|
+
commands:
|
6
|
+
- name: download
|
7
|
+
short: d
|
8
|
+
help: Download a file
|
9
|
+
|
10
|
+
args:
|
11
|
+
- name: source
|
12
|
+
required: true
|
13
|
+
help: URL to download from
|
14
|
+
- name: target
|
15
|
+
help: "Target filename (default: same as source)"
|
16
|
+
|
17
|
+
flags:
|
18
|
+
- long: --force
|
19
|
+
short: -f
|
20
|
+
help: Overwrite existing files
|
21
|
+
|
22
|
+
- name: upload
|
23
|
+
short: u
|
24
|
+
help: Upload a file
|
25
|
+
args:
|
26
|
+
- name: source
|
27
|
+
required: true
|
28
|
+
help: File to upload
|
29
|
+
|
30
|
+
flags:
|
31
|
+
- long: --user
|
32
|
+
short: -u
|
33
|
+
arg: user
|
34
|
+
help: Username to use for logging in
|
35
|
+
required: true
|
36
|
+
- long: --password
|
37
|
+
short: -p
|
38
|
+
arg: password
|
39
|
+
help: Password to use for logging in
|
@@ -0,0 +1,16 @@
|
|
1
|
+
name: download
|
2
|
+
help: Sample minimal application without subcommands
|
3
|
+
version: 0.1.0
|
4
|
+
|
5
|
+
args:
|
6
|
+
- name: source
|
7
|
+
required: true
|
8
|
+
help: URL to download from
|
9
|
+
- name: target
|
10
|
+
help: "Target filename (default: same as source)"
|
11
|
+
|
12
|
+
flags:
|
13
|
+
- long: --force
|
14
|
+
short: -f
|
15
|
+
help: Overwrite existing files
|
16
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# :command.command_filter
|
2
|
+
<%- if commands.any? -%>
|
3
|
+
action=$1
|
4
|
+
|
5
|
+
case $action in
|
6
|
+
-* )
|
7
|
+
;;
|
8
|
+
|
9
|
+
<%- commands.each do |command| -%>
|
10
|
+
<%= command.aliases.join " | " %> )
|
11
|
+
shift
|
12
|
+
<%= command.name %>_parse_args "$@"
|
13
|
+
shift $#
|
14
|
+
;;
|
15
|
+
|
16
|
+
<%- end -%>
|
17
|
+
* )
|
18
|
+
<%= name %>_usage
|
19
|
+
exit 1
|
20
|
+
;;
|
21
|
+
|
22
|
+
esac
|
23
|
+
<%- else -%>
|
24
|
+
action=root
|
25
|
+
<%- end -%>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
<%= render :root_command if commands.empty? %>
|
4
|
+
<%= render :version_command %>
|
5
|
+
<%= render :usage %>
|
6
|
+
<%= render :inspect_args %>
|
7
|
+
<%= render :command_functions %>
|
8
|
+
<%= render :parse_args %>
|
9
|
+
<%= render :initialize %>
|
10
|
+
<%= render :run %>
|
11
|
+
|
12
|
+
initialize
|
13
|
+
run "$@"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# :command.parse_args
|
2
|
+
parse_args() {
|
3
|
+
<%= render(:fixed_flags_filter).indent 2 %>
|
4
|
+
<%= render(:command_filter).indent 2 %>
|
5
|
+
<%= render(:required_args_filter).indent 2 %>
|
6
|
+
<%= render(:required_flags_filter).indent 2 %>
|
7
|
+
<%= render(:parse_args_while).indent 2 %>
|
8
|
+
}
|
9
|
+
|
10
|
+
<%- commands.each do |command| %>
|
11
|
+
<%= command.render 'parse_args_secondary' %>
|
12
|
+
<%- end -%>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# :command.parse_args_case
|
2
|
+
<%- if args.any? -%>
|
3
|
+
<%- condition = "if" -%>
|
4
|
+
<%- args.each do |arg| -%>
|
5
|
+
<%= condition %> [[ ! ${args[<%= arg.name %>]} ]]; then
|
6
|
+
args[<%= arg.name %>]=$1
|
7
|
+
shift
|
8
|
+
<%- condition = "elif" -%>
|
9
|
+
<%- end -%>
|
10
|
+
else
|
11
|
+
echo invalid argument: $key
|
12
|
+
exit 1
|
13
|
+
fi
|
14
|
+
<%- else -%>
|
15
|
+
echo invalid argument: $key
|
16
|
+
exit 1
|
17
|
+
<%- end -%>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# :command.parse_args_while
|
2
|
+
while [[ $# -gt 0 ]]; do
|
3
|
+
key="$1"
|
4
|
+
case "$key" in
|
5
|
+
<%- flags.each do |flag| -%>
|
6
|
+
<%= flag.render(:case).indent 2 %>
|
7
|
+
|
8
|
+
<%- end -%>
|
9
|
+
|
10
|
+
-* )
|
11
|
+
echo invalid option: $key
|
12
|
+
exit 1
|
13
|
+
;;
|
14
|
+
|
15
|
+
* )
|
16
|
+
<%= render(:parse_args_case).indent 4 %>
|
17
|
+
;;
|
18
|
+
|
19
|
+
esac
|
20
|
+
done
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# :command.required_args_filter
|
2
|
+
<%- required_args.each do |arg| -%>
|
3
|
+
if [[ $1 && $1 != -* ]]; then
|
4
|
+
args[<%= arg.name %>]=$1
|
5
|
+
shift
|
6
|
+
else
|
7
|
+
echo missing required argument: <%= arg.name.upcase %>
|
8
|
+
echo Usage: <%= usage_string %>
|
9
|
+
exit 1
|
10
|
+
fi
|
11
|
+
|
12
|
+
<%- end %>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# :command.run
|
2
|
+
run() {
|
3
|
+
declare -A args
|
4
|
+
parse_args "$@"
|
5
|
+
|
6
|
+
<%- condition = "if" -%>
|
7
|
+
<%- commands.each do |command| -%>
|
8
|
+
<%= condition %> [[ $action == "<%= command.name %>" ]]; then
|
9
|
+
if [[ ${args[--help]} ]]; then
|
10
|
+
long_usage=yes
|
11
|
+
<%= command.full_name.to_underscore %>_usage
|
12
|
+
else
|
13
|
+
<%= command.full_name.to_underscore %>_command
|
14
|
+
fi
|
15
|
+
<% condition = "elif" %>
|
16
|
+
<%- 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
|
23
|
+
root_command
|
24
|
+
fi
|
25
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# :command.usage
|
2
|
+
<%= full_name.to_underscore %>_usage() {
|
3
|
+
echo "<%= caption_string %>"
|
4
|
+
echo
|
5
|
+
echo "Usage:"
|
6
|
+
echo " <%= usage_string %>"
|
7
|
+
echo
|
8
|
+
<%= render(:usage_commands).indent 2 if commands.any? %>
|
9
|
+
|
10
|
+
if [[ -n $long_usage ]]; then
|
11
|
+
echo "Options:"
|
12
|
+
<%= render(:usage_fixed_flags).indent 4 %>
|
13
|
+
<%= render(:usage_flags).indent 4 if flags.any? %>
|
14
|
+
<%= render(:usage_args).indent 4 if args.any? %>
|
15
|
+
|
16
|
+
fi
|
17
|
+
}
|
18
|
+
|
19
|
+
<%- commands.each do |command| -%>
|
20
|
+
<%= command.render 'usage' %>
|
21
|
+
<%- end -%>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# :flag.case
|
2
|
+
<%= aliases.join " | " %> )
|
3
|
+
<%- if arg -%>
|
4
|
+
if [[ $2 && $2 != -* ]]; then
|
5
|
+
args[<%= long %>]="$2"
|
6
|
+
shift
|
7
|
+
shift
|
8
|
+
else
|
9
|
+
echo <%= long %> requires an argument: <%= usage_string %>
|
10
|
+
exit 1
|
11
|
+
fi
|
12
|
+
<%- else -%>
|
13
|
+
args[<%= long %>]=1
|
14
|
+
shift
|
15
|
+
<%- end -%>
|
16
|
+
;;
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bashly
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Danny Ben Shitrit
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-11-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colsole
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.5'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mister_bin
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.6'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: requires
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.1'
|
55
|
+
description: Generate bash command line tools using YAML configuration
|
56
|
+
email: db@dannyben.com
|
57
|
+
executables:
|
58
|
+
- bashly
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- README.md
|
63
|
+
- bin/bashly
|
64
|
+
- lib/bashly.rb
|
65
|
+
- lib/bashly/cli.rb
|
66
|
+
- lib/bashly/commands/generate.rb
|
67
|
+
- lib/bashly/commands/init.rb
|
68
|
+
- lib/bashly/commands/preview.rb
|
69
|
+
- lib/bashly/concerns/renderable.rb
|
70
|
+
- lib/bashly/config.rb
|
71
|
+
- lib/bashly/exceptions.rb
|
72
|
+
- lib/bashly/extensions/array.rb
|
73
|
+
- lib/bashly/extensions/string.rb
|
74
|
+
- lib/bashly/models/argument.rb
|
75
|
+
- lib/bashly/models/base.rb
|
76
|
+
- lib/bashly/models/command.rb
|
77
|
+
- lib/bashly/models/flag.rb
|
78
|
+
- lib/bashly/settings.rb
|
79
|
+
- lib/bashly/templates/bashly.yml
|
80
|
+
- lib/bashly/templates/minimal.yml
|
81
|
+
- lib/bashly/version.rb
|
82
|
+
- lib/bashly/views/argument/usage.erb
|
83
|
+
- lib/bashly/views/command/command_filter.erb
|
84
|
+
- lib/bashly/views/command/command_functions.erb
|
85
|
+
- lib/bashly/views/command/default_root_script.erb
|
86
|
+
- lib/bashly/views/command/default_script.erb
|
87
|
+
- lib/bashly/views/command/fixed_flags_filter.erb
|
88
|
+
- lib/bashly/views/command/function.erb
|
89
|
+
- lib/bashly/views/command/initialize.erb
|
90
|
+
- lib/bashly/views/command/inspect_args.erb
|
91
|
+
- lib/bashly/views/command/master_script.erb
|
92
|
+
- lib/bashly/views/command/parse_args.erb
|
93
|
+
- lib/bashly/views/command/parse_args_case.erb
|
94
|
+
- lib/bashly/views/command/parse_args_secondary.erb
|
95
|
+
- lib/bashly/views/command/parse_args_while.erb
|
96
|
+
- lib/bashly/views/command/required_args_filter.erb
|
97
|
+
- lib/bashly/views/command/required_flags_filter.erb
|
98
|
+
- lib/bashly/views/command/root_command.erb
|
99
|
+
- lib/bashly/views/command/run.erb
|
100
|
+
- lib/bashly/views/command/usage.erb
|
101
|
+
- lib/bashly/views/command/usage_args.erb
|
102
|
+
- lib/bashly/views/command/usage_commands.erb
|
103
|
+
- lib/bashly/views/command/usage_fixed_flags.erb
|
104
|
+
- lib/bashly/views/command/usage_flags.erb
|
105
|
+
- lib/bashly/views/command/version_command.erb
|
106
|
+
- lib/bashly/views/flag/case.erb
|
107
|
+
- lib/bashly/views/flag/usage.erb
|
108
|
+
homepage: https://github.com/dannyben/bashly
|
109
|
+
licenses:
|
110
|
+
- MIT
|
111
|
+
metadata: {}
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 2.3.0
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubygems_version: 3.0.3
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: Bash Command Line Tool Generator
|
131
|
+
test_files: []
|