lib/upm/tool.rb in upm-0.1.6 vs lib/upm/tool.rb in upm-0.1.7

- old
+ new

@@ -1,5 +1,7 @@ +require 'upm/tool_dsl' +require 'upm/tool_class_methods' # os:<pkg> -- automatically select the package manager for the current unix distribution # deb:<pkg> (or d: u:) # rpm:<pkg> (or yum: y:) # bsd:<pkg> (or b:) @@ -8,203 +10,58 @@ module UPM class Tool + @@tools = {} + + include UPM::Tool::DSL + COMMAND_HELP = { "install" => "install a package", "remove/uninstall" => "remove a package", "build" => "compile a package from source and install it", "search" => "using the fastest known API or service", "list" => "list installed packages (or search their names if extra arguments are supplied)", "info" => "show metadata about a package", "update/sync" => "retrieve the latest package list or manifest", - "upgrade" => "install new versions of all packages", + "upgrade" => "update package list and install updates", + "download" => "download package list and updates, but don't insatall them", "pin" => "pinning a package means it won't be automatically upgraded", "rollback" => "revert to an earlier version of a package (including its dependencies)", + "verify/check" => "verify the integrity of packages' files on the filesystem", + "audit/vuln" => "show known vulnerabilities in installed packages", "log" => "show history of package installs", "packagers" => "detect installed package managers, and pick which ones upm should wrap", "mirrors/sources" => "manage remote repositories and mirrors", - "verfiy" => "verify the integrity of installed files", "clean" => "clear out the local package cache", "monitor" => "ad-hoc package manager for custom installations (like instmon)", "keys" => "keyrings and package authentication", "default" => "configure the action to take when no arguments are passed to 'upm' (defaults to 'os:update')", + "stats" => "show statistics about package database(s)", } ALIASES = { "file" => "files", "sync" => "update", "sources" => "mirrors", "show" => "info", + "vuln" => "audit", + "vulns" => "audit", + "check" => "verify", + "u" => "upgrade", + "i" => "install", + "d" => "download", + "s" => "search", + "f" => "files", + "r" => "remove", } - @@tools = {} - def self.tools; @@tools; end - - def self.register_tools! - Dir["#{__dir__}/tools/*.rb"].each { |lib| require_relative(lib) } - end - - def self.os_release - @os_release ||= begin - open("/etc/os-release") do |io| - io.read.scan(/^(\w+)="?(.+?)"?$/) - end.to_h - rescue Errno::ENOENT - {} - end - end - - def self.current_os_names - # ID=ubuntu - # ID_LIKE=debian - os_release.values_at("ID", "ID_LIKE").compact - end - - def self.nice_os_name - os_release.values_at("PRETTY_NAME", "NAME", "ID", "ID_LIKE").first || - (`uname -o`.chomp rescue nil) - end - - def self.for_os(os_names=nil) - os_names = os_names ? [os_names].flatten : current_os_names - - tool = nil - - if os_names.any? - tool = @@tools.find { |name, tool| os_names.any? { |name| tool.os.include? name } }.last - end - - if tool.nil? - tool = @@tools.find { |name, tool| File.which(tool.identifying_binary) }.last - end - - if tool.nil? - puts "Error: couldn't find a package manager." - end - - tool - end - - ################################################################### - def initialize(name, &block) @name = name instance_eval(&block) @@tools[name] = self - end - - def call_command(name, *args) - if block = (@cmds[name] || @cmds[ALIASES[name]]) - block.call args - else - puts "Command #{name} not supported in #{@name}" - end - end - - def help - if osname = Tool.nice_os_name - puts " Detected OS: #{osname}" - end - - puts "Package manager: #{@name}" - puts - puts "Available commands:" - available = COMMAND_HELP.select do |name, desc| - names = name.split("/") - names.any? { |name| @cmds[name] } - end - - max_width = available.map(&:first).map(&:size).max - available.each do |name, desc| - puts " #{name.rjust(max_width)} | #{desc}" - end - end - - ## DSL methods - - def identifying_binary(id_bin=nil) - if id_bin - @id_bin = id_bin - else - @id_bin || @name - end - end - - def prefix(name) - @prefix = name - end - - def command(name, shell_command=nil, root: false, paged: false, &block) - @cmds ||= {} - - if block_given? - @cmds[name] = block - elsif shell_command - if shell_command.is_a? String - shell_command = shell_command.split - elsif not shell_command.is_a? Array - raise "Error: command argument must be a String or an Array; it was a #{cmd.class}" - end - - @cmds[name] = proc { |args| run(*shell_command, *args, paged: paged, root: root) } - end - end - - def os(*names) - names.any? ? @os = names : @os - end - - ## Helpers - - def run(*args, root: false, paged: false, grep: nil) - if root and File.which("sudo") - args.unshift "sudo" - end - - if !paged and !grep - system(*args) - else - - IO.popen(args, err: [:child, :out]) do |command_io| - - if grep - pattern = grep.is_a?(Regexp) ? grep.source : grep.to_s - grep_io = IO.popen(["grep", "--color=always", "-Ei", pattern], "w+") - IO.copy_stream(command_io, grep_io) - grep_io.close_write - command_io = grep_io - end - - if paged - lesspipe do |less| - IO.copy_stream(command_io, less) - end - else - IO.copy_stream(command_io, STDOUT) - end - - end - - $?.to_i == 0 - end - end - - def print_files(*paths, include: nil, exclude: nil) - lesspipe do |less| - paths.each do |path| - less.puts "<8>=== <11>#{path} <8>========".colorize - open(path) do |io| - enum = io.each_line - enum = enum.grep(include) if include - enum = enum.reject { |line| line[exclude] } if exclude - enum.each { |line| less.puts line } - end - less.puts - end - end end end # class Tool end # module UPM