arli 0.7.0 → 0.8.3
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 +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
|