aur.rb 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,178 @@
1
+ require 'json'
2
+ require 'time'
3
+ require 'net/http'
4
+ require 'aur/config'
5
+ require 'aur/packages'
6
+
7
+ module Archlinux
8
+
9
+ AurQueryError=Class.new(ArchlinuxError)
10
+
11
+ # aur query but with a local config parameter
12
+ class AurQueryCustom
13
+ extend CreateHelper
14
+ attr_accessor :config
15
+
16
+ def initialize(config: AurQuery.config)
17
+ @config=config
18
+ end
19
+
20
+ def packages(*pkgs)
21
+ @config.to_packages(infos(*pkgs))
22
+ end
23
+
24
+ # AurQuery.query(type: "info", arg: pacaur)
25
+ # AurQuery.query(type: "info", :"arg[]" => %w(cower pacaur))
26
+ # AurQuery.query(type: "search", by: "name", arg: "aur")
27
+ # by name (search by package name only)
28
+ # *name-desc* (search by package name and description)
29
+ # maintainer (search by package maintainer)
30
+ # depends (search for packages that depend on keywords)
31
+ # makedepends (search for packages that makedepend on keywords)
32
+ # optdepends (search for packages that optdepend on keywords)
33
+ # checkdepends (search for packages that checkdepend on keywords)
34
+ # => search result:
35
+ # {"ID"=>514909,
36
+ # "Name"=>"pacaur",
37
+ # "PackageBaseID"=>49145,
38
+ # "PackageBase"=>"pacaur",
39
+ # "Version"=>"4.7.90-1",
40
+ # "Description"=>"An AUR helper that minimizes user interaction",
41
+ # "URL"=>"https://github.com/rmarquis/pacaur",
42
+ # "NumVotes"=>1107,
43
+ # "Popularity"=>7.382043,
44
+ # "OutOfDate"=>nil,
45
+ # "Maintainer"=>"Spyhawk",
46
+ # "FirstSubmitted"=>1305666963,
47
+ # "LastModified"=>1527690065,
48
+ # "URLPath"=>"/cgit/aur.git/snapshot/pacaur.tar.gz"},
49
+ # => info result adds:
50
+ # "Depends"=>["cower", "expac", "sudo", "git"],
51
+ # "MakeDepends"=>["perl"],
52
+ # "License"=>["ISC"],
53
+ # "Keywords"=>["AUR", "helper", "wrapper"]}]
54
+
55
+ def query(h, url: @config[:aur_url])
56
+ uri=URI("#{url}/rpc/")
57
+ params = {v:5}.merge(h)
58
+ uri.query = URI.encode_www_form(params)
59
+ SH.logger.debug "! AurQuery: new query '#{uri}'"
60
+ res = Net::HTTP.get_response(uri)
61
+ if res.is_a?(Net::HTTPSuccess)
62
+ r= res.body
63
+ else
64
+ raise AurQueryError.new("AUR: Got error response for query #{h}")
65
+ end
66
+ data = JSON.parse(r)
67
+ case data['type']
68
+ when 'error'
69
+ raise AurQueryError.new("Error: #{data['results']}")
70
+ when 'search','info','multiinfo'
71
+ return data['results']
72
+ else
73
+ raise AurQueryError.new("Error in response data #{data}")
74
+ end
75
+ end
76
+
77
+ # Outdated packages: Aur.search(nil, by: "maintainer")
78
+ def search(arg, by: nil)
79
+ r={type: "search", arg: arg}
80
+ r[:by]=by if by
81
+ # if :by is not specified, aur defaults to name-desc
82
+ self.query(r)
83
+ end
84
+
85
+ def infos(*pkgs, slice: 150)
86
+ search=[]
87
+ pkgs.each_slice(slice) do |pkgs_slice|
88
+ r={type: "info", :"arg[]" => pkgs_slice}
89
+ search+=self.query(r)
90
+ end
91
+ search.each { |pkg| pkg[:repo]=:aur }
92
+ search
93
+ end
94
+
95
+ # try to use infos if possible
96
+ def info(pkg)
97
+ r={type: "info", arg: pkg}
98
+ self.query(r).first
99
+ end
100
+
101
+ def pkglist(type="packages", delay: 3600, query: :auto, cache: @config.cachedir) #type=pkgbase
102
+ require 'zlib'
103
+ file=cache+"#{type}.gz"
104
+ in_epoch=nil
105
+ if file.exist?
106
+ # intime=file.read.each_line.first
107
+ file.open do |io|
108
+ Zlib::GzipReader.wrap(io) do |gz|
109
+ intime=gz.each_line.first
110
+ intime.match(/^# AUR package list, generated on (.*)/) do |m|
111
+ in_epoch=Time.parse(m[1]).to_i
112
+ end
113
+ end
114
+ end
115
+ end
116
+ if query
117
+ Net::HTTP.get_response(URI("#{@config[:aur_url]}/#{type}.gz")) do |res|
118
+ date=res["date"] #There are no 'Last-Modified' field, cf https://bugs.archlinux.org/task/49092
119
+ update=true
120
+ if date
121
+ epoch=Time.parse(date).to_i
122
+ update=false if epoch and in_epoch and (epoch-in_epoch < delay) and !query==true
123
+ end
124
+ if update
125
+ file.open('w') do |io|
126
+ Zlib::GzipWriter.wrap(io) do |gz|
127
+ res.read_body(gz)
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ file.open do |io|
134
+ Zlib::GzipReader.wrap(io) do |gz|
135
+ return gz.each_line.map(&:chomp).drop(1)
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ AurQuery=AurQueryCustom.new(config: Archlinux.config)
142
+
143
+ class AurQueryCache < AurQueryCustom
144
+
145
+ attr_accessor :search_cache, :info_cache
146
+ def initialize(*args,**kwds)
147
+ super
148
+ @search_cache={}
149
+ @info_cache={}
150
+ end
151
+
152
+ def search(arg, by: nil)
153
+ r={type: "search", arg: arg}
154
+ r[:by]=by if by
155
+ if @search_cache.key?(r)
156
+ @search_cache[r]
157
+ else
158
+ res=super
159
+ res
160
+ end
161
+ end
162
+
163
+ def infos(*pkgs, slice: 150)
164
+ got = pkgs & @info_cache.keys
165
+ pkgs = pkgs - got
166
+ res=super(*pkgs, slice: slice)
167
+ res.each do |pkg|
168
+ @info_cache[pkg["Name"]]=pkg
169
+ end
170
+ pkgs.each do |name|
171
+ @info_cache[name]||=nil #missing packages
172
+ end
173
+ @info_cache.values_at(*got, *pkgs).compact
174
+ end
175
+ end
176
+
177
+ GlobalAurCache = AurQueryCache.new(config: AurQuery.config)
178
+ end
@@ -0,0 +1,334 @@
1
+ require 'cmdparse'
2
+ require 'aur/version'
3
+ require 'simplecolor'
4
+ SimpleColor.mix_in_string
5
+ #SH::Sh.default_sh_options[:log_level_exectue]="debug3" #was debug
6
+
7
+ module Archlinux
8
+ def self.cli
9
+ parser = CmdParse::CommandParser.new(handle_exceptions: :no_help)
10
+ parser.main_options.program_name = "aur.rb"
11
+ parser.main_options.banner = "Helper to manage aur (Version #{VERSION})"
12
+ parser.main_options.version = VERSION
13
+
14
+ parser.global_options do |opt|
15
+ parser.data[:color]=@config.fetch(:color, true)
16
+ parser.data[:debug]=@config.fetch(:debug, false)
17
+ parser.data[:loglevel]=@config.fetch(:loglevel, "info")
18
+ SH.log_options(opt, parser.data)
19
+
20
+ #opt.on("--[no-]db=[dbname]", "Specify database", "Default to #{@config.db}") do |v|
21
+ opt.on("--[no-]db=[dbname]", "Specify database", "Default to #{@config.db}") do |v|
22
+ @config.db=v
23
+ end
24
+ end
25
+
26
+ add_install_option = ->(cmd) do
27
+ cmd.options do |opt|
28
+ opt.on("-i", "--[no-]install", "Install the package afterwards", "Defaults to #{!! cmd.data[:install]}") do |v|
29
+ cmd.data[:install]=v
30
+ end
31
+
32
+ opt.on("--[no-]chroot[=path]", "Use a chroot", "Defaults to #{@config.opts[:chroot][:active] && @config.opts[:chroot][:root]}") do |v|
33
+ if v
34
+ @config.opts[:chroot][:active]=true
35
+ @config.opts[:chroot][:root]=v if v.is_a?(String)
36
+ else
37
+ @config.opts[:chroot][:active]=v
38
+ end
39
+ end
40
+
41
+ opt.on("--local", "Local mode", "Shortcut for --no-db --no-chroot") do |v|
42
+ @config.opts[:chroot][:active]=false
43
+ @config.db=false
44
+ end
45
+ end
46
+ end
47
+
48
+ parser.main_options do |opt|
49
+ opt.on("--config=config_file", "Set config file") do |v|
50
+ @config=Config.new(v)
51
+ end
52
+ end
53
+
54
+ parser.add_command(CmdParse::HelpCommand.new, default: true)
55
+ parser.add_command(CmdParse::VersionCommand.new)
56
+
57
+ parser.add_command('aur') do |aur_cmd|
58
+ aur_cmd.add_command('search') do |cmd|
59
+ cmd.takes_commands(false)
60
+ cmd.short_desc("Search aur")
61
+ cmd.long_desc(<<-EOS)
62
+ Search aur
63
+ EOS
64
+ cmd.argument_desc(search: "search terms")
65
+ cmd.action do |*search|
66
+ search.each do |s|
67
+ SH.logger.mark(s+":")
68
+ r=GlobalAurCache.search(s)
69
+ r.each do |pkg|
70
+ name_version="#{pkg["Name"]} (#{pkg["Version"]})"
71
+ SH.logger.info("#{name_version.color(:yellow)}: #{pkg["Description"]}")
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ aur_cmd.add_command('info') do |cmd|
78
+ cmd.takes_commands(false)
79
+ cmd.short_desc("Info on packages")
80
+ cmd.long_desc(<<-EOS)
81
+ Get infos on packages
82
+ EOS
83
+ cmd.argument_desc(packages: "packages names")
84
+ cmd.action do |*packages|
85
+ r=GlobalAurCache.infos(*packages)
86
+ r.each do |pkg|
87
+ name_version="#{pkg["Name"]} (#{pkg["Version"]})"
88
+ SH.logger.info("#{name_version.color(:yellow)}: #{pkg["Description"]}")
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ parser.add_command('build') do |build_cmd|
95
+ build_cmd.takes_commands(false)
96
+ build_cmd.short_desc("Build packages")
97
+ build_cmd.long_desc("Build existing PKGBUILD. To download pkgbuild use 'install' instead")
98
+ add_install_option.call(build_cmd)
99
+ build_cmd.action do |*pkgbuild_dirs|
100
+ mkpkg=Archlinux::MakepkgList.new(pkgbuild_dirs, cache: nil)
101
+ mkpkg.build(install: build_cmd.data[:install])
102
+ end
103
+ end
104
+
105
+ parser.add_command('install') do |install_cmd|
106
+ install_cmd.takes_commands(false)
107
+ install_cmd.short_desc("Install packages")
108
+ install_cmd.long_desc(<<-EOS)
109
+ Install of update packages
110
+ EOS
111
+ install_cmd.data={install: true}
112
+ add_install_option.call(install_cmd)
113
+ install_cmd.options do |opt|
114
+ opt.on("-u", "--[no-]update", "Update existing packages too") do |v|
115
+ install_cmd.data[:update]=v
116
+ end
117
+ end
118
+ install_cmd.options do |opt|
119
+ opt.on("-c", "--[no-]check", "Only check updates/install") do |v|
120
+ install_cmd.data[:check]=v
121
+ end
122
+ end
123
+ install_cmd.options do |opt|
124
+ opt.on("--[no-]rebuild=[mode]", "Rebuild given packages", "with --rebuild=full also rebuild their deps") do |v|
125
+ install_cmd.data[:rebuild]=v
126
+ end
127
+ end
128
+ install_cmd.options do |opt|
129
+ opt.on("--[no-]devel", "Also check/update devel packages") do |v|
130
+ install_cmd.data[:devel]=v
131
+ end
132
+ end
133
+ install_cmd.options do |opt|
134
+ opt.on("--[no-]obsolete", "Also show obsolete packages", "Not that you will get false obsolete packages unless you specifically upgrade everything") do |v|
135
+ install_cmd.data[:obsolete]=v
136
+ end
137
+ end
138
+ install_cmd.argument_desc(packages: "packages names")
139
+ install_cmd.action do |*packages|
140
+ if install_cmd.data[:devel]
141
+ Archlinux.config[:default_install_list_class]=AurMakepkgCache
142
+ end
143
+ aur=Archlinux.config.default_packages
144
+ opts={update: install_cmd.data[:update], rebuild: install_cmd.data[:rebuild]}
145
+ opts[:no_show]=[] if install_cmd.data[:obsolete]
146
+ if install_cmd.data[:check]
147
+ aur.install?(*packages, **opts)
148
+ else
149
+ opts[:install]=install_cmd.data[:install]
150
+ aur.install(*packages, **opts)
151
+ end
152
+ end
153
+ end
154
+
155
+ %w(pacman makepkg nspawn mkarchroot makechrootpkg).each do |cmd|
156
+ parser.add_command(cmd) do |devtools_cmd|
157
+ devtools_cmd.takes_commands(false)
158
+ devtools_cmd.short_desc("Launch #{cmd}")
159
+ case cmd
160
+ when "pacman"
161
+ devtools_cmd.long_desc(<<-EOS)
162
+ Launch pacman with a custom config file which makes the db accessible.
163
+ EOS
164
+ end
165
+ devtools_cmd.argument_desc(args: "#{cmd} arguments")
166
+ devtools_cmd.action do |*args|
167
+ devtools=Archlinux.config.local_devtools
168
+ devtools.public_send(cmd,*args, sudo: @config.sudo)
169
+ end
170
+ end
171
+ end
172
+
173
+ parser.add_command('db') do |db_cmd|
174
+ db_cmd.add_command('list') do |cmd|
175
+ cmd.takes_commands(false)
176
+ cmd.short_desc("List the content of the db")
177
+ cmd.options do |opt|
178
+ opt.on("-v", "--[no-]version", "Add the package version") do |v|
179
+ cmd.data[:version]=v
180
+ end
181
+ opt.on("-q", "--quiet", "Machine mode") do |v|
182
+ cmd.data[:quiet]=v
183
+ end
184
+ end
185
+ cmd.action do ||
186
+ db=Archlinux.config.db
187
+ if cmd.data[:quiet]
188
+ pkgs=db.packages
189
+ l= cmd.data[:version] ? pkgs.keys.sort : pkgs.names.sort
190
+ SH.logger.info l.join(' ')
191
+ else
192
+ SH.logger.mark "#{db.file}:"
193
+ db.packages.list(cmd.data[:version])
194
+ end
195
+ end
196
+ end
197
+
198
+ db_cmd.add_command('update') do |cmd|
199
+ cmd.takes_commands(false)
200
+ cmd.short_desc("Update the db")
201
+ cmd.long_desc(<<-EOS)
202
+ Update the db according to the packages present in its folder
203
+ EOS
204
+ cmd.options do |opt|
205
+ opt.on("-c", "--[no-]check", "Only check updates") do |v|
206
+ cmd.data[:check]=v
207
+ end
208
+ end
209
+ cmd.action do ||
210
+ db=Archlinux.config.db
211
+ if cmd.data[:check]
212
+ db.show_updates
213
+ else
214
+ db.update
215
+ end
216
+ end
217
+ end
218
+
219
+ db_cmd.add_command('add') do |cmd|
220
+ cmd.takes_commands(false)
221
+ cmd.short_desc("Add files to the db")
222
+ cmd.options do |opt|
223
+ opt.on("-f", "--[no-]force", "Force adding files that are older than the ones in the db") do |v|
224
+ cmd.data[:force]=v
225
+ end
226
+ end
227
+ cmd.action do |*files|
228
+ db=Archlinux.config.db
229
+ db.add_to_db(files, update: !cmd.data[:force])
230
+ end
231
+ end
232
+
233
+ db_cmd.add_command('rm') do |cmd|
234
+ cmd.takes_commands(false)
235
+ cmd.short_desc("Remove packages from db")
236
+ cmd.options do |opt|
237
+ opt.on("-f", "--[no-]force", "Also remove the package themselves") do |v|
238
+ cmd.data[:force]=v
239
+ end
240
+ end
241
+ cmd.action do |*files|
242
+ db=Archlinux.config.db
243
+ if cmd.data[:force]
244
+ db.rm_from_db(*files)
245
+ else
246
+ db.remove(*files)
247
+ end
248
+ end
249
+ end
250
+
251
+ db_cmd.add_command('clean') do |cmd|
252
+ cmd.takes_commands(false)
253
+ cmd.short_desc("Clean old files in the db repository")
254
+ cmd.options do |opt|
255
+ opt.on("-f", "--[no-]force", "Force clean (default to dry-run)") do |v|
256
+ cmd.data[:force]=v
257
+ end
258
+ end
259
+ cmd.action do ||
260
+ db=Archlinux.config.db
261
+ paths=db.clean(dry_run: !cmd.data[:force])
262
+ if cmd.data[:force]
263
+ SH.logger.mark "Cleaned:"
264
+ else
265
+ SH.logger.mark "To clean:"
266
+ end
267
+ SH.logger.info paths.map {|p| "- #{p}"}.join("\n")
268
+ end
269
+ end
270
+ end
271
+
272
+ parser.add_command('pkgs') do |pkgs_cmd|
273
+ pkgs_cmd.add_command('list') do |cmd|
274
+ cmd.takes_commands(false)
275
+ cmd.short_desc("List packages")
276
+ cmd.long_desc("The listed package can be '@db' (the current db), '@dbdir' (the packages in the db directory), ':core' (a pacman repo name), :local (the local repo), a file (/var/cache/pacman/pkg/linux-5.3.13.1-1-x86_64.pkg.tar.xz) or a package dir (/var/cache/pacman/pkg); @get(foo1,foo2) (query the aur), @rget(foo1,foo2) (recursively query the aur) ")
277
+ cmd.options do |opt|
278
+ opt.on("-v", "--[no-]version", "Add the package version") do |v|
279
+ cmd.data[:version]=v
280
+ end
281
+ end
282
+ cmd.action do |*repos|
283
+ pkgs=PackageClass.packages(*repos)
284
+ pkgs.list(cmd.data[:version])
285
+ end
286
+ end
287
+
288
+ pkgs_cmd.add_command('compare') do |cmd|
289
+ cmd.takes_commands(false)
290
+ cmd.short_desc("Compare packages")
291
+ cmd.long_desc(<<-EOS)
292
+ Use '--' to separate the two lists
293
+ Exemple: `compare @db -- @dbdir` is essentially the same as `db update -c`
294
+ Exemple: `compare @db -- @get(yay)` is like checking for a yay update
295
+ EOS
296
+ cmd.action do |*repos|
297
+ pkg1, pkg2 = PackageClass.packages_list(*repos)
298
+ pkg1.get_updates(pkg2)
299
+ end
300
+ end
301
+ end
302
+
303
+ parser.add_command('sign') do |cmd|
304
+ cmd.takes_commands(false)
305
+ cmd.short_desc("Sign files")
306
+ cmd.options do |opt|
307
+ opt.on("-f", "--[no-]force", "Overwrite existing signatures") do |v|
308
+ cmd.data[:force]=v
309
+ end
310
+ opt.on("-v", "--verify", "Verify signatures") do |v|
311
+ cmd.data[:verify]=v
312
+ end
313
+ end
314
+ cmd.action do |*files|
315
+ if cmd.data[:verify]
316
+ Archlinux.config.verify_sign(*files)
317
+ else
318
+ Archlinux.config.sign(*files, force: cmd.data[:force])
319
+ end
320
+ end
321
+ end
322
+
323
+ def parser.parse(*args, &b)
324
+ super(*args) do |lvl, cmd|
325
+ SH.process_log_options(self.data)
326
+ b.call(lvl, cmd) if b
327
+ end
328
+ end
329
+
330
+ @config.parser(parser)
331
+
332
+ parser
333
+ end
334
+ end