aur.rb 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,330 @@
1
+ require 'aur/helpers'
2
+ require 'aur/packages'
3
+ require 'aur/install_packages'
4
+ require 'aur/makepkg'
5
+
6
+ module Archlinux
7
+
8
+ class Config
9
+ def self.create(v)
10
+ v.is_a?(self) ? v : self.new(v)
11
+ end
12
+
13
+ attr_accessor :opts
14
+ Archlinux.delegate_h(self, :@opts)
15
+ include SH::ShConfig
16
+ include DR::PPHelper
17
+
18
+ # pass nil to prevent loading a config file
19
+ def initialize(file="aur.rb", **opts)
20
+ @file=file
21
+ if file
22
+ file=Pathname.new(file)
23
+ file=Pathname.new(ENV["XDG_CONFIG_HOME"] || "#{ENV['HOME']}/.config") + file if file.relative?
24
+ end
25
+ file_config= file&.readable? ? file.read : (SH.logger.error "Error: Config file '#{file}' unreadable" unless file.nil?; '{}')
26
+ wrap=eval("Proc.new { |config| #{file_config} }")
27
+ @opts=default_config.deep_merge(opts)
28
+ user_conf=wrap.call(self)
29
+ @opts.deep_merge!(user_conf) if user_conf.is_a?(Hash)
30
+ end
31
+
32
+ def to_pp
33
+ @file.to_s
34
+ end
35
+
36
+ def sh_config
37
+ @opts[:sh_config]
38
+ end
39
+
40
+ def default_config
41
+ {
42
+ cache: "arch_aur", #where we dl PKGBUILDs; if relative will be in XDG_CACHE_HOME
43
+ db: 'aur', #if relative the db will be in cache dir
44
+ aur_url: "https://aur.archlinux.org/", #base aur url
45
+ chroot: {
46
+ root: "/var/lib/aurbuild/x86_64", #chroot root
47
+ active: false, #do we use the chroot?
48
+ update: 'pacman -Syu --noconfirm', #how to update an existing chroot
49
+ packages: ['base-devel'], #the packages that are installed in the chroto
50
+ # It can make sense to add %w(python ruby nodejs go-pie rust git)...
51
+ },
52
+ default_packages_class: PackageList,
53
+ # default_install_list_class: AurMakepkgCache,
54
+ default_install_packages_class: AurPackageList,
55
+ default_install_list_class: AurCache,
56
+ default_get_class: Git, #we use git to fetch PKGBUILD from aur
57
+ sign: true, #can be made more atomic, cf the sign method
58
+ config_files: {
59
+ default: {
60
+ pacman: "/etc/pacman.conf", #default pacman-conf
61
+ makepkg: "/etc/makepkg.conf",
62
+ },
63
+ chroot: {
64
+ pacman: "/usr/share/devtools/pacman-extra.conf", #pacman.conf for chroot build
65
+ makepkg: "/usr/share/devtools/makepkg-x86_64.conf",
66
+ },
67
+ local: {
68
+ pacman: "/etc/pacman.conf", # pacman-conf for local makepkg build
69
+ },
70
+ },
71
+ sh_config: { #default programs options, called each time
72
+ makepkg: {default_opts: []},
73
+ makechrootpkg: {default_opts: ["-cu"]},
74
+ # So on fist thought, we do not need -u since we update 'root' before ourselves; but if we build several packages we may need the previous ones in the db, and since we only update 'root' once, they won't be available on 'copy'; so we still need '-u'
75
+ },
76
+ makepkg: {
77
+ build_args: ["-crs", "--needed"], #only used when building
78
+ },
79
+ view: "vifm -c view! -c tree -c 0", #can also be a Proc
80
+ sudo_loop: {
81
+ command: "sudo -v",
82
+ interval: 30,
83
+ active: true,
84
+ }
85
+ }
86
+ end
87
+
88
+ # packages to check
89
+ def default_packages(use_db: db != false, use_foreign: true)
90
+ if @default_packages.nil?
91
+ # by default this is the db packages + foreign packages
92
+ default=use_db ? db.packages : to_packages([])
93
+ default.merge(RepoPkgs.new(Repo.foreign_list, config: self).packages) if use_foreign
94
+ default=yield default if block_given?
95
+ @default_packages=to_packages(default.l, install: true)
96
+ else
97
+ @default_packages
98
+ end
99
+ end
100
+
101
+ def get_config_file(name, type: :default)
102
+ dig(:config_files, type, name) || dig(:config_files, :default, name)
103
+ end
104
+
105
+ def view(dir)
106
+ view=@opts[:view]
107
+ SH.sh_or_proc(view, dir)
108
+ end
109
+
110
+ def cachedir
111
+ global_cache=Pathname.new(ENV["XDG_CACHE_HOME"] || "#{ENV['HOME']}/.cache")
112
+ cache=global_cache+@opts[:cache]
113
+ cache.mkpath
114
+ cache
115
+ end
116
+
117
+ # add our default db to the list of repos
118
+ private def setup_pacman_conf(conf)
119
+ pacman=PacmanConf.create(conf, config: self)
120
+ aur=self.db(false)
121
+ if aur and !pacman[:repos].include?(aur.repo_name)
122
+ require 'uri'
123
+ pacman[:repos][aur.repo_name]={Server: ["file://#{DR::URIEscape.escape(aur.dir.to_s)}"]}
124
+ end
125
+ pacman
126
+ end
127
+
128
+ def default_pacman_conf
129
+ @default_pacman_conf ||= if (conf=get_config_file(:pacman))
130
+ PacmanConf.new(conf, config: self)
131
+ else
132
+ PacmanConf.new(config: self)
133
+ end
134
+ end
135
+
136
+ def chroot_devtools
137
+ unless @chroot_devtools
138
+ devtools_pacman=PacmanConf.new(get_config_file(:pacman, type: :chroot))
139
+ makepkg_conf=get_config_file(:makepkg, type: :chroot)
140
+ my_pacman=default_pacman_conf
141
+ devtools_pacman[:repos].merge!(my_pacman.non_official_repos)
142
+ devtools_pacman=setup_pacman_conf(devtools_pacman)
143
+ @chroot_devtools = Devtools.new(pacman_conf: devtools_pacman, makepkg_conf: makepkg_conf, config: self)
144
+ end
145
+ @chroot_devtools
146
+ end
147
+
148
+ def local_devtools
149
+ unless @local_devtools
150
+ makepkg_pacman=get_config_file(:pacman, type: :local)
151
+ makepkg_conf=get_config_file(:makepkg, type: :local)
152
+ makepkg_pacman=setup_pacman_conf(makepkg_pacman)
153
+ @local_devtools = Devtools.new(pacman_conf: makepkg_pacman, makepkg_conf: makepkg_conf, config: self)
154
+ end
155
+ @local_devtools
156
+ end
157
+
158
+ def db=(name)
159
+ case name
160
+ when DB
161
+ @db=name
162
+ when Pathname
163
+ @db=DB.new(name, config: self)
164
+ when String
165
+ if DB.db_file?(name)
166
+ @db=DB.new(name, config: self)
167
+ else
168
+ @db=DB.new(cachedir+".db"+"#{name}.db.tar.gz", config: self)
169
+ end
170
+ when true
171
+ @db=DB.new(cachedir+".db"+"aur.db.tar.gz", config: self)
172
+ when false, nil
173
+ @db=name #false for false, nil to reset
174
+ else
175
+ SH.logger.warn("Database name #{name} not suitable, fallback to default")
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
+
196
+ # note: since 'true' is frozen, we cannot extend it and keep a
197
+ # @sudo_loop_thread. Moreover we only want one sudo loop active, so we
198
+ # will call it ourselves
199
+ def sudo(arg=true)
200
+ if dig(:sudo_loop, :active)
201
+ opts=dig(:sudo_loop).clone
202
+ opts.delete(:active)
203
+ self.extend(SH::SudoLoop.configure(**opts))
204
+ self.sudo_loop
205
+ end
206
+ arg
207
+ end
208
+
209
+ def stop_sudo_loop
210
+ stop_sudo_loop if respond_to?(:stop_sudo_loop)
211
+ end
212
+
213
+ def to_packages(l=[], install: false)
214
+ Archlinux.create_class(
215
+ install ? @opts[:default_install_packages_class] :
216
+ @opts[:default_packages_class],
217
+ l, config: self)
218
+ end
219
+
220
+ #:package, :db
221
+ def use_sign?(mode)
222
+ opt_sign=@opts[:sign]
223
+ if opt_sign.is_a?(Hash)
224
+ opt_sign[mode]
225
+ else
226
+ opt_sign
227
+ end
228
+ end
229
+
230
+ # output a list of sign names, or [] if we want to sign with the default sign name, false if we don't
231
+ def sign_names
232
+ opt_sign=@opts[:sign]
233
+ signs=if opt_sign.is_a?(Hash)
234
+ opt_sign.values
235
+ else
236
+ [*opt_sign]
237
+ end
238
+ names=signs.select {|s| s.is_a?(String)}
239
+ names = false if names.empty? and ! signs.any?
240
+ return names
241
+ end
242
+
243
+ # return the files that were signed
244
+ def sign(*files, sign_name: nil, force: false)
245
+ sign_name=use_sign?(sign_name) if sign_name.is_a?(Symbol)
246
+ files.map do |file|
247
+ sig="#{file}.sig"
248
+ if !Pathname.new(file).file?
249
+ SH.logger.error "Invalid file to sign #{file}"
250
+ next
251
+ end
252
+ if Pathname.new(sig).file?
253
+ if force
254
+ SH.logger.verbose2 "Signature #{sig} already exits, overwriting"
255
+ else
256
+ SH.logger.verbose2 "Signature #{sig} already exits, skipping"
257
+ next
258
+ end
259
+ end
260
+ args=['--detach-sign', '--no-armor']
261
+ args+=['-u', sign_name] if sign_name.is_a?(String)
262
+ launch(:gpg, *args, file) do |*args|
263
+ suc, _r=SH.sh(*args)
264
+ suc ? file : nil
265
+ end
266
+ end.compact
267
+ end
268
+
269
+ def verify_sign(*files)
270
+ args=['--verify']
271
+ files.map do |file|
272
+ file="#{file}.sig" unless file.to_s =~ /(.sig|.asc)/
273
+ launch(:gpg, *args, file) do |*args|
274
+ suc, _r=SH.sh(*args)
275
+ [file, suc]
276
+ end
277
+ end.to_h
278
+ end
279
+
280
+ def install_list
281
+ @install_list ||= Archlinux.create_class(@opts[:default_install_list_class], config: self)
282
+ end
283
+
284
+ def pre_install(*args, **opts)
285
+ names=sign_names
286
+ if names
287
+ cargs=["--detach-sign", "-o", "/dev/null", "/dev/null"]
288
+ if names.empty?
289
+ launch(:gpg, *cargs)
290
+ else
291
+ args=names.map { |name| ['-u', name] }.flatten+cargs
292
+ launch(:gpg, *args)
293
+ end
294
+ end
295
+ end
296
+
297
+ def post_install(pkgs, install: false, **opts)
298
+ if (db=self.db)
299
+ if install
300
+ tools=local_devtools
301
+ # info=opts[:pkgs_info]
302
+ # to_install=info[:all_pkgs].select {|_k,v| v[:op]==:install}.
303
+ # map {|_k,v| v[:out_pkg]}
304
+ # tools.sync_db(db.repo_name, install: to_install)
305
+
306
+ # Let's just install anything and let pacman handle it
307
+ m=opts[:makepkg_list]
308
+ if m
309
+ # we need to update the package versions with the ones
310
+ # provided by the current makepkg (which may be more recent than
311
+ # the one from aur's rpc in case of devel packages)
312
+ ipkgs=pkgs.map do |pkg|
313
+ found=m.packages.find(Query.strip(pkg))
314
+ found || pkg
315
+ end
316
+ else
317
+ ipkgs=pkgs
318
+ end
319
+ tools.sync_db(db.repo_name, install: %w(--needed) + ipkgs)
320
+ end
321
+ end
322
+ end
323
+
324
+ def parser(parser) #to add cli options in the config file
325
+ end
326
+ end
327
+
328
+ self.singleton_class.attr_accessor :config
329
+ @config ||= Config.new("aur.rb")
330
+ end
@@ -0,0 +1,328 @@
1
+ require 'aur/config'
2
+ require 'aur/packages'
3
+ require 'aur/repos'
4
+ require 'time'
5
+
6
+ module Archlinux
7
+ class DB
8
+ extend CreateHelper
9
+
10
+ def self.db_file?(name)
11
+ case name
12
+ when Pathname
13
+ true
14
+ when String
15
+ if name.include?('/') or name.match(/\.db(\..*)?$/)
16
+ true
17
+ else
18
+ false #Usually we assume this is a repo name
19
+ end
20
+ end
21
+ end
22
+
23
+ attr_accessor :file, :config
24
+ def initialize(file, config: Archlinux.config)
25
+ @orig_file=Pathname.new(file)
26
+ @file=@orig_file.realdirpath rescue @orig_file.abs_path
27
+ @config=config
28
+ end
29
+
30
+ def mkpath
31
+ @file.dirname.mkpath
32
+ end
33
+
34
+ def path
35
+ if file.exist?
36
+ file.realpath
37
+ else
38
+ file
39
+ end
40
+ end
41
+
42
+ def repo_name
43
+ @file.basename.to_s.sub(/\.db(\..*)?$/,'')
44
+ end
45
+
46
+ def create
47
+ mkpath
48
+ unless @file.exist?
49
+ call(:'repo-add', path)
50
+ end
51
+ self
52
+ end
53
+
54
+ def to_s
55
+ @file.to_s
56
+ end
57
+
58
+ # a db is a tar.gz archive of packages/desc, like yay-8.998-1/descr
59
+ def bsdcat_list
60
+ res= SH.run_simple("bsdcat #{@file.shellescape}", chomp: :lines) {return nil}
61
+ list=[]; pkg={}; mode=nil
62
+ flush = lambda do
63
+ # catch old deps files which don't specify the full infos
64
+ unless pkg[:name].nil? and pkg[:base].nil?
65
+ pkg[:repo]||=path
66
+ list << pkg
67
+ end
68
+ end
69
+ res.each do |l|
70
+ next if l.empty? or l.match(/^\u0000*$/)
71
+ if (m=l.match(/(\u0000+.*\u0000+)?%([A-Z0-9]*)%$/))
72
+ mode=m[2].downcase.to_sym
73
+ if m[1] #new db entry
74
+ flush.call #store old db entry
75
+ pkg={}
76
+ end
77
+ else
78
+ l=l.to_i if mode==:csize or mode==:isize
79
+ l=Time.at(l.to_i) if mode==:builddate
80
+ Archlinux.add_to_hash(pkg, mode, l)
81
+ end
82
+ end
83
+ flush.call #don't forget the last one
84
+ list
85
+ end
86
+
87
+ def list
88
+ res= SH.run_simple("bsdtar -xOf #{@file.shellescape} '*/desc'", chomp: :lines) {return nil}
89
+ list=[]; pkg={}; mode=nil
90
+ flush = lambda do
91
+ unless pkg.empty?
92
+ pkg[:repo]||=path
93
+ list << pkg
94
+ end
95
+ end
96
+ res.each do |l|
97
+ next if l.empty?
98
+ if (m=l.match(/^%([A-Z0-9]*)%$/))
99
+ mode=m[1].downcase.to_sym
100
+ if mode==:filename #new db entry
101
+ flush.call #store old db entry
102
+ pkg={}
103
+ end
104
+ else
105
+ l=l.to_i if mode==:csize or mode==:isize
106
+ l=Time.at(l.to_i) if mode==:builddate
107
+ Archlinux.add_to_hash(pkg, mode, l)
108
+ end
109
+ end
110
+ flush.call #don't forget the last one
111
+ list
112
+ end
113
+
114
+ def files(absolute=true)
115
+ list.map do |pkg|
116
+ file=Pathname.new(pkg[:filename])
117
+ absolute ? dir + file : file
118
+ end
119
+ end
120
+ # def files
121
+ # packages.l.map {|pkg| dir+Pathname.new(pkg[:filename])}
122
+ # end
123
+
124
+ def dir
125
+ # # we memoize this because if we get called again in a dir.chdir
126
+ # # call, then the realpath will fail
127
+ # @dir ||= @file.dirname.realpath
128
+ @file.dirname
129
+ end
130
+
131
+ def call(*args, **opts)
132
+ @config.launch(*args, **opts) do |*a, **o|
133
+ dir.chdir do
134
+ SH.sh(*a, **o)
135
+ end
136
+ end
137
+ end
138
+
139
+ def move_to_db(*files, op: :mv)
140
+ files=files.map {|f| Pathname.new(f).realpath}
141
+ dir=self.dir
142
+ SH.logger.verbose "#{op}: #{files.join(', ')} to #{dir}"
143
+ files.map do |f|
144
+ if f.dirname == dir
145
+ SH.logger.verbose2 "! #{f} already exists in #{dir}"
146
+ f
147
+ else
148
+ new=dir+f.basename
149
+ SH.logger.verbose2 "-> #{op} #{f} to #{new}"
150
+ f.send(op, new)
151
+ sig=Pathname.new(f.to_s+".sig") #mv .sig too
152
+ if sig.exist?
153
+ newsig=dir+sig.basename
154
+ SH.logger.verbose2 "-> #{op} #{sig} to #{newsig}"
155
+ sig.send(op, newsig)
156
+ end
157
+ new
158
+ end
159
+ end
160
+ end
161
+
162
+ def add(*files, cmd: :'repo-add', default_opts:[], sign: @config&.use_sign?(:db), force_sign: false, **opts)
163
+ default_opts+=['-s', '-v'] if sign
164
+ default_opts+=['--key', sign] if sign.is_a?(String)
165
+ dir.chdir do
166
+ files.map! {|f| Pathname.new(f)}
167
+ existing_files=files.select {|f| f.file?}
168
+ missing_files = files-existing_files
169
+ SH.logger.warn "In #{cmd}, missing files: #{missing_files.join(', ')}" unless missing_files.empty?
170
+ unless existing_files.empty?
171
+ sign_files = @config&.use_sign?(:package)
172
+ PackageFiles.new(*existing_files, config: @config).sign(sign_name: sign_files, force: force_sign) if sign_files
173
+ call(cmd, path, *existing_files, default_opts: default_opts, **opts)
174
+ @packages=nil #we need to refresh the list
175
+ end
176
+ end
177
+ files
178
+ end
179
+
180
+ def remove(*pkgnames, cmd: :'repo-remove', default_opts:[], sign: @config&.use_sign?(:db), **opts)
181
+ default_opts+=['-s', '-v'] if sign
182
+ default_opts+=['--key', sign] if sign.is_a?(String)
183
+ dir.chdir do
184
+ call(cmd, path, *pkgnames, default_opts: default_opts, **opts)
185
+ @packages=nil #we need to refresh the list
186
+ end
187
+ pkgnames
188
+ end
189
+
190
+ def packages(refresh=false)
191
+ @packages=nil if refresh
192
+ # @packages||=PackageList.new(list, config: @config)
193
+ @packages||=@config.to_packages(list)
194
+ end
195
+
196
+ def dir_packages_cls
197
+ PackageFiles.from_dir(dir, config: @config)
198
+ end
199
+ def dir_packages
200
+ dir_packages_cls.packages
201
+ end
202
+
203
+ def package_files_cls
204
+ PackageFiles.new(*files, config: @config)
205
+ end
206
+ def package_files
207
+ package_files_cls.packages
208
+ end
209
+
210
+ # sign the files in the db (return the list of signed file, by default
211
+ # unless force: true is passed this won't resign already signed files)
212
+ def sign_files(sign_name: :package, **opts)
213
+ @config&.sign(*files, sign_name: sign_name, **opts)
214
+ end
215
+ def sign_db(sign_name: :db, **opts)
216
+ @config&.sign(@file, sign_name: sign_name, **opts) if @file.file?
217
+ end
218
+
219
+ def verify_sign_db
220
+ @config&.verify_sign(@file)
221
+ end
222
+ def verify_sign_files
223
+ @config&.verify_sign(*files)
224
+ end
225
+ # check the inline signatures
226
+ def verify_sign_pkgs(*pkgs)
227
+ packages.get_packages(*pkgs).map do |pkg|
228
+ pgpsign=pkg[:pgpsig]
229
+ if pgpsign
230
+ require 'base64'
231
+ sig=Base64.decode64(pgpsign)
232
+ filename=dir+pkg[:filename]
233
+ @config.launch(:gpg, "--enable-special-filenames", "--verify", "-", filename, mode: :capture, stdin_data: sig) do |*args|
234
+ suc, _r=SH.sh(*args)
235
+ [pkg.name, suc]
236
+ end
237
+ else
238
+ [pkg.name, false]
239
+ end
240
+ end.to_h
241
+ end
242
+
243
+ # if we missed some signatures, resign them (and add them back to the
244
+ # db to get the signatures in the db). This override the sign:false
245
+ # config options.
246
+ def resign(**opts)
247
+ signed_files=sign_files(**opts)
248
+ add(*signed_files) #note that this may try to sign again, but since the signature exists it won't launch gpg
249
+ sign_db(**opts) #idem, normally the db will be signed by repo-add -v, but in case the signature was turned off in the config, this forces the signature
250
+ end
251
+
252
+ def check
253
+ packages.same?(package_files)
254
+ end
255
+
256
+ def check_update(other=dir_packages)
257
+ self.packages.check_updates(other)
258
+ # yield up if block_given?
259
+ # refresh=up.select {|_k, u| u[:op]==:upgrade or u[:op]==:downgrade}
260
+ # add=up.select {|_k, u| u[:op]==:install}
261
+ # remove=up.select {|_k, u| u[:op]==:obsolete}
262
+ # return {refresh: refresh, add: add, remove: remove}
263
+ end
264
+
265
+ def show_updates(other=dir_packages, **showopts)
266
+ c=check_update(other)
267
+ packages.show_updates(c, **showopts)
268
+ c
269
+ end
270
+
271
+ def update(other=dir_packages, add_for: %i(upgrade downgrade install), rm_for: %i(obsolete), **showopts)
272
+ c=show_updates(other, **showopts)
273
+ to_add=c.select {|_k, u| add_for.include?(u[:op])}
274
+ to_rm=c.select {|_k, u| rm_for.include?(u[:op])}
275
+ add(*to_add.map { |_k,v| other[v[:out_pkg]].path })
276
+ # remove(*(r[:remove].map {|_k,v| packages[v[:in_pkg]].file.shellescape}))
277
+ remove(* to_rm.map {|_k,v| Query.strip(v[:in_pkg])})
278
+ c
279
+ end
280
+
281
+ # move/copy files to db and add them
282
+ # if update==true, only add more recent packages
283
+ # pkgs should be a PackageFiles or a PackageList or a list of files
284
+ def add_to_db(pkgs, update: true, op: :cp)
285
+ if update
286
+ pkgs=PackageFiles.create(pkgs).packages unless pkgs.is_a? PackageList
287
+ up=self.packages.check_updates(pkgs)
288
+ pkgs=up.select {|_k, u| u[:op]==:upgrade or u[:op]==:install}.map do |_k,v|
289
+ pkgs[v[:out_pkg]].path
290
+ end
291
+ else
292
+ pkgs=pkgs.map {|_k,v| v.path } if pkgs.is_a?(PackageList)
293
+ end
294
+ SH.logger.mark "Updating #{pkgs} in #{self}"
295
+ cp_pkgs=move_to_db(*pkgs, op: op)
296
+ add(*cp_pkgs)
297
+ end
298
+
299
+ #remove from db and also remove the package files
300
+ def rm_from_db(*pkgs)
301
+ to_rm=packages.get_packages(*pkgs)
302
+ PackageFiles.rm_files(*to_rm.map {|pkg| pkg.path}, dir: self.dir)
303
+ remove(*pkgs)
304
+ end
305
+
306
+ # in clean_dir, we clean the dir packages which have a newer
307
+ # version (in the dir).
308
+ def clean_dir(dry_run: true)
309
+ files=dir_packages_cls
310
+ files.clean(dry_run: dry_run)
311
+ end
312
+
313
+ # In clean, we clean the dir packages which are newer or not present in
314
+ # the db. This is like the reverse of `update`. In particular be
315
+ # careful that this will delete newer versions or added versions
316
+ def clean(dry_run: true)
317
+ dir_pkgs=dir_packages_cls
318
+ dir_files=dir_pkgs.files
319
+ db_files=files
320
+ to_remove=dir_files - db_files
321
+ if dry_run
322
+ to_remove
323
+ else
324
+ PackageFiles.rm_files(*to_remove, dir: self.dir)
325
+ end
326
+ end
327
+ end
328
+ end