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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d35aa5412644879ebc18e848569dff6bf7274e09831a5733d63ab5fedffa1916
4
- data.tar.gz: '09b5f1c4461fea7e092cc3ce630e7586c5bd127e849e173245f4c00c9df1d79f'
3
+ metadata.gz: 5643e0647bd7382b413354291acc5fa62ca282b21ea09a6ec7e714993fb0559c
4
+ data.tar.gz: 1370ad6461b00a72e138aaf0bed8d98e868372416c19b62cf8081db3a582467f
5
5
  SHA512:
6
- metadata.gz: d7837f1491c9bdfa1c08fd8a5bcad5f265bbef08d26c0bdfc2b8ee526d1ad50106b6b598edb92c7af658c8c9df6602e26ac94516a7502261a368d405e3894bfc
7
- data.tar.gz: 35a96cabe424135bafbab8b760b4cfc2de04b4c2d9811420c004625ab9cb8aea67bbfa1108c8ddaf68d3324eea94fe808b9ce3674100779d6ba4495c61588b75
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 rollback and pinning.
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
- All tools will give you modern, pretty, colourful, piped-to-less output, and you'll only have to remember one consistent set of commands. It'll also prompt you with a text UI whenever faced with ambiguity.
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
- It will also allow users to maintain lists of their favorite packages (and sync them to some remote server), so that they can automatically install them whenever they setup a new machine. (This can include git repos full of dotfiles/scripts, to give the user a comfortable home environment regardless of which OS they're using.)
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
- * `verfiy` - verify the integrity of installed files
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
- ## Performance
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
- It's currently very clunky on the rpi2.
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.14
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
- command, *args = ARGV
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
- begin
18
- tool.call_command command, *args
19
- rescue Interrupt
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,7 @@
1
+ # Favorite Packages
2
+
3
+ ## Concept
4
+
5
+ The user can create their own package groups manually, or dump of their current desktop's package selecitons and organize them.
6
+
7
+
@@ -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
- "info" => "show metadata about a package",
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
- "audit/vuln" => "show known vulnerabilities in installed packages",
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
- "file" => "files",
46
- "sync" => "update",
47
- "sources" => "mirrors",
48
- "show" => "info",
49
- "vuln" => "audit",
50
- "vulns" => "audit",
51
- "check" => "verify",
52
- "u" => "upgrade",
53
- "i" => "install",
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.to_h
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`, `uname -o`].map(&:chomp).uniq
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.values_at("PRETTY_NAME", "NAME", "ID", "ID_LIKE").first ||
34
- (`uname -o`.chomp rescue nil)
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.include? name } }&.last
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) }&.last
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
@@ -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 "install", "pkg install", root: true
8
- command "update", "pkg update", root: true
9
- command "upgrade", "pkg upgrade", root: true
10
- command "info", "pkg clean", root: true
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: /var/log/messages", paged: true
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
@@ -4,12 +4,28 @@ 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
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 "files", "xbps-query -f", paged: true
12
- command "search", "xbps-query --regex -Rs", paged: true
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.14
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: 2018-07-05 00:00:00.000000000 Z
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
- rubyforge_project:
68
- rubygems_version: 2.7.7
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: []