@@ -233,6 +233,7 @@ jobs:
233233 path : python/dist/
234234
235235 - name : Publish to PyPI
236+ id : pypi_publish
236237 if : steps.version_check.outputs.should_release == 'true'
237238 # Uses PyPI's OIDC Trusted Publisher flow (id-token: write below). If you see "Trusted
238239 # Publisher … not configured" in the logs, configure it for this repo + workflow on PyPI.
@@ -243,6 +244,50 @@ jobs:
243244 verbose : true
244245 skip-existing : true
245246
247+ - name : Diagnose PyPI publish failure
248+ if : failure() && steps.pypi_publish.outcome == 'failure'
249+ run : |
250+ echo "::error title=PyPI publish failed::pypa/gh-action-pypi-publish exited with an error."
251+ echo ""
252+ echo "Most common causes & fixes:"
253+ echo " 1. 'Trusted publisher … not configured' / 'invalid-publisher':"
254+ echo " PyPI does not yet trust this repository+workflow. Configure it at:"
255+ echo " https://pypi.org/manage/project/${{ steps.version_check.outputs.package_name }}/settings/publishing/"
256+ echo " Match: owner=${{ github.repository_owner }}, repo=$(basename ${{ github.repository }}),"
257+ echo " workflow=python.yml, environment=(blank unless you set one)."
258+ echo ""
259+ echo " 2. Token-based auth in use but PYPI_API_TOKEN missing/expired:"
260+ echo " Generate at https://pypi.org/manage/account/token/ and store as a repo secret."
261+ echo ""
262+ echo " 3. id-token: write missing on the job (this workflow already has it)."
263+ echo ""
264+ echo " 4. Version already published: skip-existing is true so PyPI conflicts are tolerated;"
265+ echo " a hard failure here means something else is wrong."
266+ echo ""
267+ echo "Docs: https://docs.pypi.org/trusted-publishers/"
268+ echo " docs/case-studies/issue-29/README.md"
269+ exit 1
270+
271+ - name : Verify package on PyPI
272+ if : steps.version_check.outputs.should_release == 'true' && steps.pypi_publish.outcome == 'success'
273+ run : |
274+ PKG="${{ steps.version_check.outputs.package_name }}"
275+ VER="${{ steps.version_check.outputs.current_version }}"
276+ # PyPI's CDN can lag a few seconds behind a successful upload.
277+ for i in 1 2 3 4 5; do
278+ STATUS=$(curl -sS -o /dev/null -w '%{http_code}' "https://pypi.org/pypi/${PKG}/${VER}/json")
279+ echo "Attempt $i: PyPI HTTP status for ${PKG}@${VER}: ${STATUS}"
280+ if [ "$STATUS" = "200" ]; then
281+ echo "✅ Verified ${PKG}@${VER} is on PyPI"
282+ exit 0
283+ fi
284+ sleep 5
285+ done
286+ echo "::error title=PyPI verification failed::${PKG}@${VER} is not on PyPI after publish."
287+ echo "The publish step reported success but the registry does not see the version."
288+ echo "See docs/case-studies/issue-29/README.md for the runbook."
289+ exit 1
290+
246291 - name : Create GitHub Release
247292 if : steps.version_check.outputs.should_release == 'true'
248293 env :
@@ -315,7 +360,16 @@ jobs:
315360 working-directory : ./python
316361 run : twine check dist/*
317362
363+ - name : Read package name from pyproject.toml
364+ id : pkg
365+ if : steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
366+ working-directory : ./python
367+ run : |
368+ PACKAGE_NAME=$(grep -Po '(?<=^name = ")[^"]*' pyproject.toml | head -1)
369+ echo "package_name=$PACKAGE_NAME" >> $GITHUB_OUTPUT
370+
318371 - name : Publish to PyPI
372+ id : pypi_publish
319373 if : steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
320374 # See note above: relies on PyPI Trusted Publisher; see docs/case-studies/issue-25/README.md.
321375 uses : pypa/gh-action-pypi-publish@release/v1
@@ -324,6 +378,49 @@ jobs:
324378 verbose : true
325379 skip-existing : true
326380
381+ - name : Diagnose PyPI publish failure
382+ if : failure() && steps.pypi_publish.outcome == 'failure'
383+ run : |
384+ echo "::error title=PyPI publish failed::pypa/gh-action-pypi-publish exited with an error."
385+ echo ""
386+ echo "Most common causes & fixes:"
387+ echo " 1. 'Trusted publisher … not configured' / 'invalid-publisher':"
388+ echo " PyPI does not yet trust this repository+workflow. Configure it at:"
389+ echo " https://pypi.org/manage/project/${{ steps.pkg.outputs.package_name }}/settings/publishing/"
390+ echo " Match: owner=${{ github.repository_owner }}, repo=$(basename ${{ github.repository }}),"
391+ echo " workflow=python.yml, environment=(blank unless you set one)."
392+ echo ""
393+ echo " 2. Token-based auth in use but PYPI_API_TOKEN missing/expired:"
394+ echo " Generate at https://pypi.org/manage/account/token/ and store as a repo secret."
395+ echo ""
396+ echo " 3. id-token: write missing on the job (this workflow already has it)."
397+ echo ""
398+ echo " 4. Version already published: skip-existing is true so PyPI conflicts are tolerated;"
399+ echo " a hard failure here means something else is wrong."
400+ echo ""
401+ echo "Docs: https://docs.pypi.org/trusted-publishers/"
402+ echo " docs/case-studies/issue-29/README.md"
403+ exit 1
404+
405+ - name : Verify package on PyPI
406+ if : (steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true') && steps.pypi_publish.outcome == 'success'
407+ run : |
408+ PKG="${{ steps.pkg.outputs.package_name }}"
409+ VER="${{ steps.version.outputs.new_version }}"
410+ for i in 1 2 3 4 5; do
411+ STATUS=$(curl -sS -o /dev/null -w '%{http_code}' "https://pypi.org/pypi/${PKG}/${VER}/json")
412+ echo "Attempt $i: PyPI HTTP status for ${PKG}@${VER}: ${STATUS}"
413+ if [ "$STATUS" = "200" ]; then
414+ echo "✅ Verified ${PKG}@${VER} is on PyPI"
415+ exit 0
416+ fi
417+ sleep 5
418+ done
419+ echo "::error title=PyPI verification failed::${PKG}@${VER} is not on PyPI after publish."
420+ echo "The publish step reported success but the registry does not see the version."
421+ echo "See docs/case-studies/issue-29/README.md for the runbook."
422+ exit 1
423+
327424 - name : Create GitHub Release
328425 if : steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
329426 env :
0 commit comments