Merge lp:~mwhudson/ubuntu-archive-scripts/rcbuggy-deployment-fun into lp:ubuntu-archive-scripts
- rcbuggy-deployment-fun
- Merge into trunk
Proposed by
Michael Hudson-Doyle
Status: | Merged |
---|---|
Merged at revision: | 245 |
Proposed branch: | lp:~mwhudson/ubuntu-archive-scripts/rcbuggy-deployment-fun |
Merge into: | lp:ubuntu-archive-scripts |
Diff against target: |
257 lines (+117/-45) 2 files modified
find-rcbuggy-problem-packages (+115/-43) templates/rcbuggy-problem-packages.html (+2/-2) |
To merge this branch: | bzr merge lp:~mwhudson/ubuntu-archive-scripts/rcbuggy-deployment-fun |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Package Archive Administrators | Pending | ||
Review via email: mp+372665@code.launchpad.net |
Commit message
Description of the change
This time for sure? Should use the chdists properly, does not depend on reverse-depends any more. The uninstallability testing might not work because there are no such cases currently...
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'find-rcbuggy-problem-packages' | |||
2 | --- find-rcbuggy-problem-packages 2019-09-11 01:33:41 +0000 | |||
3 | +++ find-rcbuggy-problem-packages 2019-09-12 03:12:18 +0000 | |||
4 | @@ -62,10 +62,13 @@ | |||
5 | 62 | cp = subprocess.run(cmd, **kw) | 62 | cp = subprocess.run(cmd, **kw) |
6 | 63 | return cp.stdout.decode(encoding).strip() | 63 | return cp.stdout.decode(encoding).strip() |
7 | 64 | 64 | ||
12 | 65 | def extract_uninstallable_to_src_pkg(output_fp): | 65 | all_arches = set() |
13 | 66 | # Extract a mapping from binary package name to the set of source | 66 | |
14 | 67 | # packages whose migration from proposed makes that package | 67 | def extract_bin_pkg_arch_to_blocked_src_pkgs(output_fp): |
15 | 68 | # uninstallable. | 68 | # Extract a mapping from binary package name / architectures to |
16 | 69 | # source package the migration of which would make that package | ||
17 | 70 | # uninstallable (and for convenience, return all architectures | ||
18 | 71 | # used as keys -- usually there will only be one or two). | ||
19 | 69 | 72 | ||
20 | 70 | # We're looking for sequences of lines like this: | 73 | # We're looking for sequences of lines like this: |
21 | 71 | 74 | ||
22 | @@ -76,11 +79,31 @@ | |||
23 | 76 | # (Britney tries to migrate batches of packages but it always | 79 | # (Britney tries to migrate batches of packages but it always |
24 | 77 | # tries each package on its own as well). | 80 | # tries each package on its own as well). |
25 | 78 | 81 | ||
27 | 79 | r = {} | 82 | # For each potential migration, britney checks architectures in |
28 | 83 | # sequence and stops when it finds one that regresses (or proceeds | ||
29 | 84 | # with the migration if it doesn't find one). This means that we | ||
30 | 85 | # can miss blocking packages here --- for example if migrating $src | ||
31 | 86 | # would make say $binpkg/amd64 uninstallable, but britney happens | ||
32 | 87 | # to check arm64 -- where $binpkg does not exist -- and there are | ||
33 | 88 | # regressions there, we will never find out about the problem | ||
34 | 89 | # $binpkg causes. This isn't really too bad because clearly in | ||
35 | 90 | # this case the migration of $src is blocked by other things that | ||
36 | 91 | # need to be resolved, but it does mean that packages might appear | ||
37 | 92 | # and disapper from the report depending on the order that britney | ||
38 | 93 | # checks architectures in (which is not consistent from run to | ||
39 | 94 | # run). C'est la vie. | ||
40 | 95 | |||
41 | 96 | bin_pkg_arch_to_blocked_src_pkgs = {} | ||
42 | 80 | srcpkg = None | 97 | srcpkg = None |
43 | 98 | arch_prefix = "Arch order is: " | ||
44 | 81 | for line in output_fp: | 99 | for line in output_fp: |
46 | 82 | parts = line.split() | 100 | line = line.strip() |
47 | 101 | if line.startswith(arch_prefix): | ||
48 | 102 | all_arches.update(line[len(arch_prefix):].split(', ')) | ||
49 | 103 | parts = line.split(None, 2) | ||
50 | 83 | if len(parts) >= 2: | 104 | if len(parts) >= 2: |
51 | 105 | if parts[0] in {"Trying", "trying"}: | ||
52 | 106 | srcpkg = None | ||
53 | 84 | if parts[0] == 'skipped:': | 107 | if parts[0] == 'skipped:': |
54 | 85 | srcpkg = None | 108 | srcpkg = None |
55 | 86 | # If parts[2] is '(' then this line is about trying to | 109 | # If parts[2] is '(' then this line is about trying to |
56 | @@ -88,13 +111,26 @@ | |||
57 | 88 | # looking for. | 111 | # looking for. |
58 | 89 | if parts[2].startswith('('): | 112 | if parts[2].startswith('('): |
59 | 90 | srcpkg = parts[1] | 113 | srcpkg = parts[1] |
61 | 91 | if srcpkg is not None and parts[0] == '*': | 114 | if srcpkg is not None and srcpkg[0] != '-' and parts[0] == '*': |
62 | 92 | # parts[1] is "${arch}:" | 115 | # parts[1] is "${arch}:" |
63 | 93 | # parts[2:] is a comma+space separated list of binary package names. | 116 | # parts[2:] is a comma+space separated list of binary package names. |
68 | 94 | for binpkg in parts[2:]: | 117 | arch = parts[1][:-1] |
69 | 95 | binpkg = binpkg.strip(',') | 118 | for binpkg in parts[2].split(', '): |
70 | 96 | r.setdefault(binpkg, set()).add(srcpkg) | 119 | bin_pkg_arch_to_blocked_src_pkgs.setdefault( |
71 | 97 | return r | 120 | (binpkg, arch), set()).add(srcpkg) |
72 | 121 | return bin_pkg_arch_to_blocked_src_pkgs | ||
73 | 122 | |||
74 | 123 | |||
75 | 124 | def chdist_grep_dctrl_packages(arch, *args): | ||
76 | 125 | return run_output( | ||
77 | 126 | "chdist", "grep-dctrl-packages", "{}-{}".format(series, arch), | ||
78 | 127 | "-nsPackage", *args, check=False).splitlines() | ||
79 | 128 | |||
80 | 129 | |||
81 | 130 | def chdist_grep_dctrl_sources(arch, *args): | ||
82 | 131 | return run_output( | ||
83 | 132 | "chdist", "grep-dctrl-sources", "{}-{}".format(series, arch), | ||
84 | 133 | "-nsPackage", *args, check=False).splitlines() | ||
85 | 98 | 134 | ||
86 | 99 | 135 | ||
87 | 100 | @attr.s | 136 | @attr.s |
88 | @@ -102,9 +138,66 @@ | |||
89 | 102 | source_package_name = attr.ib() | 138 | source_package_name = attr.ib() |
90 | 103 | bugs = attr.ib() | 139 | bugs = attr.ib() |
91 | 104 | block_by_regression = attr.ib(default=attr.Factory(set)) | 140 | block_by_regression = attr.ib(default=attr.Factory(set)) |
93 | 105 | block_by_uninstallability = attr.ib(default=attr.Factory(list)) | 141 | block_by_uninstallability = attr.ib(default=attr.Factory(set)) |
94 | 106 | suites = attr.ib(default=attr.Factory(set)) | 142 | suites = attr.ib(default=attr.Factory(set)) |
95 | 107 | _rdeps_lines = attr.ib(default=None) | 143 | _rdeps_lines = attr.ib(default=None) |
96 | 144 | _binpkgs = attr.ib(default=None) | ||
97 | 145 | |||
98 | 146 | def binary_pkgs(self): | ||
99 | 147 | if self._binpkgs is None: | ||
100 | 148 | self._binpkgs = set() | ||
101 | 149 | for arch in all_arches: | ||
102 | 150 | arch_binpkgs = chdist_grep_dctrl_packages( | ||
103 | 151 | arch, "-wS", self.source_package_name) | ||
104 | 152 | self._binpkgs.update({(binpkg, arch) for binpkg in arch_binpkgs}) | ||
105 | 153 | return self._binpkgs | ||
106 | 154 | |||
107 | 155 | def reverse_depends(self): | ||
108 | 156 | if self._rdeps_lines is None: | ||
109 | 157 | # These are maps rdep -> binpkg -> arches | ||
110 | 158 | reverse_recommends = {} | ||
111 | 159 | reverse_depends = {} | ||
112 | 160 | # This just maps rbdep -> binpkgs | ||
113 | 161 | reverse_build_depends = {} | ||
114 | 162 | for binpkg, arch in self.binary_pkgs(): | ||
115 | 163 | for rec in chdist_grep_dctrl_packages( | ||
116 | 164 | arch, "-wFRecommends", binpkg): | ||
117 | 165 | if (rec, arch) in self.binary_pkgs(): | ||
118 | 166 | continue | ||
119 | 167 | reverse_recommends.setdefault(rec, {}).setdefault(binpkg, set()).add(arch) | ||
120 | 168 | for dep in chdist_grep_dctrl_packages( | ||
121 | 169 | arch, "-wFDepends", binpkg): | ||
122 | 170 | if (dep, arch) in self.binary_pkgs(): | ||
123 | 171 | continue | ||
124 | 172 | reverse_depends.setdefault(dep, {}).setdefault(binpkg, set()).add(arch) | ||
125 | 173 | for bdeb in chdist_grep_dctrl_sources( | ||
126 | 174 | arch, "-wFBuild-Depends", binpkg, "--or", "-wFBuild-Depends-Indep", binpkg): | ||
127 | 175 | reverse_build_depends.setdefault(bdeb, set()).add(binpkg) | ||
128 | 176 | self._rdeps_lines = [] | ||
129 | 177 | if reverse_depends: | ||
130 | 178 | self._rdeps_lines.append("Reverse-Depends") | ||
131 | 179 | for rdep in sorted(reverse_depends): | ||
132 | 180 | for binpkg in sorted(reverse_depends[rdep]): | ||
133 | 181 | if reverse_depends[rdep][binpkg] == all_arches: | ||
134 | 182 | arches = "all architectures" | ||
135 | 183 | else: | ||
136 | 184 | arches = ", ".join(reverse_depends[rdep][binpkg]) | ||
137 | 185 | self._rdeps_lines.append(" ".join(["*", rdep, "for", binpkg, "on", arches])) | ||
138 | 186 | if reverse_recommends: | ||
139 | 187 | self._rdeps_lines.append("Reverse-Recommends") | ||
140 | 188 | for rdep in sorted(reverse_recommends): | ||
141 | 189 | for binpkg in sorted(reverse_recommends[rdep]): | ||
142 | 190 | if reverse_recommends[rdep][binpkg] == all_arches: | ||
143 | 191 | arches = "all architectures" | ||
144 | 192 | else: | ||
145 | 193 | arches = ", ".join(reverse_recommends[rdep][binpkg]) | ||
146 | 194 | self._rdeps_lines.append(" ".join(["*", rdep, "for", binpkg, "on", arches])) | ||
147 | 195 | if reverse_build_depends: | ||
148 | 196 | self._rdeps_lines.append("Reverse-Build-Depends") | ||
149 | 197 | for rdep in sorted(reverse_build_depends): | ||
150 | 198 | for binpkg in sorted(reverse_build_depends[rdep]): | ||
151 | 199 | self._rdeps_lines.append(" ".join(["*", rdep, "for", binpkg])) | ||
152 | 200 | return self._rdeps_lines | ||
153 | 108 | 201 | ||
154 | 109 | @property | 202 | @property |
155 | 110 | def comment(self): | 203 | def comment(self): |
156 | @@ -119,32 +212,12 @@ | |||
157 | 119 | return comment | 212 | return comment |
158 | 120 | 213 | ||
159 | 121 | @property | 214 | @property |
160 | 122 | def rdeps_lines(self): | ||
161 | 123 | if self._rdeps_lines is None: | ||
162 | 124 | rdeps = run_output("reverse-depends", 'src:' + self.source_package_name) | ||
163 | 125 | rbdeps = run_output("reverse-depends", '-b', 'src:' + self.source_package_name) | ||
164 | 126 | |||
165 | 127 | if rdeps == 'No reverse dependencies found': | ||
166 | 128 | rdeps = '' | ||
167 | 129 | if rbdeps == 'No reverse dependencies found': | ||
168 | 130 | rbdeps = '' | ||
169 | 131 | |||
170 | 132 | if rdeps or rbdeps: | ||
171 | 133 | if rdeps and rbdeps: | ||
172 | 134 | self._rdeps_lines = (rdeps + '\n' + rbdeps).splitlines() | ||
173 | 135 | else: | ||
174 | 136 | self._rdeps_lines = (rdeps + rbdeps).splitlines() | ||
175 | 137 | else: | ||
176 | 138 | self._rdeps_lines = [] | ||
177 | 139 | return self._rdeps_lines | ||
178 | 140 | |||
179 | 141 | @property | ||
180 | 142 | def rdeps_text_short(self): | 215 | def rdeps_text_short(self): |
182 | 143 | return "\n".join(self.rdeps_lines[:10]) | 216 | return "\n".join(self.reverse_depends()[:10]) |
183 | 144 | 217 | ||
184 | 145 | @property | 218 | @property |
185 | 146 | def rdeps_text_more(self): | 219 | def rdeps_text_more(self): |
187 | 147 | return "\n".join(self.rdeps_lines[10:]) | 220 | return "\n".join(self.reverse_depends()[10:]) |
188 | 148 | 221 | ||
189 | 149 | @property | 222 | @property |
190 | 150 | def removal_commands(self): | 223 | def removal_commands(self): |
191 | @@ -154,14 +227,14 @@ | |||
192 | 154 | cmds = [] | 227 | cmds = [] |
193 | 155 | for suite in suites: | 228 | for suite in suites: |
194 | 156 | cmd = " ".join(["remove-package", "-s", suite, "-m", shlex.quote(self.comment), self.source_package_name]) | 229 | cmd = " ".join(["remove-package", "-s", suite, "-m", shlex.quote(self.comment), self.source_package_name]) |
196 | 157 | if self.rdeps_lines: | 230 | if self.reverse_depends(): |
197 | 158 | cmd = '#' + cmd | 231 | cmd = '#' + cmd |
198 | 159 | cmds.append(cmd) | 232 | cmds.append(cmd) |
199 | 160 | return cmds | 233 | return cmds |
200 | 161 | 234 | ||
201 | 162 | @property | 235 | @property |
202 | 163 | def css_class(self): | 236 | def css_class(self): |
204 | 164 | if self.rdeps_lines: | 237 | if self.reverse_depends(): |
205 | 165 | return 'removal-not-ok' | 238 | return 'removal-not-ok' |
206 | 166 | else: | 239 | else: |
207 | 167 | return 'removal-ok' | 240 | return 'removal-ok' |
208 | @@ -178,7 +251,7 @@ | |||
209 | 178 | with open(args.ubuntu_excuses) as fp: | 251 | with open(args.ubuntu_excuses) as fp: |
210 | 179 | ubuntu_excuses = yaml.load(fp, Loader=yaml.CSafeLoader) | 252 | ubuntu_excuses = yaml.load(fp, Loader=yaml.CSafeLoader) |
211 | 180 | with open(args.ubuntu_update_output) as fp: | 253 | with open(args.ubuntu_update_output) as fp: |
213 | 181 | uninstallable_to_src_pkg = extract_uninstallable_to_src_pkg(fp) | 254 | bin_pkg_arch_to_blocked_src_pkgs = extract_bin_pkg_arch_to_blocked_src_pkgs(fp) |
214 | 182 | with open(args.debian_excuses) as fp: | 255 | with open(args.debian_excuses) as fp: |
215 | 183 | debian_excuses = yaml.load(fp, Loader=yaml.CSafeLoader) | 256 | debian_excuses = yaml.load(fp, Loader=yaml.CSafeLoader) |
216 | 184 | 257 | ||
217 | @@ -219,18 +292,17 @@ | |||
218 | 219 | if source['old-version'] != '-': | 292 | if source['old-version'] != '-': |
219 | 220 | rc_gones[item].suites.add(series) | 293 | rc_gones[item].suites.add(series) |
220 | 221 | print("checking uninstallability") | 294 | print("checking uninstallability") |
223 | 222 | for pkg in rc_gones: | 295 | for rc_gone in rc_gones.values(): |
224 | 223 | if pkg not in in_proposed_by_autopkgtest_or_missing_binaries: | 296 | if rc_gone.source_package_name not in in_proposed_by_autopkgtest_or_missing_binaries: |
225 | 224 | continue | 297 | continue |
229 | 225 | bin_pkgs = run_output("chdist", "grep-dctrl-packages", series, "-SwnsPackage", pkg, check=False) | 298 | for bin_pkg, arch in set(rc_gone.binary_pkgs()): |
230 | 226 | for bin_pkg in set(bin_pkgs.splitlines()): | 299 | rc_gone.block_by_uninstallability.update(bin_pkg_arch_to_blocked_src_pkgs.get((bin_pkg, arch), set())) |
228 | 227 | rc_gones[pkg].block_by_uninstallability.extend(uninstallable_to_src_pkg.get(bin_pkg, [])) | ||
231 | 228 | print("finding reverse-deps") | 300 | print("finding reverse-deps") |
232 | 229 | packages = [] | 301 | packages = [] |
233 | 230 | for _, rc_gone in sorted(rc_gones.items()): | 302 | for _, rc_gone in sorted(rc_gones.items()): |
234 | 231 | if not rc_gone.block_by_regression and not rc_gone.block_by_uninstallability: | 303 | if not rc_gone.block_by_regression and not rc_gone.block_by_uninstallability: |
235 | 232 | continue | 304 | continue |
237 | 233 | rc_gone.rdeps_lines | 305 | rc_gone.reverse_depends() |
238 | 234 | packages.append(rc_gone) | 306 | packages.append(rc_gone) |
239 | 235 | 307 | ||
240 | 236 | print("rendering") | 308 | print("rendering") |
241 | 237 | 309 | ||
242 | === modified file 'templates/rcbuggy-problem-packages.html' | |||
243 | --- templates/rcbuggy-problem-packages.html 2019-09-09 01:48:46 +0000 | |||
244 | +++ templates/rcbuggy-problem-packages.html 2019-09-12 03:12:18 +0000 | |||
245 | @@ -62,10 +62,10 @@ | |||
246 | 62 | {% endif %} | 62 | {% endif %} |
247 | 63 | {% if package.block_by_uninstallability %} | 63 | {% if package.block_by_uninstallability %} |
248 | 64 | <p> | 64 | <p> |
250 | 65 | At least one binary from this package is made uninstallable by the versions of {{ package.block_by_uninstallability|join(", ") }} in -proposed. | 65 | At least one binary from this package is made uninstallable by the versions of {{ package.block_by_uninstallability|sort|join(", ") }} in -proposed. |
251 | 66 | </p> | 66 | </p> |
252 | 67 | {% endif %} | 67 | {% endif %} |
254 | 68 | {% if package.rdeps_lines %} | 68 | {% if package._rdeps_lines %} |
255 | 69 | <pre class="rdeps"> | 69 | <pre class="rdeps"> |
256 | 70 | {{ package.rdeps_text_short }} | 70 | {{ package.rdeps_text_short }} |
257 | 71 | {% if package.rdeps_text_more -%} | 71 | {% if package.rdeps_text_more -%} |