From 4e616aa713ed74499aed5ddef2d4c6b5f69fd3f4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 7 Sep 2026 15:01:29 +0000 Subject: [PATCH] Fix pin-staleness job: stop update_toolchains.py crashing, bump stale pypa pins update_toolchains.py's arm-none-eabi/riscv-none-elf entries still grepped an ARG TOOLCHAIN_URL= line that [0087]/[0089] deleted from embedded_base.Dockerfile, so _pinned() raised SystemExit on the very first pin and crashed the whole checker -- every scheduled pin-staleness.yml run since has been reporting a hard failure, not real drift, with the other five pins never actually checked. Both crosses now read every verified version pinned for them out of resources/pinned_toolchains.toml and report whether the newest has fallen behind upstream, since that table (unlike the old Dockerfile ARG) genuinely holds more than one version at once. Also ran bin/update_docker.py to move the 34 pypa base image pins that had drifted to quay.io's 2026.09.05-1 build (record 0046: this reports, a human decides -- moving the pin in this reviewed diff is that decision), plus the 14 GHCR-mirrored unix cells in pinned_docker_images.toml that track them. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_013RZbGBbjecbLb7ywKhuUoW --- bin/update_toolchains.py | 94 +++++++++++++------ docs/records/0046-pin-staleness-checker.md | 37 ++++++++ ...cv-embedded-collapse-into-embedded-base.md | 10 ++ .../resources/pinned_docker_images.toml | 28 +++--- .../resources/pinned_pypa_images.toml | 68 +++++++------- 5 files changed, 159 insertions(+), 78 deletions(-) diff --git a/bin/update_toolchains.py b/bin/update_toolchains.py index 515a584..30e87ee 100755 --- a/bin/update_toolchains.py +++ b/bin/update_toolchains.py @@ -17,10 +17,15 @@ Three upstream shapes, which is why [0046] asked for one script per shape rather than one generic checker: -- **GitHub releases** (`arm-none-eabi`, `riscv-none-elf` -- both now - `embedded_base`, record 0096 --, `xtensa_esp`, `windows`) -- the pinned - tag is in the URL; compare against the repo's - own latest release. +- **GitHub releases.** For `xtensa_esp`/`windows` the pinned tag is in the + Dockerfile's own URL; compare against the repo's own latest release. + `arm-none-eabi`/`riscv-none-elf` (both `embedded_base` since record 0096) + are the same upstream shape but no longer a Dockerfile fact at all -- + [0087]/[0089] moved their real pins into `resources/pinned_toolchains.toml`'s + own `[cross]` tables, keyed by `(cross, version)` because more than one + verified version is pinned at once (per-row floor/ceiling windows, e.g. + `mimxrt`'s below-13 ceiling). This checker reads every version pinned for + that cross and reports whether the newest has fallen behind upstream. - **emsdk** (`webassembly`) -- pinned by *build hash*, which looks uncomparable and is not: `emscripten-core/emsdk` publishes `emscripten-releases-tags.json` mapping every release to its hash, so @@ -39,6 +44,7 @@ import json import re import sys +import tomllib import urllib.error import urllib.request from dataclasses import dataclass @@ -46,6 +52,8 @@ REPO = Path(__file__).resolve().parent.parent DOCKER = REPO / "docker" +RESOURCES = REPO / "src" / "cibuildmp" / "resources" +PINNED_TOOLCHAINS = RESOURCES / "pinned_toolchains.toml" GITHUB_LATEST = "https://api.github.com/repos/{repo}/releases/latest" EMSDK_TAGS = ( @@ -60,37 +68,41 @@ class Pin: dockerfile: str # How to find the pinned value in that file, group(1) being the value. pattern: str - kind: str # "github" | "emsdk" | "unversioned" - upstream: str = "" # owner/repo for "github" - - -# `arm-none-eabi`/`riscv-none-elf` below point at `embedded_base.Dockerfile` -# (record 0096 merged what used to be `arm_embedded.Dockerfile`/ -# `riscv_embedded.Dockerfile`) purely so `_pinned()` reads a file that -# exists -- **neither actually matches any more.** [0087]/[0089] already -# deleted the `ARG TOOLCHAIN_URL=` line this regex needs from both former -# files (the tarball is fetched at container run time now, per-row, not -# baked at image-build time), so `_pinned()` has raised -# `SystemExit(f"{pin.name}: no pin found...")` for both entries since -# [0087] landed -- a real, pre-existing gap this record does not close, -# only avoids widening into a harder `FileNotFoundError` by keeping the -# filename real. The actual fix (reading `resources/pinned_toolchains.toml`'s -# own per-cross pin instead of grepping a Dockerfile `ARG`) is [0090]'s own -# scope, not this Dockerfile-merge's. + kind: str # "github" | "cross-toml" | "emsdk" | "unversioned" + upstream: str = "" # owner/repo for "github"/"cross-toml" + cross: str = "" # `pinned_toolchains.toml` table key, for "cross-toml" + + +# `arm-none-eabi`/`riscv-none-elf` used to be grepped straight out of +# `embedded_base.Dockerfile`'s own `ARG TOOLCHAIN_URL=`. [0087]/[0089] +# deleted that line (the tarball is fetched at container run time now, per +# row, not baked at image-build time) and moved the real pins into +# `resources/pinned_toolchains.toml`'s own `[cross]` tables -- and unlike +# the Dockerfile's single shared `ARG`, that table genuinely holds more +# than one verified version per cross at once (e.g. `mimxrt`'s own +# below-13 ceiling, record 0088), because different `(tag, scope)` windows +# resolve to different versions. There is no longer one "the" pin to +# compare against upstream; `kind="cross-toml"` instead reads every +# version pinned for that cross and reports whether the *newest* of them +# has fallen behind upstream's own latest release -- the question this +# checker can still answer ("is it time to add a newer entry"), without +# claiming the older, intentionally-kept versions are drift. PINS = ( Pin( "arm-none-eabi", - "embedded_base.Dockerfile", - r"xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/download/v([^/]+)/", - "github", + "", + "", + "cross-toml", "xpack-dev-tools/arm-none-eabi-gcc-xpack", + "arm-none-eabi-", ), Pin( "riscv-none-elf", - "embedded_base.Dockerfile", - r"xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v([^/]+)/", - "github", + "", + "", + "cross-toml", "xpack-dev-tools/riscv-none-elf-gcc-xpack", + "riscv64-unknown-elf-", ), Pin( "xtensa-esp", @@ -135,6 +147,24 @@ def _pinned(pin: Pin) -> str: return match.group(1) +def _version_key(version: str) -> tuple[int, ...]: + return tuple(int(part) for part in re.findall(r"\d+", version)) + + +def _newest_cross_pin(pin: Pin) -> tuple[str, int]: + """(newest version string, how many versions are pinned) for `pin.cross` + in `pinned_toolchains.toml`.""" + with PINNED_TOOLCHAINS.open("rb") as handle: + data = tomllib.load(handle) + versions = list(data.get(pin.cross, {})) + if not versions: + raise SystemExit( + f"{pin.name}: no versions pinned for {pin.cross!r} in " + f"{PINNED_TOOLCHAINS.relative_to(REPO)}" + ) + return max(versions, key=_version_key), len(versions) + + def _latest_github(repo: str) -> str: data = json.loads(_get(GITHUB_LATEST.format(repo=repo))) return str(data["tag_name"]).lstrip("v") @@ -166,12 +196,16 @@ def _tarball_url(pin: Pin) -> str: def check(pin: Pin, *, slow: bool) -> int: """0 when current, 1 when behind. Prints one line either way.""" - pinned = _pinned(pin) + if pin.kind == "cross-toml": + pinned, count = _newest_cross_pin(pin) + else: + pinned = _pinned(pin) try: - if pin.kind == "github": + if pin.kind in ("github", "cross-toml"): latest = _latest_github(pin.upstream) stale = pinned.lstrip("v") != latest - arrow = f"{pinned} -> {latest}" if stale else pinned + suffix = f" (newest of {count} pinned)" if pin.kind == "cross-toml" else "" + arrow = f"{pinned} -> {latest}{suffix}" if stale else f"{pinned}{suffix}" elif pin.kind == "emsdk": version, latest_hash = _latest_emsdk() stale = pinned != latest_hash diff --git a/docs/records/0046-pin-staleness-checker.md b/docs/records/0046-pin-staleness-checker.md index 10d431b..9e5cd97 100644 --- a/docs/records/0046-pin-staleness-checker.md +++ b/docs/records/0046-pin-staleness-checker.md @@ -195,6 +195,39 @@ Still unbuilt from this record: nothing writes results anywhere but the job log ([0029]'s `stepsummary.py` is still unreused here), and the "are consumers' own `micropython` pins in scope" question stays open. +## Addendum, 2026-09-07 — `update_toolchains.py`'s own `arm-none-eabi`/`riscv-none-elf` +## rows fixed; the weekly job had been crashing, not merely reporting drift + +[0096]'s own text ("`bin/update_toolchains.py`'s `PINS`... a pre-existing gap... the real +fix... is [0090]'s own scope") turned out to point at the wrong record: [0090]'s item 1 +fixed `bin/refresh_toolchain_pins.py`'s per-row `gcc` check, a different script for a +different question (is a row's pinned compiler inside its own floor/ceiling window). It +never touched `update_toolchains.py`, so the gap [0096] described — `_pinned()` regexing +an `ARG TOOLCHAIN_URL=` line that [0087]/[0089] had already deleted from +`embedded_base.Dockerfile` for both crosses — was still live and unclosed. Found live +while investigating a failing `pin-staleness.yml` run (2026-09-07): the "toolchain +tarballs" step raised `SystemExit("arm-none-eabi: no pin found...")` and crashed the whole +checker before it ever reached `riscv-none-elf`/`xtensa-esp`/`llvm-mingw`/`emsdk`/ +`xtensa-lx106` — every scheduled run since [0087] landed had been reporting a hard +failure, not real staleness, with the other five pins never actually checked. + +Fixed by making `PINS` read those two crosses from `resources/pinned_toolchains.toml`'s +own `[cross]` tables (the same file [0087]/[0089] actually moved the real pins into) +instead of a Dockerfile `ARG` that no longer exists. That table is a genuinely different +shape from the rest of `PINS`: it holds more than one verified version per cross at once +(`arm-none-eabi-` alone carries three, kept for different rows' own floor/ceiling +windows — [0088]'s `mimxrt` ceiling among them), so there is no single "the" pin left to +compare against upstream the way a Dockerfile `ARG` was. `check()` now reads every version +pinned for a cross and reports whether the *newest* has fallen behind upstream's latest +release, which is the question this checker can still honestly answer without mislabelling +an intentionally-kept older version as drift. Verified live: the script now runs to +completion and reports each of the six pins (three came back `UNKNOWN` on an unauthenticated +GitHub API rate limit in this sandbox, exactly like the two `github`-kind pins that were +never in question — not a regression this fix introduced), instead of crashing on the first. + +Still open: `--slow`'s `xtensa-lx106` path, and everything this record's own "Still unbuilt" +paragraph above already named. + [0002]: 0002-delegate-compile-own-environment.md [0010]: 0010-pinned-data-in-resources.md [0013]: 0013-micropython-list-dedup-by-abi.md @@ -202,3 +235,7 @@ Still unbuilt from this record: nothing writes results anywhere but the job log [0033]: 0033-cibuildmp-never-builds-docker-image-itself.md [0044]: 0044-unix-native-images-landed.md [0068]: 0068-docker-dependabot-grouping-and-mipsel-ubuntu-26-04.md +[0087]: 0087-arm-riscv-embedded-thin-out-toolchain-version-lands.md +[0088]: 0088-mimxrt-own-floor.md +[0090]: 0090-toolchain-pins-checker-and-0058-text-followup.md +[0096]: 0096-arm-riscv-embedded-collapse-into-embedded-base.md diff --git a/docs/records/0096-arm-riscv-embedded-collapse-into-embedded-base.md b/docs/records/0096-arm-riscv-embedded-collapse-into-embedded-base.md index 83b3f97..989c892 100644 --- a/docs/records/0096-arm-riscv-embedded-collapse-into-embedded-base.md +++ b/docs/records/0096-arm-riscv-embedded-collapse-into-embedded-base.md @@ -154,3 +154,13 @@ digest that run's own "Record the pinned digest" step printed, replacing the int (`refresh_toolchain_pins.py`/`update_toolchains.py`'s pre-existing gap, and `plan_test_matrix.py`'s arithmetic-estimate weight) are unaffected by this addendum and stay open, as [0090]'s own scope and a future re-measurement respectively. + +## Addendum, 2026-09-07 — the "[0090]'s own scope" pointer above was wrong for half of it + +[0090] closed `refresh_toolchain_pins.py`'s half of the gap this record names above (its own +item 1), but never touched `bin/update_toolchains.py`'s `PINS` — a different script, checking +a different question, that this record's own text conflated with it. That half sat unfixed +and, worse, silently crashing (`SystemExit` on the very first pin) every week `pin-staleness.yml` +ran since [0087] landed, rather than merely reporting stale drift the way [0046] intends. Fixed +in [0046]'s own 2026-09-07 addendum, not here — noted in this record only because this record's +own forward pointer is what sent a later reader looking in the wrong place. diff --git a/src/cibuildmp/resources/pinned_docker_images.toml b/src/cibuildmp/resources/pinned_docker_images.toml index cbf9784..d054902 100644 --- a/src/cibuildmp/resources/pinned_docker_images.toml +++ b/src/cibuildmp/resources/pinned_docker_images.toml @@ -133,33 +133,33 @@ [image_group] # ── unix -- one native image per (arch, libc floor), identity-keyed ──── -manylinux_2_28_x86_64 = "quay.io/pypa/manylinux_2_28_x86_64@sha256:94c816d38ad56d2dc1df2f28007d18d306b0f5d05096c6f6b9141345df504d3e" -musllinux_1_2_x86_64 = "quay.io/pypa/musllinux_1_2_x86_64@sha256:8900a53ed236d85edd6868387b3e3327c45774156519ad492fe8b1c2a434d1dc" +manylinux_2_28_x86_64 = "quay.io/pypa/manylinux_2_28_x86_64@sha256:53390351aeb4688114b02c36a23b3e6ce1166ee9b7afc5df1a4f776354fc764c" +musllinux_1_2_x86_64 = "quay.io/pypa/musllinux_1_2_x86_64@sha256:621f8004ed526a5a6bf6a866fb415ad8da54d59a991e50b3b69167c3a768a616" -manylinux_2_28_i686 = "quay.io/pypa/manylinux_2_28_i686@sha256:bce1c1f15a59f9b4aa3e8a82aab777f0dc987213dcae66609d25651c277e86c5" -musllinux_1_2_i686 = "quay.io/pypa/musllinux_1_2_i686@sha256:e9029eacb01a207fc991eed8abccf55f653900b4b83068e37fb0ea201176a64a" +manylinux_2_28_i686 = "quay.io/pypa/manylinux_2_28_i686@sha256:48790e05c01457c8a8daa0185e2c5e09d9684c017f930b3f2aa6d8afebe6784b" +musllinux_1_2_i686 = "quay.io/pypa/musllinux_1_2_i686@sha256:7ff9262769ecadb9b889d2977f2e1fd47400a161664502ecb0eda9291d9abdfb" -manylinux_2_28_aarch64 = "quay.io/pypa/manylinux_2_28_aarch64@sha256:45ea412cfbd2cc5e51323cecab614ea8e4e5522b4d4f668946c54056b40152f6" -musllinux_1_2_aarch64 = "quay.io/pypa/musllinux_1_2_aarch64@sha256:ab0391c77648e2b15d37e3e5b8c3d43a0c4a1ca0fb3ab2f60d252be3474d2975" +manylinux_2_28_aarch64 = "quay.io/pypa/manylinux_2_28_aarch64@sha256:ad74e53b713f3b07d8c889c526dc0c6500da9827b45e38739570875fef52e28f" +musllinux_1_2_aarch64 = "quay.io/pypa/musllinux_1_2_aarch64@sha256:4dffcd49f0b6fc6928a49915f3cd939f973bbecbdfe96e1e7926b6049bc0bad5" -manylinux_2_28_ppc64le = "quay.io/pypa/manylinux_2_28_ppc64le@sha256:c30b4161aaab2ad55d56e12d52e2cae1e5f1d981194e7db4d9d57ac2516807b4" -musllinux_1_2_ppc64le = "quay.io/pypa/musllinux_1_2_ppc64le@sha256:e745f8e8e8c7c8e02e6379502d0b73f7cc4fd95283e011a78063292740f3cf42" +manylinux_2_28_ppc64le = "quay.io/pypa/manylinux_2_28_ppc64le@sha256:f1efe96ed791399d23ad3d63ba2c9b20d06139203d1f3affad4534f44d53ee0b" +musllinux_1_2_ppc64le = "quay.io/pypa/musllinux_1_2_ppc64le@sha256:fbc1983eb04e9bf311bd743863472b3ac9840edc3f2ca4e9dd4085d26f24fc84" -manylinux_2_28_s390x = "quay.io/pypa/manylinux_2_28_s390x@sha256:2ff3207ba86c8fef999eb2e1139a612501501493f3fd47bbc8a71199a29e9de4" -musllinux_1_2_s390x = "quay.io/pypa/musllinux_1_2_s390x@sha256:411d3d433dde4b3a9fce85122f7080110e2ec3543ca6c22578acb4a61c38cc14" +manylinux_2_28_s390x = "quay.io/pypa/manylinux_2_28_s390x@sha256:d93d3fe69923df58ec8ca45a6e0bb1af915d62888f95baef6d7596e5d002646f" +musllinux_1_2_s390x = "quay.io/pypa/musllinux_1_2_s390x@sha256:a7254c085d6c28ae74acb229af697133a8827535ac26b542b7be014272ba81d2" # armv7l's manylinux line starts at `_2_31` upstream (Ubuntu 20.04/apt, # unlike the AlmaLinux 8/dnf `_2_28` images) -- `_2_31` is the lower, # more compatible of the two floors pypa offers, and cibuildwheel makes # the same default choice. -manylinux_2_31_armv7l = "quay.io/pypa/manylinux_2_31_armv7l@sha256:bb0c6355c62cd0971f17a66969b1bce15222e67e4064667e25030923bb216bfe" -musllinux_1_2_armv7l = "quay.io/pypa/musllinux_1_2_armv7l@sha256:bb213c77861583faa326b24de1c413dab0e7c51af76f8a30f5abcaf749da5265" +manylinux_2_31_armv7l = "quay.io/pypa/manylinux_2_31_armv7l@sha256:3503ad3cef1b2a35ee05048eaebdb93ad1b4a6e9426af7f43dbe7658243ab0db" +musllinux_1_2_armv7l = "quay.io/pypa/musllinux_1_2_armv7l@sha256:7fa1e5ac26e79b9aa6b64aac4ca2479eadd8d00229f178eb5495edd538e9ff4b" # riscv64 is the newest arch upstream and carries the highest floor: # `manylinux_2_39` (Rocky Linux 10/dnf) is the only manylinux image pypa # publishes for it. -manylinux_2_39_riscv64 = "quay.io/pypa/manylinux_2_39_riscv64@sha256:4e74e5408c307666116196383a3bbf741b171fda049c776dac731e5b6087a1d5" -musllinux_1_2_riscv64 = "quay.io/pypa/musllinux_1_2_riscv64@sha256:3c2e2c26c10c2788046e54e816d8880011e4af9c4292cb735705a89870248082" +manylinux_2_39_riscv64 = "quay.io/pypa/manylinux_2_39_riscv64@sha256:000de3e1037325a6cc13615c1218061a56e1ece60ecdbe1a4572933a90654d18" +musllinux_1_2_riscv64 = "quay.io/pypa/musllinux_1_2_riscv64@sha256:f8bf1ede4138d95937a98362ce482453d717c62d22207f038619262b180d4ab8" # mipsel is cibuildmp's own architecture, not one of cibuildwheel's # seven, and the one documented exception to record 0043's model: pypa diff --git a/src/cibuildmp/resources/pinned_pypa_images.toml b/src/cibuildmp/resources/pinned_pypa_images.toml index 5499203..b6dc714 100644 --- a/src/cibuildmp/resources/pinned_pypa_images.toml +++ b/src/cibuildmp/resources/pinned_pypa_images.toml @@ -27,55 +27,55 @@ # comment, exactly as upstream writes them, so a bump is visibly a bump. [x86_64] -manylinux2014 = "quay.io/pypa/manylinux2014_x86_64@sha256:7efcfd38412c356969a8a3bb681facbed1e48f52a23afd44d76f74793bbb5a95" # 2026.09.01-1 -manylinux_2_28 = "quay.io/pypa/manylinux_2_28_x86_64@sha256:94c816d38ad56d2dc1df2f28007d18d306b0f5d05096c6f6b9141345df504d3e" # 2026.09.01-1 -manylinux_2_34 = "quay.io/pypa/manylinux_2_34_x86_64@sha256:070cdf2c38514f2c00eda5711a031d45560302d943091b2f7fb04d25525e8821" # 2026.09.01-1 -musllinux_1_2 = "quay.io/pypa/musllinux_1_2_x86_64@sha256:8900a53ed236d85edd6868387b3e3327c45774156519ad492fe8b1c2a434d1dc" # 2026.09.01-1 +manylinux2014 = "quay.io/pypa/manylinux2014_x86_64@sha256:493d2032114d757aaa761a9385ad8497f391503bf71acef9abeeb66682ca5d90" # 2026.09.05-1 +manylinux_2_28 = "quay.io/pypa/manylinux_2_28_x86_64@sha256:53390351aeb4688114b02c36a23b3e6ce1166ee9b7afc5df1a4f776354fc764c" # 2026.09.05-1 +manylinux_2_34 = "quay.io/pypa/manylinux_2_34_x86_64@sha256:934419fa742ff922855f464203cc0ad5afe3317a8541712a00cf3dc7fb893fc8" # 2026.09.05-1 +musllinux_1_2 = "quay.io/pypa/musllinux_1_2_x86_64@sha256:621f8004ed526a5a6bf6a866fb415ad8da54d59a991e50b3b69167c3a768a616" # 2026.09.05-1 [i686] -manylinux2014 = "quay.io/pypa/manylinux2014_i686@sha256:e19b8f58d2e621ac8d2184d1affe272ab4c409661b2d1b7f67d341fcdc74ed91" # 2026.09.01-1 -manylinux_2_28 = "quay.io/pypa/manylinux_2_28_i686@sha256:bce1c1f15a59f9b4aa3e8a82aab777f0dc987213dcae66609d25651c277e86c5" # 2026.09.01-1 -manylinux_2_34 = "quay.io/pypa/manylinux_2_34_i686@sha256:c838daebb0cdb28497ce5ea2c3ad840db5b6b8f4c30365e79b7eba1a162dcf91" # 2026.09.01-1 -musllinux_1_2 = "quay.io/pypa/musllinux_1_2_i686@sha256:e9029eacb01a207fc991eed8abccf55f653900b4b83068e37fb0ea201176a64a" # 2026.09.01-1 +manylinux2014 = "quay.io/pypa/manylinux2014_i686@sha256:f86cf7e5a6daca40ddbf857e0eeb335f39ad6f45703b61dfb00fb09a6b1cf80e" # 2026.09.05-1 +manylinux_2_28 = "quay.io/pypa/manylinux_2_28_i686@sha256:48790e05c01457c8a8daa0185e2c5e09d9684c017f930b3f2aa6d8afebe6784b" # 2026.09.05-1 +manylinux_2_34 = "quay.io/pypa/manylinux_2_34_i686@sha256:a3f3da7d656407dd0e4721be976331493516318db8a9c51674070696ab8b05a2" # 2026.09.05-1 +musllinux_1_2 = "quay.io/pypa/musllinux_1_2_i686@sha256:7ff9262769ecadb9b889d2977f2e1fd47400a161664502ecb0eda9291d9abdfb" # 2026.09.05-1 [aarch64] -manylinux2014 = "quay.io/pypa/manylinux2014_aarch64@sha256:050223dd42a540e4e23fc4b3103a170754e71ce6b3880416ee846e1facf0d957" # 2026.09.01-1 -manylinux_2_28 = "quay.io/pypa/manylinux_2_28_aarch64@sha256:45ea412cfbd2cc5e51323cecab614ea8e4e5522b4d4f668946c54056b40152f6" # 2026.09.01-1 -manylinux_2_34 = "quay.io/pypa/manylinux_2_34_aarch64@sha256:88dec51de7a784574e3359d14929fd57d28fdea9439394e56458a575c1b31149" # 2026.09.01-1 -musllinux_1_2 = "quay.io/pypa/musllinux_1_2_aarch64@sha256:ab0391c77648e2b15d37e3e5b8c3d43a0c4a1ca0fb3ab2f60d252be3474d2975" # 2026.09.01-1 +manylinux2014 = "quay.io/pypa/manylinux2014_aarch64@sha256:f4cd164263e4ec2b7da7ee40b319bb5e30f0d7a2abd7ad4730e716a512dfb529" # 2026.09.05-1 +manylinux_2_28 = "quay.io/pypa/manylinux_2_28_aarch64@sha256:ad74e53b713f3b07d8c889c526dc0c6500da9827b45e38739570875fef52e28f" # 2026.09.05-1 +manylinux_2_34 = "quay.io/pypa/manylinux_2_34_aarch64@sha256:effc0e17319a56b2c7eaff0cb5dd81a2d2c2850410841d3f017378f52ead6442" # 2026.09.05-1 +musllinux_1_2 = "quay.io/pypa/musllinux_1_2_aarch64@sha256:4dffcd49f0b6fc6928a49915f3cd939f973bbecbdfe96e1e7926b6049bc0bad5" # 2026.09.05-1 [ppc64le] -manylinux2014 = "quay.io/pypa/manylinux2014_ppc64le@sha256:932eebea660d04159dcb898558f63b11ce8ed053e900aa289b2a8e6f409cfc93" # 2026.09.01-1 -manylinux_2_28 = "quay.io/pypa/manylinux_2_28_ppc64le@sha256:c30b4161aaab2ad55d56e12d52e2cae1e5f1d981194e7db4d9d57ac2516807b4" # 2026.09.01-1 -manylinux_2_34 = "quay.io/pypa/manylinux_2_34_ppc64le@sha256:64c87825dc30a5078eadc860fc806fb747a760976ad5b95e16e82e4611e49165" # 2026.09.01-1 -musllinux_1_2 = "quay.io/pypa/musllinux_1_2_ppc64le@sha256:e745f8e8e8c7c8e02e6379502d0b73f7cc4fd95283e011a78063292740f3cf42" # 2026.09.01-1 +manylinux2014 = "quay.io/pypa/manylinux2014_ppc64le@sha256:1e6535d07dd0a943a4fc932243ddf1516739bc07c8adb5b42b6dfe0492492fad" # 2026.09.05-1 +manylinux_2_28 = "quay.io/pypa/manylinux_2_28_ppc64le@sha256:f1efe96ed791399d23ad3d63ba2c9b20d06139203d1f3affad4534f44d53ee0b" # 2026.09.05-1 +manylinux_2_34 = "quay.io/pypa/manylinux_2_34_ppc64le@sha256:172d9630f3d085a22da7b2ec405cb6966e4e818f4e2ab3eb3c4e582f7cac8c52" # 2026.09.05-1 +musllinux_1_2 = "quay.io/pypa/musllinux_1_2_ppc64le@sha256:fbc1983eb04e9bf311bd743863472b3ac9840edc3f2ca4e9dd4085d26f24fc84" # 2026.09.05-1 [s390x] -manylinux2014 = "quay.io/pypa/manylinux2014_s390x@sha256:f5b822943f7d114d6aaafd625a62ea6004b6218dd8cbf1d1b4598705612e8322" # 2026.09.01-1 -manylinux_2_28 = "quay.io/pypa/manylinux_2_28_s390x@sha256:2ff3207ba86c8fef999eb2e1139a612501501493f3fd47bbc8a71199a29e9de4" # 2026.09.01-1 -manylinux_2_34 = "quay.io/pypa/manylinux_2_34_s390x@sha256:9d8b321b0c650ea26bdd611620baa6161ab81ce7be3bbfb48e25f01c78e0b743" # 2026.09.01-1 -musllinux_1_2 = "quay.io/pypa/musllinux_1_2_s390x@sha256:411d3d433dde4b3a9fce85122f7080110e2ec3543ca6c22578acb4a61c38cc14" # 2026.09.01-1 +manylinux2014 = "quay.io/pypa/manylinux2014_s390x@sha256:328291eab4df4c724dee04444a2ce44653d6db76d3a10cd41cb75df83aee470c" # 2026.09.05-1 +manylinux_2_28 = "quay.io/pypa/manylinux_2_28_s390x@sha256:d93d3fe69923df58ec8ca45a6e0bb1af915d62888f95baef6d7596e5d002646f" # 2026.09.05-1 +manylinux_2_34 = "quay.io/pypa/manylinux_2_34_s390x@sha256:3efbd8ad5de732ef51fad2d31fe6e35cc9dc18cc12f30552a79df9640fe92899" # 2026.09.05-1 +musllinux_1_2 = "quay.io/pypa/musllinux_1_2_s390x@sha256:a7254c085d6c28ae74acb229af697133a8827535ac26b542b7be014272ba81d2" # 2026.09.05-1 [pypy_x86_64] -manylinux2014 = "quay.io/pypa/manylinux2014_x86_64@sha256:7efcfd38412c356969a8a3bb681facbed1e48f52a23afd44d76f74793bbb5a95" # 2026.09.01-1 -manylinux_2_28 = "quay.io/pypa/manylinux_2_28_x86_64@sha256:94c816d38ad56d2dc1df2f28007d18d306b0f5d05096c6f6b9141345df504d3e" # 2026.09.01-1 -manylinux_2_34 = "quay.io/pypa/manylinux_2_34_x86_64@sha256:070cdf2c38514f2c00eda5711a031d45560302d943091b2f7fb04d25525e8821" # 2026.09.01-1 +manylinux2014 = "quay.io/pypa/manylinux2014_x86_64@sha256:493d2032114d757aaa761a9385ad8497f391503bf71acef9abeeb66682ca5d90" # 2026.09.05-1 +manylinux_2_28 = "quay.io/pypa/manylinux_2_28_x86_64@sha256:53390351aeb4688114b02c36a23b3e6ce1166ee9b7afc5df1a4f776354fc764c" # 2026.09.05-1 +manylinux_2_34 = "quay.io/pypa/manylinux_2_34_x86_64@sha256:934419fa742ff922855f464203cc0ad5afe3317a8541712a00cf3dc7fb893fc8" # 2026.09.05-1 [pypy_i686] -manylinux2014 = "quay.io/pypa/manylinux2014_i686@sha256:e19b8f58d2e621ac8d2184d1affe272ab4c409661b2d1b7f67d341fcdc74ed91" # 2026.09.01-1 -manylinux_2_28 = "quay.io/pypa/manylinux_2_28_i686@sha256:bce1c1f15a59f9b4aa3e8a82aab777f0dc987213dcae66609d25651c277e86c5" # 2026.09.01-1 -manylinux_2_34 = "quay.io/pypa/manylinux_2_34_i686@sha256:c838daebb0cdb28497ce5ea2c3ad840db5b6b8f4c30365e79b7eba1a162dcf91" # 2026.09.01-1 +manylinux2014 = "quay.io/pypa/manylinux2014_i686@sha256:f86cf7e5a6daca40ddbf857e0eeb335f39ad6f45703b61dfb00fb09a6b1cf80e" # 2026.09.05-1 +manylinux_2_28 = "quay.io/pypa/manylinux_2_28_i686@sha256:48790e05c01457c8a8daa0185e2c5e09d9684c017f930b3f2aa6d8afebe6784b" # 2026.09.05-1 +manylinux_2_34 = "quay.io/pypa/manylinux_2_34_i686@sha256:a3f3da7d656407dd0e4721be976331493516318db8a9c51674070696ab8b05a2" # 2026.09.05-1 [pypy_aarch64] -manylinux2014 = "quay.io/pypa/manylinux2014_aarch64@sha256:050223dd42a540e4e23fc4b3103a170754e71ce6b3880416ee846e1facf0d957" # 2026.09.01-1 -manylinux_2_28 = "quay.io/pypa/manylinux_2_28_aarch64@sha256:45ea412cfbd2cc5e51323cecab614ea8e4e5522b4d4f668946c54056b40152f6" # 2026.09.01-1 -manylinux_2_34 = "quay.io/pypa/manylinux_2_34_aarch64@sha256:88dec51de7a784574e3359d14929fd57d28fdea9439394e56458a575c1b31149" # 2026.09.01-1 +manylinux2014 = "quay.io/pypa/manylinux2014_aarch64@sha256:f4cd164263e4ec2b7da7ee40b319bb5e30f0d7a2abd7ad4730e716a512dfb529" # 2026.09.05-1 +manylinux_2_28 = "quay.io/pypa/manylinux_2_28_aarch64@sha256:ad74e53b713f3b07d8c889c526dc0c6500da9827b45e38739570875fef52e28f" # 2026.09.05-1 +manylinux_2_34 = "quay.io/pypa/manylinux_2_34_aarch64@sha256:effc0e17319a56b2c7eaff0cb5dd81a2d2c2850410841d3f017378f52ead6442" # 2026.09.05-1 [armv7l] -manylinux_2_31 = "quay.io/pypa/manylinux_2_31_armv7l@sha256:bb0c6355c62cd0971f17a66969b1bce15222e67e4064667e25030923bb216bfe" # 2026.09.01-1 -manylinux_2_35 = "quay.io/pypa/manylinux_2_35_armv7l@sha256:cda0698d5ede519089c4ca33212e5b9439f9e418e055c3d9bcd236163173bfc0" # 2026.09.01-1 -musllinux_1_2 = "quay.io/pypa/musllinux_1_2_armv7l@sha256:bb213c77861583faa326b24de1c413dab0e7c51af76f8a30f5abcaf749da5265" # 2026.09.01-1 +manylinux_2_31 = "quay.io/pypa/manylinux_2_31_armv7l@sha256:3503ad3cef1b2a35ee05048eaebdb93ad1b4a6e9426af7f43dbe7658243ab0db" # 2026.09.05-1 +manylinux_2_35 = "quay.io/pypa/manylinux_2_35_armv7l@sha256:c1380d53d840da13bfc90717b5e98c1ac63e305a0daf73f436718df640958076" # 2026.09.05-1 +musllinux_1_2 = "quay.io/pypa/musllinux_1_2_armv7l@sha256:7fa1e5ac26e79b9aa6b64aac4ca2479eadd8d00229f178eb5495edd538e9ff4b" # 2026.09.05-1 [riscv64] -manylinux_2_39 = "quay.io/pypa/manylinux_2_39_riscv64@sha256:4e74e5408c307666116196383a3bbf741b171fda049c776dac731e5b6087a1d5" # 2026.09.01-1 -musllinux_1_2 = "quay.io/pypa/musllinux_1_2_riscv64@sha256:3c2e2c26c10c2788046e54e816d8880011e4af9c4292cb735705a89870248082" # 2026.09.01-1 +manylinux_2_39 = "quay.io/pypa/manylinux_2_39_riscv64@sha256:000de3e1037325a6cc13615c1218061a56e1ece60ecdbe1a4572933a90654d18" # 2026.09.05-1 +musllinux_1_2 = "quay.io/pypa/musllinux_1_2_riscv64@sha256:f8bf1ede4138d95937a98362ce482453d717c62d22207f038619262b180d4ab8" # 2026.09.05-1