upm 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5dd0714a5b73fc48e824d842aa8d4eb3f09f4c6cf3b72d3da04a9837bb8ce661
4
- data.tar.gz: b2b9f7acd21647b482ef8cc799fd5df72832a6500fad45a81026aa9cf567726f
3
+ metadata.gz: f8a190f9686ce55b16d74b0ff642047a17e19683187a40d40cebfc542262fcff
4
+ data.tar.gz: 470640b6500e070840f9f03131e0087d2b3fa3ab25d6450dee3d130463f082b8
5
5
  SHA512:
6
- metadata.gz: 3200e00ca2b898b2ed00c7505ddb005a9ec5bacd5a29fa7c53a89306cbd9264898259e32e46c44ec54d43c618300d54d6324a573ee224aed8005902e82bc55c5
7
- data.tar.gz: 56032bf6894c836b70ff22cfdd842b10d180778d362160bbb3156ab3f3a01e8be10c81b60edcfa332a38fb7936c1599ecb8c49285cccee2c0b503a169900edbb
6
+ metadata.gz: 39453d5478a636f20c45580e91f5a7c9251a0258274e1da16a9736e5b09a74ba7cf4204c65205f44430a35b4a2c7a316a518cd3f5eead7e448723afe747b8e28
7
+ data.tar.gz: 2b761cbb9ea039c2257e263b46302df7e3c55b4c31849366a5f535141224aa9b1e52a400b75fec88a55486f9cc35a21f96e0d8990a124eb2f0c695e0d873e66c
data/TODO.md CHANGED
@@ -1,18 +1,29 @@
1
1
  # TODO
2
2
 
3
+ ## Pipes and filters
4
+
5
+ * `Tool::DSL#run` is currently somewhat awkward; it would be simpler if returned an `Enumerator`, which could then be filtered (ie: highlight/grep), or concatenated to other `Enumerator`s.
6
+
7
+ ## Streaming pipes with colours
8
+
9
+ * Make the `run` command able to grep the output while streaming the results to the screen.
10
+ * Make run pretend to be a tty, so I don't need `--color=always`.
11
+ * Use spawn, like so:
12
+ ```
13
+ r,w = IO.pipe
14
+ spawn(*%w[echo hello world], out: w)
15
+ spawn(*%w[tr a-z A-Z], in: r)
16
+ ```
17
+
3
18
  ## More package managers
4
19
 
5
20
  Currently missing:
6
21
  * RedHat/Fedora/CentOS
7
22
  * OSX
8
- * FreeBSD
23
+ * <s>FreeBSD</s>
9
24
  * OpenBSD
10
25
  * SuSE
11
26
 
12
- ## Abbrev cmds
13
-
14
- * eg: upm install => upm i => u i
15
-
16
27
  ## Dependency-fetching features
17
28
 
18
29
  * Use upm to fetch dependencies for any library or script, across any language.
