Merge ~athos-ribeiro/ubuntu-archive-tools:checkrdepends-remote into ubuntu-archive-tools:main

Proposed by Athos Ribeiro
Status: Needs review
Proposed branch: ~athos-ribeiro/ubuntu-archive-tools:checkrdepends-remote
Merge into: ubuntu-archive-tools:main
Diff against target: 154 lines (+49/-17)
2 files modified
checkrdepends (+42/-17)
utils.py (+7/-0)
Reviewer Review Type Date Requested Status
Ubuntu Package Archive Administrators Pending
Review via email: mp+462728@code.launchpad.net

Description of the change

- Allow using checkrdepends through an archive URL instead of a local base directory.
- Allow checking Debian reverse dependencies
- Move to argparse (optparse is deprecated)
- document examples in --help

Note that while performing changes, I tried to keep the function signatures to avoid disrupting any automation using the script.

I wonder if it would make sense to use the archive URL as the default for `-B` and set the default value of `ports` to false.

To post a comment you must log in.
Revision history for this message
Athos Ribeiro (athos-ribeiro) wrote :

BTW, the reason I use checkrdepends instead of reverse-depends some times is that it supports verifying virtual packages, e.g.,

checkrdepends --no-ports -B 'http://archive.ubuntu.com/ubuntu' --binary phpapi-20230831

and I am not aware if this is currently supported in reverse-depends.

Unmerged commits

aa9f557... by Athos Ribeiro

checkrdepends: document new behavior for -B option

13e526b... by Athos Ribeiro

checkrdepends: document examples

d260a46... by Athos Ribeiro

checkrdepends: move to argparse

optparse was deprecated in python 3.2 in favor of argparse. Let's use it
instead.

09c9197... by Athos Ribeiro

checkrdepends: allow checking Debian rdeps

8168517... by Athos Ribeiro

utils: allow read_tag_file to parse remote files

