@@ -43,10 +43,28 @@ jobs:
4343 - name : Security audit
4444 run : |
4545 pip-audit -r requirements.txt || true
46- - name : Semantic Release (version, tag, GitHub release, PyPI)
47- # Choose which PYPI token to provide based on the optional dispatch input.
48- # If manually dispatched with publish_target=testpypi, the workflow will use TEST_PYPI_API_TOKEN.
46+ - name : Publish package (semantic-release or initial twine upload)
4947 env :
48+ PUBLISH_TARGET : ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.publish_target || 'pypi' }}
5049 PYPI_TOKEN : ${{ (github.event_name == 'workflow_dispatch' && github.event.inputs.publish_target == 'testpypi') && secrets.TEST_PYPI_API_TOKEN || secrets.PYPI_API_TOKEN }}
5150 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
52- run : semantic-release publish
51+ run : |
52+ set -euo pipefail
53+ git fetch --tags || true
54+ TAGS_COUNT=$(git tag -l | wc -l | tr -d ' ')
55+ echo "Found ${TAGS_COUNT} tags"
56+ if [ "${TAGS_COUNT}" -eq 0 ]; then
57+ echo "No tags found; performing initial upload via twine to ${PUBLISH_TARGET}"
58+ python -m pip install --upgrade pip
59+ python -m pip install build twine
60+ python -m build
61+ if [ "${PUBLISH_TARGET}" = "testpypi" ]; then
62+ python -m twine upload --repository-url https://test.pypi.org/legacy/ -u __token__ -p "${PYPI_TOKEN}" dist/*
63+ else
64+ python -m twine upload -u __token__ -p "${PYPI_TOKEN}" dist/*
65+ fi
66+ echo "Initial upload complete"
67+ else
68+ echo "Tags present; running semantic-release"
69+ semantic-release publish
70+ fi
0 commit comments