@@ -3,8 +3,9 @@ name: "Publish release to PyPI"
33on :
44 push :
55 tags :
6- # Publish on any tag starting with a `v`, e.g., v0.0.1a2
7- - v*
6+ # Publish package-scoped releases, e.g., tangle-cli-v0.1.1 or tangle-api-v0.1.0.
7+ - " tangle-cli-v*"
8+ - " tangle-api-v*"
89
910jobs :
1011 run :
@@ -52,21 +53,39 @@ jobs:
5253 run : |
5354 cli_wheel="$(echo dist/tangle_cli-*.whl)"
5455 uv run --isolated --no-project --find-links dist --with "${cli_wheel}[native]" tangle-cli version
55- - name : Determine packages to publish
56+ - name : Determine package to publish
5657 id : publish-plan
5758 run : |
5859 python - <<'PY'
5960 import json
6061 import os
62+ import sys
6163 import urllib.error
6264 import urllib.request
6365 from pathlib import Path
6466 import tomllib
6567
66- packages = [
67- ("tangle-cli", "pyproject.toml", "dist/tangle_cli-*", "publish_cli"),
68- ("tangle-api", "packages/tangle-api/pyproject.toml", "dist/tangle_api-*", "publish_api"),
69- ]
68+ tag = os.environ["GITHUB_REF_NAME"]
69+ release_packages = {
70+ "tangle-cli-v": ("tangle-cli", "pyproject.toml", "dist/tangle_cli-*"),
71+ "tangle-api-v": ("tangle-api", "packages/tangle-api/pyproject.toml", "dist/tangle_api-*"),
72+ }
73+
74+ for tag_prefix, package_config in release_packages.items():
75+ if tag.startswith(tag_prefix):
76+ package, pyproject_path, dist_glob = package_config
77+ tag_version = tag.removeprefix(tag_prefix)
78+ break
79+ else:
80+ supported_tags = ", ".join(f"{prefix}*" for prefix in release_packages)
81+ sys.exit(f"Unsupported release tag {tag!r}. Expected one of: {supported_tags}.")
82+
83+ package_version = tomllib.loads(Path(pyproject_path).read_text())["project"]["version"]
84+ if tag_version != package_version:
85+ sys.exit(
86+ f"Release tag {tag!r} declares {package} version {tag_version}, "
87+ f"but {pyproject_path} declares {package_version}."
88+ )
7089
7190 def version_exists(package: str, version: str) -> bool:
7291 url = f"https://pypi.org/pypi/{package}/json"
@@ -79,21 +98,18 @@ jobs:
7998 raise
8099 return version in data.get("releases", {})
81100
82- github_output = Path(os.environ["GITHUB_OUTPUT"] )
83- with github_output.open("a") as output:
84- for package, pyproject_path, dist_glob, output_name in packages:
85- version = tomllib.loads(Path(pyproject_path).read_text())["project"]["version"]
86- exists = version_exists(package, version )
87- should_publish = not exists
88- print(
89- f" {package} {version}: "
90- f"{'publish ' + dist_glob if should_publish else 'skip; version already exists on PyPI'}"
91- )
92- print(f"{output_name} ={'true' if should_publish else 'false'}", file=output)
101+ should_publish = not version_exists(package, package_version )
102+ print(
103+ f"{tag}: "
104+ f"{'publish ' + dist_glob if should_publish else package + ' ' + package_version + ' already exists on PyPI; skipping'}"
105+ )
106+
107+ with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
108+ print(f"package= {package}", file=output)
109+ print(f" version={package_version}", file=output)
110+ print(f"dist_glob={dist_glob}", file=output )
111+ print(f"should_publish ={'true' if should_publish else 'false'}", file=output)
93112 PY
94- - name : Publish tangle-cli
95- if : steps.publish-plan.outputs.publish_cli == 'true'
96- run : uv publish dist/tangle_cli-*
97- - name : Publish tangle-api
98- if : steps.publish-plan.outputs.publish_api == 'true'
99- run : uv publish dist/tangle_api-*
113+ - name : Publish selected package
114+ if : steps.publish-plan.outputs.should_publish == 'true'
115+ run : uv publish ${{ steps.publish-plan.outputs.dist_glob }}
0 commit comments