upm 0.1.14 → 0.1.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +99 -14
- data/TODO.md +19 -19
- data/VERSION +1 -1
- data/bin/upm +10 -7
- data/design/Favorite Packages.md +7 -0
- data/design/Fuzzy Installer.md +28 -0
- data/design/Updates Grouped Together for Readability.md +18 -0
- data/lib/upm/core_ext/file.rb +159 -0
- data/lib/upm/tool.rb +32 -16
- data/lib/upm/tool_class_methods.rb +20 -9
- data/lib/upm/tool_dsl.rb +48 -6
- data/lib/upm/tools/guix.rb +60 -0
- data/lib/upm/tools/opkg.rb +26 -0
- data/lib/upm/tools/pacman.rb +38 -2
- data/lib/upm/tools/pkg.rb +59 -8
- data/lib/upm/tools/pkg_add.rb +24 -0
- data/lib/upm/tools/pkgin.rb +49 -0
- data/lib/upm/tools/xbps.rb +66 -5
- data/lib/upm/tools/yum.rb +27 -0
- metadata +15 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5643e0647bd7382b413354291acc5fa62ca282b21ea09a6ec7e714993fb0559c
|
4
|
+
data.tar.gz: 1370ad6461b00a72e138aaf0bed8d98e868372416c19b62cf8081db3a582467f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9674e0839137df4b9efd4dc4730930df426c79b449b1b999ecd894bc48b00ab2df4ded3cbc915f2b333ed21aebc0f6eb7128eb6f36a5837e3c916a074e6f49e7
|
7
|
+
data.tar.gz: 996b34e98a0654a72fa3814d10101942fab1580448d9a34778baa95f3a93a6f551eee5b2b9803325f022377119c913fc827d57fca0275372646f69a04b27c44d
|
data/README.md
CHANGED
@@ -2,11 +2,33 @@
|
|
2
2
|
|
3
3
|
## Concept:
|
4
4
|
|
5
|
-
Wraps all known package managers to provide a consistent and pretty interface, along with advanced features not supported by all tools, such as
|
5
|
+
Wraps all known package managers to provide a consistent and pretty interface, along with advanced features not supported by all tools, such as:
|
6
|
+
- install log
|
7
|
+
- rollback
|
8
|
+
- pinning
|
9
|
+
- fuzzy search
|
10
|
+
- containerization/sandboxing
|
11
|
+
- learning what packages are cool (community statistics, user favorites)
|
6
12
|
|
7
|
-
|
13
|
+
No matter what package manager you're using, you'll get a modern, pretty, colourful, output that's piped-to-less, and you only have to remember one consistent set of commands. It'll also prompt you with a text UI whenever faced with ambiguity.
|
8
14
|
|
9
|
-
|
15
|
+
You can maintain lists of your favorite packages (and sync them to some remote server), so that you can automatically install them whenever you setup a new machine. (This can include git repos full of dotfiles/scripts, to give you a comfortable home environment, regardless of which OS you're using.)
|
16
|
+
|
17
|
+
## Reality:
|
18
|
+
|
19
|
+
Currently, `upm` provides a consistent interface to a number of tools: apk (Alpine), apt (Debian/Ubuntu), guix, opkg (OpenWRT), pacman (Arch), pkg (FreeBSD), pkg_add (OpenBSD), pkgin (Solaris/NetBSD), xbps (Void), and yum (Fedora).
|
20
|
+
|
21
|
+
All the tools support the basic operations (installing, searching, listing, removing), and some support more advanced features, like grabbing search results from the web and showing the installation logs, and the output is always paged to `less`.
|
22
|
+
|
23
|
+
The more advanced features, like consistent output, colorization, fuzzy filtering, etc. are not yet implemented.
|
24
|
+
|
25
|
+
## Installation:
|
26
|
+
|
27
|
+
First, install Ruby. Then:
|
28
|
+
|
29
|
+
```
|
30
|
+
gem install upm
|
31
|
+
```
|
10
32
|
|
11
33
|
## Usage:
|
12
34
|
|
@@ -18,25 +40,26 @@ u <command> <pkg>
|
|
18
40
|
|
19
41
|
## Commands:
|
20
42
|
|
21
|
-
* `install`
|
22
|
-
* `remove`
|
43
|
+
* `install`/`add` - download and install a package
|
44
|
+
* `remove`/`uninstall` - remove a previously installed package
|
23
45
|
* `build` - compile a package from source and install it
|
24
46
|
* `search` - using the fastest known API or service
|
25
47
|
* `list` - show all packages, or the contents of a specific package
|
26
48
|
* `info` - show metadata about a package
|
27
49
|
* `sync`/`update` - retrieve the latest package list or manifest
|
28
50
|
* `upgrade` - install new versions of all packages
|
29
|
-
* `
|
51
|
+
* `verify` - verify the integrity of installed files
|
30
52
|
* `audit` - show known vulnerabilities for installed packages
|
31
53
|
* `pin` - pinning a package means it won't be automatically upgraded
|
32
54
|
* `rollback` - revert to an earlier version of a package (including its dependencies)
|
33
|
-
* `log` - show history of package installs
|
55
|
+
* `log` - show history of package installs
|
34
56
|
* `packagers` - detect installed package managers, and pick which ones upm should wrap
|
35
57
|
* `sources`/`mirrors` - select remote repositories and mirrors
|
36
58
|
* `clean` - clear out the local package cache
|
37
59
|
* `monitor` - ad-hoc package manager for custom installations (like instmon)
|
38
60
|
* `keys` - keyrings and package authentication
|
39
61
|
* `default` - configure the action to take when no arguments are passed to "upm" (defaults to "os:update")
|
62
|
+
* `switch` - set a default tool (eg: if you're on a system with both `apt-get` and `nix`, you can switch to `nix` so that you don't need to prefix every package with `nix:`)
|
40
63
|
|
41
64
|
### Any command that takes a package name can be prefixed with the package tool's namespace:
|
42
65
|
|
@@ -70,21 +93,23 @@ go:<pkg>,<pkg>,<pkg>
|
|
70
93
|
* NetBSD: `pkgin`/`ports`
|
71
94
|
* SmartOS/Illumos: `pkgin`
|
72
95
|
* Windows: `apt-cyg`/`mingw-get`/`nuget`/`Windows Update`/(as-yet-not-created package manager, "winget")
|
73
|
-
* Wine: `winetricks`
|
96
|
+
* Wine/Proton/Steam: `winetricks`/`steam`
|
74
97
|
* Ruby: `rubygems`
|
75
98
|
* Python: `pip`/`easy_install`
|
76
|
-
* Javascript: `npm`
|
77
|
-
* Clojure: `leiningen`
|
78
|
-
* Java: `gradle`
|
79
|
-
* Erlang: `rebar`
|
80
|
-
* Scala: `sbt`
|
99
|
+
* Javascript/NodeJS: `npm`
|
81
100
|
* Rust: `cargo`
|
101
|
+
* Dart: `pub`
|
102
|
+
* go: `go-get`
|
82
103
|
* R: `cran`
|
104
|
+
* Qt: `qpm`
|
83
105
|
* Lua: `rocks`
|
84
106
|
* Julia: `Pkg`
|
85
107
|
* Haskell: `cabal`
|
108
|
+
* Clojure: `leiningen`
|
109
|
+
* Java: `gradle`
|
110
|
+
* Erlang: `rebar`
|
111
|
+
* Scala: `sbt`
|
86
112
|
* Perl: `cpan`
|
87
|
-
* go: `go-get`
|
88
113
|
|
89
114
|
...[and many more!](https://en.wikipedia.org/wiki/List_of_software_package_management_systems)
|
90
115
|
|
@@ -103,8 +128,68 @@ Rollback:
|
|
103
128
|
|
104
129
|
![pacman-rollback](https://raw.githubusercontent.com/epitron/scripts/master/screenshots/pacman-rollback.png)
|
105
130
|
|
131
|
+
# Future Directions
|
132
|
+
|
106
133
|
## TODOs:
|
107
134
|
|
108
135
|
* Use the pretty text-mode UI that passenger-install uses
|
109
136
|
* Context-dependent operation
|
110
137
|
* eg: if you're in a ruby project's directory, set the 'ruby' namespace to highest priority
|
138
|
+
|
139
|
+
## Dotfiles
|
140
|
+
|
141
|
+
* Manage and version-control dotfiles
|
142
|
+
* Sync sqlite databases
|
143
|
+
* sqlitesync tool?
|
144
|
+
|
145
|
+
## Themes
|
146
|
+
|
147
|
+
* Font packs
|
148
|
+
* Theme browser/downloader for GTK{2,3}, Qt, XFCE4, and Compiz
|
149
|
+
* Populate `~/.themes` and set ENVIRONMENT variables
|
150
|
+
* Store/load from favorites
|
151
|
+
|
152
|
+
## Containers, VMs, and Virtual Environments:
|
153
|
+
|
154
|
+
Containers, VMs, and Virtual Environments are another pile of tools which do roughly the same thing: they gather together the dependencies for a specific program, or small set of programs, into a bundle, and create an isolated environment in which it can run.
|
155
|
+
|
156
|
+
In the future, these could be wrapped by `ucm` (Universal Container Manager), if I get around to it.
|
157
|
+
|
158
|
+
### Container tools to wrap:
|
159
|
+
|
160
|
+
* Virtual Environments:
|
161
|
+
* Python: `virtualenv`
|
162
|
+
* Ruby: `bundler`
|
163
|
+
* Java: `gradle`
|
164
|
+
* NodeJS: `npm`
|
165
|
+
* Containerized Applications/Systems:
|
166
|
+
* AppImage
|
167
|
+
* docker
|
168
|
+
* rkt
|
169
|
+
* snapd
|
170
|
+
* systemd
|
171
|
+
* podman
|
172
|
+
* nanobox
|
173
|
+
* SmartOS zones
|
174
|
+
* BSD jails (iocage)
|
175
|
+
* Wine environments:
|
176
|
+
* wine prefixes
|
177
|
+
* playonlinuxs
|
178
|
+
* proton
|
179
|
+
* Virtual Machines:
|
180
|
+
* qemu
|
181
|
+
* virtualbox
|
182
|
+
* VMware
|
183
|
+
* firecracker
|
184
|
+
* Hypervisors:
|
185
|
+
* ESXi
|
186
|
+
* Xen
|
187
|
+
* Nova
|
188
|
+
|
189
|
+
|
190
|
+
## Similar Projects
|
191
|
+
|
192
|
+
* [PackageKit](https://en.wikipedia.org/wiki/PackageKit)
|
193
|
+
* [libraries.io](https://libraries.io)
|
194
|
+
* [pkgs.org](https://pkgs.org)
|
195
|
+
* [Repology](https://repology.org)
|
data/TODO.md
CHANGED
@@ -1,19 +1,33 @@
|
|
1
1
|
# TODO
|
2
2
|
|
3
|
-
##
|
3
|
+
## UI
|
4
|
+
* fzf/pick
|
5
|
+
* scrapers for web-based search engines (especially for "files in packages")
|
6
|
+
|
7
|
+
## Options
|
8
|
+
* Proper option/command parser
|
9
|
+
* Verbose mode (prints `run` commands)
|
10
|
+
|
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
|
+
* Some commands require special packages (eg: "command 'locate', depends: 'pkgfile'") which have their own syncable databases
|
15
|
+
|_ offer to install these dependencies (and sync them (periodically))
|
16
|
+
|_ web-based search is even nicer
|
17
|
+
* Page multi-commands (eg: 'args.each { run ..., paged: true }' should all output to the same pager)
|
18
|
+
|_ 'run ..., pager: IO'? will that break grep?
|
4
19
|
|
5
|
-
|
20
|
+
## Performance
|
21
|
+
* RPi2 is very clunky
|
22
|
+
|_ rewrite in... C? rust? go? lua?
|
6
23
|
|
7
24
|
## Custom help for command
|
8
|
-
|
9
25
|
eg: command "something", help: "does stuff", root: true do ... end
|
10
26
|
|
11
27
|
## Pipes and filters
|
12
|
-
|
13
28
|
* `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
29
|
|
15
30
|
## Streaming pipes with colours
|
16
|
-
|
17
31
|
* Make the `run` command able to grep the output while streaming the results to the screen.
|
18
32
|
* Make run pretend to be a tty, so I don't need `--color=always`.
|
19
33
|
* Use spawn, like so:
|
@@ -24,7 +38,6 @@ spawn(*%w[tr a-z A-Z], in: r)
|
|
24
38
|
```
|
25
39
|
|
26
40
|
## More package managers
|
27
|
-
|
28
41
|
Currently missing:
|
29
42
|
* RedHat/Fedora/CentOS
|
30
43
|
* OSX
|
@@ -33,31 +46,26 @@ Currently missing:
|
|
33
46
|
* SuSE
|
34
47
|
|
35
48
|
## Dependency-fetching features
|
36
|
-
|
37
49
|
* Use upm to fetch dependencies for any library or script, across any language.
|
38
50
|
* Some kind of manifest file (or personifest, to be politically correct)
|
39
51
|
* This is a little like bundler, npm, etc., but for any type of package.
|
40
52
|
|
41
53
|
## Ability to install any package from any OS to the user's home directory
|
42
|
-
|
43
54
|
Slurps up the packages and their dependencies, then unpacks them into ~/.upm/{bin,lib} or something.
|
44
55
|
(Like nix?)
|
45
56
|
|
46
57
|
Related tool: intoli/exodus
|
47
58
|
|
48
59
|
## fzf
|
49
|
-
|
50
60
|
Use fzf for "list" output (or other commands that require selecting, like "remove")
|
51
61
|
|
52
62
|
## Commandline argument parser
|
53
|
-
|
54
63
|
* Add options to commands:
|
55
64
|
* upm upgrade --download-only
|
56
65
|
* upm install --help
|
57
66
|
* upm help install
|
58
67
|
|
59
68
|
## Figure out how to integrate language package managers
|
60
|
-
|
61
69
|
* 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
70
|
* 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
71
|
* Possibilites:
|
@@ -72,36 +80,28 @@ Use fzf for "list" output (or other commands that require selecting, like "remov
|
|
72
80
|
* `upm help --ruby` should show available ruby commands
|
73
81
|
|
74
82
|
## Give identical output on every platform
|
75
|
-
|
76
83
|
* Requires parsing the output of every command into a canonical format, or reading the package databases directly.
|
77
84
|
|
78
85
|
## apt: Integrate 'acs' wrapper script
|
79
86
|
|
80
87
|
## Evaluate UPM::Tool.new block in an instance of an anonymous subclass of UPM::Tool
|
81
|
-
|
82
88
|
This will allow tools to create classes and modules inside the UPM::Tool block without namespace collisions
|
83
89
|
|
84
90
|
## Themes?
|
85
|
-
|
86
91
|
Why not!
|
87
92
|
|
88
93
|
## Mirror Selector
|
89
|
-
|
90
94
|
Do a ping test on available mirrors, and use fzf to select.
|
91
95
|
|
92
96
|
## Interrupt catcher
|
93
|
-
|
94
97
|
Don't print backtrace when ^C is pressed.
|
95
98
|
|
96
99
|
## Tests
|
97
|
-
|
98
100
|
Create fake OS environments that you can chroot into and run upm to test it out.
|
99
101
|
|
100
102
|
|
101
|
-
|
102
103
|
# DONE
|
103
104
|
|
104
105
|
## Abbrev cmds
|
105
|
-
|
106
106
|
* eg: upm install => upm i => u i
|
107
107
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.18
|
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,28 @@
|
|
1
|
+
# Fuzzy Installer
|
2
|
+
|
3
|
+
## Concept
|
4
|
+
|
5
|
+
'upm install <pkg query>' performs a search
|
6
|
+
|
7
|
+
If there's one match, install it; if there are multiple matches, show ranked results and let the user pick one (fzf-like picker, but with a hotkey that can expand descriptions).
|
8
|
+
|
9
|
+
Ranking of packages by source use statistics and heuristics
|
10
|
+
- Which version is newest?
|
11
|
+
- Which version is more stable?
|
12
|
+
- What has the user picked in the past? (eg: a distro's global ruby-<gem> package vs a globally installed gem vs a locally installed gem)
|
13
|
+
- What tools are installed? (docker? gem?)
|
14
|
+
- Which package is less likely to break stuff? (some package managers are better than others (*cough*pip))
|
15
|
+
- Is it a service that should be instaled in a container?
|
16
|
+
|
17
|
+
## Depends on
|
18
|
+
|
19
|
+
- A database of package aliases
|
20
|
+
- A ranking algorithm
|
21
|
+
- Package type prioritization (manually specified or machine learned, per-package rules based on all users who installed it)
|
22
|
+
-
|
23
|
+
- Distributed statistics
|
24
|
+
- Users can anonymously donate their stats
|
25
|
+
- Stats are all public
|
26
|
+
- Ranking algorithm can run locally or on a distributed system
|
27
|
+
|
28
|
+
#
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Updates Grouped Together for Readability
|
2
|
+
|
3
|
+
## Concept
|
4
|
+
|
5
|
+
The design of package update screens is pretty abysmal -- usually just a big blob of package names and versions. This can be improved.
|
6
|
+
|
7
|
+
## Groups
|
8
|
+
|
9
|
+
Packages can be grouped by any of the following:
|
10
|
+
|
11
|
+
- semantic version (MAJOR.MINOR.PATCH):
|
12
|
+
- MAJOR: incompatible API changes
|
13
|
+
- MINOR: backwards-compatible features added
|
14
|
+
- PATCH: bugfixes
|
15
|
+
- package's category/tags
|
16
|
+
- which repository the package came from
|
17
|
+
- who packaged it
|
18
|
+
- a tree-view of package dependencies (useful for seeing dependency relationships during package updates)
|
@@ -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,43 +25,57 @@ 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)",
|
26
|
-
"
|
28
|
+
"selection/manual" => "list manually installed packages", # this should probably be a `list` option ("upm list --manually-added" or smth (would be nice: rewrite in go and use ipfs' arg parsing library))
|
29
|
+
"files" => "list files in a package",
|
30
|
+
"rdeps/depends" => "reverse dependencies (which packages depend on this package?)",
|
31
|
+
"locate" => "search contents of packages (local or remote)",
|
32
|
+
"info/show" => "show metadata about a package",
|
27
33
|
"update/sync" => "retrieve the latest package list or manifest",
|
28
34
|
"upgrade" => "update package list and install updates",
|
35
|
+
"selfupdate" => "update the package manager",
|
29
36
|
"download" => "download package list and updates, but don't insatall them",
|
30
37
|
"pin" => "pinning a package means it won't be automatically upgraded",
|
31
38
|
"rollback" => "revert to an earlier version of a package (including its dependencies)",
|
32
39
|
"verify/check" => "verify the integrity of packages' files on the filesystem",
|
33
|
-
"
|
40
|
+
"repair" => "fix corrupted packages",
|
41
|
+
"audit/vulns" => "show known vulnerabilities in installed packages",
|
34
42
|
"log" => "show history of package installs",
|
35
43
|
"packagers" => "detect installed package managers, and pick which ones upm should wrap",
|
36
|
-
"mirrors/sources" => "manage remote repositories and mirrors",
|
37
44
|
"clean" => "clear out the local package cache",
|
38
45
|
"monitor" => "ad-hoc package manager for custom installations (like instmon)",
|
39
46
|
"keys" => "keyrings and package authentication",
|
40
47
|
"default" => "configure the action to take when no arguments are passed to 'upm' (defaults to 'os:update')",
|
41
48
|
"stats" => "show statistics about package database(s)",
|
49
|
+
"repos/mirrors/sources/channels" => "manage subscriptions to remote repositories/mirrors/channels",
|
42
50
|
}
|
43
51
|
|
44
52
|
ALIASES = {
|
45
|
-
"
|
46
|
-
"
|
47
|
-
"
|
48
|
-
"
|
49
|
-
"
|
50
|
-
"
|
51
|
-
"
|
52
|
-
"
|
53
|
-
"
|
54
|
-
"d" => "download",
|
55
|
-
"s" => "search",
|
56
|
-
"f" => "files",
|
57
|
-
"r" => "remove",
|
53
|
+
"u" => "upgrade",
|
54
|
+
"i" => "install",
|
55
|
+
"d" => "download",
|
56
|
+
"s" => "search",
|
57
|
+
"f" => "files",
|
58
|
+
"r" => "remove",
|
59
|
+
"m" => "mirrors",
|
60
|
+
"file" => "files",
|
61
|
+
"vuln" => "audit",
|
58
62
|
"source-search" => "search-sources",
|
59
63
|
}
|
60
64
|
|
65
|
+
COMMAND_HELP.keys.each do |key|
|
66
|
+
cmd, *alts = key.split("/")
|
67
|
+
alts.each do |alt|
|
68
|
+
ALIASES[alt] = cmd
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
61
72
|
def initialize(name, &block)
|
62
73
|
@name = name
|
74
|
+
|
75
|
+
set_default :cache_dir, "~/.cache/upm"
|
76
|
+
set_default :config_dir, "~/.cache/upm"
|
77
|
+
set_default :max_database_age, 15*60 # 15 minutes
|
78
|
+
|
63
79
|
instance_eval(&block)
|
64
80
|
|
65
81
|
@@tools[name] = self
|
@@ -2,6 +2,11 @@ module UPM
|
|
2
2
|
class Tool
|
3
3
|
class << self
|
4
4
|
|
5
|
+
def error(message)
|
6
|
+
$stderr.puts message
|
7
|
+
exit 1
|
8
|
+
end
|
9
|
+
|
5
10
|
def tools; @@tools; end
|
6
11
|
|
7
12
|
def register_tools!
|
@@ -10,11 +15,12 @@ module UPM
|
|
10
15
|
|
11
16
|
def os_release
|
12
17
|
@os_release ||= begin
|
13
|
-
open("/etc/os-release") do |io|
|
18
|
+
pairs = open("/etc/os-release") do |io|
|
14
19
|
io.read.scan(/^(\w+)="?(.+?)"?$/)
|
15
|
-
end
|
20
|
+
end
|
21
|
+
Hash[pairs]
|
16
22
|
rescue Errno::ENOENT
|
17
|
-
|
23
|
+
nil
|
18
24
|
end
|
19
25
|
end
|
20
26
|
|
@@ -25,13 +31,18 @@ module UPM
|
|
25
31
|
else
|
26
32
|
# `uname -s` => Darwin|FreeBSD|OpenBSD
|
27
33
|
# `uname -o` => Android|Cygwin
|
28
|
-
[`uname -s
|
34
|
+
names = [`uname -s`]
|
35
|
+
names << `uname -o` unless names.first =~ /OpenBSD/
|
36
|
+
names.map(&:chomp).uniq
|
29
37
|
end
|
30
38
|
end
|
31
39
|
|
32
40
|
def nice_os_name
|
33
|
-
os_release
|
34
|
-
(
|
41
|
+
if os_release
|
42
|
+
os_release.values_at("PRETTY_NAME", "NAME", "ID", "ID_LIKE").first
|
43
|
+
else
|
44
|
+
(`uname -o 2> /dev/null`.chomp rescue nil)
|
45
|
+
end
|
35
46
|
end
|
36
47
|
|
37
48
|
def installed
|
@@ -44,14 +55,14 @@ module UPM
|
|
44
55
|
tool = nil
|
45
56
|
|
46
57
|
if os_names.any?
|
47
|
-
tool = @@tools.find { |name, tool| os_names.any? { |name| tool.os
|
58
|
+
tool = @@tools.find { |name, tool| os_names.any? { |name| tool.os&.include? name } }
|
48
59
|
end
|
49
60
|
|
50
61
|
if tool.nil?
|
51
|
-
tool = @@tools.find { |name, tool| File.which(tool.identifying_binary) }
|
62
|
+
tool = @@tools.find { |name, tool| File.which(tool.identifying_binary) }
|
52
63
|
end
|
53
64
|
|
54
|
-
tool
|
65
|
+
tool&.last
|
55
66
|
end
|
56
67
|
|
57
68
|
end
|
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(*names)
|
48
|
-
names.any? ? @os = names : @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
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class GUIXPackage < Struct.new(:name, :version, :out, :path)
|
2
|
+
def self.from_line(line)
|
3
|
+
new(*line.chomp.split)
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.installed
|
7
|
+
@installed_packages ||= IO.popen(["guix", "package", "--list-installed"]) { |io| io.each_line.map { |line| GUIXPackage.from_line(line) } }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.avaialble
|
11
|
+
@available_packages ||= IO.popen(["guix", "package", "--list-available"]) { |io| io.each_line.map { |line| GUIXPackage.from_line(line) } }
|
12
|
+
end
|
13
|
+
|
14
|
+
def installed?
|
15
|
+
!!path[%r{^/gnu/store/}]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
UPM::Tool.new "guix" do
|
21
|
+
|
22
|
+
identifying_binary "guix"
|
23
|
+
|
24
|
+
command "install", "guix install"
|
25
|
+
command "remove", "guix remove"
|
26
|
+
command "info", "guix show"
|
27
|
+
command "rollback", "guix package –roll-back"
|
28
|
+
command "selfupdate", "guix pull"
|
29
|
+
command "upgrade", "guix package --upgrade"
|
30
|
+
|
31
|
+
command "files", paged: true do |args|
|
32
|
+
error "Ope, you forgot the package name!" if args.empty?
|
33
|
+
|
34
|
+
args.each do |arg|
|
35
|
+
if pkg = GUIXPackage.installed.find { |pkg| pkg.name == arg }
|
36
|
+
run "find", pkg.path, paged: true
|
37
|
+
else
|
38
|
+
error "#{arg.inspect} not found"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
command "search" do |args|
|
44
|
+
query = args.join(" ")
|
45
|
+
highlight_query = args.join(".+")
|
46
|
+
grep_query = args.join(".+")
|
47
|
+
run "guix", "package", "--list-available", sort: true, grep: grep_query, paged: true, highlight: highlight_query
|
48
|
+
end
|
49
|
+
|
50
|
+
command "list" do |args|
|
51
|
+
if args.any?
|
52
|
+
highlight_query = args.join(".+")
|
53
|
+
grep_query = /#{highlight_query}/
|
54
|
+
run "guix", "package", "--list-installed", grep: grep_query, highlight: highlight_query, paged: true
|
55
|
+
else
|
56
|
+
run "guix", "package", "--list-installed", paged: true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
UPM::Tool.new "opkg" do
|
2
|
+
|
3
|
+
os "openwrt", "lede"
|
4
|
+
|
5
|
+
command "install", "opkg install", root: true
|
6
|
+
command "update", "opkg update", root: true
|
7
|
+
command "upgrade", root: true do |args|
|
8
|
+
pkgs = `opkg list-upgradable`.each_line.map { |line| line.split.first }
|
9
|
+
run "opkg", "upgrade", *pkgs
|
10
|
+
end
|
11
|
+
|
12
|
+
command "search" do |args|
|
13
|
+
query = args.join
|
14
|
+
run "opkg", "list", grep: query, paged: true
|
15
|
+
end
|
16
|
+
|
17
|
+
command "list" do |args|
|
18
|
+
if args.any?
|
19
|
+
query = args.join
|
20
|
+
run "opkg", "list-installed", grep: query, paged: true
|
21
|
+
else
|
22
|
+
run "opkg", "list-installed", paged: true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/lib/upm/tools/pacman.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
UPM::Tool.new "pacman" do
|
2
|
-
|
3
2
|
os "arch"
|
4
3
|
|
5
4
|
bin = ["pacman", "--color=always"]
|
6
5
|
|
7
6
|
command "install", [*bin, "-S"], root: true
|
8
7
|
command "update", [*bin, "-Sy"], root: true
|
9
|
-
command "upgrade", [*bin, "-Syu"], root: true
|
8
|
+
command "upgrade", [*bin, "-Syu", "--noconfirm"], root: true
|
10
9
|
command "remove", [*bin, "-R"], root: true
|
11
10
|
|
12
11
|
command "verify", root: true do |args|
|
@@ -17,6 +16,7 @@ UPM::Tool.new "pacman" do
|
|
17
16
|
command "audit", "arch-audit", paged: true
|
18
17
|
command "files", [*bin, "-Ql"], paged: true
|
19
18
|
command "search", [*bin, "-Ss"], paged: true, highlight: true
|
19
|
+
command "locate", ["pkgfile", "-r"], paged: true
|
20
20
|
|
21
21
|
command "info" do |args|
|
22
22
|
run(*bin, "-Qi", *args, paged: true) || run(*bin, "-Si", *args, paged: true)
|
@@ -33,6 +33,7 @@ UPM::Tool.new "pacman" do
|
|
33
33
|
|
34
34
|
command "mirrors" do
|
35
35
|
print_files("/etc/pacman.d/mirrorlist", exclude: /^(#|$)/)
|
36
|
+
print_files("/etc/pacman.conf", include: /^Server\s*=/, exclude: /^(#|$)/)
|
36
37
|
end
|
37
38
|
|
38
39
|
command "depends" do |args|
|
@@ -105,4 +106,39 @@ UPM::Tool.new "pacman" do
|
|
105
106
|
|
106
107
|
end
|
107
108
|
|
109
|
+
command "rdeps" do |args|
|
110
|
+
packages_that_depend_on = proc do |package|
|
111
|
+
result = []
|
112
|
+
|
113
|
+
# [`pacman -Sii #{package}`, `pacman -Qi #{package}`].each do |output|
|
114
|
+
[`pacman -Sii #{package}`].each do |output|
|
115
|
+
output.each_line do |l|
|
116
|
+
if l =~ /Required By\s+: (.+)/
|
117
|
+
result += $1.strip.split unless $1["None"]
|
118
|
+
break
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
result
|
124
|
+
end
|
125
|
+
|
126
|
+
args.each do |package|
|
127
|
+
puts "<8>=== <14>#{package} <8>============".colorize
|
128
|
+
puts
|
129
|
+
|
130
|
+
packages = packages_that_depend_on.call package
|
131
|
+
|
132
|
+
if packages.empty?
|
133
|
+
puts " <12>None".colorize
|
134
|
+
else
|
135
|
+
run "pacman", "-Ss", "^(#{packages.join '|'})$" # upstream packages
|
136
|
+
run "pacman", "-Qs", "^(#{packages.join '|'})$" # packages that are only installed locally
|
137
|
+
end
|
138
|
+
|
139
|
+
puts
|
140
|
+
end
|
141
|
+
end
|
108
142
|
end
|
143
|
+
|
144
|
+
|
data/lib/upm/tools/pkg.rb
CHANGED
@@ -1,24 +1,69 @@
|
|
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
|
-
|
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
|
11
25
|
command "audit", "pkg audit", root: true
|
26
|
+
command "clean", "pkg clean -a",root: true
|
12
27
|
command "verify", "pkg check --checksums", root: true
|
28
|
+
command "which", "pkg which"
|
29
|
+
|
30
|
+
command "files" do |args|
|
31
|
+
if args.empty?
|
32
|
+
run "pkg", "info", "--list-files", "--all", paged: true
|
33
|
+
else
|
34
|
+
run "pkg", "list", *args, paged: true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# the "pkg-provides" plugin is similar to arch's "pkgfile" (requires updates), and needs to be added to the plugins section of pkg's config ("pkg plugins" shows loaded plugins)
|
39
|
+
command "provides" do |args|
|
40
|
+
run "pkg", "info", "--list-files", "--all", grep: /#{args.join(".+")}/, highlight: true
|
41
|
+
end
|
13
42
|
|
14
|
-
command "files", "pkg list", paged: true
|
15
43
|
command "search", "pkg search", paged: true, highlight: true
|
16
44
|
command "search-sources" do |*args|
|
45
|
+
require 'upm/freshports_search'
|
17
46
|
query = args.join(" ")
|
18
47
|
FreshportsSearch.new.search!(query)
|
19
48
|
end
|
20
49
|
|
21
|
-
command "log", "grep pkg
|
50
|
+
# command "log", "grep -E 'pkg.+installed' /var/log/messages", paged: true
|
51
|
+
command "log" do
|
52
|
+
require 'upm/core_ext/file'
|
53
|
+
lesspipe do |less|
|
54
|
+
open("/var/log/messages").reverse_each_line do |line|
|
55
|
+
# Jan 19 18:25:21 freebsd pkg[815]: pcre-8.43_2 installed
|
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/
|
58
|
+
timestamp = DateTime.parse($1)
|
59
|
+
host = $2
|
60
|
+
pkgname = $3
|
61
|
+
pkgver = $4
|
62
|
+
less.puts "#{timestamp} | #{pkgname} #{pkgver}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
22
67
|
|
23
68
|
command "build" do |*args|
|
24
69
|
# svn checkout --depth empty svn://svn.freebsd.org/ports/head /usr/ports
|
@@ -48,4 +93,10 @@ UPM::Tool.new "pkg" do
|
|
48
93
|
print_files("/etc/pkg/FreeBSD.conf", exclude: /^(#|$)/)
|
49
94
|
end
|
50
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
|
+
|
51
102
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
UPM::Tool.new "pkg_add" do
|
2
|
+
|
3
|
+
os "OpenBSD"
|
4
|
+
|
5
|
+
command "install", "pkg_add", root: true
|
6
|
+
command "remove", "pkg_delete", root: true
|
7
|
+
command "upgrade", "pkg_add -u", root: true
|
8
|
+
command "clean", "yum clean", root: true
|
9
|
+
command "info", "pkg_info", paged: true
|
10
|
+
command "files", "pkg_info -L", paged: true
|
11
|
+
command "search", "pkg_info -Q", paged: true
|
12
|
+
command "verify", "pkg_check"
|
13
|
+
|
14
|
+
command "list" do |args|
|
15
|
+
if args.any?
|
16
|
+
highlight_query = args.join(".+")
|
17
|
+
grep_query = /#{highlight_query}/
|
18
|
+
run "pkg_info", grep: grep_query, highlight: highlight_query, paged: true
|
19
|
+
else
|
20
|
+
run "pkg_info", paged: true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
UPM::Tool.new "pkgin" do
|
2
|
+
|
3
|
+
os "MINIX", "NetBSD"
|
4
|
+
|
5
|
+
command "install", "pkgin install", root: true
|
6
|
+
command "update", "pkgin update", root: true
|
7
|
+
command "upgrade", "pkgin upgrade", root: true
|
8
|
+
command "info", "pkgin clean", root: true
|
9
|
+
command "audit", "pkgin audit", root: true
|
10
|
+
command "verify", "pkgin check --checksums", root: true
|
11
|
+
|
12
|
+
command "files", "pkgin list", paged: true
|
13
|
+
command "search", "pkgin search", paged: true, highlight: true
|
14
|
+
command "search-sources" do |*args|
|
15
|
+
query = args.join(" ")
|
16
|
+
FreshportsSearch.new.search!(query)
|
17
|
+
end
|
18
|
+
|
19
|
+
command "log", "grep pkg: /var/log/messages", paged: true
|
20
|
+
|
21
|
+
command "build" do |*args|
|
22
|
+
# svn checkout --depth empty svn://svn.freebsd.org/ports/head /usr/ports
|
23
|
+
# cd /usr/ports
|
24
|
+
# svn update --set-depth files
|
25
|
+
# svn update Mk
|
26
|
+
# svn update Templates
|
27
|
+
# svn update Tools
|
28
|
+
# svn update --set-depth files $category
|
29
|
+
# cd $category
|
30
|
+
# svn update $port
|
31
|
+
puts "Not implemented"
|
32
|
+
end
|
33
|
+
|
34
|
+
command "info", "pkg info", paged: true
|
35
|
+
|
36
|
+
command "list" do |args|
|
37
|
+
if args.any?
|
38
|
+
query = args.join
|
39
|
+
run "pkg", "info", grep: query, highlight: query, paged: true
|
40
|
+
else
|
41
|
+
run "pkg", "info", paged: true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
command "mirrors" do
|
46
|
+
print_files("/etc/pkg/FreeBSD.conf", exclude: /^(#|$)/)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/lib/upm/tools/xbps.rb
CHANGED
@@ -4,12 +4,28 @@ UPM::Tool.new "xbps" do
|
|
4
4
|
|
5
5
|
identifying_binary "xbps-install"
|
6
6
|
|
7
|
-
command "install",
|
8
|
-
command "
|
9
|
-
command "
|
7
|
+
command "install", "xbps-install", root: true
|
8
|
+
command "remove", "xbps-remove", root: true
|
9
|
+
command "update", "xbps-install -S", root: true
|
10
|
+
command "upgrade", "xbps-install -Su", root: true
|
11
|
+
command "files", "xbps-query -f", paged: true
|
12
|
+
command "locate", "xlocate", paged: true
|
13
|
+
command "selection", "xbps-query -m", paged: true
|
14
|
+
command "rdeps", "xbps-query -X", paged: true
|
10
15
|
|
11
|
-
command "
|
12
|
-
|
16
|
+
command "info" do |args|
|
17
|
+
args.each do |arg|
|
18
|
+
unless run("xbps-query", "-S", arg)
|
19
|
+
run("xbps-query", "-R", "-S", arg)
|
20
|
+
end
|
21
|
+
puts
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
command "search" do |args|
|
26
|
+
query = args.join(".*")
|
27
|
+
run "xbps-query", "--regex", "-Rs", query, highlight: /(#{ args.join("|") })/i, paged: true
|
28
|
+
end
|
13
29
|
|
14
30
|
command "list" do |args|
|
15
31
|
if args.any?
|
@@ -20,4 +36,49 @@ UPM::Tool.new "xbps" do
|
|
20
36
|
end
|
21
37
|
end
|
22
38
|
|
39
|
+
|
40
|
+
|
41
|
+
class XBPSPackage < Struct.new(:name, :version, :date)
|
42
|
+
def self.from_line(line)
|
43
|
+
# zd1211-firmware-1.5_3: 2021-09-01 15:22 UTC
|
44
|
+
if line =~ /^([\w\-]+)-([\d\.]+_\d+): (.+)$/
|
45
|
+
name, version, date = $1, $2, $3
|
46
|
+
date = DateTime.parse($3)
|
47
|
+
new(name, version, date)
|
48
|
+
else
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
"[#{date.strftime("%Y-%m-%d %H:%M:%S")}] #{name} #{version}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
command "log" do |args|
|
59
|
+
fakedata = %{
|
60
|
+
xset-1.2.4_1: 2021-11-05 15:03 UTC
|
61
|
+
xsetroot-1.1.2_1: 2021-11-05 15:03 UTC
|
62
|
+
xtools-0.63_1: 2021-11-05 16:36 UTC
|
63
|
+
xtrans-1.4.0_2: 2021-11-05 15:26 UTC
|
64
|
+
xvidcore-1.3.7_1: 2021-11-05 16:45 UTC
|
65
|
+
xvinfo-1.1.4_2: 2021-11-05 15:03 UTC
|
66
|
+
xwd-1.0.8_1: 2021-11-05 15:03 UTC
|
67
|
+
xwininfo-1.1.5_1: 2021-11-05 15:03 UTC
|
68
|
+
xwud-1.0.5_1: 2021-11-05 15:03 UTC
|
69
|
+
xz-5.2.5_2: 2021-11-05 15:12 UTC
|
70
|
+
zd1211-firmware-1.5_3: 2021-09-01 15:22 UTC
|
71
|
+
zip-3.0_6: 2021-11-05 17:45 UTC
|
72
|
+
zlib-1.2.11_4: 2021-09-01 14:14 UTC
|
73
|
+
zlib-devel-1.2.11_4: 2021-11-05 15:26 UTC
|
74
|
+
}
|
75
|
+
|
76
|
+
data = IO.popen(["xbps-query", "-p", "install-date", "-s", ""], &:read)
|
77
|
+
packages = data.each_line.map do |line|
|
78
|
+
XBPSPackage.from_line(line.strip)
|
79
|
+
end.compact
|
80
|
+
packages.sort_by!(&:date)
|
81
|
+
packages.each { |pkg| puts pkg }
|
82
|
+
end
|
23
83
|
end
|
84
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
UPM::Tool.new "yum" do
|
2
|
+
|
3
|
+
os "centos", "fedora", "rhel"
|
4
|
+
|
5
|
+
command "install", "yum install", root: true
|
6
|
+
command "remove", "yum remove", root: true
|
7
|
+
command "update", "yum updateinfo", root: true
|
8
|
+
command "upgrade", "yum update", root: true
|
9
|
+
command "clean", "yum clean", root: true
|
10
|
+
|
11
|
+
command "files", "rpm -ql", paged: true
|
12
|
+
command "search" do |args|
|
13
|
+
query = args.join(".+")
|
14
|
+
run "yum", "search", *args, sort: true, paged: true, highlight: query
|
15
|
+
end
|
16
|
+
|
17
|
+
command "list" do |args|
|
18
|
+
if args.any?
|
19
|
+
highlight_query = args.join(".+")
|
20
|
+
grep_query = /#{highlight_query}/
|
21
|
+
run "yum", "list", "installed", grep: grep_query, highlight: highlight_query, paged: true
|
22
|
+
else
|
23
|
+
run "yum", "list", "installed", paged: true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
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.18
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- epitron
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-08 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.
|
@@ -28,9 +28,13 @@ files:
|
|
28
28
|
- TODO.md
|
29
29
|
- VERSION
|
30
30
|
- bin/upm
|
31
|
+
- design/Favorite Packages.md
|
32
|
+
- design/Fuzzy Installer.md
|
33
|
+
- design/Updates Grouped Together for Readability.md
|
31
34
|
- lib/upm.rb
|
32
35
|
- lib/upm/colored.rb
|
33
36
|
- lib/upm/core_ext.rb
|
37
|
+
- lib/upm/core_ext/file.rb
|
34
38
|
- lib/upm/freshports_search.rb
|
35
39
|
- lib/upm/lesspipe.rb
|
36
40
|
- lib/upm/log_parser.rb
|
@@ -40,16 +44,21 @@ files:
|
|
40
44
|
- lib/upm/tool_dsl.rb
|
41
45
|
- lib/upm/tools/apk.rb
|
42
46
|
- lib/upm/tools/apt.rb
|
47
|
+
- lib/upm/tools/guix.rb
|
48
|
+
- lib/upm/tools/opkg.rb
|
43
49
|
- lib/upm/tools/pacman.rb
|
44
50
|
- lib/upm/tools/pkg.rb
|
51
|
+
- lib/upm/tools/pkg_add.rb
|
52
|
+
- lib/upm/tools/pkgin.rb
|
45
53
|
- lib/upm/tools/xbps.rb
|
54
|
+
- lib/upm/tools/yum.rb
|
46
55
|
- spec/core_ext_spec.rb
|
47
56
|
- spec/spec_helper.rb
|
48
57
|
homepage: http://github.com/epitron/upm/
|
49
58
|
licenses:
|
50
59
|
- WTFPL
|
51
60
|
metadata: {}
|
52
|
-
post_install_message:
|
61
|
+
post_install_message:
|
53
62
|
rdoc_options: []
|
54
63
|
require_paths:
|
55
64
|
- lib
|
@@ -64,9 +73,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
73
|
- !ruby/object:Gem::Version
|
65
74
|
version: '0'
|
66
75
|
requirements: []
|
67
|
-
|
68
|
-
|
69
|
-
signing_key:
|
76
|
+
rubygems_version: 3.2.22
|
77
|
+
signing_key:
|
70
78
|
specification_version: 4
|
71
79
|
summary: Universal Package Manager
|
72
80
|
test_files: []
|