bashly 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![demo](demo/cast.svg)
|
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: []
|