@@ -37,17 +48,6 @@ Use fzf for "list" output (or other commands that require selecting, like "remov
37
48
  * upm install --help
38
49
  * upm help install
39
50
 
40
- ## Streaming pipes with colours
41
-
42
- * Make the `run` command able to grep the output while streaming the results to the screen.
43
- * Make run pretend to be a tty, so I don't need `--color=always`.
44
- * Use spawn, like so:
45
- ```
46
- r,w = IO.pipe
47
- spawn(*%w[echo hello world], out: w)
48
- spawn(*%w[tr a-z A-Z], in: r)
49
- ```
50
-
51
51
  ## Figure out how to integrate language package managers
52
52
 
53
53
  * The packages that you can get through gem/pip/luarocks/etc. are often duplicated in the OS-level package managers. Should there be a preference?
@@ -88,3 +88,12 @@ Don't print backtrace when ^C is pressed.
88
88
  ## Tests
89
89
 
90
90
  Create fake OS environments that you can chroot into and run upm to test it out.
91
+
92
+
93
+
94
+ # DONE
95
+
96
+ ## Abbrev cmds
97
+
98
+ * eg: upm install => upm i => u i
99
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.6
1
+ 0.1.7
data/bin/upm CHANGED
@@ -4,11 +4,18 @@ $LOAD_PATH.unshift(File.expand_path(File.join('..', 'lib'), bin_dir))
4
4
 
5
5
  require 'upm'
6
6
 
7
- tool = UPM::Tool.for_os
7
+ unless tool = UPM::Tool.for_os
8
+ $stderr.puts "Error: I don't recognize this OS, or its package manager."
9
+ exit 1
10
+ end
11
+
8
12
  command, *args = ARGV
9
13
 
10
14
  if command.nil?
11
15
  tool.help
12
16
  else
13
- tool.call_command command, *args
17
+ begin
18
+ tool.call_command command, *args
19
+ rescue Interrupt
20
+ end
14
21
  end
data/lib/upm/core_ext.rb CHANGED
@@ -5,21 +5,38 @@ class DateTime
5
5
  end
6
6
 
7
7
  class File
8
- def self.which(bin)
9
- ENV["PATH"].split(":").each do |dir|
10
- full_path = File.join(dir, bin)
11
- return full_path if File.exists? full_path
8
+
9
+ #
10
+ # Overly clever which(), which returns an array if more than one argument was supplied,
11
+ # or string/nil if only one argument was supplied.
12
+ #
13
+ def self.which(*bins)
14
+ results = []
15
+ bins = bins.flatten
16
+ paths = ENV["PATH"].split(":").map { |path| File.realpath(path) rescue nil }.compact.uniq
17
+
18
+ paths.each do |dir|
19
+ bins.each do |bin|
20
+
21
+ full_path = File.join(dir, bin)
22
+
23
+ if File.exists?(full_path)
24
+ if bins.size == 1
25
+ return full_path
26
+ else
27
+ results << full_path
28
+ end
29
+ end
30
+
31
+ end
12
32
  end
13
- nil
33
+
34
+ bins.size == 1 ? nil : results
14
35
  end
15
36
 
16
37
  def self.which_is_best?(*bins)
17
- bins.flatten.each do |bin|
18
- if location = which(bin)
19
- return location
20
- end
21
- end
22
- nil
38
+ result = which(*bins.flatten)
39
+ result.is_a?(Array) ? result.first : result
23
40
  end
24
41
  end
25
42
 
@@ -147,4 +164,4 @@ module Enumerable
147
164
  end
148
165
 
149
166
  alias_method :cut_between, :split_between
150
- end
167
+ end
data/lib/upm/lesspipe.rb CHANGED
@@ -33,6 +33,16 @@ def lesspipe(*args)
33
33
 
34
34
  output = args.first if args.any?
35
35
 
36
+ # Don't page, just output to STDOUT
37
+ if options[:disabled]
38
+ if output
39
+ puts output
40
+ else
41
+ yield STDOUT
42
+ end
43
+ return
44
+ end
45
+
36
46
  params = []
37
47
  params << "-R" unless options[:color] == false
38
48
  params << "-S" unless options[:wrap] == true
@@ -0,0 +1,124 @@
1
+ require 'zlib'
2
+ require 'digest/sha2'
3
+ require 'digest/md5'
4
+ require 'pp'
5
+
6
+ module UPM
7
+ class PacmanVerifier
8
+
9
+ SKIP_FILES = %w[
10
+ /.BUILDINFO
11
+ /.INSTALL
12
+ /.PKGINFO
13
+ /.CHANGELOG
14
+ ]
15
+
16
+ PACKAGE_ROOT = "/var/lib/pacman/local/"
17
+
18
+ def compare(key, a, b)
19
+ a == b ? nil : [key, a, b]
20
+ end
21
+
22
+ def verify!(*included)
23
+ $stderr.puts "Checking integrity of #{included.any? ? included.size : "installed"} packages..."
24
+
25
+ report = []
26
+
27
+ Dir.entries(PACKAGE_ROOT).each do |package_dir|
28
+ mtree_path = File.join(PACKAGE_ROOT, package_dir, "mtree")
29
+ next unless File.exists?(mtree_path)
30
+
31
+ chunks = package_dir.split("-")
32
+ version = chunks[-2..-1].join("-")
33
+ package = chunks[0...-2].join("-")
34
+
35
+ next unless included.any? and included.include?(package)
36
+
37
+ puts "<8>[<7>+<8>] <10>#{package} <3>#{version}".colorize
38
+
39
+ result = []
40
+ defaults = {}
41
+
42
+ Zlib::GzipReader.open(mtree_path) do |io|
43
+ lines = io.each_line.drop(1)
44
+
45
+ lines.each do |line|
46
+ path, *expected = line.split
47
+ expected = expected.map { |opt| opt.split("=") }.to_h
48
+
49
+ if path == "/set"
50
+ defaults = expected
51
+ next
52
+ end
53
+
54
+ path = path[1..-1] if path[0] == "."
55
+ path = path.gsub(/\\(\d{3})/) { |m| $1.to_i(8).chr } # unescape \### codes
56
+
57
+ # next if expected["type"] == "dir"
58
+ next if SKIP_FILES.include?(path)
59
+
60
+ expected = defaults.merge(expected)
61
+ lstat = File.lstat(path)
62
+
63
+ errors = expected.map do |key, val|
64
+ case key
65
+ when "type"
66
+ compare("type", lstat.ftype[0...val.size], val)
67
+ when "link"
68
+ next if val == "/dev/null"
69
+ compare("link", File.readlink(path), val)
70
+ when "gid"
71
+ compare("gid", lstat.gid, val.to_i)
72
+ when "uid"
73
+ compare("uid", lstat.uid, val.to_i)
74
+ when "mode"
75
+ compare("mode", "%o" % (lstat.mode & 0xFFF), val)
76
+ when "size"
77
+ compare("size", lstat.size, val.to_i)
78
+ when "time"
79
+ next if expected["type"] == "dir"
80
+ next if expected["link"] == "/dev/null"
81
+ compare("time", lstat.mtime.to_i, val.to_i)
82
+ when "sha256digest"
83
+ compare("sha256digest", Digest::SHA256.file(path).hexdigest, val)
84
+ when "md5digest"
85
+ next if expected["sha256digest"]
86
+ compare("md5digest", Digest::MD5.file(path).hexdigest, val)
87
+ else
88
+ raise "Unknown key: #{key}=#{val}"
89
+ end
90
+ end.compact
91
+
92
+ if errors.any?
93
+ puts " <4>[<12>*<4>] <11>#{path}".colorize
94
+ errors.each do |key, a, e| # a=actual, e=expected
95
+ puts " <7>expected <14>#{key} <7>to be <2>#{e} <7>but was <4>#{a}".colorize
96
+ result << [path, "expected #{key.inspect} to be #{e.inspect} but was #{a.inspect}"]
97
+ end
98
+ end
99
+ rescue Errno::EACCES
100
+ puts " <1>[<9>!<1>] <11>Can't read <7>#{path} <8>(<9>permission denied<8>)".colorize
101
+ result << [path, "permission denied"]
102
+ rescue Errno::ENOENT
103
+ puts " <4>[<12>?<4>] <12>Missing file <15>#{path}".colorize
104
+ result << [path, "missing"]
105
+ end
106
+ end # gzip
107
+
108
+ report << [package, result] if result.any?
109
+ end # mtree
110
+
111
+ puts
112
+ puts "#{report.size} packages with errors (#{report.map { |result| result.size }.sum} errors total)"
113
+ puts
114
+
115
+ if report.any?
116
+ puts "Packages with problems:"
117
+ report.each do |package, errors|
118
+ puts " #{package} (#{errors.size} errors)"
119
+ end
120
+ end
121
+ end # verify!
122
+
123
+ end # PacmanAuditor
124
+ end # UPM
data/lib/upm/tool.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'upm/tool_dsl'
2
+ require 'upm/tool_class_methods'
1
3
 
2
4
  # os:<pkg> -- automatically select the package manager for the current unix distribution
3
5
  # deb:<pkg> (or d: u:)
@@ -10,6 +12,10 @@ module UPM
10
12
 
11
13
  class Tool
12
14
 
15
+ @@tools = {}
16
+
17
+ include UPM::Tool::DSL
18
+
13
19
  COMMAND_HELP = {
14
20
  "install" => "install a package",
15
21
  "remove/uninstall" => "remove a package",
@@ -18,17 +24,20 @@ module UPM
18
24
  "list" => "list installed packages (or search their names if extra arguments are supplied)",
19
25
  "info" => "show metadata about a package",
20
26
  "update/sync" => "retrieve the latest package list or manifest",
21
- "upgrade" => "install new versions of all packages",
27
+ "upgrade" => "update package list and install updates",
28
+ "download" => "download package list and updates, but don't insatall them",
22
29
  "pin" => "pinning a package means it won't be automatically upgraded",
23
30
  "rollback" => "revert to an earlier version of a package (including its dependencies)",
31
+ "verify/check" => "verify the integrity of packages' files on the filesystem",
32
+ "audit/vuln" => "show known vulnerabilities in installed packages",
24
33
  "log" => "show history of package installs",
25
34
  "packagers" => "detect installed package managers, and pick which ones upm should wrap",
26
35
  "mirrors/sources" => "manage remote repositories and mirrors",
27
- "verfiy" => "verify the integrity of installed files",
28
36
  "clean" => "clear out the local package cache",
29
37
  "monitor" => "ad-hoc package manager for custom installations (like instmon)",
30
38
  "keys" => "keyrings and package authentication",
31
39
  "default" => "configure the action to take when no arguments are passed to 'upm' (defaults to 'os:update')",
40
+ "stats" => "show statistics about package database(s)",
32
41
  }
33
42
 
34
43
  ALIASES = {
@@ -36,58 +45,17 @@ module UPM
36
45
  "sync" => "update",
37
46
  "sources" => "mirrors",
38
47
  "show" => "info",
48
+ "vuln" => "audit",
49
+ "vulns" => "audit",
50
+ "check" => "verify",
51
+ "u" => "upgrade",
52
+ "i" => "install",
53
+ "d" => "download",
54
+ "s" => "search",
55
+ "f" => "files",
56
+ "r" => "remove",
39
57
  }
40
58
 
41
- @@tools = {}
42
- def self.tools; @@tools; end
43
-
44
- def self.register_tools!
45
- Dir["#{__dir__}/tools/*.rb"].each { |lib| require_relative(lib) }
46
- end
47
-
48
- def self.os_release
49
- @os_release ||= begin
50
- open("/etc/os-release") do |io|
51
- io.read.scan(/^(\w+)="?(.+?)"?$/)
52
- end.to_h
53
- rescue Errno::ENOENT
54
- {}
55
- end
56
- end
57
-
58
- def self.current_os_names
59
- # ID=ubuntu
60
- # ID_LIKE=debian
61
- os_release.values_at("ID", "ID_LIKE").compact
62
- end
63
-
64
- def self.nice_os_name
65
- os_release.values_at("PRETTY_NAME", "NAME", "ID", "ID_LIKE").first ||
66
- (`uname -o`.chomp rescue nil)
67
- end
68
-
69
- def self.for_os(os_names=nil)
70
- os_names = os_names ? [os_names].flatten : current_os_names
71
-
72
- tool = nil
73
-
74
- if os_names.any?
75
- tool = @@tools.find { |name, tool| os_names.any? { |name| tool.os.include? name } }.last
76
- end
77
-
78
- if tool.nil?
79
- tool = @@tools.find { |name, tool| File.which(tool.identifying_binary) }.last
80
- end
81
-
82
- if tool.nil?
83
- puts "Error: couldn't find a package manager."
84
- end
85
-
86
- tool
87
- end
88
-
89
- ###################################################################
90
-
91
59
  def initialize(name, &block)
92
60
  @name = name
93
61
  instance_eval(&block)
@@ -95,117 +63,6 @@ module UPM
95
63
  @@tools[name] = self
96
64
  end
97
65
 
98
- def call_command(name, *args)
99
- if block = (@cmds[name] || @cmds[ALIASES[name]])
100
- block.call args
101
- else
102
- puts "Command #{name} not supported in #{@name}"
103
- end
104
- end
105
-
106
- def help
107
- if osname = Tool.nice_os_name
108
- puts " Detected OS: #{osname}"
109
- end
110
-
111
- puts "Package manager: #{@name}"
112
- puts
113
- puts "Available commands:"
114
- available = COMMAND_HELP.select do |name, desc|
115
- names = name.split("/")
116
- names.any? { |name| @cmds[name] }
117
- end
118
-
119
- max_width = available.map(&:first).map(&:size).max
120
- available.each do |name, desc|
121
- puts " #{name.rjust(max_width)} | #{desc}"
122
- end
123
- end
124
-
125
- ## DSL methods
126
-
127
- def identifying_binary(id_bin=nil)
128
- if id_bin
129
- @id_bin = id_bin
130
- else
131
- @id_bin || @name
132
- end
133
- end
134
-
135
- def prefix(name)
136
- @prefix = name
137
- end
138
-
139
- def command(name, shell_command=nil, root: false, paged: false, &block)
140
- @cmds ||= {}
141
-
142
- if block_given?
143
- @cmds[name] = block
144
- elsif shell_command
145
- if shell_command.is_a? String
146
- shell_command = shell_command.split
147
- elsif not shell_command.is_a? Array
148
- raise "Error: command argument must be a String or an Array; it was a #{cmd.class}"
149
- end
150
-
151
- @cmds[name] = proc { |args| run(*shell_command, *args, paged: paged, root: root) }
152
- end
153
- end
154
-
155
- def os(*names)
156
- names.any? ? @os = names : @os
157
- end
158
-
159
- ## Helpers
160
-
161
- def run(*args, root: false, paged: false, grep: nil)
162
- if root and File.which("sudo")
163
- args.unshift "sudo"
164
- end
165
-
166
- if !paged and !grep
167
- system(*args)
168
- else
169
-
170
- IO.popen(args, err: [:child, :out]) do |command_io|
171
-
172
- if grep
173
- pattern = grep.is_a?(Regexp) ? grep.source : grep.to_s
174
- grep_io = IO.popen(["grep", "--color=always", "-Ei", pattern], "w+")
175
- IO.copy_stream(command_io, grep_io)
176
- grep_io.close_write
177
- command_io = grep_io
178
- end
179
-
180
- if paged
181
- lesspipe do |less|
182
- IO.copy_stream(command_io, less)
183
- end
184
- else
185
- IO.copy_stream(command_io, STDOUT)
186
- end
187
-
188
- end
189
-
190
- $?.to_i == 0
191
- end
192
- end
193
-
194
- def print_files(*paths, include: nil, exclude: nil)
195
- lesspipe do |less|
196
- paths.each do |path|
197
- less.puts "<8>=== <11>#{path} <8>========".colorize
198
- open(path) do |io|
199
- enum = io.each_line
200
- enum = enum.grep(include) if include
201
- enum = enum.reject { |line| line[exclude] } if exclude
202
- enum.each { |line| less.puts line }
203
- end
204
- less.puts
205
- end
206
- end
207
- end
208
-
209
66
  end # class Tool
210
67
 
211
68
  end # module UPM
@@ -0,0 +1,59 @@
1
+ module UPM
2
+ class Tool
3
+ class << self
4
+
5
+ def tools; @@tools; end
6
+
7
+ def register_tools!
8
+ Dir["#{__dir__}/tools/*.rb"].each { |lib| require_relative(lib) }
9
+ end
10
+
11
+ def os_release
12
+ @os_release ||= begin
13
+ open("/etc/os-release") do |io|
14
+ io.read.scan(/^(\w+)="?(.+?)"?$/)
15
+ end.to_h
16
+ rescue Errno::ENOENT
17
+ {}
18
+ end
19
+ end
20
+
21
+ def current_os_names
22
+ # eg: ID=ubuntu, ID_LIKE=debian
23
+ if os_release
24
+ os_release.values_at("ID", "ID_LIKE").compact
25
+ else
26
+ # `uname -s` => Darwin|FreeBSD|OpenBSD
27
+ # `uname -o` => Android|Cygwin
28
+ [`uname -s`, `uname -o`].map(&:chomp).uniq
29
+ end
30
+ end
31
+
32
+ def nice_os_name
33
+ os_release.values_at("PRETTY_NAME", "NAME", "ID", "ID_LIKE").first ||
34
+ (`uname -o`.chomp rescue nil)
35
+ end
36
+
37
+ def installed
38
+ @@tools.select { |tool| File.which(tool.identifying_binary) }
39
+ end
40
+
41
+ def for_os(os_names=nil)
42
+ os_names = os_names ? [os_names].flatten : current_os_names
43
+
44
+ tool = nil
45
+
46
+ if os_names.any?
47
+ tool = @@tools.find { |name, tool| os_names.any? { |name| tool.os.include? name } }&.last
48
+ end
49
+
50
+ if tool.nil?
51
+ tool = @@tools.find { |name, tool| File.which(tool.identifying_binary) }&.last
52
+ end
53
+
54
+ tool
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,144 @@
1
+ module UPM
2
+ class Tool
3
+ module DSL
4
+ def identifying_binary(id_bin=nil)
5
+ if id_bin
6
+ @id_bin = id_bin
7
+ else
8
+ @id_bin || @name
9
+ end
10
+ end
11
+
12
+ def prefix(name)
13
+ @prefix = name
14
+ end
15
+
16
+ def command(name, shell_command=nil, root: false, paged: false, highlight: nil, &block)
17
+ @cmds ||= {}
18
+
19
+ if block_given?
20
+ @cmds[name] = block
21
+ elsif shell_command
22
+ if shell_command.is_a? String
23
+ shell_command = shell_command.split
24
+ elsif not shell_command.is_a? Array
25
+ raise "Error: command argument must be a String or an Array; it was a #{cmd.class}"
26
+ end
27
+
28
+ @cmds[name] = proc do |args|
29
+ query = highlight && args.join(" ")
30
+ run(*shell_command, *args, paged: paged, root: root, highlight: query)
31
+ end
32
+ end
33
+ end
34
+
35
+ def os(*names)
36
+ names.any? ? @os = names : @os
37
+ end
38
+
39
+ ## Helpers
40
+
41
+ def run(*args, root: false, paged: false, grep: nil, highlight: nil)
42
+ if root
43
+ if Process.uid != 0
44
+ if File.which("sudo")
45
+ args.unshift "sudo"
46
+ elsif File.which("su")
47
+ args = ["su", "-c"] + args
48
+ else
49
+ raise "Error: You must be root to run this command. (And I couldn't find the 'sudo' *or* 'su' commands.)"
50
+ end
51
+ end
52
+ end
53
+
54
+ if !paged and !grep
55
+ system(*args)
56
+ else
57
+
58
+ IO.popen(args, err: [:child, :out]) do |command_io|
59
+
60
+ # if grep
61
+ # pattern = grep.is_a?(Regexp) ? grep.source : grep.to_s
62
+ # grep_io = IO.popen(["grep", "--color=always", "-Ei", pattern], "w+")
63
+ # IO.copy_stream(command_io, grep_io)
64
+ # grep_io.close_write
65
+ # command_io = grep_io
66
+ # end
67
+
68
+ # if paged
69
+ # lesspipe do |less|
70
+ # IO.copy_stream(command_io, less)
71
+ # end
72
+ # else
73
+ # IO.copy_stream(command_io, STDOUT)
74
+ # end
75
+
76
+ highlight_proc = if highlight
77
+ proc { |line| line.gsub(highlight) { |m| "\e[33;1m#{m}\e[0m" } }
78
+ else
79
+ proc { |line| line }
80
+ end
81
+
82
+ grep_proc = if grep
83
+ proc { |line| line[grep] }
84
+ else
85
+ proc { true }
86
+ end
87
+
88
+ lesspipe(disabled: !paged) do |less|
89
+ command_io.each_line do |line|
90
+ less.puts highlight_proc.(line) if grep_proc.(line)
91
+ end
92
+ end
93
+
94
+ end
95
+
96
+ $?.to_i == 0
97
+ end
98
+ end
99
+
100
+ def print_files(*paths, include: nil, exclude: nil)
101
+ lesspipe do |less|
102
+ paths.each do |path|
103
+ less.puts "<8>=== <11>#{path} <8>========".colorize
104
+ open(path) do |io|
105
+ enum = io.each_line
106
+ enum = enum.grep(include) if include
107
+ enum = enum.reject { |line| line[exclude] } if exclude
108
+ enum.each { |line| less.puts line }
109
+ end
110
+ less.puts
111
+ end
112
+ end
113
+ end
114
+
115
+ def call_command(name, *args)
116
+ if block = (@cmds[name] || @cmds[ALIASES[name]])
117
+ block.call args
118
+ else
119
+ puts "Command #{name} not supported in #{@name}"
120
+ end
121
+ end
122
+
123
+ def help
124
+ if osname = Tool.nice_os_name
125
+ puts " Detected OS: #{osname}"
126
+ end
127
+
128
+ puts "Package manager: #{@name}"
129
+ puts
130
+ puts "Available commands:"
131
+ available = COMMAND_HELP.select do |name, desc|
132
+ names = name.split("/")
133
+ names.any? { |name| @cmds[name] }
134
+ end
135
+
136
+ max_width = available.map(&:first).map(&:size).max
137
+ available.each do |name, desc|
138
+ puts " #{name.rjust(max_width)} | #{desc}"
139
+ end
140
+ end
141
+
142
+ end # DSL
143
+ end # Tool
144
+ end # UPM
@@ -1,3 +1,4 @@
1
+
1
2
  UPM::Tool.new "pacman" do
2
3
 
3
4
  os "arch"
@@ -9,17 +10,25 @@ UPM::Tool.new "pacman" do
9
10
  command "upgrade", [*bin, "-Syu"], root: true
10
11
  command "remove", [*bin, "-R"], root: true
11
12
 
13
+ # command "check", [*bin, "-Qkk"]
14
+ command "verify" do |args|
15
+ require 'upm/pacman_verifier'
16
+ UPM::PacmanVerifier.new.verify!(*args)
17
+ end
18
+
19
+ command "audit", "arch-audit", paged: true
12
20
  command "files", [*bin, "-Ql"], paged: true
13
21
  command "search", [*bin, "-Ss"], paged: true
14
22
 
23
+
15
24
  command "info" do |args|
16
- run(*bin, "-Qi", *args) || run(*bin, "-Si", *args)
25
+ run(*bin, "-Qi", *args, paged: true) || run(*bin, "-Si", *args, paged: true)
17
26
  end
18
27
 
19
28
  command "list" do |args|
20
29
  if args.any?
21
30
  query = args.join
22
- run(*bin, "-Q", grep: query, paged: true)
31
+ run(*bin, "-Q", grep: query, highlight: query, paged: true)
23
32
  else
24
33
  run(*bin, "-Q", paged: true)
25
34
  end
@@ -0,0 +1,24 @@
1
+ UPM::Tool.new "pkg" do
2
+
3
+ os "FreeBSD"
4
+
5
+ command "install", "pkg install", root: true
6
+ command "update", "pkg update", root: true
7
+ command "upgrade", "pkg upgrade", root: true
8
+ command "info", "pkg clean", root: true
9
+ command "check", "pkg check --checksums", root: true
10
+
11
+ command "files", "pkg list", paged: true
12
+ command "search", "pkg search", paged: true, highlight: true
13
+ command "info", "pkg info", paged: true
14
+
15
+ command "list" do |args|
16
+ if args.any?
17
+ query = args.join
18
+ run "pkg", "info", grep: query, highlight: query, paged: true
19
+ else
20
+ run "pkg", "info", paged: true
21
+ end
22
+ end
23
+
24
+ end
@@ -4,18 +4,19 @@ UPM::Tool.new "xbps" do
4
4
 
5
5
  identifying_binary "xbps-install"
6
6
 
7
- command "install", "xbps-install", root: true
8
- command "update", "xbps-install -S", root: true
9
- command "upgrade", "xbps-install -Su", root: true
10
- command "files", "xbps-query -f"
11
- command "search", "xbps-query --regex -Rs"
12
- # command "info", ""
7
+ command "install", "xbps-install", root: true
8
+ command "update", "xbps-install -S", root: true
9
+ command "upgrade", "xbps-install -Su", root: true
10
+
11
+ command "files", "xbps-query -f", paged: true
12
+ command "search", "xbps-query --regex -Rs", paged: true
13
13
 
14
14
  command "list" do |args|
15
15
  if args.any?
16
- run "xbps-query", "-f", *args
16
+ query = args.join
17
+ run "xbps-query", "-l", grep: query, paged: true
17
18
  else
18
- run "xbps-query", "-l"
19
+ run "xbps-query", "-l", paged: true
19
20
  end
20
21
  end
21
22
 
@@ -0,0 +1,18 @@
1
+ require_relative "spec_helper"
2
+ require "upm/core_ext"
3
+
4
+ describe File do
5
+
6
+ it "whiches" do
7
+ File.which("ls").should == "/usr/bin/ls"
8
+ File.which("ls", "rm").should == ["/usr/bin/ls", "/usr/bin/rm"]
9
+ File.which("zzzzzzzzzzzzzzzzzzzzzzzzzzzz").should == nil
10
+ end
11
+
12
+ it "which_is_best?s" do
13
+ File.which_is_best?("ls", "rm", "sudo").should == "/usr/bin/ls"
14
+ File.which_is_best?("sudo").should == "/usr/bin/sudo"
15
+ File.which_is_best?("zzzzzzzzzzzzzzzzzz").should == nil
16
+ end
17
+
18
+ end
@@ -0,0 +1 @@
1
+ $:.unshift File.join(__dir__, "../lib")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: upm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - epitron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-04 00:00:00.000000000 Z
11
+ date: 2018-05-19 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Wrap all known command-line package tools with a consistent and pretty
14
14
  interface.
@@ -33,10 +33,16 @@ files:
33
33
  - lib/upm/core_ext.rb
34
34
  - lib/upm/lesspipe.rb
35
35
  - lib/upm/log_parser.rb
36
+ - lib/upm/pacman_verifier.rb
36
37
  - lib/upm/tool.rb
38
+ - lib/upm/tool_class_methods.rb
39
+ - lib/upm/tool_dsl.rb
37
40
  - lib/upm/tools/apt.rb
38
41
  - lib/upm/tools/pacman.rb
42
+ - lib/upm/tools/pkg.rb
39
43
  - lib/upm/tools/xbps.rb
44
+ - spec/core_ext_spec.rb
45
+ - spec/spec_helper.rb
40
46
  homepage: http://github.com/epitron/upm/
41
47
  licenses:
42
48
  - WTFPL