|
10 | 10 | from typing_extensions import Literal |
11 | 11 |
|
12 | 12 | from bs4 import BeautifulSoup, Tag # type: ignore |
13 | | -from pydantic import BaseModel |
| 13 | +from pydantic import BaseModel, RootModel |
14 | 14 | from requests_gssapi import HTTPSPNEGOAuth |
15 | 15 |
|
16 | 16 | from .constants import DATETIME_MIN_UTC |
@@ -157,6 +157,55 @@ def get_erratum_for_link(link: str, full: bool = False) -> Erratum | FullErratum |
157 | 157 | return get_erratum(erratum_id, full=full) |
158 | 158 |
|
159 | 159 |
|
| 160 | +class ErratumBuilds(RootModel): |
| 161 | + # Map variant and architecture to a set of subpackage names |
| 162 | + # we ship for that architecture |
| 163 | + # { |
| 164 | + # "AppStream": { |
| 165 | + # "SRPMS": {"libtiff"} |
| 166 | + # "aarch64": {"libtiff", "libtiff-devel", ...} |
| 167 | + # } |
| 168 | + # } |
| 169 | + root: dict[str, dict[str, set[str]]] |
| 170 | + |
| 171 | + |
| 172 | +def variant_to_base_variant(variant: str) -> str: |
| 173 | + return variant.split("-")[0] |
| 174 | + |
| 175 | + |
| 176 | +def nvr_to_package_name(nvr: str) -> str: |
| 177 | + return nvr.rsplit("-", 2)[0] |
| 178 | + |
| 179 | + |
| 180 | +def get_erratum_builds(id: int | str) -> ErratumBuilds: |
| 181 | + data = ET_api_get(f"erratum/{id}/builds_list") |
| 182 | + |
| 183 | + if len(data) != 1: |
| 184 | + raise ValueError("Expected JSON object to have a single product version key.") |
| 185 | + detail = next(iter(data.values())) |
| 186 | + |
| 187 | + builds = detail.get("builds", []) |
| 188 | + if len(builds) != 1: |
| 189 | + raise ValueError("Expected a single build in the 'builds' list.") |
| 190 | + |
| 191 | + build = builds[0] |
| 192 | + if len(build) != 1: |
| 193 | + raise ValueError("Expected build to have a single NVR key.") |
| 194 | + build_detail = next(iter(build.values())) |
| 195 | + |
| 196 | + variant_arch = build_detail["variant_arch"] |
| 197 | + |
| 198 | + file_list_map = { |
| 199 | + variant_to_base_variant(variant): { |
| 200 | + arch: set([nvr_to_package_name(rpm["filename"]) for rpm in rmps]) |
| 201 | + for arch, rmps in arches.items() |
| 202 | + } |
| 203 | + for variant, arches in variant_arch.items() |
| 204 | + } |
| 205 | + |
| 206 | + return ErratumBuilds(root=file_list_map) |
| 207 | + |
| 208 | + |
160 | 209 | class RHELVersion(BaseModel): |
161 | 210 | major: int |
162 | 211 | minor: int |
@@ -381,6 +430,19 @@ def get_erratum_build_nvr(erratum_id: str | int, package_name: str) -> str | Non |
381 | 430 | return None |
382 | 431 |
|
383 | 432 |
|
| 433 | +def errata_have_same_file_lists(errata_id1: int | str, errata_id2: int | str): |
| 434 | + """Check if the given errata have the same file lists |
| 435 | +
|
| 436 | + After stripping package versions and RHEL release versions, |
| 437 | + do the two errata ship the same subpackages for each |
| 438 | + variant and architecture? |
| 439 | +
|
| 440 | + Throws an exception if either errata has more than one build |
| 441 | + attached to it. |
| 442 | + """ |
| 443 | + return get_erratum_builds(errata_id1) == get_erratum_builds(errata_id2) |
| 444 | + |
| 445 | + |
384 | 446 | class RuleParseError(Exception): |
385 | 447 | pass |
386 | 448 |
|
|
0 commit comments