arli 0.7.0 → 0.8.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +0 -2
- data/README.md +149 -48
- data/arli.gemspec +1 -3
- data/docs/arli-in-action.png +0 -0
- data/docs/arlifile.png +0 -0
- data/exe/arli +5 -1
- data/lib/arli.rb +2 -2
- data/lib/arli/actions.rb +6 -1
- data/lib/arli/actions/action.rb +57 -37
- data/lib/arli/actions/dir_name.rb +18 -16
- data/lib/arli/actions/git_repo.rb +8 -8
- data/lib/arli/actions/move_to_library_path.rb +54 -0
- data/lib/arli/actions/{zip_file.rb → unzip_file.rb} +14 -10
- data/lib/arli/arli_file.rb +52 -32
- data/lib/arli/cli/app.rb +11 -4
- data/lib/arli/cli/command_finder.rb +14 -11
- data/lib/arli/cli/parser.rb +70 -29
- data/lib/arli/cli/parser_factory.rb +105 -50
- data/lib/arli/cli/runner.rb +8 -4
- data/lib/arli/commands.rb +1 -0
- data/lib/arli/commands/base.rb +11 -6
- data/lib/arli/commands/bundle.rb +43 -0
- data/lib/arli/commands/install.rb +56 -13
- data/lib/arli/commands/search.rb +82 -27
- data/lib/arli/configuration.rb +37 -18
- data/lib/arli/errors.rb +6 -0
- data/lib/arli/library.rb +4 -46
- data/lib/arli/library/installer.rb +71 -0
- data/lib/arli/library/proxy.rb +79 -0
- data/lib/arli/lock/file.rb +65 -0
- data/lib/arli/lock/formats.rb +4 -0
- data/lib/arli/lock/formats/base.rb +26 -0
- data/lib/arli/lock/formats/cmake.rb +24 -0
- data/lib/arli/lock/formats/json.rb +25 -0
- data/lib/arli/lock/formats/text.rb +13 -0
- data/lib/arli/lock/formats/yaml.rb +14 -0
- data/lib/arli/output.rb +94 -11
- data/lib/arli/version.rb +1 -1
- metadata +17 -35
- data/docs/arli-full.png +0 -0
- data/lib/arli/installer.rb +0 -52
@@ -9,13 +9,13 @@ module Arli
|
|
9
9
|
def default_help
|
10
10
|
gp = global_parser
|
11
11
|
gp.parse!(%w(--help))
|
12
|
-
gp
|
12
|
+
print_parser_help(gp)
|
13
13
|
end
|
14
14
|
|
15
15
|
def parse_argv(parser, argv)
|
16
16
|
if parser
|
17
17
|
parser.parse!(argv)
|
18
|
-
parser
|
18
|
+
print_parser_help(parser)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -34,47 +34,95 @@ module Arli
|
|
34
34
|
|
35
35
|
def command_parsers
|
36
36
|
@command_parsers ||= {
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
37
|
+
search: Hashie::Mash.new(
|
38
|
+
{
|
39
|
+
sentence: 'Search standard Arduino Library Database with over 4K entries',
|
40
|
+
description: ["This command provides both the simple name-based search interface,\n",
|
41
|
+
"and the most sophisticated field-by-field search using a downloaded, \n",
|
42
|
+
"and locally cached Public Arduino Database JSON file, maintained\n",
|
43
|
+
"by Arduino and the Community. If you know of another database,\n",
|
44
|
+
"that's what the --database flag is for."],
|
45
|
+
examples: [
|
46
|
+
{ desc: 'Search using the regular expression containing the name:',
|
47
|
+
cmd: 'arli search AudioZero' },
|
48
|
+
|
49
|
+
{ desc: 'Same exact search as above, but using ruby hash syntax:',
|
50
|
+
cmd: %Q{arli search 'name: /AudioZero/'} },
|
51
|
+
|
52
|
+
{ desc: 'Lets get a particular version of the library',
|
53
|
+
cmd: %Q{arli search 'name: "AudioZero", version: "1.0,2"'} },
|
54
|
+
|
55
|
+
{ desc: 'Search using case insensitive name search, and :',
|
56
|
+
cmd: %Q{arli search 'name: /adafruit/i'} },
|
57
|
+
|
58
|
+
{ desc: 'Finally, search for the exact name match:',
|
59
|
+
cmd: %Q{arli search '^Time$'} },
|
60
|
+
],
|
61
|
+
|
62
|
+
parser: -> (command_name) {
|
63
|
+
make_parser(command_name) do |parser|
|
64
|
+
parser.banner = usage_line 'search ' + '[ name | search-expression ]'.magenta
|
65
|
+
parser.option_search
|
66
|
+
parser.option_help(command_name: command_name)
|
67
|
+
end
|
68
|
+
}
|
69
|
+
}),
|
70
|
+
|
71
|
+
bundle: Hashie::Mash.new(
|
72
|
+
{
|
73
|
+
sentence: 'Installs all libraries specified in Arlifile',
|
74
|
+
description:
|
75
|
+
[
|
76
|
+
"This command reads ", "Arlifile".bold.green, " (from the current folder, by default),\n",
|
77
|
+
"and then it installs all dependent libraries specified there, checking if \n",
|
78
|
+
"each already exists, and if not — downloading them, and installing them into\n",
|
79
|
+
"your Arduino Library folder. Both the folder with the Arlifile, as well as the\n",
|
80
|
+
"destination library path folder can be changed with the command line flags.\n",
|
81
|
+
],
|
82
|
+
examples: [
|
83
|
+
{ desc: 'Install all libs defined in Arlifile:',
|
84
|
+
cmd: 'arli bundle ' },
|
85
|
+
|
86
|
+
{ desc: 'Custom Arlifile location, and destination path:',
|
87
|
+
cmd: 'arli bundle -a ./src -l ./libraries' }
|
88
|
+
],
|
89
|
+
|
90
|
+
parser: -> (command_name) {
|
91
|
+
make_parser(command_name) do |parser|
|
92
|
+
parser.banner = usage_line 'bundle'
|
93
|
+
parser.option_bundle
|
94
|
+
parser.option_help(command_name: command_name)
|
95
|
+
end
|
96
|
+
} }),
|
97
|
+
|
98
|
+
install: Hashie::Mash.new(
|
99
|
+
{
|
100
|
+
sentence: 'Installs a single library either by searching, or url or local ZIP',
|
101
|
+
description: [
|
102
|
+
"This command installs a single library into your library path\n",
|
103
|
+
"using the third argument to the command #{'arli install'.bold.white}\n".dark ,
|
104
|
+
"which can be a library name, local ZIP file, or a remote URL \n",
|
105
|
+
"(either ZIP or Git Repo)\n"
|
106
|
+
|
107
|
+
],
|
108
|
+
examples: [
|
109
|
+
{ desc: 'Install the latest version of this library',
|
110
|
+
cmd: 'arli install "Adafruit GFX Library"' },
|
111
|
+
|
112
|
+
{ desc: 'Install the library from a Github URL',
|
113
|
+
cmd: 'arli install https://github.com/jfturcot/SimpleTimer' },
|
114
|
+
|
115
|
+
{ desc: 'Install a local ZIP file',
|
116
|
+
cmd: 'arli install ~/Downloads/DHT-Library.zip' },
|
117
|
+
],
|
118
|
+
|
119
|
+
parser: -> (command_name) {
|
120
|
+
make_parser(command_name) do |parser|
|
121
|
+
parser.banner = usage_line 'install' + ' [ "library name" | url | local-zip ] '.magenta
|
122
|
+
parser.option_install
|
123
|
+
parser.option_help(command_name: command_name)
|
124
|
+
end
|
125
|
+
} }),
|
78
126
|
}
|
79
127
|
end
|
80
128
|
|
@@ -93,21 +141,28 @@ module Arli
|
|
93
141
|
end
|
94
142
|
|
95
143
|
def global_usage(command)
|
96
|
-
|
97
|
-
|
98
|
-
'
|
144
|
+
'Usage:'.magenta.bold +
|
145
|
+
"\n " + arli_command + ' options '.yellow +
|
146
|
+
"\n " + arli_command + ' ' + ((command || 'command')).green + ' [ options ] '.yellow + "\n"
|
147
|
+
end
|
148
|
+
|
149
|
+
def arli_command
|
150
|
+
@arli_command ||= Arli::Configuration::ARLI_COMMAND.blue.bold
|
99
151
|
end
|
100
152
|
|
101
153
|
def command_usage(command)
|
102
|
-
|
103
|
-
command.green +
|
104
|
-
'
|
105
|
-
'Command Options'
|
154
|
+
'Usage:'.magenta.bold +
|
155
|
+
"\n " + arli_command + ' ' + command.green + ' [options]'.yellow + "\n\n" +
|
156
|
+
'Options'.magenta.bold
|
106
157
|
end
|
107
158
|
|
108
159
|
def usage_line(command = nil)
|
109
160
|
command ? command_usage(command) : global_usage(command)
|
110
161
|
end
|
162
|
+
|
163
|
+
def print_parser_help(parser)
|
164
|
+
parser.print
|
165
|
+
end
|
111
166
|
end
|
112
167
|
|
113
168
|
|
data/lib/arli/cli/runner.rb
CHANGED
@@ -17,12 +17,13 @@ module Arli
|
|
17
17
|
$stdout = @stdout
|
18
18
|
|
19
19
|
Arli::CLI::App.new(@argv).start
|
20
|
+
print_debug_info
|
20
21
|
0
|
21
22
|
rescue StandardError => e
|
22
23
|
b = e.backtrace
|
23
|
-
@stderr.puts("#{b.shift.bold}:\n")
|
24
|
+
@stderr.puts("#{b.shift.bold}:\n") if Arli.config.debug
|
24
25
|
@stderr.puts("#{e.message.bold.red} (#{e.class.name.yellow})")
|
25
|
-
@stderr.puts(b.map { |s| "\tfrom #{s}" }.join("\n")
|
26
|
+
@stderr.puts(b.map { |s| "\tfrom #{s}" }.join("\n").red)
|
26
27
|
1
|
27
28
|
rescue SystemExit => e
|
28
29
|
e.status
|
@@ -30,11 +31,14 @@ module Arli
|
|
30
31
|
$stderr = STDERR
|
31
32
|
$stdin = STDIN
|
32
33
|
$stdout = STDOUT
|
33
|
-
ap(Arli.config.to_hash, indent: 6, index: false) if Arli.config.debug
|
34
34
|
end
|
35
35
|
@kernel.exit(exit_code)
|
36
36
|
end
|
37
|
+
|
38
|
+
def print_debug_info
|
39
|
+
$stdout.puts JSON.pretty_generate(Arli.config.to_hash).gsub(/("\w+":)/, '\1'.bold.blue) \
|
40
|
+
if Arli.config.debug
|
41
|
+
end
|
37
42
|
end
|
38
43
|
end
|
39
|
-
|
40
44
|
end
|
data/lib/arli/commands.rb
CHANGED
data/lib/arli/commands/base.rb
CHANGED
@@ -15,14 +15,9 @@ module Arli
|
|
15
15
|
|
16
16
|
def initialize(config: Arli.config)
|
17
17
|
self.config = config
|
18
|
-
FileUtils.mkdir_p(library_path) unless Dir.exist?(library_path)
|
19
18
|
setup
|
20
19
|
end
|
21
20
|
|
22
|
-
def run(*args)
|
23
|
-
raise ArgumentError, 'This method must be implemented in subclasses'
|
24
|
-
end
|
25
|
-
|
26
21
|
def runtime
|
27
22
|
config.runtime
|
28
23
|
end
|
@@ -35,12 +30,22 @@ module Arli
|
|
35
30
|
config.libraries.path
|
36
31
|
end
|
37
32
|
|
33
|
+
def temp_path
|
34
|
+
config.libraries.temp_dir
|
35
|
+
end
|
36
|
+
|
38
37
|
def setup
|
38
|
+
FileUtils.mkdir_p(library_path) unless Dir.exist?(library_path)
|
39
|
+
FileUtils.mkdir_p(temp_path) unless Dir.exist?(temp_path)
|
40
|
+
end
|
39
41
|
|
42
|
+
def run(*args)
|
43
|
+
raise Arli::Errors::AbstractMethodCalled,
|
44
|
+
'This method must be implemented in subclasses'
|
40
45
|
end
|
41
46
|
|
42
47
|
def params
|
43
|
-
|
48
|
+
''
|
44
49
|
end
|
45
50
|
end
|
46
51
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'arli'
|
3
|
+
require 'net/http'
|
4
|
+
require_relative 'base'
|
5
|
+
require_relative '../arli_file'
|
6
|
+
require_relative '../lock/formats'
|
7
|
+
require_relative '../lock/file'
|
8
|
+
|
9
|
+
module Arli
|
10
|
+
module Commands
|
11
|
+
class Bundle < Base
|
12
|
+
|
13
|
+
attr_accessor :arlifile, :lock_file
|
14
|
+
|
15
|
+
def setup
|
16
|
+
super
|
17
|
+
self.arlifile = Arli::ArliFile.new(config: config)
|
18
|
+
self.lock_file = Arli::Lock::File.new(config: config)
|
19
|
+
end
|
20
|
+
|
21
|
+
def params
|
22
|
+
if arlifile&.libraries
|
23
|
+
"libraries: \n • " + arlifile.libraries.map(&:name).join("\n • ")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
install_with_arli_file
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def install_with_arli_file
|
34
|
+
arlifile.install
|
35
|
+
post_install
|
36
|
+
end
|
37
|
+
|
38
|
+
def post_install
|
39
|
+
lock_file.lock!(*arlifile.libraries)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,31 +1,74 @@
|
|
1
|
+
require 'hashie/mash'
|
2
|
+
require 'net/http'
|
1
3
|
require 'json'
|
2
4
|
require 'arli'
|
3
|
-
|
5
|
+
|
6
|
+
require 'arduino/library'
|
4
7
|
require_relative 'base'
|
5
|
-
require_relative '
|
8
|
+
require_relative 'bundle'
|
6
9
|
|
7
10
|
module Arli
|
8
11
|
module Commands
|
9
|
-
class Install <
|
12
|
+
class Install < Bundle
|
13
|
+
require 'arduino/library/include'
|
14
|
+
|
15
|
+
attr_accessor :library
|
10
16
|
|
11
|
-
|
17
|
+
def setup
|
18
|
+
validate_argument
|
19
|
+
|
20
|
+
self.library = identify_library(runtime.argv.first)
|
21
|
+
validate_library
|
12
22
|
|
13
|
-
|
14
|
-
super(*args)
|
23
|
+
self.arlifile = Arli::ArliFile.new(config: config, libraries: [library]) if library
|
15
24
|
end
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
26
|
+
# arg can be 'Adafruit GFX Library'
|
27
|
+
def identify_library(arg)
|
28
|
+
if File.exist?(arg)
|
29
|
+
begin
|
30
|
+
Arduino::Library::Model.from(arg)
|
31
|
+
rescue
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
elsif arg =~ %r[https?://]
|
35
|
+
Arduino::Library::Model.from_hash(url: arg, name: File.basename(arg))
|
36
|
+
else
|
37
|
+
results = search(name: /^#{arg}$/i)
|
38
|
+
validate_search(arg, results)
|
39
|
+
results.sort.last if results && !results.empty?
|
40
|
+
end
|
21
41
|
end
|
22
42
|
|
23
43
|
def params
|
24
|
-
"
|
44
|
+
" • #{library.to_s}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def post_install
|
48
|
+
#
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def validate_search(arg, results)
|
54
|
+
raise Arli::Errors::LibraryNotFound,
|
55
|
+
"Can't find library by argument #{arg.bold.yellow}" if results.nil? || results.empty?
|
56
|
+
raise Arli::Errors::TooManyMatchesError,
|
57
|
+
"More than one match found for #{arg.bold.yellow}" if results.map(&:name).uniq.size > 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate_library
|
61
|
+
raise Arli::Errors::LibraryNotFound,
|
62
|
+
"Library #{cfg.to_hash} was not found" unless library
|
63
|
+
end
|
64
|
+
|
65
|
+
def validate_argument
|
66
|
+
raise InvalidInstallSyntaxError,
|
67
|
+
"Missing installation argument: a name, a file or a URL." unless runtime.argv.first
|
25
68
|
end
|
26
69
|
|
27
|
-
def
|
28
|
-
|
70
|
+
def cfg
|
71
|
+
config.install
|
29
72
|
end
|
30
73
|
end
|
31
74
|
end
|
data/lib/arli/commands/search.rb
CHANGED
@@ -5,69 +5,124 @@ require 'arli'
|
|
5
5
|
require 'arli/commands/base'
|
6
6
|
require 'arli/errors'
|
7
7
|
require 'arduino/library'
|
8
|
-
require 'awesome_print'
|
9
8
|
|
10
9
|
module Arli
|
11
10
|
module Commands
|
12
11
|
class Search < Base
|
12
|
+
|
13
|
+
LibraryWithVersion = Struct.new(:name, :versions)
|
14
|
+
|
13
15
|
require 'arduino/library/include'
|
14
16
|
|
15
17
|
attr_accessor :search_string,
|
16
18
|
:search_opts,
|
17
19
|
:results,
|
18
20
|
:limit,
|
19
|
-
:database
|
21
|
+
:database,
|
22
|
+
:format,
|
23
|
+
:hash,
|
24
|
+
:custom_print
|
20
25
|
|
21
26
|
def initialize(*args)
|
22
27
|
super(*args)
|
28
|
+
self.format = :short
|
29
|
+
self.hash = Hash.new
|
30
|
+
self.custom_print = true
|
23
31
|
end
|
24
32
|
|
25
|
-
def
|
26
|
-
|
33
|
+
def run
|
34
|
+
self.search_opts = process_search_options!
|
35
|
+
self.results = search(database, **search_opts).sort
|
27
36
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
"name: /#{search}/"
|
32
|
-
end
|
37
|
+
results.map do |lib|
|
38
|
+
hash[lib.name] ||= LibraryWithVersion.new(lib.name, [])
|
39
|
+
hash[lib.name].versions << lib.version
|
33
40
|
|
34
|
-
|
41
|
+
method = "to_s_#{format}".to_sym
|
42
|
+
if lib.respond_to?(method)
|
43
|
+
self.custom_print = false
|
44
|
+
lib.send(method)
|
45
|
+
end
|
46
|
+
end
|
35
47
|
|
36
|
-
|
37
|
-
|
38
|
-
|
48
|
+
if custom_print
|
49
|
+
previous = nil
|
50
|
+
results.each do |lib|
|
51
|
+
print_lib(hash[lib.name]) unless previous == lib.name
|
52
|
+
previous = lib.name
|
53
|
+
end
|
39
54
|
end
|
55
|
+
print_total_with_help
|
56
|
+
rescue Exception => e
|
57
|
+
error e
|
58
|
+
puts e.backtrace.join("\n") if ENV['DEBUG']
|
59
|
+
end
|
40
60
|
|
61
|
+
def print_lib(lib)
|
62
|
+
$stdout.puts "#{lib.name.bold.magenta} " +
|
63
|
+
(lib.versions ?
|
64
|
+
"(#{lib.versions.size} versions: #{lib.versions.reverse[0..5].join(', ').blue})": '')
|
65
|
+
end
|
66
|
+
|
67
|
+
def process_search_options!
|
68
|
+
self.search_string = extract_search_argument!
|
69
|
+
raise_error_unless_search_string!
|
70
|
+
|
71
|
+
self.limit = config.search.results.limit
|
72
|
+
search_opts = {}
|
41
73
|
begin
|
42
|
-
|
74
|
+
search_opts = eval("{ #{search_string} }")
|
43
75
|
rescue => e
|
44
|
-
|
45
|
-
e.message.red
|
76
|
+
handle_error(e)
|
46
77
|
end
|
47
78
|
|
48
79
|
unless search_opts.is_a?(::Hash) && search_opts.size > 0
|
49
|
-
raise Arli::Errors::
|
80
|
+
raise Arli::Errors::InvalidSearchSyntaxError,
|
81
|
+
"Search string '#{search_string}' did not eval to Hash.\n"
|
50
82
|
end
|
51
83
|
|
52
84
|
self.database = db_default
|
53
85
|
|
54
86
|
search_opts.merge!(limit: limit) if limit && limit > 0
|
55
87
|
search_opts.delete(:limit) if limit == 0
|
88
|
+
search_opts
|
56
89
|
end
|
57
90
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
91
|
+
def extract_search_argument!
|
92
|
+
search = runtime.argv.first
|
93
|
+
if search =~ /:/
|
94
|
+
search
|
95
|
+
elsif search
|
96
|
+
"#{config.search.default_field}: /^#{search}$/"
|
62
97
|
end
|
63
|
-
puts "\nTotal matches: #{results.size.to_s.bold.magenta}"
|
64
|
-
rescue Exception => e
|
65
|
-
error e
|
66
|
-
puts e.backtrace.join("\n") if ENV['DEBUG']
|
67
98
|
end
|
68
99
|
|
69
|
-
def
|
70
|
-
|
100
|
+
def raise_error_unless_search_string!
|
101
|
+
unless search_string
|
102
|
+
raise Arli::Errors::InvalidSyntaxError,
|
103
|
+
'Expected an argument or a flag to follow the command ' + 'search'.bold.green
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def handle_and_raise_error(e)
|
108
|
+
message = e.message
|
109
|
+
if message =~ /undefined method.*Arduino::Library::Model/
|
110
|
+
message = "Invalid attributed search. Possible values are:" +
|
111
|
+
"\n#{Arduino::Library::Types::LIBRARY_PROPERTIES.keys}"
|
112
|
+
end
|
113
|
+
raise Arli::Errors::InvalidSearchSyntaxError,
|
114
|
+
"Search string '#{search_string}' is invalid.\n" +
|
115
|
+
message.red
|
116
|
+
end
|
117
|
+
|
118
|
+
def print_total_with_help
|
119
|
+
puts "———————————————————————"
|
120
|
+
puts " Total Versions : #{results.size.to_s.bold.magenta}\n"
|
121
|
+
puts "Unique Libraries : #{hash.keys.size.to_s.bold.magenta}\n"
|
122
|
+
puts "———————————————————————"
|
123
|
+
if results.size == Arli::Configuration::DEFAULT_RESULTS_LIMIT
|
124
|
+
puts "Hint: use #{'-m 0'.bold.green} to disable the limit, or set it to another value."
|
125
|
+
end
|
71
126
|
end
|
72
127
|
|
73
128
|
def params
|