Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 72 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,78 @@ jobs:
path: dist
merge-multiple: true

- name: Validate wheels
run: |
pip install twine
for whl in dist/*.whl; do
unzip -qt "$whl"
done
twine check --strict dist/*

- name: Check PyPI for existing version
id: pypi-check
run: |
version="${GITHUB_REF_NAME}"
python3 - "$version" <<'PYCHECK'
import hashlib, json, os, sys, urllib.request
from pathlib import Path

version = sys.argv[1]
dist = Path("dist")
url = f"https://pypi.org/pypi/taskito/{version}/json"
req = urllib.request.Request(url, headers={"User-Agent": "taskito-release/1.0"})

try:
resp = urllib.request.urlopen(req, timeout=30)
remote = {f["filename"]: f["digests"]["sha256"] for f in json.loads(resp.read())["urls"]}
except urllib.error.HTTPError as e:
if e.code == 404:
remote = {}
else:
raise

local = {}
for p in sorted(dist.iterdir()):
if p.suffix in (".whl", ".gz"):
local[p.name] = hashlib.sha256(p.read_bytes()).hexdigest()

conflicts = [f for f in local if f in remote and local[f] != remote[f]]
if conflicts:
for f in conflicts:
print(f"::error::Hash conflict: {f} (local {local[f][:12]}… vs remote {remote[f][:12]}…)")
sys.exit(1)

missing = [f for f in local if f not in remote]
out = os.environ["GITHUB_OUTPUT"]
with open(out, "a") as fh:
if not missing:
fh.write("all_uploaded=true\n")
print(f"::notice::All {len(local)} artifacts on PyPI — skipping upload")
else:
fh.write("all_uploaded=false\n")
print(f"::notice::{len(missing)}/{len(local)} artifact(s) missing on PyPI")
PYCHECK

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
if: steps.pypi-check.outputs.all_uploaded != 'true'
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
with:
packages-dir: dist/
skip-existing: true
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Verify version on PyPI
run: |
version="${GITHUB_REF_NAME}"
for _attempt in 1 2 3 4 5; do
status=$(curl -s -o /dev/null -w '%{http_code}' --head \
--connect-timeout 10 --max-time 30 \
-H "User-Agent: taskito-release/1.0" \
"https://pypi.org/pypi/taskito/${version}/json")
if [ "$status" = "200" ]; then
echo "taskito==${version} live on PyPI"
exit 0
fi
sleep 15
done
echo "::error::taskito==${version} not found on PyPI"
exit 1