Make CI pip indexes explicit with safe diagnostics and sequential fallback
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
"""Install CI tooling with explicit, sequential indexes and secret-safe diagnostics."""
|
||||
|
||||
import ast
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
PRIMARY = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
|
||||
FALLBACK = "https://pypi.org/simple"
|
||||
URL = re.compile(r"https?://[^\s\"'<>]+")
|
||||
|
||||
|
||||
def host(url):
|
||||
return urlsplit(url).hostname or "invalid"
|
||||
|
||||
|
||||
def safe_output(text):
|
||||
# Pip/proxy exceptions may contain credentials or signed query strings.
|
||||
return URL.sub(lambda m: "https://" + host(m.group()) + "/[redacted-url]", text)
|
||||
|
||||
|
||||
def install_env(index):
|
||||
env = os.environ.copy()
|
||||
for key in (
|
||||
"PIP_EXTRA_INDEX_URL",
|
||||
"PIP_NO_INDEX",
|
||||
"PIP_FIND_LINKS",
|
||||
"PIP_TRUSTED_HOST",
|
||||
"PIP_NO_BUILD_ISOLATION",
|
||||
):
|
||||
env.pop(key, None)
|
||||
env["PIP_CONFIG_FILE"] = os.devnull
|
||||
env["PIP_INDEX_URL"] = index
|
||||
return env
|
||||
|
||||
|
||||
def diagnostics():
|
||||
for key in ("PIP_INDEX_URL", "PIP_EXTRA_INDEX_URL", "HTTPS_PROXY", "HTTP_PROXY"):
|
||||
values = os.getenv(key, "").split()
|
||||
print(
|
||||
f"Inherited {key} hosts: {','.join(host(v) for v in values) or 'unset'}",
|
||||
flush=True,
|
||||
)
|
||||
print("Inherited PIP_NO_INDEX set:", bool(os.getenv("PIP_NO_INDEX")), flush=True)
|
||||
result = subprocess.run(
|
||||
[sys.executable, "-m", "pip", "config", "list"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
for line in result.stdout.splitlines():
|
||||
key, separator, value = line.partition("=")
|
||||
if separator and key.endswith(("index-url", "extra-index-url")):
|
||||
try:
|
||||
value = ast.literal_eval(value)
|
||||
except (ValueError, SyntaxError):
|
||||
value = ""
|
||||
print(
|
||||
"Inherited pip config",
|
||||
key,
|
||||
"hosts:",
|
||||
",".join(host(v) for v in str(value).split()),
|
||||
flush=True,
|
||||
)
|
||||
print(
|
||||
"Effective CI pip config files: disabled; extra indexes/no-index disabled; build isolation retained",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
diagnostics()
|
||||
indexes = list(
|
||||
dict.fromkeys(
|
||||
[
|
||||
os.getenv("CI_PIP_INDEX_URL") or PRIMARY,
|
||||
os.getenv("CI_PIP_FALLBACK_INDEX_URL") or FALLBACK,
|
||||
]
|
||||
)
|
||||
)
|
||||
for index in indexes:
|
||||
parsed = urlsplit(index)
|
||||
if parsed.scheme != "https" or not parsed.hostname:
|
||||
raise SystemExit("CI pip indexes must be valid HTTPS URLs")
|
||||
for index in indexes:
|
||||
print("Effective CI Job pip index host:", host(index), flush=True)
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"--disable-pip-version-check",
|
||||
"--no-input",
|
||||
"--retries",
|
||||
"2",
|
||||
"--timeout",
|
||||
"30",
|
||||
"--index-url",
|
||||
index,
|
||||
"-e",
|
||||
".[test]",
|
||||
],
|
||||
env=install_env(index),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
)
|
||||
print(safe_output(result.stdout), flush=True)
|
||||
if result.returncode == 0:
|
||||
print(
|
||||
"CI dependency installation succeeded; index host:",
|
||||
host(index),
|
||||
flush=True,
|
||||
)
|
||||
return
|
||||
print(
|
||||
"CI install failed at host",
|
||||
host(index),
|
||||
"exit",
|
||||
result.returncode,
|
||||
"; trying next configured index if available",
|
||||
flush=True,
|
||||
)
|
||||
raise SystemExit("CI dependency installation failed on all configured indexes")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user