upm 0.1.16 → 0.1.17
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/TODO.md +13 -19
- data/VERSION +1 -1
- data/bin/upm +10 -7
- data/lib/upm/core_ext/file.rb +159 -0
- data/lib/upm/tool.rb +8 -0
- data/lib/upm/tool_dsl.rb +48 -6
- data/lib/upm/tools/pacman.rb +1 -0
- data/lib/upm/tools/pkg.rb +32 -10
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c5ec80be1569a58c23402685f57f02f46cebd10ae87b2684eb2aa8d132aa7cf
|
4
|
+
data.tar.gz: d2fee9bb7b69d36df657e749a109dd29cdc3bbabef9b83b7a1731f83afa504f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1c9ea83290760c4fc2c7ea7cab2580ea50ef5053751d49e7414f6d57b7b2e84b35064ec5ae4dbece2c5f00b5c7456ff80d72caf084d4c43a9668834937e25e8
|
7
|
+
data.tar.gz: 51dbea8086d5162b4f5b90b83028e924195f34a293c2481c05cc3e4e573752f1291f7bb73d2ecf0941ee6fbd351c6294f347c6f2ee0b4250065fc99cb7fdb6ce
|
data/TODO.md
CHANGED
@@ -1,19 +1,27 @@
|
|
1
1
|
# TODO
|
2
2
|
|
3
|
-
##
|
3
|
+
## UI
|
4
|
+
* fzf
|
5
|
+
* search
|
4
6
|
|
5
|
-
|
7
|
+
## Options
|
8
|
+
* Proper option/command parser
|
9
|
+
* Verbose mode (prints `run` commands)
|
6
10
|
|
7
|
-
##
|
11
|
+
## DSL
|
12
|
+
* Call commands from within other commands, or specify dependencies (eg: command "install", "pkg install", depends: "update" )
|
13
|
+
* DSL setting defaults (eg: cache_dir "~/.cache/upm")
|
14
|
+
|
15
|
+
## Performance
|
16
|
+
* RPi2 is very clunky
|
8
17
|
|
18
|
+
## Custom help for command
|
9
19
|
eg: command "something", help: "does stuff", root: true do ... end
|
10
20
|
|
11
21
|
## Pipes and filters
|
12
|
-
|
13
22
|
* `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.
|
14
23
|
|
15
24
|
## Streaming pipes with colours
|
16
|
-
|
17
25
|
* Make the `run` command able to grep the output while streaming the results to the screen.
|
18
26
|
* Make run pretend to be a tty, so I don't need `--color=always`.
|
19
27
|
* Use spawn, like so:
|
@@ -24,7 +32,6 @@ spawn(*%w[tr a-z A-Z], in: r)
|
|
24
32
|
```
|
25
33
|
|
26
34
|
## More package managers
|
27
|
-
|
28
35
|
Currently missing:
|
29
36
|
* RedHat/Fedora/CentOS
|
30
37
|
* OSX
|
@@ -33,31 +40,26 @@ Currently missing:
|
|
33
40
|
* SuSE
|
34
41
|
|
35
42
|
## Dependency-fetching features
|
36
|
-
|
37
43
|
* Use upm to fetch dependencies for any library or script, across any language.
|
38
44
|
* Some kind of manifest file (or personifest, to be politically correct)
|
39
45
|
* This is a little like bundler, npm, etc., but for any type of package.
|
40
46
|
|
41
47
|
## Ability to install any package from any OS to the user's home directory
|
42
|
-
|
43
48
|
Slurps up the packages and their dependencies, then unpacks them into ~/.upm/{bin,lib} or something.
|
44
49
|
(Like nix?)
|
45
50
|
|
46
51
|
Related tool: intoli/exodus
|
47
52
|
|
48
53
|
## fzf
|
49
|
-
|
50
54
|
Use fzf for "list" output (or other commands that require selecting, like "remove")
|
51
55
|
|
52
56
|
## Commandline argument parser
|
53
|
-
|
54
57
|
* Add options to commands:
|
55
58
|
* upm upgrade --download-only
|
56
59
|
* upm install --help
|
57
60
|
* upm help install
|
58
61
|
|
59
62
|
## Figure out how to integrate language package managers
|
60
|
-
|
61
63
|
* 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?
|
62
64
|
* Should the search command show matches from all available package tools? (There could be a configure step where the user says which package managers should be included, and which have preference)
|
63
65
|
* Possibilites:
|
@@ -72,36 +74,28 @@ Use fzf for "list" output (or other commands that require selecting, like "remov
|
|
72
74
|
* `upm help --ruby` should show available ruby commands
|
73
75
|
|
74
76
|
## Give identical output on every platform
|
75
|
-
|
76
77
|
* Requires parsing the output of every command into a canonical format, or reading the package databases directly.
|
77
78
|
|
78
79
|
## apt: Integrate 'acs' wrapper script
|
79
80
|
|
80
81
|
## Evaluate UPM::Tool.new block in an instance of an anonymous subclass of UPM::Tool
|
81
|
-
|
82
82
|
This will allow tools to create classes and modules inside the UPM::Tool block without namespace collisions
|
83
83
|
|
84
84
|
## Themes?
|
85
|
-
|
86
85
|
Why not!
|
87
86
|
|
88
87
|
## Mirror Selector
|
89
|
-
|
90
88
|
Do a ping test on available mirrors, and use fzf to select.
|
91
89
|
|
92
90
|
## Interrupt catcher
|
93
|
-
|
94
91
|
Don't print backtrace when ^C is pressed.
|
95
92
|
|
96
93
|
## Tests
|
97
|
-
|
98
94
|
Create fake OS environments that you can chroot into and run upm to test it out.
|
99
95
|
|
100
96
|
|
101
|
-
|
102
97
|
# DONE
|
103
98
|
|
104
99
|
## Abbrev cmds
|
105
|
-
|
106
100
|
* eg: upm install => upm i => u i
|
107
101
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.17
|
data/bin/upm
CHANGED
@@ -9,13 +9,16 @@ unless tool = UPM::Tool.for_os
|
|
9
9
|
exit 1
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
if command.nil?
|
12
|
+
if ARGV.any? { |arg| ["help", "version", "--help", "--version", "-h", "-v"].include? arg }
|
15
13
|
tool.help
|
16
14
|
else
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
command, *args = ARGV
|
16
|
+
if command.nil?
|
17
|
+
tool.help
|
18
|
+
else
|
19
|
+
begin
|
20
|
+
tool.call_command command, *args
|
21
|
+
rescue Interrupt
|
22
|
+
end
|
20
23
|
end
|
21
|
-
end
|
24
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
#
|
2
|
+
# Beginning of File reached! (Raised when reading a file backwards.)
|
3
|
+
#
|
4
|
+
class BOFError < Exception; end
|
5
|
+
|
6
|
+
class File
|
7
|
+
|
8
|
+
#
|
9
|
+
# A streaming `reverse_each` implementation. (For large files, it's faster and uses less memory.)
|
10
|
+
#
|
11
|
+
def reverse_each(&block)
|
12
|
+
return to_enum(:reverse_each) unless block_given?
|
13
|
+
|
14
|
+
seek_end
|
15
|
+
reverse_each_from_current_pos(&block)
|
16
|
+
end
|
17
|
+
alias_method :reverse_each_line, :reverse_each
|
18
|
+
|
19
|
+
#
|
20
|
+
# Read the previous `length` bytes. After the read, `pos` will be at the beginning of the region that you just read.
|
21
|
+
# Returns `nil` when the beginning of the file is reached.
|
22
|
+
#
|
23
|
+
# If the `block_aligned` argument is `true`, reads will always be aligned to file positions which are multiples of 512 bytes.
|
24
|
+
# (This should increase performance slightly.)
|
25
|
+
#
|
26
|
+
def reverse_read(length, block_aligned=false)
|
27
|
+
raise "length must be a multiple of 512" if block_aligned and length % 512 != 0
|
28
|
+
|
29
|
+
end_pos = pos
|
30
|
+
return nil if end_pos == 0
|
31
|
+
|
32
|
+
if block_aligned
|
33
|
+
misalignment = end_pos % length
|
34
|
+
length += misalignment
|
35
|
+
end
|
36
|
+
|
37
|
+
if length >= end_pos # this read will take us to the beginning of the file
|
38
|
+
seek(0)
|
39
|
+
else
|
40
|
+
seek(-length, IO::SEEK_CUR)
|
41
|
+
end
|
42
|
+
|
43
|
+
start_pos = pos
|
44
|
+
data = read(end_pos - start_pos)
|
45
|
+
seek(start_pos)
|
46
|
+
|
47
|
+
data
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Read each line of file backwards (from the current position.)
|
52
|
+
#
|
53
|
+
def reverse_each_from_current_pos
|
54
|
+
return to_enum(:reverse_each_from_current_pos) unless block_given?
|
55
|
+
|
56
|
+
# read the rest of the current line, in case we started in the middle of a line
|
57
|
+
start_pos = pos
|
58
|
+
fragment = readline rescue ""
|
59
|
+
seek(start_pos)
|
60
|
+
|
61
|
+
while data = reverse_read(4096)
|
62
|
+
lines = data.each_line.to_a
|
63
|
+
lines.last << fragment unless lines.last[-1] == "\n"
|
64
|
+
|
65
|
+
fragment = lines.first
|
66
|
+
|
67
|
+
lines[1..-1].reverse_each { |line| yield line }
|
68
|
+
end
|
69
|
+
|
70
|
+
yield fragment
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Seek to `EOF`
|
75
|
+
#
|
76
|
+
def seek_end
|
77
|
+
seek(0, IO::SEEK_END)
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Seek to `BOF`
|
82
|
+
#
|
83
|
+
def seek_start
|
84
|
+
seek(0)
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Read the previous line (leaving `pos` at the beginning of the string that was read.)
|
89
|
+
#
|
90
|
+
def reverse_readline
|
91
|
+
raise BOFError.new("beginning of file reached") if pos == 0
|
92
|
+
|
93
|
+
seek_backwards_to("\n", 512, -2)
|
94
|
+
new_pos = pos
|
95
|
+
data = readline
|
96
|
+
seek(new_pos)
|
97
|
+
data
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Scan through the file until `string` is found, and set the IO's +pos+ to the first character of the matched string.
|
102
|
+
#
|
103
|
+
def seek_to(string, blocksize=512)
|
104
|
+
raise "Error: blocksize must be at least as large as the string" if blocksize < string.size
|
105
|
+
|
106
|
+
loop do
|
107
|
+
data = read(blocksize)
|
108
|
+
|
109
|
+
if index = data.index(string)
|
110
|
+
seek(-(data.size - index), IO::SEEK_CUR)
|
111
|
+
break
|
112
|
+
elsif eof?
|
113
|
+
return nil
|
114
|
+
else
|
115
|
+
seek(-(string.size - 1), IO::SEEK_CUR)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
pos
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# Scan backwards in the file until `string` is found, and set the IO's +pos+ to the first character after the matched string.
|
124
|
+
#
|
125
|
+
def seek_backwards_to(string, blocksize=512, rindex_end=-1)
|
126
|
+
raise "Error: blocksize must be at least as large as the string" if blocksize < string.size
|
127
|
+
|
128
|
+
loop do
|
129
|
+
data = reverse_read(blocksize)
|
130
|
+
|
131
|
+
if index = data.rindex(string, rindex_end)
|
132
|
+
seek(index+string.size, IO::SEEK_CUR)
|
133
|
+
break
|
134
|
+
elsif pos == 0
|
135
|
+
return nil
|
136
|
+
else
|
137
|
+
seek(string.size - 1, IO::SEEK_CUR)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
pos
|
142
|
+
end
|
143
|
+
alias_method :reverse_seek_to, :seek_backwards_to
|
144
|
+
|
145
|
+
#
|
146
|
+
# Iterate over each line of the file, yielding the line and the byte offset of the start of the line in the file
|
147
|
+
#
|
148
|
+
def each_line_with_offset
|
149
|
+
return to_enum(:each_line_with_offset) unless block_given?
|
150
|
+
|
151
|
+
offset = 0
|
152
|
+
|
153
|
+
each_line do |line|
|
154
|
+
yield line, offset
|
155
|
+
offset = tell
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
data/lib/upm/tool.rb
CHANGED
@@ -16,6 +16,8 @@ module UPM
|
|
16
16
|
|
17
17
|
include UPM::Tool::DSL
|
18
18
|
|
19
|
+
# TODO: Show unlisted commands
|
20
|
+
|
19
21
|
COMMAND_HELP = {
|
20
22
|
"install" => "install a package",
|
21
23
|
"remove/uninstall" => "remove a package",
|
@@ -23,6 +25,7 @@ module UPM
|
|
23
25
|
"search-sources" => "search package source (for use with 'build' command)",
|
24
26
|
"build" => "build a package from source and install it",
|
25
27
|
"list" => "list installed packages (or search their names if extra arguments are supplied)",
|
28
|
+
"files" => "list files in a package",
|
26
29
|
"info" => "show metadata about a package",
|
27
30
|
"update/sync" => "retrieve the latest package list or manifest",
|
28
31
|
"upgrade" => "update package list and install updates",
|
@@ -60,6 +63,11 @@ module UPM
|
|
60
63
|
|
61
64
|
def initialize(name, &block)
|
62
65
|
@name = name
|
66
|
+
|
67
|
+
set_default :cache_dir, "~/.cache/upm"
|
68
|
+
set_default :config_dir, "~/.cache/upm"
|
69
|
+
set_default :max_database_age, 15*60 # 15 minutes
|
70
|
+
|
63
71
|
instance_eval(&block)
|
64
72
|
|
65
73
|
@@tools[name] = self
|
data/lib/upm/tool_dsl.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'fileutils'
|
3
|
+
|
1
4
|
module UPM
|
2
5
|
class Tool
|
3
6
|
module DSL
|
@@ -13,6 +16,28 @@ module UPM
|
|
13
16
|
@prefix = name
|
14
17
|
end
|
15
18
|
|
19
|
+
def os(*args)
|
20
|
+
args.any? ? @os = args : @os
|
21
|
+
end
|
22
|
+
|
23
|
+
def max_database_age(age)
|
24
|
+
@max_database_age = age.to_i
|
25
|
+
end
|
26
|
+
|
27
|
+
def cache_dir(dir)
|
28
|
+
@cache_dir = Pathname.new(dir).expand_path
|
29
|
+
@cache_dir.mkpath unless @cache_dir.exist?
|
30
|
+
end
|
31
|
+
|
32
|
+
def config_dir(dir)
|
33
|
+
@config_dir = Pathname.new(dir).expand_path
|
34
|
+
@config_dir.mkpath unless @config_dir.exist?
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_default(key, value)
|
38
|
+
send(key, value)
|
39
|
+
end
|
40
|
+
|
16
41
|
def command(name, shell_command=nil, root: false, paged: false, highlight: nil, &block)
|
17
42
|
@cmds ||= {}
|
18
43
|
|
@@ -38,16 +63,10 @@ module UPM
|
|
38
63
|
end
|
39
64
|
|
40
65
|
else
|
41
|
-
|
42
66
|
raise "Error: Must supply a block or shell command"
|
43
|
-
|
44
67
|
end
|
45
68
|
end
|
46
69
|
|
47
|
-
def os(*args)
|
48
|
-
args.any? ? @os = args : @os
|
49
|
-
end
|
50
|
-
|
51
70
|
## Helpers
|
52
71
|
|
53
72
|
def run(*args, root: false, paged: false, grep: nil, highlight: nil, sort: false)
|
@@ -139,7 +158,30 @@ module UPM
|
|
139
158
|
end
|
140
159
|
end
|
141
160
|
|
161
|
+
def database_lastupdate_file
|
162
|
+
raise "Error: Tool 'name' is not set" unless @name
|
163
|
+
raise "Error: 'cache_dir' is not set" unless @cache_dir
|
164
|
+
@cache_dir/"#{@name}-last-update"
|
165
|
+
end
|
166
|
+
|
167
|
+
def database_updated!
|
168
|
+
FileUtils.touch(database_lastupdate_file)
|
169
|
+
end
|
170
|
+
|
171
|
+
def database_lastupdate
|
172
|
+
database_lastupdate_file.exist? ? File.mtime(database_lastupdate_file) : 0
|
173
|
+
end
|
174
|
+
|
175
|
+
def database_age
|
176
|
+
Time.now.to_i - database_lastupdate.to_i
|
177
|
+
end
|
178
|
+
|
179
|
+
def database_needs_updating?
|
180
|
+
database_age > @max_database_age
|
181
|
+
end
|
182
|
+
|
142
183
|
def help
|
184
|
+
puts " UPM version: #{File.read("#{__dir__}/../../VERSION")}"
|
143
185
|
if osname = Tool.nice_os_name
|
144
186
|
puts " Detected OS: #{osname}"
|
145
187
|
end
|
data/lib/upm/tools/pacman.rb
CHANGED
data/lib/upm/tools/pkg.rb
CHANGED
@@ -1,19 +1,32 @@
|
|
1
|
-
require 'upm/freshports_search'
|
2
|
-
|
3
1
|
UPM::Tool.new "pkg" do
|
4
2
|
|
5
3
|
os "FreeBSD"
|
6
4
|
|
7
|
-
command "
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
command "update", root: true do
|
6
|
+
if database_needs_updating?
|
7
|
+
run "pkg", "update"
|
8
|
+
database_updated!
|
9
|
+
else
|
10
|
+
puts "Database has already been updated recently. Skipping."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
command "install", root: true do |args|
|
15
|
+
call_command "update"
|
16
|
+
run "pkg", "install", "--no-repo-update", *args
|
17
|
+
end
|
18
|
+
|
19
|
+
command "upgrade", root: true do
|
20
|
+
call_command "update"
|
21
|
+
run "pkg", "upgrade", "--no-repo-update"
|
22
|
+
end
|
23
|
+
|
24
|
+
command "remove", "pkg remove", root: true
|
12
25
|
command "audit", "pkg audit", root: true
|
26
|
+
command "clean", "pkg clean -a",root: true
|
13
27
|
command "verify", "pkg check --checksums", root: true
|
14
28
|
command "which", "pkg which"
|
15
29
|
|
16
|
-
# command "files", "pkg list", paged: true
|
17
30
|
command "files" do |args|
|
18
31
|
if args.empty?
|
19
32
|
run "pkg", "info", "--list-files", "--all", paged: true
|
@@ -29,16 +42,19 @@ UPM::Tool.new "pkg" do
|
|
29
42
|
|
30
43
|
command "search", "pkg search", paged: true, highlight: true
|
31
44
|
command "search-sources" do |*args|
|
45
|
+
require 'upm/freshports_search'
|
32
46
|
query = args.join(" ")
|
33
47
|
FreshportsSearch.new.search!(query)
|
34
48
|
end
|
35
49
|
|
36
50
|
# command "log", "grep -E 'pkg.+installed' /var/log/messages", paged: true
|
37
51
|
command "log" do
|
52
|
+
require 'upm/core_ext/file'
|
38
53
|
lesspipe do |less|
|
39
|
-
open("/var/log/messages").
|
54
|
+
open("/var/log/messages").reverse_each_line do |line|
|
40
55
|
# Jan 19 18:25:21 freebsd pkg[815]: pcre-8.43_2 installed
|
41
|
-
|
56
|
+
# Apr 1 16:55:58 freebsd pkg[73957]: irssi-1.2.2,1 installed
|
57
|
+
if line =~ /^(\S+\s+\S+\s+\S+) (\S+) pkg(?:\[\d+\])?: (\S+)-(\S+) installed/
|
42
58
|
timestamp = DateTime.parse($1)
|
43
59
|
host = $2
|
44
60
|
pkgname = $3
|
@@ -77,4 +93,10 @@ UPM::Tool.new "pkg" do
|
|
77
93
|
print_files("/etc/pkg/FreeBSD.conf", exclude: /^(#|$)/)
|
78
94
|
end
|
79
95
|
|
96
|
+
# pkg clean # cleans /var/cache/pkg/
|
97
|
+
# rm -rf /var/cache/pkg/* # just remove it all
|
98
|
+
# pkg update -f # forces update of repository catalog
|
99
|
+
# rm /var/db/pkg/repo-*.sqlite # removes all remote repository catalogs
|
100
|
+
# pkg bootstrap -f # forces reinstall of pkg
|
101
|
+
|
80
102
|
end
|
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.
|
4
|
+
version: 0.1.17
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- epitron
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-14 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.
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- lib/upm.rb
|
35
35
|
- lib/upm/colored.rb
|
36
36
|
- lib/upm/core_ext.rb
|
37
|
+
- lib/upm/core_ext/file.rb
|
37
38
|
- lib/upm/freshports_search.rb
|
38
39
|
- lib/upm/lesspipe.rb
|
39
40
|
- lib/upm/log_parser.rb
|
@@ -55,7 +56,7 @@ homepage: http://github.com/epitron/upm/
|
|
55
56
|
licenses:
|
56
57
|
- WTFPL
|
57
58
|
metadata: {}
|
58
|
-
post_install_message:
|
59
|
+
post_install_message:
|
59
60
|
rdoc_options: []
|
60
61
|
require_paths:
|
61
62
|
- lib
|
@@ -70,8 +71,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
71
|
- !ruby/object:Gem::Version
|
71
72
|
version: '0'
|
72
73
|
requirements: []
|
73
|
-
rubygems_version: 3.1.
|
74
|
-
signing_key:
|
74
|
+
rubygems_version: 3.1.4
|
75
|
+
signing_key:
|
75
76
|
specification_version: 4
|
76
77
|
summary: Universal Package Manager
|
77
78
|
test_files: []
|