Reuse packages in test publish script (#8257)

This commit is contained in:
konsti 2024-10-17 11:44:19 +02:00 committed by GitHub
parent d930367f8c
commit e0a2bc92cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 83 additions and 49 deletions

View File

@ -1061,6 +1061,7 @@ jobs:
UV_TEST_PUBLISH_PASSWORD: ${{ secrets.UV_TEST_PUBLISH_PASSWORD }} UV_TEST_PUBLISH_PASSWORD: ${{ secrets.UV_TEST_PUBLISH_PASSWORD }}
UV_TEST_PUBLISH_GITLAB_PAT: ${{ secrets.UV_TEST_PUBLISH_GITLAB_PAT }} UV_TEST_PUBLISH_GITLAB_PAT: ${{ secrets.UV_TEST_PUBLISH_GITLAB_PAT }}
UV_TEST_PUBLISH_CODEBERG_TOKEN: ${{ secrets.UV_TEST_PUBLISH_CODEBERG_TOKEN }} UV_TEST_PUBLISH_CODEBERG_TOKEN: ${{ secrets.UV_TEST_PUBLISH_CODEBERG_TOKEN }}
UV_TEST_PUBLISH_CLOUDSMITH_TOKEN: ${{ secrets.UV_TEST_PUBLISH_CLOUDSMITH_TOKEN }}
cache-test-ubuntu: cache-test-ubuntu:
timeout-minutes: 10 timeout-minutes: 10

View File

@ -7,20 +7,25 @@
# /// # ///
""" """
Test `uv publish` by uploading a new version of astral-test-<test case> to testpypi, Test `uv publish` by uploading a new version of astral-test-<test case> to one of
authenticating by one of various options. multiple indexes, exercising different options of passing credentials.
Locally, execute the credentials setting script, then run:
```shell
uv run scripts/publish/test_publish.py local
```
# Setup # Setup
**astral-test-token** **pypi-token**
Set the `UV_TEST_PUBLISH_TOKEN` environment variables. Set the `UV_TEST_PUBLISH_TOKEN` environment variables.
**astral-test-password** **pypi-password-env**
Set the `UV_TEST_PUBLISH_PASSWORD` environment variable. Set the `UV_TEST_PUBLISH_PASSWORD` environment variable.
This project also uses token authentication since it's the only thing that PyPI This project also uses token authentication since it's the only thing that PyPI
supports, but they both CLI options. supports, but they both CLI options.
**astral-test-keyring** **pypi-keyring**
```console ```console
uv pip install keyring uv pip install keyring
keyring set https://test.pypi.org/legacy/?astral-test-keyring __token__ keyring set https://test.pypi.org/legacy/?astral-test-keyring __token__
@ -29,19 +34,19 @@ The query parameter a horrible hack stolen from
https://github.com/pypa/twine/issues/565#issue-555219267 https://github.com/pypa/twine/issues/565#issue-555219267
to prevent the other projects from implicitly using the same credentials. to prevent the other projects from implicitly using the same credentials.
**astral-test-gitlab-pat** **pypi-trusted-publishing**
This one only works in GitHub Actions on astral-sh/uv in `ci.yml` - sorry!
**gitlab**
The username is astral-test-user, the password is a token. The username is astral-test-user, the password is a token.
Web: https://gitlab.com/astral-test-publish/astral-test-gitlab-pat/-/packages Web: https://gitlab.com/astral-test-publish/astral-test-token/-/packages
Docs: https://docs.gitlab.com/ee/user/packages/pypi_repository/ Docs: https://docs.gitlab.com/ee/user/packages/pypi_repository/
**astral-test-codeberg** **codeberg**
The username is astral-test-user, the password is a token (the actual account password would also The username is astral-test-user, the password is a token (the actual account password would also
work). work).
Web: https://codeberg.org/astral-test-user/-/packages/pypi/astral-test-codeberg/0.1.0 Web: https://codeberg.org/astral-test-user/-/packages/pypi/astral-test-token/0.1.0
Docs: https://forgejo.org/docs/latest/user/packages/pypi/ Docs: https://forgejo.org/docs/latest/user/packages/pypi/
**astral-test-trusted-publishing**
This one only works in GitHub Actions on astral-sh/uv in `ci.yml` - sorry!
""" """
import os import os
@ -56,26 +61,47 @@ from packaging.utils import parse_sdist_filename, parse_wheel_filename
cwd = Path(__file__).parent cwd = Path(__file__).parent
# Map CLI target name to package name.
# Trusted publishing can only be tested on GitHub Actions, so we have separate local
# and all targets.
local_targets = {
"pypi-token": "astral-test-token",
"pypi-password-env": "astral-test-password",
"pypi-keyring": "astral-test-keyring",
"gitlab": "astral-test-token",
"codeberg": "astral-test-token",
}
all_targets = local_targets | {
"pypi-trusted-publishing": "astral-test-trusted-publishing"
}
project_urls = { project_urls = {
"astral-test-token": "https://test.pypi.org/simple/astral-test-token/", "astral-test-password": ["https://test.pypi.org/simple/astral-test-password/"],
"astral-test-password": "https://test.pypi.org/simple/astral-test-password/", "astral-test-keyring": ["https://test.pypi.org/simple/astral-test-keyring/"],
"astral-test-keyring": "https://test.pypi.org/simple/astral-test-keyring/", "astral-test-trusted-publishing": [
"astral-test-trusted-publishing": "https://test.pypi.org/simple/astral-test-trusted-publishing/", "https://test.pypi.org/simple/astral-test-trusted-publishing/"
"astral-test-gitlab-pat": "https://gitlab.com/api/v4/projects/61853105/packages/pypi/simple/astral-test-gitlab-pat", ],
"astral-test-codeberg": "https://codeberg.org/api/packages/astral-test-user/pypi/simple/astral-test-forgejo-codeberg", "astral-test-token": [
"https://test.pypi.org/simple/astral-test-token/",
"https://gitlab.com/api/v4/projects/61853105/packages/pypi/simple/astral-test-token",
"https://codeberg.org/api/packages/astral-test-user/pypi/simple/astral-test-token",
],
} }
def get_new_version(project_name: str) -> str: def get_new_version(project_name: str) -> str:
"""Return the next free path version on pypi""" """Return the next free path version on pypi"""
data = httpx.get(project_urls[project_name]).text # To keep the number of packages small we reuse them across targets, so we have to
# pick a version that doesn't exist on any target yet
versions = set() versions = set()
for filename in list(m.group(1) for m in re.finditer(">([^<]+)</a>", data)): for url in project_urls[project_name]:
if filename.endswith(".whl"): data = httpx.get(url).text
[_name, version, _build, _tags] = parse_wheel_filename(filename) for filename in list(m.group(1) for m in re.finditer(">([^<]+)</a>", data)):
else: if filename.endswith(".whl"):
[_name, version] = parse_sdist_filename(filename) [_name, version, _build, _tags] = parse_wheel_filename(filename)
versions.add(version) else:
[_name, version] = parse_sdist_filename(filename)
versions.add(version)
max_version = max(versions) max_version = max(versions)
# Bump the path version to obtain an empty version # Bump the path version to obtain an empty version
@ -97,7 +123,11 @@ def create_project(project_name: str, uv: Path):
pyproject_toml.write_text(toml) pyproject_toml.write_text(toml)
def publish_project(project_name: str, uv: Path): def publish_project(target: str, uv: Path):
project_name = all_targets[target]
print(f"\nPublish {project_name} for {target}")
# Create the project # Create the project
create_project(project_name, uv) create_project(project_name, uv)
@ -105,7 +135,7 @@ def publish_project(project_name: str, uv: Path):
check_call([uv, "build"], cwd=cwd.joinpath(project_name)) check_call([uv, "build"], cwd=cwd.joinpath(project_name))
# Upload the project # Upload the project
if project_name == "astral-test-token": if target == "pypi-token":
env = os.environ.copy() env = os.environ.copy()
env["UV_PUBLISH_TOKEN"] = os.environ["UV_TEST_PUBLISH_TOKEN"] env["UV_PUBLISH_TOKEN"] = os.environ["UV_TEST_PUBLISH_TOKEN"]
check_call( check_call(
@ -118,7 +148,7 @@ def publish_project(project_name: str, uv: Path):
cwd=cwd.joinpath(project_name), cwd=cwd.joinpath(project_name),
env=env, env=env,
) )
elif project_name == "astral-test-password": elif target == "pypi-password-env":
env = os.environ.copy() env = os.environ.copy()
env["UV_PUBLISH_PASSWORD"] = os.environ["UV_TEST_PUBLISH_PASSWORD"] env["UV_PUBLISH_PASSWORD"] = os.environ["UV_TEST_PUBLISH_PASSWORD"]
check_call( check_call(
@ -133,7 +163,7 @@ def publish_project(project_name: str, uv: Path):
cwd=cwd.joinpath(project_name), cwd=cwd.joinpath(project_name),
env=env, env=env,
) )
elif project_name == "astral-test-keyring": elif target == "pypi-keyring":
check_call( check_call(
[ [
uv, uv,
@ -147,7 +177,19 @@ def publish_project(project_name: str, uv: Path):
], ],
cwd=cwd.joinpath(project_name), cwd=cwd.joinpath(project_name),
) )
elif project_name == "astral-test-gitlab-pat": elif target == "pypi-trusted-publishing":
check_call(
[
uv,
"publish",
"--publish-url",
"https://test.pypi.org/legacy/",
"--trusted-publishing",
"always",
],
cwd=cwd.joinpath(project_name),
)
elif target == "gitlab":
env = os.environ.copy() env = os.environ.copy()
env["UV_PUBLISH_PASSWORD"] = os.environ["UV_TEST_PUBLISH_GITLAB_PAT"] env["UV_PUBLISH_PASSWORD"] = os.environ["UV_TEST_PUBLISH_GITLAB_PAT"]
check_call( check_call(
@ -162,7 +204,7 @@ def publish_project(project_name: str, uv: Path):
cwd=cwd.joinpath(project_name), cwd=cwd.joinpath(project_name),
env=env, env=env,
) )
elif project_name == "astral-test-codeberg": elif target == "codeberg":
env = os.environ.copy() env = os.environ.copy()
env["UV_PUBLISH_USERNAME"] = "astral-test-user" env["UV_PUBLISH_USERNAME"] = "astral-test-user"
env["UV_PUBLISH_PASSWORD"] = os.environ["UV_TEST_PUBLISH_CODEBERG_TOKEN"] env["UV_PUBLISH_PASSWORD"] = os.environ["UV_TEST_PUBLISH_CODEBERG_TOKEN"]
@ -176,25 +218,14 @@ def publish_project(project_name: str, uv: Path):
cwd=cwd.joinpath(project_name), cwd=cwd.joinpath(project_name),
env=env, env=env,
) )
elif project_name == "astral-test-trusted-publishing":
check_call(
[
uv,
"publish",
"--publish-url",
"https://test.pypi.org/legacy/",
"--trusted-publishing",
"always",
],
cwd=cwd.joinpath(project_name),
)
else: else:
raise ValueError(f"Unknown project name: {project_name}") raise ValueError(f"Unknown target: {target}")
def main(): def main():
parser = ArgumentParser() parser = ArgumentParser()
parser.add_argument("projects", choices=list(project_urls) + ["all"], nargs="+") target_choices = list(all_targets) + ["local", "all"]
parser.add_argument("targets", choices=target_choices, nargs="+")
parser.add_argument("--uv") parser.add_argument("--uv")
args = parser.parse_args() args = parser.parse_args()
@ -207,12 +238,14 @@ def main():
executable_suffix = ".exe" if os.name == "nt" else "" executable_suffix = ".exe" if os.name == "nt" else ""
uv = cwd.parent.parent.joinpath(f"target/debug/uv{executable_suffix}") uv = cwd.parent.parent.joinpath(f"target/debug/uv{executable_suffix}")
if args.projects == ["all"]: if args.targets == ["local"]:
projects = list(project_urls) targets = list(local_targets)
elif args.targets == ["all"]:
targets = list(all_targets)
else: else:
projects = args.projects targets = args.targets
for project_name in projects: for project_name in targets:
publish_project(project_name, uv) publish_project(project_name, uv)