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.
@@ -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
@@ -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
- def initialize(lib_path: Arli.library_path,
18
- arlifile_path: nil)
13
+ def_delegators :@dependencies, *(Array.new.methods - Object.methods)
19
14
 
20
- self.lib_path = lib_path
21
- self.file = arlifile_path ? "#{arlifile_path}/#{Arli::Config::DEFAULT_FILENAME}" :
22
- Arli::Config::DEFAULT_FILENAME
23
- raise(Arli::Errors::ArliFileNotFound, 'Arlifile could not be found') unless file || !File.exist?(file)
24
- self.dependencies = []
15
+ attr_accessor :dependencies,
16
+ :file_hash,
17
+ :file,
18
+ :library_path
25
19
 
26
- parse!
27
- end
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
- def libraries
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 within_lib_path
35
- FileUtils.mkpath(lib_path) unless Dir.exist?(lib_path)
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(&_block)
42
- within_lib_path do
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 parse!
60
- self.file_hash = ::YAML.load(::File.read(self.file))
61
- self.dependencies = file_hash['dependencies'].map do |lib|
62
- ::Arduino::Library::Model.from_hash(lib)
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
@@ -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
- class CLI
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'
@@ -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