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.
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