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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36e927dff0f1b722f702c94b7d6cfcaf05c75e89fa42d491175f9972d08d2e92
4
- data.tar.gz: cbde740ccc8cbd0aedb16393b579c2d38f2a614747762edfe777fe5fd9574a8d
3
+ metadata.gz: 9c5ec80be1569a58c23402685f57f02f46cebd10ae87b2684eb2aa8d132aa7cf
4
+ data.tar.gz: d2fee9bb7b69d36df657e749a109dd29cdc3bbabef9b83b7a1731f83afa504f4
5
5
  SHA512:
6
- metadata.gz: f47b0ba2f51bb57a3dc52dab2df1e3ca8f85f96cf0e5bb32882a7514b999dc7885c3041d438e7ac1a1a2c34128c100b62b65006b5c0346147816816efd4eb751
7
- data.tar.gz: '00483805c6f1a67d352c2e83e1266a5546b3934649d01472f00e85731a8e9eb7d6b434ddddef75814f6dccf10a8355aa7fbbf81ae328df983efba02044db46e0'
6
+ metadata.gz: e1c9ea83290760c4fc2c7ea7cab2580ea50ef5053751d49e7414f6d57b7b2e84b35064ec5ae4dbece2c5f00b5c7456ff80d72caf084d4c43a9668834937e25e8
7
+ data.tar.gz: 51dbea8086d5162b4f5b90b83028e924195f34a293c2481c05cc3e4e573752f1291f7bb73d2ecf0941ee6fbd351c6294f347c6f2ee0b4250065fc99cb7fdb6ce
data/TODO.md CHANGED
@@ -1,19 +1,27 @@
1
1
  # TODO
2
2
 
3
- ## Performance
3
+ ## UI
4
+ * fzf
5
+ * search
4
6
 
5
- It's currently very clunky on the rpi2.
7
+ ## Options
8
+ * Proper option/command parser
9
+ * Verbose mode (prints `run` commands)
6
10
 
7
- ## Custom help for command
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.16
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
- 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,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
@@ -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
@@ -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
@@ -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|
@@ -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 "install", "pkg install", root: true
8
- command "update", "pkg update", root: true
9
- command "upgrade", "pkg upgrade", root: true
10
- command "remove", "pkg remove", root: true
11
- 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
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").each_line do |line|
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
- if line =~ /^(\S+ \S+ \S+) (\S+) pkg(?:\[\d+\])?: (\S+)-(\S+) 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/
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.16
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-01-22 00:00:00.000000000 Z
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.2
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: []