This enables passing URLS to checkrdepends to use it from remote
repositories instead of keeping local copies.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/checkrdepends b/checkrdepends
index a49bd7b..ada537e 100755
--- a/checkrdepends
+++ b/checkrdepends
@@ -15,7 +15,7 @@
15# along with this program. If not, see <http://www.gnu.org/licenses/>.15# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
17from collections import defaultdict17from collections import defaultdict
18import optparse18import argparse
19import os19import os
20import re20import re
21import sys21import sys
@@ -23,9 +23,15 @@ import sys
23from utils import read_tag_file23from utils import read_tag_file
2424
2525
26EXAMPLE_USAGE='''Examples:
27 %(prog)s --no-ports -B 'http://archive.ubuntu.com/ubuntu' postgresql-common
28 %(prog)s -B 'http://ports.ubuntu.com/ubuntu-ports' -a arm64 -a armhf -a ppc64el -a riscv64 -a s390x postgresql-common
29 %(prog)s --no-ports -B 'http://deb.debian.org/debian' --vendor debian -s sid symfony
30'''
31
26default_base = '/home/ubuntu-archive/mirror/ubuntu'32default_base = '/home/ubuntu-archive/mirror/ubuntu'
27default_suite = 'noble'33default_suite = 'noble'
28components = ('main', 'restricted', 'universe', 'multiverse')34default_vendor = 'ubuntu'
2935
30# Cut-down RE from deb822.PkgRelation.36# Cut-down RE from deb822.PkgRelation.
31re_dep = re.compile(r'^\s*([a-zA-Z0-9.+\-]{2,})')37re_dep = re.compile(r'^\s*([a-zA-Z0-9.+\-]{2,})')
@@ -45,7 +51,10 @@ def parse_relation_packages(raw):
45 yield dep, dep_list51 yield dep, dep_list
4652
4753
48def primary_arches(suite):54def primary_arches(opts):
55 if opts.vendor == 'debian':
56 return ('all', 'amd64', 'i386')
57
49 return ('amd64', 'i386')58 return ('amd64', 'i386')
5059
5160
@@ -129,10 +138,13 @@ def read_di(debs, path):
129138
130139
131def pockets(opts):140def pockets(opts):
132 if '-' in opts.suite:141 if '-' in opts.suite or opts.suite in ('sid', 'unstable'):
133 return ('',)142 return ('',)
134 else:143 elif opts.vendor == 'ubuntu':
135 return ('', '-updates', '-security', '-backports')144 return ('', '-updates', '-security', '-backports')
145 elif opts.vendor == 'debian':
146 # TODO: support debian security (different archive)
147 return ('', '-updates', '-backports')
136148
137149
138def render_dep(name, field, arch, why):150def render_dep(name, field, arch, why):
@@ -147,12 +159,19 @@ def render_dep(name, field, arch, why):
147159
148160
149def search(opts, pkgs):161def search(opts, pkgs):
162 if opts.vendor == 'ubuntu':
163 components = ('main', 'restricted', 'universe', 'multiverse')
164 elif opts.vendor == 'debian':
165 components = ('main', 'contrib', 'non-free', 'non-free-firmware')
166 else:
167 raise ValueError(f"unsupported vendor {opts.vendor}")
168
150 for pocket in pockets(opts):169 for pocket in pockets(opts):
151 pocket_base = '%s/dists/%s%s' % (opts.archive_base, opts.suite, pocket)170 pocket_base = '%s/dists/%s%s' % (opts.archive_base, opts.suite, pocket)
152 if opts.arches:171 if opts.arches:
153 arches = opts.arches172 arches = opts.arches
154 else:173 else:
155 arches = list(primary_arches(opts.suite))174 arches = list(primary_arches(opts))
156 if opts.ports:175 if opts.ports:
157 arches.extend(ports_arches(opts.suite))176 arches.extend(ports_arches(opts.suite))
158177
@@ -241,32 +260,38 @@ def search(opts, pkgs):
241260
242261
243def main():262def main():
244 parser = optparse.OptionParser(usage='%prog [options] pkg [...]')263 parser = argparse.ArgumentParser(usage='%(prog)s [options] pkg [...]',
245 parser.add_option('-B', '--archive-base', dest='archive_base',264 epilog=EXAMPLE_USAGE,
246 help=('archive base directory (default: %s)' %265 formatter_class=argparse.RawDescriptionHelpFormatter)
266 parser.add_argument('packages', nargs='+')
267 parser.add_argument('-B', '--archive-base', dest='archive_base',
268 help=('archive base directory or URL (default: %s)' %
247 default_base),269 default_base),
248 default=default_base)270 default=default_base)
249 parser.add_option('-s', '--suite', dest='suite',271 parser.add_argument('-s', '--suite', dest='suite',
250 help='suite to check (default: %s)' % default_suite,272 help='suite to check (default: %s)' % default_suite,
251 default=default_suite)273 default=default_suite)
252 parser.add_option('-a', '--arch', dest='arches', action='append',274 parser.add_argument('-a', '--arch', dest='arches', action='append',
253 help='check only this architecture '275 help='check only this architecture '
254 '(may be given multiple times)')276 '(may be given multiple times)')
255 parser.add_option('-b', '--binary', dest='binary', action='store_true',277 parser.add_argument('-b', '--binary', dest='binary', action='store_true',
256 help='treat arguments as binary packages, not source')278 help='treat arguments as binary packages, not source')
257 parser.add_option('--no-ports', dest='ports',279 parser.add_argument('--no-ports', dest='ports',
258 default=True, action='store_false',280 default=True, action='store_false',
259 help='skip ports architectures')281 help='skip ports architectures')
260 parser.add_option('-d', '--directory', dest='directory', metavar='DIR',282 parser.add_argument('-d', '--directory', dest='directory', metavar='DIR',
261 help='output to directory DIR (one file per package) '283 help='output to directory DIR (one file per package) '
262 'instead of standard output')284 'instead of standard output')
263 opts, args = parser.parse_args()285 parser.add_argument('-V', '--vendor', dest='vendor',
286 help='distro to check (default: %s; supported: ubuntu,debian)' % default_vendor,
287 default=default_vendor)
288 args = parser.parse_args()
264289
265 if 'CHECKRDEPENDS_PROFILE' in os.environ:290 if 'CHECKRDEPENDS_PROFILE' in os.environ:
266 import profile291 import profile
267 profile.run('search(opts, args)')292 profile.run('search(args, args.packages)')
268 else:293 else:
269 search(opts, args)294 search(args, args.packages)
270295
271296
272if __name__ == '__main__':297if __name__ == '__main__':
diff --git a/utils.py b/utils.py
index b529317..ea7a5c2 100644
--- a/utils.py
+++ b/utils.py
@@ -22,6 +22,7 @@ import gzip
22import os22import os
23import re23import re
24import tempfile24import tempfile
25import urllib.error
25import urllib.request26import urllib.request
2627
27import apt_pkg28import apt_pkg
@@ -40,6 +41,12 @@ except ImportError:
4041
41def read_tag_file(path, pkg=None):42def read_tag_file(path, pkg=None):
42 tmp = tempfile.NamedTemporaryFile(prefix='checkrdepends.', delete=False)43 tmp = tempfile.NamedTemporaryFile(prefix='checkrdepends.', delete=False)
44 if path.startswith(('http://', 'https://')):
45 try:
46 path = urllib.request.urlopen(path)
47 except urllib.error.HTTPError as e:
48 print(f'Error while fetching "{path}": {e}')
49 raise
43 try:50 try:
44 compressed = gzip.open(path)51 compressed = gzip.open(path)
45 try:52 try:

Subscribers

People subscribed via source and target branches