Merge lp:~mwhudson/ubuntu-archive-scripts/find-rcbuggy-problem-packages into lp:ubuntu-archive-scripts

Proposed by Michael Hudson-Doyle
Status: Merged
Merged at revision: 239
Proposed branch: lp:~mwhudson/ubuntu-archive-scripts/find-rcbuggy-problem-packages
Merge into: lp:ubuntu-archive-scripts
Diff against target: 358 lines (+340/-0)
3 files modified
find-rcbuggy-problem-packages (+244/-0)
run-proposed-migration (+6/-0)
templates/rcbuggy-problem-packages.html (+90/-0)
To merge this branch: bzr merge lp:~mwhudson/ubuntu-archive-scripts/find-rcbuggy-problem-packages
Reviewer Review Type Date Requested Status
Ubuntu Package Archive Administrators Pending
Review via email: mp+372119@code.launchpad.net

Commit message

Generate a report of packages that have been removed from testing whose removal from Ubuntu would aid other packages though proposed migration.

Description of the change

This branch implements this idea:

Packages that have been removed from Debian testing should not automatically be removed from Ubuntu devel, because a package may be buggy in Debian but not in Ubuntu (or vice-versa). However, packages that have been removed from Debian testing for bugs, and which are also blocking things in the proposed-migration queue, are candidates for fast-tracking for removal from devel + devel-proposed.

Currently we waste too much time identifying packages in this category.

Create a report that shows packages that are in this situation, so that they can be more quickly identified. Report should provide recommended commands for their removal. Iterate until the report is correct and a simple button-pushing exercise for the archive admins, then automate.

Should be added to lp:ubuntu-archive-scripts

Example of a package removed from testing that we then manually removed from devel:

https://launchpad.net/debian/+source/twitter-bootstrap/+publishinghistory

To post a comment you must log in.
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

The output looks like this fwiw: https://paste.ubuntu.com/p/6NC4VPMJTw/

247. By Michael Hudson-Doyle

print remove-package command for packages not in proposed (oops)

248. By Michael Hudson-Doyle

html output

249. By Michael Hudson-Doyle

make html a bit nicer

250. By Michael Hudson-Doyle

MOAR CSS

251. By Michael Hudson-Doyle

we do not care about a package blocking its own migration!

252. By Michael Hudson-Doyle

more css tweakery

253. By Michael Hudson-Doyle

