Improve publish test script resilience (#10984)

This commit is contained in:
konsti 2025-01-27 20:20:33 +01:00 committed by GitHub
parent c88a4baaac
commit 3c6aee30fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -53,12 +53,13 @@ Docs: https://forgejo.org/docs/latest/user/packages/pypi/
import os import os
import re import re
import shutil import shutil
import sys
import time import time
from argparse import ArgumentParser from argparse import ArgumentParser
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from shutil import rmtree from shutil import rmtree
from subprocess import PIPE, check_call, check_output, run from subprocess import PIPE, check_call, run
from time import sleep from time import sleep
import httpx import httpx
@ -180,12 +181,12 @@ def get_latest_version(project_name: str, client: httpx.Client) -> Version:
break break
except httpx.HTTPError as err: except httpx.HTTPError as err:
error = err error = err
print(f"Error getting version, sleeping for 1s: {err}") print(f"Error getting version, sleeping for 1s: {err}", file=sys.stderr)
time.sleep(1) time.sleep(1)
except InvalidSdistFilename as err: except InvalidSdistFilename as err:
# Sometimes there's a link that says "status page" # Sometimes there's a link that says "status page"
error = err error = err
print(f"Invalid index page, sleeping for 1s: {err}") print(f"Invalid index page, sleeping for 1s: {err}", file=sys.stderr)
time.sleep(1) time.sleep(1)
else: else:
raise RuntimeError(f"Failed to fetch {url}") from error raise RuntimeError(f"Failed to fetch {url}") from error
@ -292,7 +293,7 @@ def wait_for_index(
just `get_filenames` fails non-deterministically. just `get_filenames` fails non-deterministically.
""" """
for _ in range(50): for _ in range(50):
output = check_output( result = run(
[ [
uv, uv,
"pip", "pip",
@ -310,16 +311,31 @@ def wait_for_index(
], ],
text=True, text=True,
input=f"{project_name}", input=f"{project_name}",
stdout=PIPE,
) )
if f"{project_name}=={version}" in output and output.count("--hash") == 2: # codeberg sometimes times out
if result.returncode != 0:
print(
f"uv pip compile not updated, missing 2 files for {version}, "
+ f"sleeping for 2s: `{index_url}`:\n",
file=sys.stderr,
)
sleep(2)
continue
if (
f"{project_name}=={version}" in result.stdout
and result.stdout.count("--hash") == 2
):
break break
print( print(
f"uv pip compile not updated, missing 2 files for {version}, " f"uv pip compile not updated, missing 2 files for {version}, "
+ f"sleeping for 2s: `{index_url}`:\n" + f"sleeping for 2s: `{index_url}`:\n"
+ "```\n" + "```\n"
+ output.replace("\\\n ", "") + result.stdout.replace("\\\n ", "")
+ "```" + "```",
file=sys.stderr,
) )
sleep(2) sleep(2)
@ -333,33 +349,58 @@ def publish_project(target: str, uv: Path, client: httpx.Client):
""" """
project_name = all_targets[target].project_name project_name = all_targets[target].project_name
print(f"\nPublish {project_name} for {target}") # If a version was recently uploaded by another run of this script,
# `get_latest_version` may get a cached version and uploading fails. In this case
# we wait and try again.
retries = 3
while True:
print(f"\nPublish {project_name} for {target}", file=sys.stderr)
# The distributions are build to the dist directory of the project. # The distributions are build to the dist directory of the project.
previous_version = get_latest_version(project_name, client) previous_version = get_latest_version(project_name, client)
version = get_new_version(previous_version) version = get_new_version(previous_version)
project_dir = build_project_at_version(target, version, uv) project_dir = build_project_at_version(target, version, uv)
# Upload configuration # Upload configuration
publish_url = all_targets[target].publish_url publish_url = all_targets[target].publish_url
index_url = all_targets[target].index_url index_url = all_targets[target].index_url
env, extra_args = target_configuration(target) env, extra_args = target_configuration(target)
env = {**os.environ, **env} env = {**os.environ, **env}
expected_filenames = [path.name for path in project_dir.joinpath("dist").iterdir()] expected_filenames = [
# Ignore the gitignore file in dist path.name for path in project_dir.joinpath("dist").iterdir()
expected_filenames.remove(".gitignore") ]
# Ignore our test file # Ignore the gitignore file in dist
expected_filenames.remove(".DS_Store") expected_filenames.remove(".gitignore")
# Ignore our test file
expected_filenames.remove(".DS_Store")
print( print(
f"\n=== 1. Publishing a new version: {project_name} {version} {publish_url} ===" f"\n=== 1. Publishing a new version: {project_name} {version} {publish_url} ===",
) file=sys.stderr,
args = [uv, "publish", "--publish-url", publish_url, *extra_args] )
check_call(args, cwd=project_dir, env=env)
args = [uv, "publish", "--publish-url", publish_url, *extra_args]
result = run(args, cwd=project_dir, env=env, stderr=PIPE)
if result.returncode == 0:
# Successful upload
break
retries -= 1
if retries > 0:
print(
f"Publish failed, retrying after 10s: {result.stderr}", file=sys.stderr
)
sleep(10)
else:
# Raise the error after three failures
result.check_returncode()
if publish_url == TEST_PYPI_PUBLISH_URL: if publish_url == TEST_PYPI_PUBLISH_URL:
# Confirm pypi behaviour: Uploading the same file again is fine. # Confirm pypi behaviour: Uploading the same file again is fine.
print(f"\n=== 2. Publishing {project_name} {version} again (PyPI) ===") print(
f"\n=== 2. Publishing {project_name} {version} again (PyPI) ===",
file=sys.stderr,
)
wait_for_index(index_url, project_name, version, uv) wait_for_index(index_url, project_name, version, uv)
args = [uv, "publish", "--publish-url", publish_url, *extra_args] args = [uv, "publish", "--publish-url", publish_url, *extra_args]
output = run( output = run(
@ -377,7 +418,10 @@ def publish_project(target: str, uv: Path, client: httpx.Client):
) )
mode = "index" if all_targets[target].index else "check URL" mode = "index" if all_targets[target].index else "check URL"
print(f"\n=== 3. Publishing {project_name} {version} again with {mode} ===") print(
f"\n=== 3. Publishing {project_name} {version} again with {mode} ===",
file=sys.stderr,
)
wait_for_index(index_url, project_name, version, uv) wait_for_index(index_url, project_name, version, uv)
# Test twine-style and index-style uploads for different packages. # Test twine-style and index-style uploads for different packages.
if index := all_targets[target].index: if index := all_targets[target].index:
@ -418,7 +462,8 @@ def publish_project(target: str, uv: Path, client: httpx.Client):
print( print(
f"\n=== 4. Publishing modified {project_name} {version} " f"\n=== 4. Publishing modified {project_name} {version} "
f"again with skip existing (error test) ===" f"again with skip existing (error test) ===",
file=sys.stderr,
) )
wait_for_index(index_url, project_name, version, uv) wait_for_index(index_url, project_name, version, uv)
args = [ args = [