upm 0.1.16 → 0.1.17

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: 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: []