update run-proposed-migration

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Updated to produce HTML output, preview at http://michael.hudsondoyle.geek.nz/rcbuggy-problem-packages.html

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'find-rcbuggy-problem-packages'
2--- find-rcbuggy-problem-packages 1970-01-01 00:00:00 +0000
3+++ find-rcbuggy-problem-packages 2019-09-09 01:50:04 +0000
4@@ -0,0 +1,244 @@
5+#!/usr/bin/python3
6+
7+# This script highlights packages that are being kept out of Debian
8+# testing by release critical bugs whose removal from the Ubuntu devel
9+# series would aid the migration of other packages out of proposed.
10+#
11+# The packages that are being kept out of Debian testing by release
12+# critical bugs can be found fairly easily by looking at the output
13+# from Debian's britney runs. We do this first (and call these
14+# "rc-gone packages").
15+#
16+# Such packages can inhibit the migration of other packages in two
17+# ways:
18+#
19+# 1) autopkgtest regressions
20+# 2) by becoming uninstallable
21+#
22+# The first is fairly easy to find: scan through Ubuntu's excuses.yaml
23+# and look for the rc-gone package in the list of autopkgtest
24+# regressions for any package.
25+#
26+# The second is a bit more mind-bending to detect. If the package is
27+# caught up in a transition, a rebuild will be attempted as a matter
28+# of course, and if this succeeds and passes its autopkgtests then
29+# removing the package will not aid proposed migration. So we look for
30+# rc-gone packages in proposed that are missing builds or failing
31+# autopkgtests. For such source packages, we see if any of their
32+# binary packages are reported as being made uninstallable by the
33+# migration of any source package in proposed. If so, removing the
34+# rc-gone package will help proposed migration.
35+
36+import argparse
37+import os
38+import shlex
39+import subprocess
40+
41+import attr
42+from jinja2 import Environment, FileSystemLoader
43+import yaml
44+
45+env = Environment(
46+ loader=FileSystemLoader(os.path.dirname(os.path.abspath(__file__)) + '/templates'),
47+ autoescape=True,
48+ extensions=['jinja2.ext.i18n'],
49+)
50+env.install_null_translations(True)
51+
52+def parse_args():
53+ parser = argparse.ArgumentParser()
54+ parser.add_argument('--ubuntu-excuses', action='store')
55+ parser.add_argument('--ubuntu-update_output', action='store')
56+ parser.add_argument('--debian-excuses', action='store')
57+ parser.add_argument('--output', action='store')
58+ return parser.parse_args()
59+
60+args = parse_args()
61+
62+def run_output(*cmd, **extra):
63+ kw = dict(check=True, encoding='ascii', stdout=subprocess.PIPE)
64+ kw.update(extra)
65+ cp = subprocess.run(cmd, **kw)
66+ return cp.stdout.strip()
67+
68+def extract_uninstallable_to_src_pkg(output_fp):
69+ # Extract a mapping from binary package name to the set of source
70+ # packages whose migration from proposed makes that package
71+ # uninstallable.
72+
73+ # We're looking for sequences of lines like this:
74+
75+ # skipped: camlp5 (0, 3, 57)
76+ # got: 13+0: a-1:a-0:a-0:i-6:p-0:s-6
77+ # * s390x: hol-light, libaac-tactics-ocaml-dev, libcoq-ocaml-dev, libledit-ocaml-dev, ocaml-ulex08
78+
79+ # (Britney tries to migrate batches of packages but it always
80+ # tries each package on its own as well).
81+
82+ r = {}
83+ srcpkg = None
84+ for line in output_fp:
85+ parts = line.split()
86+ if len(parts) >= 2:
87+ if parts[0] == 'skipped:':
88+ srcpkg = None
89+ # If parts[2] is '(' then this line is about trying to
90+ # migrate a single package, which is what we are
91+ # looking for.
92+ if parts[2].startswith('('):
93+ srcpkg = parts[1]
94+ if srcpkg is not None and parts[0] == '*':
95+ # parts[1] is "${arch}:"
96+ # parts[2:] is a comma+space separated list of binary package names.
97+ for binpkg in parts[2:]:
98+ binpkg = binpkg.strip(',')
99+ r.setdefault(binpkg, set()).add(srcpkg)
100+ return r
101+
102+
103+@attr.s
104+class RCGone:
105+ source_package_name = attr.ib()
106+ bugs = attr.ib()
107+ block_by_regression = attr.ib(default=attr.Factory(set))
108+ block_by_uninstallability = attr.ib(default=attr.Factory(list))
109+ suites = attr.ib(default=attr.Factory(set))
110+ _rdeps_lines = attr.ib(default=None)
111+
112+ @property
113+ def comment(self):
114+ comment = "removed from testing (Debian bug"
115+ if len(self.bugs) > 1:
116+ comment += "s"
117+ comment += " " + ", ".join('#' + b for b in self.bugs) + ")"
118+ if self.block_by_regression:
119+ comment += ', blocks {} by regression'.format(', '.join(sorted(self.block_by_regression)))
120+ if self.block_by_uninstallability:
121+ comment += ', blocks {} by uninstallability'.format(', '.join(sorted(self.block_by_uninstallability)))
122+ return comment
123+
124+ @property
125+ def rdeps_lines(self):
126+ if self._rdeps_lines is None:
127+ rdeps = run_output("reverse-depends", 'src:' + self.source_package_name)
128+ rbdeps = run_output("reverse-depends", '-b', 'src:' + self.source_package_name)
129+
130+ if rdeps == 'No reverse dependencies found':
131+ rdeps = ''
132+ if rbdeps == 'No reverse dependencies found':
133+ rbdeps = ''
134+
135+ if rdeps or rbdeps:
136+ if rdeps and rbdeps:
137+ self._rdeps_lines = (rdeps + '\n' + rbdeps).splitlines()
138+ else:
139+ self._rdeps_lines = (rdeps + rbdeps).splitlines()
140+ else:
141+ self._rdeps_lines = []
142+ return self._rdeps_lines
143+
144+ @property
145+ def rdeps_text_short(self):
146+ return "\n".join(self.rdeps_lines[:10])
147+
148+ @property
149+ def rdeps_text_more(self):
150+ return "\n".join(self.rdeps_lines[10:])
151+
152+ @property
153+ def removal_commands(self):
154+ suites = self.suites
155+ if not suites:
156+ suites = [series]
157+ cmds = []
158+ for suite in suites:
159+ cmd = " ".join(["remove-package", "-s", suite, "-m", shlex.quote(self.comment), self.source_package_name])
160+ if self.rdeps_lines:
161+ cmd = '#' + cmd
162+ cmds.append(cmd)
163+ return cmds
164+
165+ @property
166+ def css_class(self):
167+ if self.rdeps_lines:
168+ return 'removal-not-ok'
169+ else:
170+ return 'removal-ok'
171+
172+
173+if 'SERIES' in os.environ:
174+ series = os.environ['SERIES']
175+else:
176+ series = run_output('distro-info', '-d')
177+
178+
179+def main():
180+ print("loading data")
181+ with open(args.ubuntu_excuses) as fp:
182+ ubuntu_excuses = yaml.load(fp, Loader=yaml.CSafeLoader)
183+ with open(args.ubuntu_update_output) as fp:
184+ uninstallable_to_src_pkg = extract_uninstallable_to_src_pkg(fp)
185+ with open(args.debian_excuses) as fp:
186+ debian_excuses = yaml.load(fp, Loader=yaml.CSafeLoader)
187+
188+ print("finding rcgone packages")
189+ rc_gones = {}
190+
191+ for source in debian_excuses['sources']:
192+ if source['old-version'] == '-':
193+ info = source['policy_info']
194+ rc_bugs = info.get('rc-bugs', {})
195+ if rc_bugs.get('verdict') == "REJECTED_PERMANENTLY":
196+ bugs = []
197+ for k in 'shared-bugs', 'unique-source-bugs', 'unique-target-bugs':
198+ bugs.extend(rc_bugs[k])
199+ rc_gones[source['item-name']] = RCGone(
200+ source_package_name=source['item-name'],
201+ bugs=bugs)
202+ in_proposed_by_autopkgtest_or_missing_binaries = set()
203+ print("checking autopkgtests")
204+ for source in ubuntu_excuses['sources']:
205+ item = source['item-name']
206+ if 'autopkgtest' in source['reason']:
207+ in_proposed_by_autopkgtest_or_missing_binaries.add(item)
208+ for package, results in sorted(source['policy_info']['autopkgtest'].items()):
209+ package = package.split('/')[0]
210+ if package not in rc_gones:
211+ continue
212+ for arch, result in sorted(results.items()):
213+ outcome, log, history, wtf1, wtf2 = result
214+ if outcome == "REGRESSION" and package != item:
215+ rc_gones[package].block_by_regression.add(item)
216+ break
217+ if 'missing-builds' in source:
218+ in_proposed_by_autopkgtest_or_missing_binaries.add(item)
219+ if item in rc_gones:
220+ if source['new-version'] != '-':
221+ rc_gones[item].suites.add(series+"-proposed")
222+ if source['old-version'] != '-':
223+ rc_gones[item].suites.add(series)
224+ print("checking uninstallability")
225+ for pkg in rc_gones:
226+ if pkg not in in_proposed_by_autopkgtest_or_missing_binaries:
227+ continue
228+ bin_pkgs = run_output("chdist", "grep-dctrl-packages", series, "-SwnsPackage", pkg, check=False)
229+ for bin_pkg in set(bin_pkgs.splitlines()):
230+ rc_gones[pkg].block_by_uninstallability.extend(uninstallable_to_src_pkg.get(bin_pkg, []))
231+ print("finding reverse-deps")
232+ packages = []
233+ for _, rc_gone in sorted(rc_gones.items()):
234+ if not rc_gone.block_by_regression and not rc_gone.block_by_uninstallability:
235+ continue
236+ rc_gone.rdeps_lines
237+ packages.append(rc_gone)
238+
239+ print("rendering")
240+ t = env.get_template('rcbuggy-problem-packages.html')
241+ with open(args.output, 'w', encoding='utf-8') as fp:
242+ fp.write(t.render(
243+ packages=packages,
244+ now=ubuntu_excuses["generated-date"].strftime("%Y.%m.%d %H:%M:%S")))
245+
246+
247+if __name__ == '__main__':
248+ main()
249
250=== modified file 'run-proposed-migration'
251--- run-proposed-migration 2019-04-20 00:23:18 +0000
252+++ run-proposed-migration 2019-09-09 01:50:04 +0000
253@@ -52,4 +52,10 @@
254 generate-team-p-m --excuses-yaml $SERIES/update_excuses.yaml \
255 --subscribers-json ~/public_html/package-team-mapping.json \
256 $SERIES/update_excuses_by_team.html $SERIES/update_excuses_by_team.yaml
257+ rm -f /tmp/debian-excuses.yaml
258+ wget -O /tmp/debian-excuses.yaml https://release.debian.org/britney/excuses.yaml
259+ find-rcbuggy-problem-packages --ubuntu-excuses $SERIES/update_excuses.yaml \
260+ --ubuntu-update_output $SERIES/update_output.txt \
261+ --debian-excuses /tmp/debian-excuses.yaml \
262+ --output $SERIES/rcbuggy-problem-packages.html
263 fi
264
265=== added file 'templates/rcbuggy-problem-packages.html'
266--- templates/rcbuggy-problem-packages.html 1970-01-01 00:00:00 +0000
267+++ templates/rcbuggy-problem-packages.html 2019-09-09 01:50:04 +0000
268@@ -0,0 +1,90 @@
269+<!DOCTYPE HTML>
270+<html>
271+ <head>
272+ <meta charset="utf-8">
273+ <title>rcbuggy problem packages</title>
274+ <style>
275+ body {
276+ font-family: ubuntu;
277+ background-color: #ffd;
278+ }
279+ .package {
280+ margin: 0.5em;
281+ padding: 0.5em;
282+ }
283+ .removal-ok {
284+ background-color: #dfd;
285+ }
286+ .removal-not-ok {
287+ background-color: #ddd;
288+ }
289+ h2 {
290+ margin: 0em;
291+ }
292+ .rdeps {
293+ margin-left: 1em;
294+ }
295+ .commands {
296+ white-space: pre-wrap;
297+ display: inline-block;
298+ margin: 0em;
299+ padding: 0.2em;
300+ border: solid;
301+ }
302+ .removal-ok .commands {
303+ border-color: #afa;
304+ background-color: #cfc;
305+ }
306+ .removal-not-ok .commands {
307+ border-color: #aaa;
308+ background-color: #ccc;
309+ }
310+ </style>
311+ </head>
312+ <body lang="en">
313+ <h1>rcbuggy problem packages</h1>
314+ <p>
315+ Generated: {{ now }}
316+ </p>
317+ {% for package in packages %}
318+ <div class="package {{ package.css_class }}">
319+ <h2 id="{{ package.source_package_name }}">{{ package.source_package_name }}</h2>
320+ <div class="details">
321+ <p>
322+ Kept out of Debian testing by Debian bug{% if package.bugs|length > 1 %}s{% endif %}
323+ {% for bug in package.bugs %}
324+ <a href="https://bugs.debian.org/{{bug}}">#{{bug}}</a>{% if not loop.last %},{% endif %}{% endfor %}.
325+ </p>
326+ {% if package.block_by_regression %}
327+ <p>
328+ The failures of this package's autopkgtests block migration of {{ package.block_by_regression|join(", ") }}.
329+ </p>
330+ {% endif %}
331+ {% if package.block_by_uninstallability %}
332+ <p>
333+ At least one binary from this package is made uninstallable by the versions of {{ package.block_by_uninstallability|join(", ") }} in -proposed.
334+ </p>
335+ {% endif %}
336+ {% if package.rdeps_lines %}
337+ <pre class="rdeps">
338+{{ package.rdeps_text_short }}
339+{% if package.rdeps_text_more -%}
340+<details><summary>more...</summary>
341+{{ package.rdeps_text_more }}
342+</details>
343+{% endif -%}
344+ </pre>
345+ {% endif %}
346+ <p>
347+ Suggested removal commands:
348+ </p>
349+ <pre class="commands">
350+{% for cmd in package.removal_commands -%}
351+{{ cmd }}
352+{% endfor -%}
353+ </pre>
354+ </div>
355+ </div>
356+ {% endfor %}
357+ </body>
358+</html>

Subscribers

People subscribed via source and target branches