arli 0.5.1 → 0.6.1
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/.rake_tasks~ +8 -0
- data/README.md +122 -30
- data/Rakefile +24 -2
- data/arli.gemspec +4 -1
- data/exe/arli +2 -4
- data/lib/arli.rb +19 -21
- data/lib/arli/actions.rb +19 -0
- data/lib/arli/actions/action.rb +32 -0
- data/lib/arli/actions/backup.rb +30 -0
- data/lib/arli/actions/dir_name.rb +66 -0
- data/lib/arli/actions/git_repo.rb +43 -0
- data/lib/arli/actions/zip_file.rb +54 -0
- data/lib/arli/arli_file.rb +22 -37
- data/lib/arli/cli.rb +4 -190
- data/lib/arli/cli/app.rb +73 -0
- data/lib/arli/cli/command_finder.rb +72 -0
- data/lib/arli/{parser.rb → cli/parser.rb} +66 -56
- data/lib/arli/cli/parser_factory.rb +97 -0
- data/lib/arli/cli/runner.rb +40 -0
- data/lib/arli/commands.rb +8 -0
- data/lib/arli/commands/base.rb +23 -30
- data/lib/arli/commands/install.rb +14 -9
- data/lib/arli/commands/search.rb +35 -10
- data/lib/arli/configuration.rb +62 -0
- data/lib/arli/extensions.rb +9 -0
- data/lib/arli/installer.rb +30 -78
- data/lib/arli/library.rb +49 -0
- data/lib/arli/output.rb +94 -0
- data/lib/arli/version.rb +1 -1
- metadata +63 -8
- data/lib/arli/config.rb +0 -16
- data/lib/arli/installers/zip_file.rb +0 -61
- data/lib/arli/logger.rb +0 -13
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative 'action'
|
2
|
+
|
3
|
+
module Arli
|
4
|
+
module Actions
|
5
|
+
# The purpose of this action is to fix the directory
|
6
|
+
# name of the library, that's possibly incorrect.
|
7
|
+
# For example, library "Adafruit Unified Sensor" installs
|
8
|
+
# into the folder 'Adafruit_Unified_Sensor'
|
9
|
+
# while the source files inside are 'Adafruit_Sensor.h'
|
10
|
+
# This action renames invalid library folders based on the
|
11
|
+
# source files found inside.
|
12
|
+
class DirName < Action
|
13
|
+
attr_accessor :sources, :headers
|
14
|
+
|
15
|
+
def act
|
16
|
+
find_source_files
|
17
|
+
|
18
|
+
# so "dir" is the 'Adafruit_Unified_Sensor'
|
19
|
+
# but we found header Adafruit_Sensor we should
|
20
|
+
# rename the folder
|
21
|
+
|
22
|
+
return if headers.include?(dir)
|
23
|
+
return if sources.include?(dir)
|
24
|
+
|
25
|
+
___
|
26
|
+
|
27
|
+
# if we end up setting this, we'll also move the folder.
|
28
|
+
canonical_dir =
|
29
|
+
if_only_one(headers) ||
|
30
|
+
if_only_one(sources) ||
|
31
|
+
if_header_a_substring(headers)
|
32
|
+
|
33
|
+
if canonical_dir
|
34
|
+
library.canonical_dir = canonical_dir
|
35
|
+
FileUtils.rm_rf(canonical_dir) if Dir.exist?(canonical_dir)
|
36
|
+
___ " (#{canonical_dir.bold.green}) "
|
37
|
+
FileUtils.mv(dir, library.canonical_dir)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def if_header_a_substring(files)
|
42
|
+
files.find { |file| dir.start_with?(file) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def if_only_one(file_names)
|
46
|
+
if file_names.size == 1 && file_names.first != dir
|
47
|
+
file_names.first
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def find_source_files
|
52
|
+
Dir.chdir(dir) do
|
53
|
+
self.sources = files_with_extension('**.{cpp,c}')
|
54
|
+
self.headers = files_with_extension('**.{h}')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
EXT_REGEX = /\.(cpp|h|c)$/
|
59
|
+
EXT_CHOMP = ->(f) { f.gsub(EXT_REGEX, '') }
|
60
|
+
|
61
|
+
def files_with_extension(pattern)
|
62
|
+
Dir.glob(pattern).map(&EXT_CHOMP)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative 'action'
|
2
|
+
module Arli
|
3
|
+
module Actions
|
4
|
+
class GitRepo < Action
|
5
|
+
|
6
|
+
def act
|
7
|
+
c = library.exists? ? git_update_command : git_clone_command
|
8
|
+
___ 'running ' + c.blue + ' '
|
9
|
+
execute(c)
|
10
|
+
ok
|
11
|
+
rescue Exception => e
|
12
|
+
fuck
|
13
|
+
raise e
|
14
|
+
end
|
15
|
+
|
16
|
+
def git_update_command
|
17
|
+
"cd #{path} && git pull --rebase 2>&1"
|
18
|
+
end
|
19
|
+
|
20
|
+
def git_clone_command
|
21
|
+
"git clone -v #{library.url} #{path} 2>&1"
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
# @param <String> *args — list of arguments or a single string
|
27
|
+
def execute(*args)
|
28
|
+
cmd = args.join(' ')
|
29
|
+
raise 'No command to run was given' unless cmd
|
30
|
+
info("\n" + cmd.green) if Arli.debug?
|
31
|
+
o, e, s = Open3.capture3(cmd)
|
32
|
+
info("\n" + o) if o if Arli.debug?
|
33
|
+
info("\n" + e.red) if e && Arli.debug?
|
34
|
+
rescue Exception => e
|
35
|
+
error "Error running [#{args.join(' ')}]\n" +
|
36
|
+
"Current folder is [#{Dir.pwd.yellow}]", e
|
37
|
+
raise e
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'archive/zip'
|
2
|
+
require_relative 'action'
|
3
|
+
|
4
|
+
module Arli
|
5
|
+
module Actions
|
6
|
+
class ZipFile < Action
|
7
|
+
|
8
|
+
def act
|
9
|
+
___
|
10
|
+
library.rm_rf!
|
11
|
+
download!
|
12
|
+
if File.exist?(zip_archive)
|
13
|
+
ok; ___
|
14
|
+
FileUtils.rm_rf(zip_folder) if zip_folder
|
15
|
+
unzip(zip_archive, '.')
|
16
|
+
if Dir.exist?(zip_folder)
|
17
|
+
ok; ___
|
18
|
+
FileUtils.move(zip_folder, dir)
|
19
|
+
ok
|
20
|
+
end
|
21
|
+
end
|
22
|
+
rescue Exception => e
|
23
|
+
fuck
|
24
|
+
puts
|
25
|
+
raise(e)
|
26
|
+
ensure
|
27
|
+
delete_zip!
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def delete_zip!
|
33
|
+
FileUtils.rm_f(zip_archive) if File.exist?(zip_archive)
|
34
|
+
end
|
35
|
+
|
36
|
+
def download!
|
37
|
+
File.write(zip_archive, Net::HTTP.get(URI.parse(library.url)))
|
38
|
+
end
|
39
|
+
|
40
|
+
def zip_archive
|
41
|
+
@zip_archive ||= File.basename(library.url)
|
42
|
+
end
|
43
|
+
|
44
|
+
# list the contents of the archive and grab the top level folder
|
45
|
+
def zip_folder
|
46
|
+
@zip_folder ||= `unzip -Z1 #{zip_archive} | awk 'BEGIN{FS="/"}{print $1}' | uniq | tail -1`.chomp
|
47
|
+
end
|
48
|
+
|
49
|
+
def unzip(file, destination)
|
50
|
+
`unzip -o #{file} -d #{destination}`
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/arli/arli_file.rb
CHANGED
@@ -8,60 +8,45 @@ module Arli
|
|
8
8
|
require 'arduino/library/include'
|
9
9
|
|
10
10
|
include Enumerable
|
11
|
-
|
12
11
|
extend Forwardable
|
13
|
-
def_delegators :@dependencies, *(Array.new.methods - Object.methods)
|
14
|
-
|
15
|
-
attr_accessor :dependencies, :file_hash, :file, :lib_path, :resolved
|
16
12
|
|
17
|
-
|
18
|
-
arlifile_path: nil)
|
13
|
+
def_delegators :@dependencies, *(Array.new.methods - Object.methods)
|
19
14
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
self.dependencies = []
|
15
|
+
attr_accessor :dependencies,
|
16
|
+
:file_hash,
|
17
|
+
:file,
|
18
|
+
:library_path
|
25
19
|
|
26
|
-
|
27
|
-
|
20
|
+
def initialize(config: Arli.config)
|
21
|
+
self.library_path = config.libraries.path
|
22
|
+
self.file = "#{config.arlifile.path}/#{config.arlifile.name}"
|
28
23
|
|
24
|
+
unless file && File.exist?(file)
|
25
|
+
raise(Arli::Errors::ArliFileNotFound,
|
26
|
+
"Arlifile could not be found at\n#{file.bold.yellow}")
|
27
|
+
end
|
29
28
|
|
30
|
-
|
31
|
-
self.dependencies
|
29
|
+
FileUtils.mkpath(library_path) unless Dir.exist?(library_path)
|
30
|
+
self.dependencies = parse_file
|
32
31
|
end
|
33
32
|
|
34
|
-
def
|
35
|
-
|
36
|
-
Dir.chdir(lib_path) do
|
33
|
+
def within_library_path
|
34
|
+
Dir.chdir(library_path) do
|
37
35
|
yield if block_given?
|
38
36
|
end
|
39
37
|
end
|
40
38
|
|
41
|
-
def each_dependency(&
|
42
|
-
|
43
|
-
dependencies.each do |dependency|
|
44
|
-
yield(dependency)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def error(*args)
|
50
|
-
STDERR.puts *args.join("\n")
|
51
|
-
end
|
52
|
-
|
53
|
-
def info(*args)
|
54
|
-
STDOUT.puts *args.join("\n") if Arli.debug?
|
39
|
+
def each_dependency(&block)
|
40
|
+
within_library_path { dependencies.each(&block) }
|
55
41
|
end
|
56
42
|
|
57
43
|
private
|
58
44
|
|
59
|
-
def
|
60
|
-
self.file_hash
|
61
|
-
|
62
|
-
::Arduino::Library::Model.
|
45
|
+
def parse_file
|
46
|
+
self.file_hash = ::YAML.load(::File.read(self.file))
|
47
|
+
file_hash['dependencies'].map do |lib|
|
48
|
+
::Arli::Library.new(::Arduino::Library::Model.from(lib))
|
63
49
|
end
|
64
50
|
end
|
65
|
-
|
66
51
|
end
|
67
52
|
end
|
data/lib/arli/cli.rb
CHANGED
@@ -1,194 +1,8 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
require 'hashie/mash'
|
3
|
-
require 'hashie/extensions/symbolize_keys'
|
4
|
-
require 'colored2'
|
5
|
-
require 'arli'
|
6
|
-
require 'arli/parser'
|
7
|
-
require 'arli/commands/install'
|
8
|
-
require 'arli/commands/search'
|
9
|
-
|
10
1
|
module Arli
|
11
|
-
|
12
|
-
|
13
|
-
COMMAND = 'arli'.freeze
|
14
|
-
PARSER = ::Arli::CLI::Parser
|
15
|
-
|
16
|
-
attr_accessor :argv, :parser, :command_name, :command
|
17
|
-
attr_accessor :options
|
18
|
-
|
19
|
-
def initialize(argv = ARGV.dup)
|
20
|
-
self.argv = argv
|
21
|
-
self.options = Hashie::Mash.new
|
22
|
-
end
|
23
|
-
|
24
|
-
def parse
|
25
|
-
if argv.first
|
26
|
-
if argv.first =~ /^-.*$/
|
27
|
-
self.parser = self.class.global
|
28
|
-
elsif command_detected?
|
29
|
-
self.parser = parse_command_options!
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
if parser
|
34
|
-
parser.parse!(argv)
|
35
|
-
parser.print
|
36
|
-
options.merge!(parser.options)
|
37
|
-
end
|
38
|
-
|
39
|
-
self.options = Hashie::Extensions::SymbolizeKeys.symbolize_keys!(options.to_h)
|
40
|
-
|
41
|
-
unless options[:help]
|
42
|
-
self.command = create_command if command_name
|
43
|
-
execute
|
44
|
-
end
|
45
|
-
|
46
|
-
rescue OptionParser::InvalidOption => e
|
47
|
-
report_exception(e, 'Command line usage error!')
|
48
|
-
|
49
|
-
rescue Arli::Errors::InvalidCommandError
|
50
|
-
report_exception(e, 'This command does not exist')
|
51
|
-
|
52
|
-
rescue Exception => e
|
53
|
-
report_exception(e)
|
54
|
-
raise e if options[:trace]
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def report_exception(e, header = nil)
|
60
|
-
error header.bold.yellow if header
|
61
|
-
printf ' ☠ '
|
62
|
-
error e.message if e && e.respond_to?(:message)
|
63
|
-
end
|
64
|
-
|
65
|
-
def execute
|
66
|
-
if command
|
67
|
-
header
|
68
|
-
command.run
|
69
|
-
else
|
70
|
-
gp = self.class.global
|
71
|
-
gp.parse!(%w(--help))
|
72
|
-
gp.print
|
73
|
-
nil
|
74
|
-
end
|
75
|
-
rescue NameError => e
|
76
|
-
error e.inspect
|
77
|
-
error e.backtrace.join("\n") if options[:trace]
|
78
|
-
end
|
79
|
-
|
80
|
-
def create_command
|
81
|
-
command_class = ::Arli::Commands.const_get(command_name.to_s.capitalize)
|
82
|
-
|
83
|
-
options[:lib_home] ||= ::Arli.config.library_path
|
84
|
-
options[:argv] = argv
|
2
|
+
module CLi
|
85
3
|
|
86
|
-
info "created command #{command_name.to_s.green},\noptions: #{options.inspect.blue}" if Arli.debug?
|
87
|
-
|
88
|
-
command_class.new(options)
|
89
|
-
end
|
90
|
-
|
91
|
-
def header
|
92
|
-
out = ''
|
93
|
-
out << "Arli (#{::Arli::VERSION.yellow})"
|
94
|
-
out << " running #{command.name.to_s.blue}" if command
|
95
|
-
out << "\n"
|
96
|
-
out << "Library Folder: #{options[:lib_home].gsub(/#{ENV['HOME']}/, '~').green}\n" if options[:lib_home]
|
97
|
-
out << '——————————————————————————————————————————————————————————'
|
98
|
-
info out
|
99
|
-
end
|
100
|
-
|
101
|
-
def info(*args)
|
102
|
-
self.class.output(*args)
|
103
|
-
end
|
104
|
-
|
105
|
-
def error(*args)
|
106
|
-
self.class.output(*(args.compact.map { |a| a.to_s.red }))
|
107
|
-
end
|
108
|
-
|
109
|
-
def parse_command_options!
|
110
|
-
self.class.parser_for(command_name)
|
111
|
-
end
|
112
|
-
|
113
|
-
def command_detected?
|
114
|
-
self.command_name = argv.shift if argv.first && argv.first !~ /^-.*$/
|
115
|
-
if self.command_name
|
116
|
-
self.command_name = command_name.to_sym
|
117
|
-
unless self.class.commands.key?(command_name)
|
118
|
-
raise Arli::Errors::InvalidCommandError, "Error: #{command_name ? command_name.to_s.red : 'nil'} is not a valid arli command_name!"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
self.command_name
|
122
|
-
end
|
123
|
-
|
124
|
-
class << self
|
125
|
-
|
126
|
-
def output(*args)
|
127
|
-
puts args.join("\n") unless args.empty?
|
128
|
-
end
|
129
|
-
|
130
|
-
def global
|
131
|
-
@global ||= PARSER.new do |parser|
|
132
|
-
parser.banner = usage_line
|
133
|
-
parser.sep
|
134
|
-
parser.option_help(commands: true)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def usage_line(command = nil)
|
139
|
-
command ? command_usage(command) : global_usage(command)
|
140
|
-
end
|
141
|
-
|
142
|
-
def global_usage(command)
|
143
|
-
"Usage:\n " + COMMAND.blue +
|
144
|
-
' [options] '.yellow + '[ ' + (command || 'command').green +
|
145
|
-
' [options] '.yellow + ' ]' + "\n"
|
146
|
-
end
|
147
|
-
|
148
|
-
def command_usage(command)
|
149
|
-
"Usage:\n " + COMMAND.blue + ' ' +
|
150
|
-
command.green +
|
151
|
-
' [options]'.yellow + "\n\n" +
|
152
|
-
'Command Options'
|
153
|
-
end
|
154
|
-
|
155
|
-
def commands
|
156
|
-
@commands ||= {
|
157
|
-
install: {
|
158
|
-
description: 'installs libraries defined in Arlifile',
|
159
|
-
parser: -> (command_name) {
|
160
|
-
PARSER.new do |parser|
|
161
|
-
parser.banner = usage_line 'install'
|
162
|
-
parser.option_lib_home
|
163
|
-
parser.option_dependency_file
|
164
|
-
parser.option_abort_if_exists
|
165
|
-
parser.option_help(command_name: command_name)
|
166
|
-
end
|
167
|
-
} },
|
168
|
-
|
169
|
-
search: {
|
170
|
-
description: 'Flexible Search of the Arduino Library Database',
|
171
|
-
example: 'arli search '.green + %Q['name: /AudioZero/, version: "1.0.1"'].green,
|
172
|
-
parser: -> (command_name) {
|
173
|
-
PARSER.new do |parser|
|
174
|
-
parser.banner = usage_line 'search ' + '<query>'.magenta
|
175
|
-
parser.option_search
|
176
|
-
parser.option_help(command_name: command_name)
|
177
|
-
end
|
178
|
-
}
|
179
|
-
}
|
180
|
-
}
|
181
|
-
end
|
182
|
-
|
183
|
-
def parser_for(cmd)
|
184
|
-
if commands[cmd]
|
185
|
-
cmd_hash = commands[cmd]
|
186
|
-
commands[cmd][:parser][cmd_hash]
|
187
|
-
else
|
188
|
-
raise(Arli::Errors::InvalidCommandError,
|
189
|
-
"'#{cmd}' is not a valid command_name.\nSupported commands are:\n\t#{commands.keys.join("\n\t")}")
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
4
|
end
|
194
5
|
end
|
6
|
+
|
7
|
+
require_relative 'cli/parser_factory'
|
8
|
+
require_relative 'cli/runner'
|
data/lib/arli/cli/app.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'optparse'
|
3
|
+
require 'colored2'
|
4
|
+
|
5
|
+
require_relative 'parser'
|
6
|
+
require_relative 'command_finder'
|
7
|
+
require_relative 'parser_factory'
|
8
|
+
require_relative '../commands'
|
9
|
+
require_relative '../output'
|
10
|
+
|
11
|
+
|
12
|
+
module Arli
|
13
|
+
module CLI
|
14
|
+
class App
|
15
|
+
include Arli::Output
|
16
|
+
|
17
|
+
attr_accessor :argv, :config, :command
|
18
|
+
|
19
|
+
def initialize(argv, config: Arli.config)
|
20
|
+
self.argv = argv
|
21
|
+
self.config = config
|
22
|
+
self.config.runtime.argv = argv
|
23
|
+
end
|
24
|
+
|
25
|
+
def start
|
26
|
+
if argv.empty?
|
27
|
+
factory.default_help
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
parse_global_flags
|
32
|
+
return if Arli.config.help
|
33
|
+
|
34
|
+
finder = CommandFinder.new(argv, config: config)
|
35
|
+
finder.parse!
|
36
|
+
return if Arli.config.help
|
37
|
+
|
38
|
+
if finder.command
|
39
|
+
self.command = finder.command
|
40
|
+
execute!
|
41
|
+
else
|
42
|
+
factory.default_help
|
43
|
+
end
|
44
|
+
rescue OptionParser::InvalidOption => e
|
45
|
+
report_exception(e, 'Invalid flags or options')
|
46
|
+
rescue Arli::Errors::InvalidCommandError => e
|
47
|
+
report_exception(e, 'Unknown command')
|
48
|
+
rescue Exception => e
|
49
|
+
report_exception(e)
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_global_flags
|
53
|
+
if argv.first && argv.first.start_with?('-')
|
54
|
+
parser = factory.global_parser
|
55
|
+
factory.parse_argv(parser, argv)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def execute!
|
60
|
+
if command
|
61
|
+
header(command: command) if config.verbose
|
62
|
+
command.run
|
63
|
+
else
|
64
|
+
factory.default_help
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def factory
|
69
|
+
Arli::CLI::ParserFactory
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|