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
1diff --git a/checkrdepends b/checkrdepends
2index a49bd7b..ada537e 100755
3--- a/checkrdepends
4+++ b/checkrdepends
5@@ -15,7 +15,7 @@
6 # along with this program. If not, see <http://www.gnu.org/licenses/>.
7
8 from collections import defaultdict
9-import optparse
10+import argparse
11 import os
12 import re
13 import sys
14@@ -23,9 +23,15 @@ import sys
15 from utils import read_tag_file
16
17
18+EXAMPLE_USAGE='''Examples:
19+ %(prog)s --no-ports -B 'http://archive.ubuntu.com/ubuntu' postgresql-common
20+ %(prog)s -B 'http://ports.ubuntu.com/ubuntu-ports' -a arm64 -a armhf -a ppc64el -a riscv64 -a s390x postgresql-common
21+ %(prog)s --no-ports -B 'http://deb.debian.org/debian' --vendor debian -s sid symfony
22+'''
23+
24 default_base = '/home/ubuntu-archive/mirror/ubuntu'
25 default_suite = 'noble'
26-components = ('main', 'restricted', 'universe', 'multiverse')
27+default_vendor = 'ubuntu'
28
29 # Cut-down RE from deb822.PkgRelation.
30 re_dep = re.compile(r'^\s*([a-zA-Z0-9.+\-]{2,})')
31@@ -45,7 +51,10 @@ def parse_relation_packages(raw):
32 yield dep, dep_list
33
34
35-def primary_arches(suite):
36+def primary_arches(opts):
37+ if opts.vendor == 'debian':
38+ return ('all', 'amd64', 'i386')
39+
40 return ('amd64', 'i386')
41
42
43@@ -129,10 +138,13 @@ def read_di(debs, path):
44
45
46 def pockets(opts):
47- if '-' in opts.suite:
48+ if '-' in opts.suite or opts.suite in ('sid', 'unstable'):
49 return ('',)
50- else:
51+ elif opts.vendor == 'ubuntu':
52 return ('', '-updates', '-security', '-backports')
53+ elif opts.vendor == 'debian':
54+ # TODO: support debian security (different archive)
55+ return ('', '-updates', '-backports')
56
57
58 def render_dep(name, field, arch, why):
59@@ -147,12 +159,19 @@ def render_dep(name, field, arch, why):
60
61
62 def search(opts, pkgs):
63+ if opts.vendor == 'ubuntu':
64+ components = ('main', 'restricted', 'universe', 'multiverse')
65+ elif opts.vendor == 'debian':
66+ components = ('main', 'contrib', 'non-free', 'non-free-firmware')
67+ else:
68+ raise ValueError(f"unsupported vendor {opts.vendor}")
69+
70 for pocket in pockets(opts):
71 pocket_base = '%s/dists/%s%s' % (opts.archive_base, opts.suite, pocket)
72 if opts.arches:
73 arches = opts.arches
74 else:
75- arches = list(primary_arches(opts.suite))
76+ arches = list(primary_arches(opts))
77 if opts.ports:
78 arches.extend(ports_arches(opts.suite))
79
80@@ -241,32 +260,38 @@ def search(opts, pkgs):
81
82
83 def main():
84- parser = optparse.OptionParser(usage='%prog [options] pkg [...]')
85- parser.add_option('-B', '--archive-base', dest='archive_base',
86- help=('archive base directory (default: %s)' %
87+ parser = argparse.ArgumentParser(usage='%(prog)s [options] pkg [...]',
88+ epilog=EXAMPLE_USAGE,
89+ formatter_class=argparse.RawDescriptionHelpFormatter)
90+ parser.add_argument('packages', nargs='+')
91+ parser.add_argument('-B', '--archive-base', dest='archive_base',
92+ help=('archive base directory or URL (default: %s)' %
93 default_base),
94 default=default_base)
95- parser.add_option('-s', '--suite', dest='suite',
96+ parser.add_argument('-s', '--suite', dest='suite',
97 help='suite to check (default: %s)' % default_suite,
98 default=default_suite)
99- parser.add_option('-a', '--arch', dest='arches', action='append',
100+ parser.add_argument('-a', '--arch', dest='arches', action='append',
101 help='check only this architecture '
102 '(may be given multiple times)')
103- parser.add_option('-b', '--binary', dest='binary', action='store_true',
104+ parser.add_argument('-b', '--binary', dest='binary', action='store_true',
105 help='treat arguments as binary packages, not source')
106- parser.add_option('--no-ports', dest='ports',
107+ parser.add_argument('--no-ports', dest='ports',
108 default=True, action='store_false',
109 help='skip ports architectures')
110- parser.add_option('-d', '--directory', dest='directory', metavar='DIR',
111+ parser.add_argument('-d', '--directory', dest='directory', metavar='DIR',
112 help='output to directory DIR (one file per package) '
113 'instead of standard output')
114- opts, args = parser.parse_args()
115+ parser.add_argument('-V', '--vendor', dest='vendor',
116+ help='distro to check (default: %s; supported: ubuntu,debian)' % default_vendor,
117+ default=default_vendor)
118+ args = parser.parse_args()
119
120 if 'CHECKRDEPENDS_PROFILE' in os.environ:
121 import profile
122- profile.run('search(opts, args)')
123+ profile.run('search(args, args.packages)')
124 else:
125- search(opts, args)
126+ search(args, args.packages)
127
128
129 if __name__ == '__main__':
130diff --git a/utils.py b/utils.py
131index b529317..ea7a5c2 100644
132--- a/utils.py
133+++ b/utils.py
134@@ -22,6 +22,7 @@ import gzip
135 import os
136 import re
137 import tempfile
138+import urllib.error
139 import urllib.request
140
141 import apt_pkg
142@@ -40,6 +41,12 @@ except ImportError:
143
144 def read_tag_file(path, pkg=None):
145 tmp = tempfile.NamedTemporaryFile(prefix='checkrdepends.', delete=False)
146+ if path.startswith(('http://', 'https://')):
147+ try:
148+ path = urllib.request.urlopen(path)
149+ except urllib.error.HTTPError as e:
150+ print(f'Error while fetching "{path}": {e}')
151+ raise
152 try:
153 compressed = gzip.open(path)
154 try:

Subscribers

People subscribed via source and target branches