ci: new regression test for the Python module

Signed-off-by: d9pouces <github@19pouces.net>
This commit is contained in:
d9pouces
2026-09-04 19:20:41 +02:00
parent 2c2b4fc340
commit a8435e9c96
16 changed files with 594 additions and 39 deletions
@@ -0,0 +1,80 @@
"""Build a minimal, offline, hand-rolled wheel and PEP 503 "simple" index for
integration-testing the `python` module of id-configurator.
No network access and no build backend (setuptools/build/pip) are used: the
wheel is a plain zip file assembled with the standard library only, so this
script can run in a minimal Python image with nothing installed.
"""
from __future__ import annotations
import base64
import hashlib
import pathlib
import zipfile
PACKAGE = "idconfiguratortestpkg"
VERSION = "0.0.1"
WHEEL_NAME = f"{PACKAGE}-{VERSION}-py3-none-any.whl"
OUT_DIR = pathlib.Path("/srv")
METADATA = f"""Metadata-Version: 2.1
Name: {PACKAGE}
Version: {VERSION}
Summary: Test-only package served by the id-configurator integration tests
"""
WHEEL_FILE = """Wheel-Version: 1.0
Generator: id-configurator-tests
Root-Is-Purelib: true
Tag: py3-none-any
"""
INIT_PY = '"""Test-only package used to verify the python module mirror."""\n'
def _record_line(path: str, content: bytes) -> str:
digest = base64.urlsafe_b64encode(hashlib.sha256(content).digest()).rstrip(b"=")
return f"{path},sha256={digest.decode('ascii')},{len(content)}"
def build_wheel(packages_dir: pathlib.Path) -> bytes:
dist_info = f"{PACKAGE}-{VERSION}.dist-info"
files = {
f"{PACKAGE}/__init__.py": INIT_PY.encode("utf-8"),
f"{dist_info}/METADATA": METADATA.encode("utf-8"),
f"{dist_info}/WHEEL": WHEEL_FILE.encode("utf-8"),
}
record_lines = [_record_line(name, content) for name, content in files.items()]
record_lines.append(f"{dist_info}/RECORD,,")
files[f"{dist_info}/RECORD"] = ("\n".join(record_lines) + "\n").encode("utf-8")
packages_dir.mkdir(parents=True, exist_ok=True)
wheel_path = packages_dir / WHEEL_NAME
with zipfile.ZipFile(wheel_path, "w", zipfile.ZIP_DEFLATED) as archive:
for name, content in files.items():
archive.writestr(name, content)
return wheel_path.read_bytes()
def write_simple_index(simple_dir: pathlib.Path, wheel_content: bytes) -> None:
package_dir = simple_dir / PACKAGE
package_dir.mkdir(parents=True, exist_ok=True)
digest = hashlib.sha256(wheel_content).hexdigest()
html = f"""<!DOCTYPE html>
<html>
<body>
<a href="../../packages/{WHEEL_NAME}#sha256={digest}">{WHEEL_NAME}</a>
</body>
</html>
"""
(package_dir / "index.html").write_text(html, encoding="utf-8")
def main() -> None:
wheel_content = build_wheel(OUT_DIR / "packages")
write_simple_index(OUT_DIR / "simple", wheel_content)
if __name__ == "__main__":
main()
@@ -0,0 +1,16 @@
# Minimal PEP 503 "simple" package index, serving one hand-built test wheel
# over TLS, used by tox-docker to exercise the `python` module integration
# test (tests/configs/Python.yaml.tmpl, tests/dockerfiles/Python.Dockerfile).
#
# Built by tox-docker, whose build context is this directory (not the repo
# root) - every COPY source below must live under tests/dockerfiles/repos/.
FROM python:3.13-slim AS wheelbuild
COPY build_test_wheel.py /build_test_wheel.py
RUN python3 /build_test_wheel.py
FROM nginx:alpine
COPY --from=wheelbuild /srv /srv
COPY repo-server.crt.pem /etc/nginx/certs/repo-server.crt.pem
COPY repo-server.key.pem /etc/nginx/certs/repo-server.key.pem
COPY pypi-repo.nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 8443
@@ -0,0 +1,10 @@
server {
listen 8443 ssl default_server;
server_name _;
ssl_certificate /etc/nginx/certs/repo-server.crt.pem;
ssl_certificate_key /etc/nginx/certs/repo-server.key.pem;
root /srv;
autoindex off;
}
@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDqTCCApGgAwIBAgIUUuJBYcNFFtJcKH2PDPTA+QZMP3QwDQYJKoZIhvcNAQEL
BQAwQjEeMBwGA1UECgwVaWQtY29uZmlndXJhdG9yLXRlc3RzMSAwHgYDVQQDDBdp
ZC1jb25maWd1cmF0b3IgVGVzdCBDQTAeFw0yNjA5MDMyMTM5NDNaFw00NjA4Mjky
MTM5NDNaMDYxHjAcBgNVBAoMFWlkLWNvbmZpZ3VyYXRvci10ZXN0czEUMBIGA1UE
AwwLcmVwby1zZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCu
xCtsiQxqntOBY+LlKxgXs5N0WSKJY5jeof0SzIJWNr7W8GBMbh9fqnXms4Vnimhs
AYa6RsTmfGpSzLO73+NlzpuApV9kTBXZmeHVqrJ6S/mFKBZDN74uFiDWl93K1Z/G
VccntAdbCsNCMRt71CZ7DD0vFfQSN4qQ3fPDT9EpV8Xv6Pj/MFJvEMp+pu4f7mwh
xJQBtlC72okHvcMp6MsbfDFYRTUWFtsDpCct7pps1sRVyalLvxIaFugzpuiTU2+Q
0FdlBi5IRhCZjrOlW4kGAyjiP+q/SwZ8j1YJnjifoxLrcn+26ONCNfv/JKsnl8Yr
hKGwbrlzEgeoekE8TH9xAgMBAAGjgaIwgZ8wCQYDVR0TBAIwADALBgNVHQ8EBAMC
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwMAYDVR0RBCkwJ4IUaG9zdC5kb2NrZXIu
aW50ZXJuYWyCCWxvY2FsaG9zdIcEfwAAATAdBgNVHQ4EFgQUmPGECu+/yUR3pzKk
RSDMtRQvFEAwHwYDVR0jBBgwFoAUm8yew7GscAnqm3zlxid8fru246MwDQYJKoZI
hvcNAQELBQADggEBAPZ3xMe6vECPfrWGEru2eSL75JCUl+zLZm40EkCLidiBTXch
dvuJYHEjp6oSnTdgPOPlyjgFY/jMkHuRvl46Zwr74eFBZyYqaYxm50Drc4/09/VF
bo59/fRbO6UCk/yvTw83q5URL2d9zw79dnWJaOfCHlm9laY2DvD5cPZbYNZ1dlEg
nwNiRmw2/twIjuvMBGG+Ooy1V2rOOzR+ytcyf7Ekpotc4DpS/NLC0cPL/gjDoJMc
Ot/YZo55Myrrk8IN681ou5Kv+vAUURxCcMaJaFMNfM4HBPW2dwrOp5tplGw0C/pX
Kqub6yjjUmzPCIRiWUsW+4mg3UxaL41Q9013Mlw=
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCuxCtsiQxqntOB
Y+LlKxgXs5N0WSKJY5jeof0SzIJWNr7W8GBMbh9fqnXms4VnimhsAYa6RsTmfGpS
zLO73+NlzpuApV9kTBXZmeHVqrJ6S/mFKBZDN74uFiDWl93K1Z/GVccntAdbCsNC
MRt71CZ7DD0vFfQSN4qQ3fPDT9EpV8Xv6Pj/MFJvEMp+pu4f7mwhxJQBtlC72okH
vcMp6MsbfDFYRTUWFtsDpCct7pps1sRVyalLvxIaFugzpuiTU2+Q0FdlBi5IRhCZ
jrOlW4kGAyjiP+q/SwZ8j1YJnjifoxLrcn+26ONCNfv/JKsnl8YrhKGwbrlzEgeo
ekE8TH9xAgMBAAECggEADfew5MUoEEloHzo4qX1bn2FQLDqHfEvqegVssLKJl1+O
ukAoSIawmTiKOola5LiQhrlK53S9sRIWfIGbVJOcz/rGpAetHqeTqeCwYB96980+
QJPzzr5HKjwrtoFMTgHrk0vKiMDM3OWr6PL8Me+PSvXic4xUnzQZlGE4zAGDQ10V
xCs0ZrCYGVbMxzOWSBZivd5Tbj7OeCuLEPRqUX5D824bloaudLtO+0pEe8oaFEdt
cgwMPUGdz9+XPQlaYPOtcTbk0fTcrUYzyjcYR1CwI48xj6jXnLvFAdTjZnjSgmSr
lcxOJNxpRgj8MYvHjHQekyxxBUF3HsaWfbTscWzghQKBgQDyVMgirF7bykCe+E/q
HZSio3xdX8TTVyJvxBZeG+sAHFvc0Z0ihA1CrIZIZOYK6c+mF1CHVQ5jPEc0GKwC
mzXjqO3kLEUumbrBGXtmPQd7wJ/jVTv9u4JsfqCzMVj3xNTEbpmp5FvZ2JofEb5f
7mFWKl1L1JZ58M5B1XUgYSqZ/wKBgQC4n8NZgq1k1jsxGzTggJzDvi230lkIJG1+
Tf3wm8AoqCoGtG4O53a6fpsNKlNgNgivk5cUUACj2HRXC44tz5/MydRLRzePLG8M
MCjrmPEHCD9zKKn9z0DMjHVL2TAy3aRD1Flo8HcERMD8xyklX2kPtJqkiKUJm2Gm
5qrRlRuGjwKBgHYCeZABFxS7sqp3zBr7msV1Dfw+xhART9DT0fy0ZcWhCc9IONh1
LCv67k3U7P4FjLyaf/nXnvEOirJBDYsbYKvkOS0zreEatgqRSTmpoLpQPMytVlCi
/smtBL8e2e+W5/Jc1fIZJe25gJx+vJHk+XvHl7V06m6pqRgARlClYOSrAoGAZ4uS
8FzOCsPsJrpviYoYYeADnBkpPdvQXQo45f00ZJkMGP6Qy2Mdv7LKGzQl1sv3SyGn
lgjYkwwPE9KzRxYvNZfsrNugWXg+P8Oy74+zfpIto9SmombenNoZ3xRMZ/rO6gvq
qWRUG9zDsAGzHfbI4boVETph9WVj+0tbenf6HBkCgYBWM8J+M+UoHmKZyarZFKum
uuVHFM4WzdBVM22NyFFrkYmfCD6tnNb2ZfcwabX2NY+d2AJLA9aNdVlgQHb/SJaC
orwNrOuW0wLjGEivXZieGTRHwrcS7IMtG78nm/fMuEMjEwTQkW61bGmSmWxCCnGe
2uuQwlL68XytRGkpgPKK6g==
-----END PRIVATE KEY-----