3 # This script reports which binary/source packages that can be safely
4 # deleted from one of the main APTs suite in our custom repo. It requires a
5 # .build-manifest as the source for which packages that are used
6 # during build and thus cannot be deleted.
11 raise 'please install the ruby-debian package'
17 class NoSource
< StandardError
23 apt_repo_hostnames
= [
25 'umjqavufhoix3smyq6az2sx4istmuvsgmz4bq5u5x56rnayejoo6l2qd.onion',
27 ['main', 'contrib', 'non-free'].each
do |repo
|
28 apt_repo_filenames
= apt_repo_hostnames
.map
do |hostname
|
29 "/var/lib/apt/lists/#{hostname}_dists_#{suite}_#{repo}_source_Sources"
31 apt_repo_filename
= apt_repo_filenames
.find
do |filename
|
34 next if apt_repo_filename
.nil?
36 @apt_sources << Debian
::Sources.new(apt_repo_filename
)
38 return unless @apt_sources.empty
?
40 raise "could not find Tails custom APT repo's sources, " \
41 "please add this to your APT sources:\n" \
42 'deb-src [arch=amd64] http://deb.tails.boum.org/ ' \
43 "#{suite} main contrib non-free"
46 def source_package(package
)
48 @apt_sources.each
do |repo
|
49 repo
.each_package
do |dsc
|
50 # The -dbg(sym) packages are not listed, so we look for the
51 # original package's source instead, which will be the same.
52 matches
<< dsc
.package
if dsc
.binary
.include?(package
.sub(/-dbg(sym)?$/, ''))
55 raise NoSource
, "found no source package for #{package}" if matches
.size
.zero
?
57 raise "found multiple source packages for #{package}: #{matches.join(', ')}" \
58 if matches
.uniq
.size
> 1
64 Options
= Struct
.new(:suite, :build_manifest, keyword_init
: true)
67 def self.parse(options
)
68 args
= Options
.new(suite
: nil, build_manifest
: nil)
70 opt_parser
= OptionParser
.new
do |opts
|
73 'Look for cruft in APT suite SUITE'
78 '--build-manifest MANIFEST',
79 'Use specified build manifest instead of downloading the latest one'
81 args
.build_manifest
= build_manifest
83 opts
.on('-h', '--help', 'Prints this help') do
88 opt_parser
.parse
!(options
)
90 !args
.suite
.nil? or raise 'Please use --suite SUITE'
95 options
= Parser
.parse(ARGV)
97 allowed_suites
= ['stable', 'devel', 'testing']
98 unless allowed_suites
.include?(options
.suite
)
99 raise "we only support checking the following' " \
100 "custom APT suites: #{allowed_suites.join(', ')}"
103 if options
.build_manifest
.nil?
104 url
= "https://nightly.tails.boum.org/build_Tails_ISO_#{options.suite}/lastSuccessful/archive/latest.build-manifest"
106 manifest
= YAML
.safe_load(
107 URI
.parse(url
).open
.read
109 rescue OpenURI
::HTTPError
110 raise "got HTTP 404 when attempting to fetch: #{url}\n" \
111 'Please try again in a while -- Jenkins sometimes needs some time ' \
112 'to create the latest.build-manifest symlink after a build completes'
115 manifest
= YAML
.load_file(options
.build_manifest
)
118 APT
= AptSources
.new(options
.suite
).freeze
119 all_source_packages
= []
120 used_source_packages
= []
121 binary_cruft_candidates
= []
122 no_source_packages
= []
124 custom_packages
= `ssh reprepro@incoming.deb.tails.boum.org \
125 reprepro list #{options.suite}`
126 custom_packages
.each_line(chomp
: true) do |line
|
127 type
, name
, version = line
.split
129 all_source_packages
<< name
131 installed
= manifest
['packages']['binary'].find
{ |x
| x
['package'] == name
}
132 if installed
.nil? || version != installed
['version']
133 binary_cruft_candidates
<< name
136 used_source_packages
<< APT
.source_package(name
)
138 no_source_packages
<< name
144 source_cruft
= all_source_packages
.uniq
- used_source_packages
146 binary_cruft_candidates
.each
do |p
|
148 next if used_source_packages
.include?(APT
.source_package(p
))
150 # If we don't have a source for a package, it should be a package
151 # we forgot to clean up when we removed its sources.
157 list
.each_with_index
{ |item
, i
| puts
" #{i + 1}. `#{item}`" }
160 unless binary_cruft
.empty
?
161 puts
"## Binary packages that are not used\n\n"
162 puts_list(binary_cruft
)
164 puts
"### Clean up command\n\n" \
165 ' ssh reprepro@incoming.deb.tails.boum.org ' \
166 "reprepro remove #{options.suite} #{binary_cruft.join(' ')}"
170 unless no_source_packages
.empty
?
171 puts
"## Binary packages that are used but lack source\n\n"
172 puts_list(no_source_packages
)
174 puts
'**Please investigate!** Most likely you want to just upload the ' \
175 'missing source packages, or these binary packages are installed ' \
176 "by mistake and should be considered as cruft and removed with:\n\n" \
177 ' ssh reprepro@incoming.deb.tails.boum.org ' \
178 "reprepro remove #{options.suite} #{no_source_packages.join(' ')}"
182 unless source_cruft
.empty
?
183 puts
"## Source packages that are not used\n\n"
184 puts_list(source_cruft
)
186 puts
"### Clean up command\n\n" \
187 ' ssh reprepro@incoming.deb.tails.boum.org ' \
188 "reprepro removesrcs #{options.suite} #{source_cruft.join(' ')}"