aur.rb 0.1.0 → 0.2.0

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: c21f4da799946a33333f0361e3ad2b92a191fa2519f8ffdfbdf61ac9c7322002
4
- data.tar.gz: e59e5616ccaa0d8678eb17da201c85f79406b1d1b8fc981344b61d571b0240d0
3
+ metadata.gz: 999e692d5181b3abf4c5d0649c456a7e4e717fd3f7b2a4b88d38d340a0bd9c45
4
+ data.tar.gz: 4a44d86a52faf23ce9fbf720b0b2875239e49307d0e80cad58bf48045a52b3e8
5
5
  SHA512:
6
- metadata.gz: fda385190d3693a71127bf4866b7ceae8ccade180cbb9101c1499d7c08b9a5dff7302fe0a2ec222a0e5e3d3af5609f7754b557cac6b2e75ba5ccd6798b6bbb84
7
- data.tar.gz: 2532269251f3a31201c0ba6418a53b52d5d3600acc84359ab07022c1aa370ff699d4c7156a832b2f3f003c809d2bf4824a136680a34886a38e4f8635b5371e9c
6
+ metadata.gz: 2f3d706085720f78e4fb7cb3d37d4fceb6a99982e11c80b8dc2a59678966824d0913c9becc59707b1823f7f37c9f1ccb2fc360a138d83b11744edabad26713e7
7
+ data.tar.gz: 70a7f785463661f9cd9412c4fb9f08ef74893fd0e2ce20b6cc28aeeb9779a835718601913b549f17297fad241b18b00c386353865291bb8743149855215c7530
@@ -1,4 +1,138 @@
1
- ### 0.1.0 / 2018-09-07
1
+ == Release v0.2.0 (2020-02-21) ==
2
2
 
3
- * Initial release:
3
+ * TODO++
4
+ * TODO.md
5
+ * config: fix sign_names
6
+ * config: use devtools makepkg in chroot
7
+ * pre build: pre sign an empty files to initialize keyring
8
+ * DR::URI is now DR::URIEscape
9
+ * Use back URI.escape (via a wrapper to disable warnings)
10
+ * Ruby 2.7 warning fixes
11
+ * Fixes for ruby 2.7
12
+ * Update for ruby 2.7
13
+ * packages: add @get() and @rget()
14
+ * config.rb: Bug fix
15
+ * Default packages: allow customisations
16
+ * cli: pkgs compare
17
+ * Bug fixes
18
+ * cli: aur.rb pkgs list ...
19
+ * Bug fix
20
+ * cli: db list, db rm
21
+ * better update infos
22
+ * db: clean up update code
23
+ * aur.rb: TODO + fix db rm bug
24
+ * cli: Add devtools commands
25
+ * cli: aur search => allow multiple terms
26
+ * cli: --no-chroot, --local
27
+ * makepkg: get list of built packages for post_install callback
28
+ * cli: --install/--no-install option
29
+ * Add build command
30
+ * config: use PPHelper
31
+ * Distinguish package list class from an install package list class
32
+ * Split packages into install_packages
33
+ * DB#clean_obsolete
34
+ * PackageFiles#rm_pkgs
35
+ * Documentation updates
36
+ * Doc updates
37
+ * Bug fixes, DB#show_updates
38
+ * Improve messages
39
+ * Fix verbosity level
40
+ * Fix logger invocations for new SH.logger api
41
+ * cli: use standard log options
42
+ * Logger: color mode
43
+ * Gemspec: add metadata
44
+ * Update Rakefile
45
+ * cli: sign
46
+ * TODO++
47
+ * Verbosity
48
+ * pretty_print: don't expand Config
49
+ * sign: bug fixes (+ reduce verbosity of exec)
50
+ * db: Move sig files
51
+ * makepkg: resign files when rebuilding
52
+ * repos.rb: clean return the paths too
53
+ * config.rb: post_install now install correct (updated) version
54
+ * packages.rb: fix --rebuild=true
55
+ * makepkg.rb: improve visibility of logging
56
+ * cli.rb: --verbose
57
+ * More debug informations
58
+ * cli.rb: debug and log level
59
+ * db: allow to return PackageFiles rather than Packages
60
+ * PackageFiles#clean
61
+ * add_to_db: bug fixes
62
+ * DB#add_to_db
63
+ * post_install: call with list of packages success
64
+ * cli.rb: --devel switch
65
+ * makepkg.rb: we need to use `reset --hard`
66
+ * packages.rb: Bug fix in AurMakepkgCache
67
+ * Add debug statements
68
+ * TODO++
69
+ * Helpers: create_class
70
+ * db.rb: use realdirpath and fallback to abs path
71
+ * cli.rb: `db update`
72
+ * A package already built is a success
73
+ * Implement --rebuild
74
+ * cli: pacman command
75
+ * config: default to not devel
76
+ * post_install
77
+ * packages: unify how to get a MakepkgList
78
+ * makepkg: missing require
79
+ * cli
80
+ * Use SH.log
81
+ * aur_rpc: rework class vs modules layout
82
+ * Global aur cache
83
+ * packages.rb: AurMakepkgCache preserve MakepkgList
84
+ * Package by package install method
85
+ * bug fixes + config.default_packages
86
+ * repos.rb: RepoPkgs, LocalRepo
87
+ * bugfixes + TODO++
88
+ * MakepkgList.from_dir, Repo.foreign_packages
89
+ * verify signatures
90
+ * db.rb: use bsdtar rather than bsdcat to parse db files
91
+ * packages.rb: add keys which have array values
92
+ * Makepkg: edit and edit_pkgbuild
93
+ * db and packages: more bug fixes
94
+ * Bug fixes
95
+ * no_load_config: don't read the user config file
96
+ * Bug fixes + update TODO list
97
+ * Bug fixes
98
+ * makepkg: methods now return success or failure
99
+ * Comments + api change
100
+ * PackageFiles: default to bsdtar rathern than expac
101
+ * More bug fixes
102
+ * Bug fixes and be less verbose
103
+ * makepkg: view before pkgver
104
+ * config: use deep_merge
105
+ * @config.sign: sign files rather than just a file
106
+ * makepkg: done_view and done_build
107
+ * makepkg: move git stuff to its own class
108
+ * devtools + makepkg: unify api
109
+ * sign: uniformise api
110
+ * Rework sudo_loop, put it in shell_helpers
111
+ * packages: chain queries
112
+ * makepkg: get options + db sign files
113
+ * makepkg: check pkgver
114
+ * More bug fixes
115
+ * Bug fixes
116
+ * Bug fixes
117
+ * VirtualMakepkgCache
118
+ * MakepkgList cache
119
+ * packages: to_ext_query
120
+ * aur_rpc: AurQueryCustom to set individual @config
121
+ * Pass along config and allow to specify the default PackageList class
122
+ * packages: fix ups
123
+ * packages: better api for do_install
124
+ * packages: ignore
125
+ * config.rb: sudo_loop, get_config_file
126
+ * config.rb: sudo loop + options
127
+ * Warn when a package was not built
128
+ * packages update/install: do it against another list
129
+ * Inject config
130
+ * Split into different files
131
+
132
+ == Release v0.1.0 (2018-09-07) ==
133
+
134
+ * Update library paths
135
+ * Add code + gemspec dependencies
136
+ * Update README.md
137
+ * Initial commit.
4
138
 
@@ -1,4 +1,4 @@
1
- Copyright (c) 2018 Damien Robert
1
+ Copyright © 2018–2020 Damien Robert
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -24,8 +24,28 @@ installation.
24
24
 
25
25
  $ gem install aur.rb
26
26
 
27
+ ## Notes
28
+
29
+ There are three ways to install packages. The first is to simply use
30
+ `makepkg -si`. Here aur dependency packages built need to be installed
31
+ because `pacman` won't find them. So it may be cause for conflict.
32
+
33
+ The other way is to build dependency packages and add them to a database.
34
+ This allows further call to `makepkg -s` to find them if they are needed to
35
+ build a particular package. It may still cause conflict because they are
36
+ installed against the full system.
37
+
38
+ The last way is to build in a chroot. Here a database is needed too so the
39
+ chroot can access to the previously built dependency packages.
40
+
41
+ Adding a database require modifying `pacman.conf`, but `aur.rb` will
42
+ generate a temporary `pacman.conf` with the current database location
43
+ and use that when needed.
44
+
45
+ With this feature it is easy to simulate `checkupdates` too.
46
+
27
47
  ## Copyright
28
48
 
29
- Copyright © 2018 Damien Robert
49
+ Copyright © 2018–2020 Damien Robert
30
50
 
31
51
  MIT License. See [LICENSE.txt](./LICENSE.txt) for details.
data/Rakefile CHANGED
@@ -1,15 +1,5 @@
1
1
  require 'rake'
2
2
 
3
- begin
4
- require 'rubygems/tasks'
5
- Gem::Tasks.new(sign: {checksum: true, pgp: true},
6
- scm: {status: true}) do |tasks|
7
- tasks.console.command = 'pry'
8
- end
9
- rescue LoadError => e
10
- warn e.message
11
- end
12
-
13
3
  require 'rake/testtask'
14
4
  Rake::TestTask.new do |test|
15
5
  test.libs << 'test'
@@ -27,3 +17,10 @@ rescue LoadError => e
27
17
  end
28
18
  task :doc => :yard
29
19
 
