arli 0.5.1 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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