20
+ begin
21
+ require 'dr/rake_gems'
22
+ Gem::MyTasks.new
23
+ rescue LoadError => e
24
+ warn e.message
25
+ end
26
+
data/TODO.md ADDED
@@ -0,0 +1,69 @@
1
+ # Api
2
+
3
+ - tests
4
+ - check if splitpackages are handled correctly
5
+ - add doc
6
+
7
+ ## Improvements
8
+
9
+ - replace AurMakepkgCache with a more generic aggregator
10
+ - using tsort in rget won't do a breadth first search, which would reduce
11
+ the number of aur queries. With tsort packages are queried one by one.
12
+ - split Makepkg into a class for downloading/viewing/querying and the
13
+ class for installing. This will allow to support both github and the aur
14
+ rpc.
15
+ - preference to decide which :provides to choose
16
+ (and favorise local packages for provides)
17
+
18
+ ## UI
19
+
20
+ - view only when updated/new + add trusted PKGBUILD
21
+ - confirm before installing or updating pkgver (this is somewhat orthogonal to exiting when view return false since we may want to not view the files)
22
+ - due to vercmp, we need to reset packages before pulling
23
+ => use stash?
24
+
25
+ # Bugs
26
+
27
+ - in `official` we need to also list packages provided by them, eg
28
+ libarchive provides libarchive.so (but libarchive.so does not exist
29
+ separately, but eg aurutils-git requires it)
30
+ - also when we have custom packages in our db (not in aur), we get missing
31
+ warnings for these packages
32
+ - when using AurMakepkgCache, missing packages info are repeated several times
33
+ - in `sync_db` setup a simily local cache so that the packages don't get copied twice (ie both in `/var/cache/pacman` and in `~/.cache/arch_aur/.db`).
34
+ - makechrootpkg does not propagate 'PKGEXT' setting. So if we use zstd in
35
+ our local makepkg.conf, `aur.rb` will expect a `zst` package to be
36
+ built, but an `xz` will be built instead (per the default
37
+ `makepkg.conf` in the chroot). Not sure how to fix this. The solution
38
+ is to specify devtool's `makepkg.conf` for chroot builds (cf the
39
+ config).
40
+
41
+ # CLI
42
+
43
+ - in db update, allow to be more atomic, same for clean
44
+ That is we want to be able to specify which packages we want to
45
+ update/clean
46
+
47
+ - in `aur install`:
48
+ - add --no-fetch and --fetch=only (or add an extra command specialised in
49
+ fetching PKGBUILD?);
50
+ - add --extra-db= to specify an external db
51
+ - add --buildopts="..." that gets passed to our build function
52
+
53
+ - Add packages informations:
54
+ - 'package info'
55
+ - 'package compare' [DONE]
56
+ - 'package list' [DONE]
57
+
58
+ - Expand around our temp pacman config features to remake `checkupdates` in
59
+ a more flexible way. For instance to only update our extra database on
60
+ interact with our `aur` database. Would need features to symlink the
61
+ local database too.
62
+
63
+ - Clean `~/.cache/arch_aur`. Essentially a wrapper to call
64
+ `for i in *(/); do git -C $i clean -ndx; done`
65
+
66
+ # Tools
67
+
68
+ - use https://github.com/falconindy/pkgbuild-introspection/ to speed up .SRCINFO
69
+
@@ -66,4 +66,5 @@ Gem::Specification.new do |gem|
66
66
  end
67
67
 
68
68
  gem.metadata['yard.run']='yri'
69
+ gem.metadata['project_name']='Archlinux'
69
70
  end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'aur'
4
+ require 'aur/cli'
5
+
6
+ Archlinux.cli.parse
data/lib/aur.rb CHANGED
@@ -1,1861 +1,51 @@
1
1
  require 'aur/version'
2
- require 'net/http'
3
- require 'json'
4
- require 'time'
5
- require 'forwardable'
6
- require 'tsort'
7
- require 'dr/base/utils'
8
- require 'shell_helpers'
2
+ require 'aur/config'
3
+ require 'aur/aur_rpc'
4
+ require 'aur/db'
5
+ require 'aur/repos'
6
+ require 'aur/versions'
7
+ require 'aur/devtools'
8
+ require 'aur/makepkg'
9
+ require 'aur/packages'
10
+ require 'aur/install_packages'
9
11
 
10
- module Archlinux
11
- ArchlinuxError=Class.new(StandardError)
12
- Utils=::DR::Utils
13
- Pathname=::SH::Pathname
14
-
15
- def self.delegate_h(klass, var)
16
- klass.extend(Forwardable)
17
- methods=[:[], :[]=, :any?, :assoc, :clear, :compact, :compact!, :delete, :delete_if, :dig, :each, :each_key, :each_pair, :each_value, :empty?, :fetch, :fetch_values, :has_key?, :has_value?, :include?, :index, :invert, :keep_if, :key, :key?, :keys, :length, :member?, :merge, :merge!, :rassoc, :reject, :reject!, :select, :select!, :shift, :size, :slice, :store, :to_a, :to_h, :to_s, :transform_keys, :transform_keys!, :transform_values, :transform_values!, :update, :value?, :values, :values_at]
18
- klass.include(Enumerable)
19
- klass.send(:def_delegators, var, *methods)
20
- end
21
-
22
- class Config
23
- def self.create(v)
24
- v.is_a?(self) ? v : self.new(v)
25
- end
26
-
27
- attr_accessor :opts
28
- Archlinux.delegate_h(self, :@opts)
29
- include SH::ShConfig
30
-
31
- def initialize(file, **opts)
32
- file=Pathname.new(file)
33
- file=Pathname.new(ENV["XDG_CONFIG_HOME"] || "#{ENV['HOME']}/.config") + file if file.relative?
34
- file_config= file.readable? ? file.read : '{}'
35
- wrap=eval("Proc.new { |context| #{file_config} }")
36
- @opts=default_config.merge(opts)
37
- @opts.merge!(wrap.call(self))
38
- end
39
-
40
- def sh_config
41
- @opts[:sh_config]
42
- end
43
-
44
- def default_config
45
- {
46
- cache: "arch_aur", #where we dl PKGBUILDs
47
- db: 'aur', #if relative the db will be in cachedir
48
- aur_url: "https://aur.archlinux.org/", #base aur url
49
- chroot: "/var/lib/aurbuild/x86_64", #chroot root
50
- build_chroot: false, #do we use the chroot?
51
- chroot_update: 'pacman -Syu --noconfirm', #how to update an existing chroot
52
- sign: true, #can be made more atomic, cf the sign method
53
- devtools_pacman: "/usr/share/devtools/pacman-extra.conf", #pacman.conf for chroot build
54
- pacman_conf: "/etc/pacman.conf", #default pacman-conf (for makepkg build)
55
- sh_config: { #default programs options
56
- makepkg: {default_opts: ["-crs", "--needed"]},
57
- makechrootpkg: {default_opts: ["-cu"]},
58
- },
59
- view: "vifm -c view! -c tree -c 0",
60
- git_update: "git pull",
61
- git_clone: "git clone",
62
- }
63
- end
64
-
65
- #:makepkg, :makechrootpkg, :repo (=repo-add, repo-remove)
66
- def sign(mode)
67
- opt_sign=@opts[:sign]
68
- if opt_sign.is_a?(Hash)
69
- opt_sign[mode]
70
- else
71
- opt_sign
72
- end
73
- end
74
-
75
- def view(dir)
76
- view=@opts[:view]
77
- case view
78
- when Proc
79
- view.call(dir)
80
- else
81
- success, _rest=SH.sh("#{view} #{dir.shellescape}")
82
- return success
83
- end
84
- end
85
-
86
- def git_update
87
- method=@opts[:git_update]
88
- case method
89
- when Proc
90
- method.call(dir)
91
- else
92
- success, _rest=SH.sh(method)
93
- return success
94
- end
95
- end
96
-
97
- def git_clone(url, dir)
98
- method=@opts[:git_clone]
99
- case method
100
- when Proc
101
- method.call(url, dir)
102
- else
103
- success, _rest=SH.sh("#{method} #{url.shellescape} #{dir.shellescape}")
104
- return success
105
- end
106
- end
107
-
108
- def cachedir
109
- global_cache=Pathname.new(ENV["XDG_CACHE_HOME"] || "#{ENV['HOME']}/.cache")
110
- cache=global_cache+@opts[:cache]
111
- cache.mkpath
112
- cache
113
- end
114
-
115
- def setup_pacman_conf(conf)
116
- pacman=PacmanConf.create(conf)
117
- aur=self.db(false)
118
- if aur and !pacman[:repos].include?(aur.repo_name)
119
- pacman[:repos][aur.repo_name]={Server: ["file://#{URI.escape(aur.dir.to_s)}"]}
120
- end
121
- pacman
122
- end
123
-
124
- def default_pacman_conf
125
- @default_pacman_conf ||= if (conf=@opts[:pacman_conf])
126
- PacmanConf.new(conf)
127
- else
128
- PacmanConf
129
- end
130
- end
131
-
132
- def devtools
133
- unless @devtools_config
134
- require 'uri'
135
- # here we need the raw value, since this will be used by pacstrap
136
- # which calls pacman --root; so the inferred path for DBPath and so
137
- # on would not be correct since it is specified
138
- devtools_pacman=PacmanConf.new(@opts[:devtools_pacman], raw: true)
139
- # here we need to expand the config, so that Server =
140
- # file://...$repo...$arch get their real values
141
- my_pacman=default_pacman_conf
142
- devtools_pacman[:repos].merge!(my_pacman.non_official_repos)
143
- setup_pacman_conf(devtools_pacman)
144
- @devtools_config = Devtools.new(pacman_conf: devtools_pacman)
145
- end
146
- @devtools_config
147
- end
148
-
149
- def makepkg_config
150
- unless @makepkg_config
151
- makepkg_pacman=default_pacman_conf
152
- setup_pacman_conf(makepkg_pacman)
153
- @makepkg_config = Devtools.new(pacman_conf: makepkg_pacman)
154
- end
155
- @makepkg_config
156
- end
157
-
158
- def db=(name)
159
- case name
160
- when DB
161
- @db=name
162
- when Pathname
163
- @db=DB.new(name)
164
- when String
165
- if DB.db_file?(name)
166
- @db=DB.new(name)
167
- else
168
- @db=DB.new(cachedir+".db"+"#{name}.db.tar.gz")
169
- end
170
- when true
171
- @db=DB.new(cachedir+".db"+"aur.db.tar.gz")
172
- when false, nil
173
- @db=name #false for false, nil to reset
174
- else
175
- SH.logger.warn("Database name #{name} not suitable")
176
- @db=nil
177
- end
178
- # reset these so the pacman_conf gets the correct db name
179
- @makepkg_config=nil
180
- @devtools_config=nil
181
- end
182
-
183
- def db(create=true)
184
- @db.nil? and self.db=@opts[:db]
185
- if create and @db
186
- @db.create
187
- end
188
- @db
189
- end
190
-
191
- # if changing a setting, we may need to reset the rest
192
- def reset
193
- self.db=nil
194
- end
195
- end
196
-
197
- self.singleton_class.attr_accessor :config
198
- @config=Config.new("aur.rb")
199
-
200
- def self.add_to_hash(h, key, value)
201
- case h[key]
202
- when nil
203
- h[key] = value
204
- when Array
205
- h[key] << value
206
- else
207
- h[key]=[h[key], value]
208
- end
209
- end
210
-
211
- class Version
212
- def self.create(v)
213
- v.is_a?(self) ? v : self.new(v)
214
- end
215
-
216
- include Comparable
217
- attr_reader :epoch, :version, :pkgrel
218
- def initialize(v)
219
- @v=v
220
- parse(v)
221
- end
222
-
223
- private def parse(v)
224
- if v.nil? or v.empty?
225
- @epoch=-1 #any version is better than empty version
226
- @version=Gem::Version.new(0)
227
- @pkgrel=nil
228
- return
229
- end
230
- epoch, rest = v.split(':', 2)
231
- if rest.nil?
232
- rest=epoch; epoch=0
233
- @real_epoch=false
234
- else
235
- @real_epoch=true
236
- end
237
- @epoch=epoch
238
- version, pkgrel=Utils.rsplit(rest, '-', 2)
239
- version.tr!('+_','.')
240
- @version=Gem::Version.new(version) rescue Gem::Version.new("0.#{version}")
241
- @pkgrel=pkgrel
242
- end
243
-
244
- def <=>(w)
245
- w=self.class.new(w.to_s)
246
- r= @epoch <=> w.epoch
247
- if r==0
248
- r= @version <=> w.version
249
- if r==0 and @pkgrel and w.pkgrel
250
- r= @pkgrel <=> w.pkgrel
251
- end
252
- end
253
- r
254
- end
255
-
256
- def to_s
257
- @v
258
- # r=""
259
- # r << "#{@epoch}:" if @real_epoch
260
- # r << "#{@version}"
261
- # r << "-#{@pkgrel}" if @pkgrel
262
- # r
263
- end
264
-
265
- # strip version information from a package name
266
- def self.strip(v)
267
- v.sub(/[><=]+[\w.\-+:]*$/,'')
268
- end
269
- end
270
-
271
- # ex: pacman>=2.0.0
272
- QueryError=Class.new(ArchlinuxError)
273
- class Query
274
- def self.create(v)
275
- v.is_a?(self) ? v : self.new(v)
276
- end
277
-
278
- def self.strip(query)
279
- self.create(query).name
280
- end
281
-
282
- include Comparable
283
- attr_accessor :name, :op, :version, :op2, :version2
284
- def initialize(query)
285
- @query=query
286
- @name, @op, version, @op2, version2=parse(query)
287
- @version=Version.new(version)
288
- @version2=Version.new(version2)
289
- end
290
-
291
- def to_s
292
- @query
293
- end
294
-
295
- def max
296
- strict=false
297
- if @op=='<=' or @op=='='
298
- max=@version
299
- elsif @op=="<"
300
- max=@version; strict=true
301
- elsif @op2=="<="
302
- max=@version2
303
- elsif @op2=="<"
304
- max=@version2; strict=true
305
- end
306
- return max, strict #nil means Float::INFINITY, ie no restriction
307
- end
308
-
309
- def min
310
- strict=false
311
- if @op=='>=' or @op=='='
312
- min=@version
313
- elsif @op==">"
314
- min=@version; strict=true
315
- elsif @op2==">="
316
- min=@version2
317
- elsif @op2==">"
318
- min=@version2; strict=true
319
- end
320
- return min, strict
321
- end
322
-
323
- def <=>(other)
324
- other=self.class.create(other)
325
- min, strict=self.min
326
- omin, ostrict=other.min
327
- return 1 if min==omin and strict
328
- return -1 if min==omin and ostrict
329
- min <=> omin
330
- end
331
-
332
- # here we check if a package can satisfy a query
333
- # note that other can itself be a query, think of a package that
334
- # requires foo>=2.0 and bar which provides foo>=3
335
- # satisfy? is symmetric, it means that the intersection of the
336
- # available version ranges is non empty
337
- def satisfy?(other)
338
- case other
339
- when Version
340
- omin=other; omax=other; ominstrict=false; omaxstrict=false
341
- oname=@name #we assume the name comparison was already done
342
- else
343
- other=self.class.create(other)
344
- omax, omaxstrict=other.max
345
- omin, ominstrict=other.min
346
- oname=other.name
347
- end
348
- return false unless @name==oname
349
- min, strict=self.min
350
- return false if omax and min and (omax < min or omax == min && (strict or omaxstrict))
351
- max, strict=self.max
352
- return false if max and omin and (omin > max or omin == max && (strict or ominstrict))
353
- true
354
- end
355
-
356
- private def parse(query)
357
- if (m=query.match(/^([^><=]*)([><=]*)([\w.\-+:]*)([><=]*)([\w.\-+:]*)$/))
358
- name=m[1]
359
- op=m[2]
360
- version=m[3]
361
- op2=m[4]
362
- version2=m[5]
363
- if op.nil?
364
- name, version=Utils.rsplit(name, '-', 2)
365
- op="="; op2=nil; version2=nil
366
- end
367
- return name, op, version, op2, version2
368
- else
369
- raise QueryError.new("Bad query #{query}")
370
- end
371
- end
372
- end
373
-
374
- module AurQuery
375
- extend self
376
- attr_accessor :config
377
- @config=Archlinux.config
378
-
379
- AurQueryError=Class.new(ArchlinuxError)
380
-
381
- # AurQuery.query(type: "info", arg: pacaur)
382
- # AurQuery.query(type: "info", :"arg[]" => %w(cower pacaur))
383
- # AurQuery.query(type: "search", by: "name", arg: "aur")
384
- # by name (search by package name only)
385
- # *name-desc* (search by package name and description)
386
- # maintainer (search by package maintainer)
387
- # depends (search for packages that depend on keywords)
388
- # makedepends (search for packages that makedepend on keywords)
389
- # optdepends (search for packages that optdepend on keywords)
390
- # checkdepends (search for packages that checkdepend on keywords)
391
- # => search result:
392
- # {"ID"=>514909,
393
- # "Name"=>"pacaur",
394
- # "PackageBaseID"=>49145,
395
- # "PackageBase"=>"pacaur",
396
- # "Version"=>"4.7.90-1",
397
- # "Description"=>"An AUR helper that minimizes user interaction",
398
- # "URL"=>"https://github.com/rmarquis/pacaur",
399
- # "NumVotes"=>1107,
400
- # "Popularity"=>7.382043,
401
- # "OutOfDate"=>nil,
402
- # "Maintainer"=>"Spyhawk",
403
- # "FirstSubmitted"=>1305666963,
404
- # "LastModified"=>1527690065,
405
- # "URLPath"=>"/cgit/aur.git/snapshot/pacaur.tar.gz"},
406
- # => info result adds:
407
- # "Depends"=>["cower", "expac", "sudo", "git"],
408
- # "MakeDepends"=>["perl"],
409
- # "License"=>["ISC"],
410
- # "Keywords"=>["AUR", "helper", "wrapper"]}]
411
-
412
- def query(h)
413
- uri=URI("#{@config[:aur_url]}/rpc/")
414
- params = {v:5}.merge(h)
415
- uri.query = URI.encode_www_form(params)
416
- res = Net::HTTP.get_response(uri)
417
- if res.is_a?(Net::HTTPSuccess)
418
- r= res.body
419
- else
420
- raise AurQueryError.new("AUR: Got error response for query #{h}")
421
- end
422
- data = JSON.parse(r)
423
- case data['type']
424
- when 'error'
425
- raise AurQueryError.new("Error: #{data['results']}")
426
- when 'search','info','multiinfo'
427
- return data['results']
428
- else
429
- raise AurQueryError.new("Error in response data #{data}")
430
- end
431
- end
432
-
433
- # Outdated packages: Aur.search(nil, by: "maintainer")
434
- def search(arg, by: nil)
435
- r={type: "search", arg: arg}
436
- r[:by]=by if by
437
- # if :by is not specified, aur defaults to name-desc
438
- self.query(r)
439
- end
440
-
441
- def infos(*pkgs, slice: 150)
442
- search=[]
443
- pkgs.each_slice(slice) do |pkgs_slice|
444
- r={type: "info", :"arg[]" => pkgs_slice}
445
- search+=self.query(r)
446
- end
447
- search.each { |pkg| pkg[:repo]=:aur }
448
- search
449
- end
450
-
451
- # try to use infos if possible
452
- def info(pkg)
453
- r={type: "info", arg: pkg}
454
- self.query(r).first
455
- end
456
-
457
- def packages(*pkgs, klass: AurPackageList)
458
- klass.new(infos(*pkgs))
459
- end
460
-
461
- def pkglist(type="packages", delay: 3600, query: :auto) #type=pkgbase
462
- require 'zlib'
463
- cache=self.cachedir
464
- file=cache+"#{type}.gz"
465
- in_epoch=nil
466
- if file.exist?
467
- # intime=file.read.each_line.first
468
- file.open do |io|
469
- Zlib::GzipReader.wrap(io) do |gz|
470
- intime=gz.each_line.first
471
- intime.match(/^# AUR package list, generated on (.*)/) do |m|
472
- in_epoch=Time.parse(m[1]).to_i
473
- end
474
- end
475
- end
476
- end
477
- if query
478
- Net::HTTP.get_response(URI("#{@config[:aur_url]}/#{type}.gz")) do |res|
479
- date=res["date"]
480
- update=true
481
- if date
482
- epoch=Time.parse(date).to_i
483
- update=false if epoch and in_epoch and (epoch-in_epoch < delay) and !query==true
484
- end
485
- if update
486
- file.open('w') do |io|
487
- Zlib::GzipWriter.wrap(io) do |gz|
488
- res.read_body(gz)
489
- end
490
- end
491
- end
492
- end
493
- end
494
- file.open do |io|
495
- Zlib::GzipReader.wrap(io) do |gz|
496
- return gz.each_line.map(&:chomp).drop(1)
497
- end
498
- end
499
- end
500
- end
501
-
502
- class DB
503
- def self.create(v)
504
- v.is_a?(self) ? v : self.new(v)
505
- end
506
-
507
- def self.db_file?(name)
508
- case name
509
- when Pathname
510
- true
511
- when String
512
- if name.include?('/') or name.match(/\.db(\..*)?$/)
513
- true
514
- else
515
- false #Usually we assume this is a repo name
516
- end
517
- end
518
- end
519
-
520
- attr_accessor :file, :config
521
- def initialize(file, config: Archlinux.config)
522
- @file=Pathname.new(file)
523
- @config=config
524
- end
525
-
526
- def mkpath
527
- @file.dirname.mkpath
528
- end
529
-
530
- def path
531
- if file.exist?
532
- file.realpath
533
- else
534
- file
535
- end
536
- end
537
-
538
- def repo_name
539
- @file.basename.to_s.sub(/\.db(\..*)?$/,'')
540
- end
541
-
542
- def create
543
- mkpath
544
- unless @file.exist?
545
- call(:'repo-add', path.shellescape)
546
- end
547
- self
548
- end
549
-
550
- def to_s
551
- @file.to_s
552
- end
553
-
554
- # a db is a tar.gz archive of packages/desc, like yay-8.998-1/descr
555
- def list
556
- require 'dr/sh'
557
- res= SH.run_simple("bsdcat #{@file.shellescape}", chomp: :lines) {return nil}
558
- list=[]; pkg={}; mode=nil
559
- flush = lambda do
560
- # catch old deps files which don't specify the full infos
561
- unless pkg[:name].nil? and pkg[:base].nil?
562
- pkg[:repo]||=path
563
- list << pkg
564
- end
565
- end
566
- res.each do |l|
567
- next if l.empty? or l.match(/^\u0000*$/)
568
- if (m=l.match(/(\u0000+.*\u0000+)?%([A-Z0-9]*)%$/))
569
- mode=m[2].downcase.to_sym
570
- if m[1] #new db entry
571
- flush.call #store old db entry
572
- pkg={}
573
- end
574
- else
575
- l=l.to_i if mode==:csize or mode==:isize
576
- l=Time.at(l.to_i) if mode==:builddate
577
- Archlinux.add_to_hash(pkg, mode, l)
578
- end
579
- end
580
- flush.call #don't forget the last one
581
- list
582
- end
583
-
584
- def files(absolute=true)
585
- list.map do |pkg|
586
- file=Pathname.new(pkg[:filename])
587
- absolute ? dir + file : file
588
- end
589
- end
590
- # def files
591
- # packages.l.map {|pkg| dir+Pathname.new(pkg[:filename])}
592
- # end
593
-
594
- def dir
595
- @file.dirname.realpath
596
- end
597
-
598
- def call(*args)
599
- @config.launch(*args) do |*args|
600
- dir.chdir do
601
- SH.sh(*args)
602
- end
603
- end
604
- end
605
-
606
- def move_to_db(*files, op: :mv)
607
- files=files.map {|f| Pathname.new(f).realpath}
608
- dir=self.dir
609
- files.map do |f|
610
- if f.dirname == dir
611
- f
612
- else
613
- new=dir+f.basename
614
- f.send(op, new)
615
- new
616
- end
617
- end
618
- files
619
- end
620
-
621
- def add(*files, cmd: :'repo-add', default_opts:[], sign: @config.sign(:repo), **opts)
622
- default_opts+=['-s', '-v'] if sign
623
- default_opts+=['--key', sign] if sign.is_a?(String)
624
- unless files.empty?
625
- call(cmd, path.shellescape, *files, default_opts: default_opts, **opts)
626
- end
627
- end
628
-
629
- def remove(*args, **opts)
630
- add(*args, cmd: :'repo-remove', **opts)
631
- end
632
-
633
- def packages(refresh=false)
634
- @packages=nil if refresh
635
- @packages||=PackageList.new(list)
636
- end
637
-
638
- def dir_packages
639
- PackageFiles.from_dir(dir).packages
640
- end
641
-
642
- def package_files
643
- PackageFiles.new(*files).packages
644
- end
645
-
646
- def check
647
- packages.same?(package_files)
648
- end
649
-
650
- def check_update(other=dir_packages)
651
- up=self.packages.check_updates(other)
652
- refresh=up.select {|_k, u| u[:op]==:upgrade or u[:op]==:downgrade}
653
- add=up.select {|_k, u| u[:op]==:install}
654
- remove=up.select {|_k, u| u[:op]==:obsolete}
655
- return {refresh: refresh, add: add, remove: remove}
656
- end
657
-
658
- def update(other=dir_packages)
659
- r=check_update(other)
660
- add(*(r[:refresh].merge(r[:add])).map {|_k,v| other[v[:out_pkg]].file.shellescape})
661
- # remove(*(r[:remove].map {|_k,v| packages[v[:in_pkg]].file.shellescape}))
662
- remove(*(r[:remove].map {|_k,v| Query.strip(v[:in_pkg])}))
663
- r
664
- end
665
- end
666
-
667
- class Repo
668
- def self.create(v)
669
- v.is_a?(self) ? v : self.new(v)
670
- end
671
-
672
- def initialize(name)
673
- @repo=name
674
- end
675
-
676
- def list(mode: :pacsift)
677
- command= case mode
678
- when :pacman
679
- "pacman -Slq #{@repo.shellescape}" #returns pkg
680
- when :pacsift
681
- "pacsift --exact --repo=#{@repo.shellescape} <&-" #returns repo/pkg
682
- when :paclist
683
- "paclist #{@repo.shellescape}" #returns pkg version
684
- end
685
- SH.run_simple(command, chomp: :lines) {return nil}
686
- end
687
-
688
- def packages(refresh=false)
689
- @packages=nil if refresh
690
- @packages ||= PackageList.new(self.class.info(*list))
691
- end
692
-
693
- def self.pacman_info(*pkgs, local: false) #local=true refers to the local db info
694
- list=[]
695
- to_list=lambda do |s|
696
- return [] if s=="None"
697
- s.split
698
- end
699
- # Note: core/pacman only works for -Sddp or -Si, not for -Qi
700
- # Indeed local/pacman works for pacinfo, but not for pacman (needs
701
- # the -Q options)
702
- res=SH.run_simple({'COLUMNS' => '1000'}, "pacman -#{local ? 'Q': 'S'}i #{pkgs.shelljoin}", chomp: :lines)
703
- key=nil; info={}
704
- res.each do |l|
705
- if key==:optional_deps and (m=l.match(/^\s+(\w*):\s+(.*)$/))
706
- #here we cannot split(':') because we need to check for the leading space
707
- info[key][m[1]]=m[2]
708
- else
709
- key, value=l.split(/\s*:\s*/,2)
710
- if key.nil? #new package
711
- list << info; key=nil; info={}
712
- next
713
- end
714
- key=key.strip.downcase.gsub(' ', '_').to_sym
715
- case key
716
- when :optional_deps
717
- dep, reason=value.split(/\s*:\s*/,2)
718
- value={dep => reason}
719
- when :groups, :provides, :depends_on, :required_by, :optional_for, :conflicts_with, :replaces
720
- value=to_list.call(value)
721
- when :install_script
722
- value=false if value=="No"
723
- value=true if value=="Yes"
724
- when :install_date, :build_date
725
- value=Time.parse(value)
726
- end
727
- info[key]=value
728
- end
729
- end
730
- # no need to add the last info, pacman -Q/Si always end with a new line
731
- list
732
- end
733
-
734
- def self.pacinfo(*pkgs) #this refers to the local db info
735
- list=[]; info={}
736
- res=SH.run_simple("pacinfo #{pkgs.shelljoin}", chomp: :lines)
737
- res.each do |l|
738
- key, value=l.split(/\s*:\s*/,2)
739
- if key.nil? #next package
740
- list << info; info={}
741
- next
742
- end
743
- key=key.downcase.gsub(' ', '_').to_sym
744
- case key
745
- when :install_script
746
- value=false if value=="No"
747
- value=true if value=="Yes"
748
- when :install_date, :build_date
749
- value=Time.parse(value)
750
- end
751
- Archlinux.add_to_hash(info, key, value)
752
- end
753
- # no need to add at the end, pacinfo always end with a new line
754
- list
755
- end
756
-
757
- def self.info(*packages)
758
- if SH.find_executable("pacinfo")
759
- pacinfo(*packages)
760
- else
761
- pacman_info(*packages)
762
- end
763
- end
764
-
765
- def self.packages(*packages)
766
- PackageList.new(info(*packages))
767
- end
768
- end
769
-
770
- class PackageFiles
771
- def self.create(v)
772
- v.is_a?(self) ? v : self.new(v)
773
- end
774
-
775
- attr_accessor :files
776
- def initialize(*files)
777
- @files=files.map {|file| DR::Pathname.new(file)}
778
- end
779
-
780
- def infos(slice=200) #the command line should not be too long
781
- format={
782
- filename: "%f",
783
- pkgname: "%n",
784
- pkgbase: "%e",
785
- version: "%v",
786
- url: "%u",
787
- description: "%d",
788
- packager: "%p",
789
- architecture: "%a",
790
- build_date: "%b",
791
- download_size: "%k",
792
- install_size: "%m",
793
- depends: "%D",
794
- conflicts: "%H",
795
- opt_depends: "%O",
796
- provides: "%P",
797
- replaces: "%T",
798
- # format << "%r\n" #repo
799
- }
800
- total=format.keys.count
801
- r=[]; delim=" , "
802
- split=lambda do |l| l.split(delim) end
803
- @files.each_slice(slice) do |files|
804
- SH.run_simple("expac --timefmt=%s #{format.values.join("\n").shellescape} -l #{delim.shellescape} -p #{files.shelljoin}", chomp: :lines).each_slice(total).with_index do |l,i|
805
- info={}
806
- format.keys.each_with_index do |k, kk|
807
- value=l[kk]
808
- value=split[value] if %i(depends conflicts opt_depends provides replaces).include?(k)
809
- value=nil if k==:pkgbase and value=="(null)"
810
- value=Time.at(value.to_i) if k==:build_date
811
- value=value.to_i if k==:download_size or k==:install_size
812
- info[k]=value if value
813
- end
814
- info[:repo]=files[i]
815
- r<<info
816
- end
817
- end
818
- r
819
- end
820
-
821
- def packages(refresh=false)
822
- @packages=nil if refresh
823
- @packages ||= PackageList.new(self.infos)
824
- end
825
-
826
- def self.from_dir(dir)
827
- dir=Pathname.new(dir)
828
- self.new(*dir.glob('*.pkg.*').map {|g| next if g.to_s.end_with?('~') or g.to_s.end_with?('.sig'); f=dir+g; next unless f.readable?; f}.compact)
829
- end
830
- end
831
-
832
- class Makepkg
833
- def self.create(v)
834
- v.is_a?(self) ? v : self.new(v)
835
- end
836
-
837
- attr_accessor :dir, :base, :env, :config, :asdeps
838
-
839
- def initialize(dir, config: Archlinux.config, env: {}, asdeps: false)
840
- @dir=DR::Pathname.new(dir)
841
- @base=@dir.basename
842
- @config=config
843
- @env=env
844
- @asdeps=asdeps
845
- db=@config.db
846
- @env['PKGDEST']=db.dir.to_s if db
847
- end
848
-
849
- def name
850
- @base.to_s
851
- end
852
-
853
- def call(*args, run: :run_simple, **opts)
854
- @config.launch(:makepkg, *args, **opts) do |*args, **opts|
855
- @dir.chdir do
856
- SH.public_send(run, @env, *args, **opts)
857
- end
858
- end
859
- end
860
-
861
- def info
862
- stdin=call("--printsrcinfo", chomp: :lines)
863
- mode=nil; r={}; current={}; pkgbase=nil; pkgname=nil
864
- stdin.each do |l|
865
- key, value=l.split(/\s*=\s*/,2)
866
- next if key.nil?
867
- if key=="pkgbase"
868
- mode=:pkgbase; current[:pkgbase]=value
869
- elsif key=="pkgname"
870
- if mode==:pkgbase
871
- r=current
872
- r[:pkgs]={repo: @dir}
873
- else
874
- r[:pkgs][pkgname]=current
875
- end
876
- current={}; mode=:pkgname; pkgname=value
877
- else
878
- key=key.strip.to_sym
879
- Archlinux.add_to_hash(current, key, value)
880
- end
881
- end
882
- r[:pkgs][pkgname]=current #don't forget to update the last one
883
- r
884
- end
885
-
886
- def packages(refresh=false)
887
- @packages=nil if refresh
888
- unless @packages
889
- r=info
890
- pkgs=r.delete(:pkgs)
891
- r[:pkgbase]
892
- base=Package.new(r)
893
- list=pkgs.map do |name, pkg|
894
- pkg[:name]=name
895
- Package.new(pkg).merge(base)
896
- end
897
- @packages=PackageList.new(list)
898
- end
899
- @packages
900
- end
901
-
902
- def url
903
- @config[:aur_url]+@base.to_s+".git"
904
- end
905
-
906
- def get(logdir: nil, view: false)
907
- #SH.sh("vcs clone_or_update --diff #{url.shellescape} #{@dir.shellescape}")
908
- if logdir
909
- logdir=DR::Pathname.new(logdir)
910
- logdir.mkpath
911
- end
912
- if @dir.exist?
913
- # TODO: what if @dir exist but is not a git directory?
914
- @dir.chdir do
915
- unless @config.git_update
916
- SH.logger.error("Error in updating #{@dir}")
917
- end
918
- if logdir
919
- SH::VirtualFile.new("orderfile", "PKGBUILD").create(true) do |tmp|
920
- patch=SH.run_simple("git diff -O#{tmp} HEAD@{1}")
921
- (logdir+"#{@dir.basename}.patch").write(patch) unless patch.empty?
922
- (logdir+"#{@dir.basename}").on_ln_s(@dir.realpath, rm: :symlink)
923
- end
924
- end
925
- end
926
- else
927
- unless @config.git_clone(url, @dir)
928
- SH.logger.error("Error in cloning #{url} to #{@dir}")
929
- end
930
- SH.sh("git clone #{url.shellescape} #{@dir.shellescape}")
931
- if logdir
932
- (logdir+"!#{@dir.basename}").on_ln_s(@dir.realpath)
933
- end
934
- end
935
- if view
936
- return @config.view(@dir)
937
- else
938
- return true
939
- end
940
- end
941
-
942
- # raw call to makepkg
943
- def makepkg(*args, **opts)
944
- call(*args, run: :sh, **opts)
945
- end
946
-
947
- def make(*args, sign: config.sign(:makepkg), default_opts: [], force: false, asdeps: @asdeps, **opts)
948
- default_opts << "--sign" if sign
949
- default_opts << "--key=#{sign}" if sign.is_a?(String)
950
- default_opts << "--force" if force
951
- default_opts << "--asdeps" if asdeps
952
-
953
- tools=@config.makepkg_config #this set up pacman and makepkg config files
954
- success=false
955
- @dir.chdir do
956
- success=tools.makepkg(*args, default_opts: default_opts, env: @env, **opts)
957
- end
958
- success
959
- end
960
-
961
- def mkarchroot
962
- @config.devtools.mkarchroot("base-devel")
963
- end
964
-
965
- def makechroot(*args, sign: @config.sign(:makechrootpkg), force: false, **opts)
966
- unless force
967
- if list.all? {|f| f.exist?}
968
- SH.logger.info "Skipping #{@dir} since it is already built (use force=trye to override)"
969
- return false
970
- end
971
- end
972
- devtools=@config.devtools
973
- success=false
974
- @dir.chdir do
975
- success=devtools.makechrootpkg(*args, env: @env, **opts)
976
- end
977
- self.sign(sign) if sign and success
978
- success
979
- end
980
-
981
- def add_to_db(db=@config.db)
982
- SH.logger.warn "Bad database #{db}" unless db.is_a?(DB)
983
- db.add(*list.select {|l| l.exist?})
984
- end
985
-
986
- def build(*makepkg_args, mkarchroot: false, chroot: @config[:build_chroot], **opts)
987
- SH.logger.info "=> Building #{@dir}"
988
- if chroot
989
- self.mkarchroot if mkarchroot
990
- success, _r=makechroot(*makepkg_args, **opts)
991
- else
992
- success, _r=make(*makepkg_args, **opts)
993
- end
994
- if success and (db=@config.db)
995
- add_to_db(db)
996
- if !chroot #sync db
997
- tools=@config.makepkg_config
998
- tools.sync_db(db.repo_name)
999
- end
1000
- end
1001
- end
1002
-
1003
- def install(*args, view: true, **opts)
1004
- r=get(view: view)
1005
- build(*args, **opts) if r
1006
- end
1007
-
1008
- def list(**opts)
1009
- call("--packagelist", chomp: :lines, err: "/dev/null", **opts).map {|f| Pathname.new(f)}
1010
- end
1011
-
1012
- def sign(sign, **opts)
1013
- list(**opts).each do |pkg|
1014
- if pkg.file?
1015
- if (sig=Pathname.new("#{pkg}.sig")).file?
1016
- SH.logger.warn "Signature #{sig} already exits, skipping"
1017
- else
1018
- SH.sh("gpg #{sign.is_a?(String) ? "-u #{sign}" : ""} --detach-sign --no-armor #{pkg.shellescape}")
1019
- end
1020
- end
1021
- end
1022
- end
1023
-
1024
- end
1025
-
1026
- class MakepkgList
1027
- def self.create(v)
1028
- v.is_a?(self) ? v : self.new(v)
1029
- end
1030
-
1031
- Archlinux.delegate_h(self, :@l)
1032
- attr_accessor :config, :cache, :l
1033
-
1034
- def initialize(l, config: Archlinux.config, cache: config.cachedir)
1035
- @config=config
1036
- @cache=Pathname.new(cache)
1037
- @l={}
1038
- l.each do |m|
1039
- unless m.is_a?(Makepkg)
1040
- m=Pathname.new(m)
1041
- m = @cache+m if m.relative?
1042
- v=Makepkg.new(m, config: @config)
1043
- @l[v.name]=v
1044
- end
1045
- end
1046
- end
1047
-
1048
- def packages(refresh=false)
1049
- @packages = nil if refresh
1050
- @packages ||= @l.values.reduce do |list, makepkg|
1051
- list.merge(makepkg.packages)
1052
- end
1053
- end
1054
-
1055
- def get(*args, view: true)
1056
- Dir.mktmpdir("aur_view") do |d|
1057
- @l.values.each do |l|
1058
- l.get(*args, logdir: d)
1059
- end
1060
- if view
1061
- return @config.view(d)
1062
- else
1063
- return true
1064
- end
1065
- end
1066
- end
1067
-
1068
- def make(*args)
1069
- @l.values.each do |l|
1070
- l.make(*args)
1071
- end
1072
- end
1073
-
1074
- def makechroot(*args)
1075
- @l.values.each do |l|
1076
- l.makechroot(*args)
1077
- end
1078
- end
1079
-
1080
- def mkarchroot
1081
- @config.devtools.mkarchroot("base-devel")
1082
- end
1083
-
1084
- def list
1085
- @l.values.flat_map { |l| l.list }
1086
- end
1087
-
1088
- def add_to_db(db=@config.db)
1089
- SH.logger.warn "Bad database #{db}" unless db.is_a?(DB)
1090
- db.add(*list.select {|l| l.exist?})
1091
- end
1092
-
1093
- def build(*args, chroot: @config[:build_chroot], **opts)
1094
- mkarchroot if chroot
1095
- @l.values.each do |l|
1096
- l.build(*args, chroot: chroot, **opts)
1097
- end
1098
- end
1099
-
1100
- def install(*args, view: true, **opts)
1101
- r=get(view: view)
1102
- build(*args, **opts) if r
1103
- end
1104
- end
1105
-
1106
- PackageError=Class.new(ArchlinuxError)
1107
- class Package
1108
- def self.create(v)
1109
- v.is_a?(self) ? v : self.new(v)
1110
- end
1111
-
1112
- Archlinux.delegate_h(self, :@props)
1113
- attr_reader :name, :props
1114
-
1115
- def initialize(*args)
1116
- case args.length
1117
- when 2
1118
- name, props=args
1119
- when 1
1120
- props=args.first
1121
- name=nil
1122
- else
1123
- raise PackageError.new("Error the number of arguments should be 1 or 2")
1124
- end
1125
- @name=name
1126
- @props={}
1127
- self.props=(props)
1128
- @name=@props[:name] || @props[:pkgname] || @props[:pkgbase] unless @name
1129
- end
1130
-
1131
- def props=(props)
1132
- [:groups, :depends, :make_depends, :check_depends, :conflicts, :replaces, :provides, :depends_for, :opt_depends_for].each do |k|
1133
- props.key?(k) or @props[k]=[]
1134
- end
1135
- @props[:opt_depends]||={}
1136
- props.each do |k,v|
1137
- k=Utils.to_snake_case(k.to_s).to_sym
1138
- k=:opt_depends if k==:optdepends || k==:optional_deps
1139
- k=:make_depends if k==:makedepends
1140
- k=:check_depends if k==:checkdepends
1141
- k=:build_date if k==:checkdepends
1142
- k=:depends if k==:depends_on or k==:requires
1143
- k=:conflicts if k==:conflicts_with
1144
- k=:pkgbase if k==:base
1145
- k=:depends_for if k==:required_by
1146
- k=:opt_depends_for if k==:optional_for
1147
- k=:description if k==:desc
1148
- case k
1149
- when :first_submitted, :last_modified, :out_of_date, :build_date, :install_date
1150
- if v and !v.is_a?(Time)
1151
- v= v.is_a?(Integer) ? Time.at(v) : Time.parse(v)
1152
- end
1153
- when :repository, :groups, :depends, :make_depends, :check_depends, :conflicts, :replaces, :provides, :depends_for, :opt_depends_for
1154
- v=Array(v)
1155
- when :opt_depends
1156
- unless v.is_a?(Hash)
1157
- w={}
1158
- Array(v).each do |l|
1159
- l.match(/(\w*)\s+:\s+(.*)/) do |m|
1160
- w[m[1]]=m[2]
1161
- end
1162
- end
1163
- v=w
1164
- end
1165
- end
1166
- @props[k]=v
1167
- end
1168
- end
1169
-
1170
- def merge(h)
1171
- h.each do |k,v|
1172
- if @props[k].nil? or ((k==:package_size or k==:download_size or k==:installed_size) and (@props[k]=="0.00 B" or @props[k]==0))
1173
- @props[k]=v
1174
- elsif k==:repository
1175
- @props[k]=(Array(@props[k])+v).uniq
1176
- end
1177
- end
1178
- self
1179
- end
1180
-
1181
- def dependencies(l=%i(depends))
1182
- l.map {|i| a=@props[i]; a.is_a?(Hash) ? a.keys : Array(a)}.flatten.uniq
1183
- end
1184
-
1185
- def version
1186
- Version.new(@props[:version])
1187
- end
1188
-
1189
- def name_version
1190
- r=self.name
1191
- version=self.version
1192
- r+="="+version.to_s if version
1193
- r
1194
- end
1195
-
1196
- def file
1197
- Pathname.new(@props[:filename])
1198
- end
1199
-
1200
- def same?(other)
1201
- # @props.slice(*(@props.keys - [:repo])) == other.props.slice(*(other.props.keys - [:repo]))
1202
- slice=%i(version description depends provides opt_depends replaces conflicts)
1203
- # p name, other.name, @props.slice(*slice), other.props.slice(*slice)
1204
- name == other.name && @props.slice(*slice) == other.props.slice(*slice)
1205
- end
1206
- end
1207
-
1208
- class PackageList
1209
- def self.create(v)
1210
- v.is_a?(self) ? v : self.new(v)
1211
- end
1212
-
1213
- Archlinux.delegate_h(self, :@l)
1214
- attr_accessor :children_mode, :ext_query
1215
- attr_reader :l, :versions, :provides_for
1216
-
1217
- def initialize(list)
1218
- @l={}
1219
- @versions={} #hash of versions for quick look ups
1220
- @provides_for={} #hash of provides for quick look ups
1221
- @ext_query=nil #how to get missing packages, default
1222
- @children_mode=%i(depends) #children, default
1223
- @ignore=[] #ignore packages update
1224
- @query_ignore=[] #query ignore packages (ie these won't be returned by a query)
1225
- merge(list)
1226
- end
1227
-
1228
- def packages
1229
- @versions.keys
1230
- end
1231
-
1232
- def name_of(pkg)
1233
- pkg.name_version
1234
- end
1235
-
1236
- def same?(other)
1237
- unless @l.keys == other.keys
1238
- SH.logger.warn("Inconsistency in the package names")
1239
- return false
1240
- end
1241
- r=true
1242
- @l.each do |name, pkg|
1243
- unless pkg.same?(other[name])
1244
- SH.logger.warn("Inconsistensy for the package #{name}")
1245
- r=false
1246
- end
1247
- end
1248
- return r
1249
- end
1250
-
1251
- def merge(l)
1252
- # l=l.values if l.is_a?(PackageList) # this is handled below
1253
- l= case l
1254
- when Hash
1255
- l.values.compact
1256
- when PackageList
1257
- l.values
1258
- else
1259
- l.to_a
1260
- end
1261
- l.each do |pkg|
1262
- pkg=Package.new(pkg) unless pkg.is_a?(Package)
1263
- name=name_of(pkg)
1264
- if @l.key?(name)
1265
- @l[name].merge(pkg)
1266
- else
1267
- @l[name]=pkg
1268
- end
1269
-
1270
- @versions[pkg.name]||={}
1271
- @versions[pkg.name][pkg.version.to_s]=name
1272
-
1273
- pkg[:provides].each do |p|
1274
- pkg=Query.strip(p)
1275
- @provides_for[pkg]||={}
1276
- @provides_for[pkg][p]=name #todo: do we pass the name or the full pkg?
1277
- end
1278
- end
1279
- self
1280
- end
1281
-
1282
- def latest
1283
- r={}
1284
- @versions.each do |pkg, versions|
1285
- v=versions.keys.max do |v1,v2|
1286
- Version.create(v1) <=> Version.create(v2)
1287
- end
1288
- r[pkg]=@l[versions[v]]
1289
- end
1290
- r
1291
- end
1292
-
1293
- def find(q, **opts)
1294
- return q if @l.key?(q)
1295
- q=Query.create(q); pkg=q.name
1296
- query(q, **opts) do |found, type|
1297
- if type==:version
1298
- unless found.empty? #we select the most up to date
1299
- max = found.max { |v,w| Version.create(v) <=> Version.create(w) }
1300
- return @versions[pkg][max]
1301
- end
1302
- elsif type==:provides
1303
- max = found.max { |v,w| Query.create(v) <=> Query.create(w) }
1304
- return @provides_for[pkg][max]
1305
- end
1306
- end
1307
- return nil
1308
- end
1309
-
1310
- def query(q, provides: false, &b) #provides: do we check Provides?
1311
- q=Query.new(q) unless q.is_a?(Query)
1312
- matches=[]; pkg=q.name
1313
- if @versions.key?(pkg)
1314
- matches+=(found=@versions[pkg].keys.select {|v| q.satisfy?(Version.create(v))}).map {|k| @versions[pkg][k]}
1315
- yield(found, :version) if block_given?
1316
- end
1317
- if provides and @provides_for.key?(pkg)
1318
- matches+=(found=@provides_for[pkg].keys.select {|v| q.satisfy?(Query.create(v))}).map {|k| @provides_for[pkg][k]}
1319
- yield(found, :provides) if block_given?
1320
- end
1321
- matches
1322
- end
1323
-
1324
- def to_a
1325
- @l.values
1326
- end
1327
-
1328
- # here the arguments are Strings
1329
- # return the arguments replaced by eventual provides + missing packages
1330
- # are added to @l
1331
- def resolve(*queries, provides: true, ext_query: @ext_query, fallback: true, **opts)
1332
- got={}; missed=[]
1333
- pkgs=queries.map {|p| Query.strip(p)}
1334
- ignored = pkgs & @query_ignore
1335
- queries.each do |query|
1336
- if ignored.include?(Query.strip(query))
1337
- got[query]=nil #=> means the query was ignored
1338
- else
1339
- pkg=self.find(query, provides: provides, **opts)
1340
- if pkg
1341
- got[query]=pkg
1342
- else
1343
- missed << query
1344
- end
1345
- end
1346
- end
1347
- # we do it this way to call ext_query in batch
1348
- if ext_query and !missed.empty?
1349
- found, new_pkgs=ext_query.call(*missed, provides: provides)
1350
- self.merge(new_pkgs)
1351
- got.merge!(found)
1352
- missed-=found.keys
1353
- end
1354
- if fallback and !missed.empty?
1355
- new_queries={}
1356
- missed.each do |query|
1357
- if (query_pkg=Query.strip(query)) != query
1358
- new_queries[query]=query_pkg
1359
- # missed.delete(query)
1360
- end
1361
- end
1362
- unless new_queries.empty?
1363
- SH.logger.warn "Trying fallback for packages: #{new_queries.keys.join(', ')}"
1364
- fallback_got=self.resolve(*new_queries.values, provides: provides, ext_query: ext_query, fallback: false, **opts)
1365
- got.merge!(fallback_got)
1366
- SH.logger.warn "Missing packages: #{missed.map {|m| r=m; r<<" [fallback: #{fallback}]" if (fallback=fallback_got[new_queries[m]]); r}.join(', ')}"
1367
- end
1368
- else
1369
- SH.logger.warn "Missing packages: #{missed.join(', ')}" if !missed.empty? and ext_query != false #ext_query=false is a hack to silence this message
1370
- end
1371
- got
1372
- end
1373
-
1374
- def get(*args)
1375
- #compact because the resolution can be nil for an ignored package
1376
- resolve(*args).values.compact
1377
- end
1378
-
1379
- def children(node, mode=@children_mode, verbose: false, **opts, &b)
1380
- deps=@l.fetch(node).dependencies(mode)
1381
- SH.logger.info "- #{node} => #{deps}" if verbose
1382
- deps=get(*deps, **opts)
1383
- SH.logger.info " => #{deps}" if verbose
1384
- if b
1385
- deps.each(&b)
1386
- else
1387
- deps
1388
- end
1389
- end
1390
-
1391
- private def call_tsort(l, method: :tsort, **opts, &b)
1392
- each_node=l.method(:each)
1393
- s=self
1394
- each_child = lambda do |node, &b|
1395
- s.children(node, **opts, &b)
1396
- end
1397
- TSort.public_send(method, each_node, each_child, &b)
1398
- end
1399
-
1400
- def tsort(l, **opts, &b)
1401
- if b
1402
- call_tsort(l, method: :each_strongly_connected_component, **opts, &b)
1403
- else
1404
- r=call_tsort(l, method: :strongly_connected_components, **opts)
1405
- cycles=r.select {|c| c.length > 1}
1406
- SH.logger.warn "Cycles detected: #{cycles}" unless cycles.empty?
1407
- r.flatten
1408
- end
1409
- end
1410
-
1411
- # recursive get
1412
- def rget(*pkgs)
1413
- l=get(*pkgs)
1414
- tsort(l)
1415
- end
1416
-
1417
- # check updates compared to another list
1418
- def check_updates(l)
1419
- l=self.class.create(l)
1420
- a=self.latest; b=l.latest
1421
- r={}
1422
- b.each do |k, v|
1423
- if a.key?(k)
1424
- v1=a[k].version
1425
- v2=v.version
1426
- h={in: v1.to_s, out: v2.to_s, in_pkg: name_of(a[k]), out_pkg: name_of(v)}
1427
- case v1 <=> v2
1428
- when -1
1429
- h[:op]=:upgrade
1430
- when 0
1431
- h[:op]=:equal
1432
- when 1
1433
- h[:op]=:downgrade
1434
- end
1435
- r[k]=h
1436
- else
1437
- #new installation
1438
- r[k]={op: :install,
1439
- in: nil,
1440
- out: v.version.to_s, out_pkg: name_of(v)}
1441
- end
1442
- end
1443
- (a.keys-b.keys).each do |k|
1444
- r[k]={op: :obsolete,
1445
- in: a[k].version.to_s,
1446
- out: nil, in_pkg: name_of(a[k])}
1447
- end
1448
- r
1449
- end
1450
-
1451
- def select_updates(r)
1452
- r.select {|_k,v| v[:op]==:upgrade or v[:op]==:install}.map {|_k, v| v[:out_pkg]}
1453
- end
1454
-
1455
- def get_updates(l, verbose: true, obsolete: true)
1456
- c=check_updates(l)
1457
- show_updates(c, obsolete: obsolete) if verbose
1458
- select_updates(c)
1459
- end
1460
-
1461
- #take the result of check_updates and pretty print them
1462
- def show_updates(r, obsolete: true)
1463
- require 'simplecolor'
1464
- r.each do |k,v|
1465
- next if v[:op]==:equal
1466
- next if obsolete and v[:op]==:obsolete
1467
- vin= v[:in] ? v[:in] : "(none)"
1468
- vout= v[:out] ? v[:out] : "(none)"
1469
- op = "->"; op="<-" if v[:op]==:downgrade
1470
- extra=""
1471
- extra=" [#{v[:op]}]" if v[:op]!=:upgrade
1472
- SH.logger.info SimpleColor.color(" -> #{k}: #{vin} #{op} #{vout}#{extra}", :black)
1473
- end
1474
- end
1475
-
1476
- def check_update(ext_query=@ext_query)
1477
- if ext_query
1478
- _found, new_pkgs=ext_query.call(*packages)
1479
- check_updates(new_pkgs)
1480
- end
1481
- end
1482
-
1483
- def update(**opts)
1484
- install(update: true, **opts)
1485
- end
1486
-
1487
- # take a list of packages to install
1488
- def install(*packages, update: false, ext_query: @ext_query, verbose: true, obsolete: true)
1489
- packages+=self.packages if update
1490
- if ext_query
1491
- _found, new_pkgs=ext_query.call(*packages)
1492
- SH.logger.info "# Checking packages" if verbose
1493
- u=get_updates(new_pkgs, verbose: verbose, obsolete: obsolete)
1494
- new=self.class.new(l.values).merge(new_pkgs)
1495
- # The updates or new packages may need new deps
1496
- SH.logger.info "# Checking dependencies" if verbose
1497
- full=new.rget(*u)
1498
- full_updates=get_updates(new.values_at(*full), verbose: verbose, obsolete: obsolete)
1499
- yield u, full_updates if block_given?
1500
- full_updates
1501
- else
1502
- SH.logger.warn "External query not defined"
1503
- end
1504
- end
1505
- end
1506
-
1507
- class AurCache < PackageList
1508
- def self.create(v)
1509
- v.is_a?(self) ? v : self.new(v)
1510
- end
1511
-
1512
- def initialize(l)
1513
- super
1514
- @ext_query=method(:ext_query)
1515
- @query_ignore=AurPackageList.official
1516
- end
1517
-
1518
- def ext_query(*queries, provides: false)
1519
- pkgs=queries.map {|p| Query.strip(p)}
1520
- # in a query like foo>1000, even if foo exist and was queried,
1521
- # the query fails so it gets called in ext_query
1522
- # remove these packages
1523
- # TODO: do the same for a provides query
1524
- pkgs-=self.packages
1525
- if pkgs.empty?
1526
- l=self.class.new([])
1527
- else
1528
- SH.logger.warn "! Calling aur for infos on: #{pkgs.join(', ')}"
1529
- l=AurQuery.packages(*pkgs)
1530
- @query_ignore += pkgs - l.packages #these don't exist in aur
1531
- end
1532
- r=l.resolve(*queries, ext_query: false, fallback: false)
1533
- return r, l
1534
- end
1535
- end
1536
-
1537
- class AurPackageList < PackageList
1538
- def self.create(v)
1539
- v.is_a?(self) ? v : self.new(v)
1540
- end
1541
-
1542
- def self.official
1543
- @official||=%w(core extra community).map {|repo| Repo.new(repo).list(mode: :pacman)}.flatten.compact
1544
- end
1545
-
1546
- def self.cache
1547
- @cache ||= AurCache.new([])
1548
- end
1549
-
1550
- def initialize(l)
1551
- super
1552
- @missed=[]
1553
- @ext_query=method(:ext_query)
1554
- @children_mode=%i(depends make_depends check_depends)
1555
- end
1556
-
1557
- def official
1558
- self.class.official
1559
- end
1560
-
1561
- def ext_query(*queries, provides: false)
1562
- cache=self.class.cache
1563
- got=cache.resolve(*queries, fallback: false, provides: provides)
1564
- return got, self.class.new(cache.l.slice(*got.values.compact))
1565
- end
1566
-
1567
- def do_update(**opts, &b)
1568
- do_install(update: true, **opts)
1569
- end
1570
-
1571
- def do_install(*args, **opts)
1572
- install_opts={}
1573
- %i(update ext_query verbose obsolete).each do |key|
1574
- opts.key?(key) && install_opts[key]=opts.delete(key)
1575
- end
1576
- deps=[]
1577
- l=install(*args, **install_opts) do |orig, with_deps|
1578
- deps=with_deps-orig
1579
- end
1580
- unless l.empty?
1581
- m=MakepkgList.new(l.map {|p| Query.strip(p)})
1582
- deps.each { |dep| m[Query.strip(dep)]&.asdeps=true }
1583
- if block_given?
1584
- yield m
1585
- else
1586
- m.install(**opts)
1587
- end
1588
- m
1589
- end
1590
- end
1591
- end
1592
-
1593
- class PacmanConf
1594
- def self.create(v)
1595
- v.is_a?(self) ? v : self.new(v, {}) #pass empty keywords so that a Hash is seen as an argument and not a list of keywords
1596
- end
1597
-
1598
- Archlinux.delegate_h(self, :@pacman_conf)
1599
- attr_accessor :pacman_conf, :config
1600
-
1601
- def initialize(conf="/etc/pacman.conf", config: Archlinux.config, **keys)
1602
- @config=config
1603
- if conf.is_a?(String) or conf.is_a?(Pathname)
1604
- conf=parse(conf, **keys)
1605
- end
1606
- @pacman_conf=conf
1607
- end
1608
-
1609
- def self.parse(content)
1610
- list=%i(HoldPkg IgnorePkg IgnoreGroup NoUpgrade NoExtract SigLevel LocalFileSigLevel RemoteFileSigLevel Usage Server)
1611
- mode=:options
1612
- config={options: {}, repos: {}}
1613
- content=content.each_line if content.is_a?(String)
1614
- content.each do |l|
1615
- if (m=l.match(/^\[([\w-]+)\]$/))
1616
- mode=m[1]
1617
- if mode == "options"
1618
- mode=:options
1619
- else
1620
- config[:repos][mode]||={}
1621
- end
1622
- else
1623
- key, value=l.split(' = ', 2)
1624
- key=key.to_sym
1625
- h = mode==:options ? config[:options] : config[:repos][mode]
1626
- if list.include?(key)
1627
- h[key]||=[]
1628
- h[key]<<value
1629
- else
1630
- h[key]=value
1631
- end
1632
- end
1633
- end
1634
- config
1635
- end
1636
-
1637
- def parse(file, raw: false, args: nil)
1638
- unless args
1639
- if raw
1640
- args=[:'pacconf', "--raw", "--config=#{file}"]
1641
- else
1642
- args=[:'pacman-conf', "--config=#{file}"]
1643
- end
1644
- end
1645
- output=@config.launch(*args) do |*args|
1646
- SH.run_simple(*args, chomp: :lines)
1647
- end
1648
- self.class.parse(output)
1649
- end
1650
-
1651
- def non_official_repos
1652
- repos=@pacman_conf[:repos]
1653
- repos.slice(*(repos.keys - %i(core extra community multilib testing community-testing multilib-testing)))
1654
- end
1655
-
1656
- def to_s
1657
- r=[]
1658
- print_values=lambda do |h, section|
1659
- r<<"[#{section}]" if section
1660
- h.each do |k,v|
1661
- case v
1662
- when nil
1663
- r << k
1664
- when Array
1665
- v.each { |vv| r << "#{k} = #{vv}" }
1666
- else
1667
- r << "#{k} = #{v}"
1668
- end
1669
- end
1670
- end
1671
- print_values.call(@pacman_conf[:options], "options")
1672
- @pacman_conf[:repos].each do |section, props|
1673
- print_values.call(props, section)
1674
- end
1675
- r.join("\n")+"\n"
1676
- end
1677
-
1678
- def tempfile
1679
- SH::VirtualFile.new("pacman.conf", to_s)
1680
- end
1681
- end
1682
-
1683
- class Devtools
1684
- def self.create(v)
1685
- v.is_a?(self) ? v : self.new(v)
1686
- end
1687
- Archlinux.delegate_h(self, :@opts)
1688
-
1689
- attr_accessor :config, :opts
1690
- def initialize(config: Archlinux.config, **opts)
1691
- @config=config
1692
- @opts=@config.opts.merge(opts)
1693
- # %i(pacman_conf makepkg_conf).each do |key|
1694
- # @opts[key]=@opts[key].tempfile.file if @opts[key].respond_to?(:tempfile)
1695
- # end
1696
- @opts[:chroot]=Pathname.new(@opts[:chroot]) if @opts[:chroot]
1697
- add_binds
1698
- end
1699
-
1700
- def add_binds
1701
- require 'uri'
1702
- if (conf=@opts[:pacman_conf]).is_a?(PacmanConf)
1703
- conf[:repos].each do |_name, opts|
1704
- opts[:Server].each do |server|
1705
- server.match(%r!file://(.*)!) do |m|
1706
- @opts[:bind_ro]||=[]
1707
- @opts[:bind_ro] << URI.unescape(m[1])
1708
- end
1709
- end
1710
- end
1711
- end
1712
- end
1713
-
1714
- def files
1715
- %i(pacman_conf makepkg_conf).each do |key|
1716
- if @opts[key]
1717
- file=@opts[key]
1718
- file=file.tempfile.file if file.respond_to?(:tempfile)
1719
- yield key, file
1720
- end
1721
- end
1722
- end
1723
-
1724
- def pacman_config
1725
- Pacman.create(@opts[:pacman_conf])
1726
- end
1727
-
1728
- def pacman(*args, default_opts: [], **opts)
1729
- files do |key, file|
1730
- default_opts += ["--config", file] if key==:pacman_conf
1731
- end
1732
- @config.launch(:pacman, *args, default_opts: default_opts, **opts) do |*args|
1733
- SH.sh(*args)
1734
- end
1735
- end
1736
-
1737
- def makepkg(*args, default_opts: [], **opts)
1738
- files do |key, file|
1739
- # trick to pass options to pacman
1740
- args << "PACMAN_OPTS+=--config=#{file.shellescape}"
1741
- default_opts += ["--config", file] if key==:makepkg_conf
1742
- end
1743
- @config.launch(:makepkg, *args, default_opts: default_opts, **opts) do |*args|
1744
- SH.sh(*args)
1745
- end
1746
- end
1747
-
1748
-
1749
- def nspawn(*args, root: @opts[:chroot]+'root', default_opts: [], **opts)
1750
- files do |key, file|
1751
- default_opts += ["-C", file] if key==:pacman_conf
1752
- default_opts += ["-M", file] if key==:makepkg_conf
1753
- end
1754
- if (binds_ro=@opts[:bind_ro])
1755
- binds_ro.each do |b|
1756
- args.unshift("--bind-ro=#{b}")
1757
- end
1758
- end
1759
- if (binds_rw=@opts[:bind_rw])
1760
- binds_rw.each do |b|
1761
- args.unshift("--bind=#{b}")
1762
- end
1763
- end
1764
- args.unshift root
1765
-
1766
- @config.launch(:'arch-nspawn', *args, default_opts: default_opts, **opts) do |*args|
1767
- SH.sh(*args)
1768
- end
1769
- end
1770
-
1771
- # this takes the same options as nspawn
1772
- def mkarchroot(*args, nspawn: @config[:chroot_update], default_opts: [], **opts)
1773
- files do |key, file|
1774
- default_opts += ["-C", file] if key==:pacman_conf
1775
- default_opts += ["-M", file] if key==:makepkg_conf
1776
- end
1777
- chroot=@opts[:chroot]
1778
- chroot.sudo_mkpath unless chroot.directory?
1779
- args.unshift(chroot+'root')
1780
- if (chroot+'root'+'.arch-chroot').file?
1781
- # Note that if nspawn is not called (and the chroot does not
1782
- # exist), then the passed pacman.conf will not be replace the one
1783
- # in the chroot. And when makechrootpkg calls nspawn, it does not
1784
- # transmit the -C/-M options. So even if we don't want to update,
1785
- # we should call a dummy bin like 'true'
1786
- if nspawn
1787
- nspawn=nspawn.shellsplit if nspawn.is_a?(String)
1788
- self.nspawn(*nspawn, **opts)
1789
- end
1790
- else
1791
- @config.launch(:mkarchroot, *args, default_opts: default_opts, escape: true, **opts) do |*args|
1792
- SH.sh(*args)
1793
- end
1794
- end
1795
- end
1796
-
1797
- def makechrootpkg(*args, default_opts: [], **opts)
1798
- default_opts+=['-r', @opts[:chroot]]
1799
- if (binds_ro=@opts[:bind_ro])
1800
- binds_ro.each do |b|
1801
- default_opts += ["-D", b]
1802
- end
1803
- end
1804
- if (binds_rw=@opts[:bind_rw])
1805
- binds_rw.each do |b|
1806
- default_opts += ["-d", b]
1807
- end
1808
- end
1809
-
1810
- #makechrootpkg calls itself with sudo --preserve-env=SOURCE_DATE_EPOCH,GNUPGHOME so it does not keep PKGDEST..., work around this by providing our own sudo
1811
- @config.launch(:makechrootpkg, *args, default_opts: default_opts, sudo: 'sudo --preserve-env=GNUPGHOME,PKGDEST,SOURCE_DATE_EPOCH', **opts) do |*args|
1812
- SH.sh(*args)
1813
- end
1814
- end
1815
-
1816
- def tmp_pacman(conf, **opts)
1817
- PacmanConf.create(conf).tempfile.create(true) do |file|
1818
- pacman=lambda do |*args, **pac_opts|
1819
- @config.launch(:pacman, *args, default_opts: ["--config", file], **opts.merge(pac_opts)) do |*args|
1820
- SH.sh(*args)
1821
- end
1822
- end
1823
- yield pacman, file
1824
- end
1825
- end
1826
-
1827
- def sync_db(*names)
1828
- conf=PacmanConf.create(@opts[:pacman_conf])
1829
- new_conf={options: conf[:options], repos: {}}
1830
- repos=conf[:repos]
1831
- names.each do |name|
1832
- if repos[name]
1833
- new_conf[:repos][name]=repos[name]
1834
- else
1835
- SH.logger.warn "sync_db: unknown repo #{name}"
1836
- end
1837
- end
1838
- tmp_pacman(new_conf) do |pacman|
1839
- if block_given?
1840
- yield(pacman)
1841
- else
1842
- pacman['-Syu', sudo: true]
1843
- end
1844
- end
1845
- end
12
+ =begin
13
+ # query aur
14
+ Archlinux::AurQuery.packages("pacaur")
1846
15
 
1847
- end
1848
- end
16
+ # Make a package
17
+ m=Archlinux::Makepkg.new("pacaur")
18
+ m.edit
19
+ m.makepkg("--geninteg") #update PKGBUILD (todo: add a function for that?)
20
+ m.install
1849
21
 
1850
- =begin
22
+ # install a package
1851
23
  aur=Archlinux::AurPackageList.new([])
1852
24
  l=aur.install("pacaur")
1853
25
 
1854
- aur=Archlinux::AurPackageList.new(Archlinux.config.db.packages)
1855
- aur.do_update
26
+ # check update and new installation compared to a db
27
+ aur=Archlinux.config.db.packages
28
+ aur=Archlinux.config.default_packages #or get default packages
29
+ aur.update?
30
+ aur.install?("pacaur", update: true)
31
+
32
+ # Clean/Change cache:
33
+ Archlinux.config.instance_variable_set(:@install_list, nil)
34
+ aur.install_list=Archlinux.config.install_list
35
+
36
+
37
+ # Update a db with the latest packages available on the local filesystem
38
+ db=Archlinux.config.db
39
+ db.check_udpate / db.update
40
+ # see package names
41
+ db.packages.l.keys
42
+
43
+ # Check for useless packages in the db
44
+ pkgs = Archlinux.config.db.packages
45
+ needed = pkgs.rget(*wanted_pkgs)
46
+ # present = pkgs.latest.keys ## we want the version
47
+ present = pkgs.l.keys
48
+ notneeded=present - needed
49
+ files=pkgs.slice(*notneeded).map {|k,v| v.file}
50
+ db.remove(*files)
1856
51
  =end
1857
-
1858
- # TODO:
1859
- # --devel
1860
- # aur provides
1861
- # @ignore