| name: Publish to PyPI |
| on: push |
| permissions: |
| contents: read |
| |
| jobs: |
| |
| build: |
| name: Build distribution |
| runs-on: ubuntu-latest |
| |
| steps: |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 |
| - name: Set up Python |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 |
| with: |
| python-version: "3.x" |
| - name: Install build tools |
| run: python -m pip install build twine |
| - name: Check the tag matches the package version |
| if: startsWith(github.ref, 'refs/tags/') |
| run: | |
| version="$(sed -n 's/^__version__ = "\(.*\)"$/\1/p' idna/package_data.py)" |
| if [ "$GITHUB_REF_NAME" != "v$version" ]; then |
| echo "::error::Tag $GITHUB_REF_NAME does not match idna.__version__ $version" >&2 |
| exit 1 |
| fi |
| - name: Build a binary wheel and a source tarball |
| run: python -m build |
| - name: Check the distributions |
| run: twine check --strict dist/* |
| - name: Install test dependencies |
| run: python -m pip install --require-hashes -r .github/requirements/test.txt |
| - name: Install the wheel and run the test suite against it |
| # Run from outside the checkout with --import-mode=importlib so that |
| # ``import idna`` resolves to the installed wheel, not the source tree: |
| # only an artifact that passes the suite gets published. |
| # --force-reinstall because twine (via requests) has already pulled |
| # the released idna from PyPI, and pip would otherwise skip our wheel |
| # as "already installed" whenever the version numbers coincide. |
| run: | |
| python -m pip install --no-deps --force-reinstall dist/*.whl |
| cd "$RUNNER_TEMP" |
| python -c 'import idna, sys; assert "site-packages" in idna.__file__, idna.__file__; print(idna.__version__, idna.__file__)' |
| pytest -q --import-mode=importlib -p no:cacheprovider "$GITHUB_WORKSPACE/tests" |
| - name: Store the distribution packages |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 |
| with: |
| name: python-package-distributions |
| path: dist/ |
| |
| publish-to-pypi: |
| name: Publish to PyPI |
| if: startsWith(github.ref, 'refs/tags/v') # only publish to PyPI on version tag pushes |
| needs: |
| - build |
| runs-on: ubuntu-latest |
| environment: |
| name: pypi |
| url: https://pypi.org/p/idna |
| permissions: |
| id-token: write # IMPORTANT: mandatory for trusted publishing |
| |
| steps: |
| - name: Download all the dists |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 |
| with: |
| name: python-package-distributions |
| path: dist/ |
| - name: Publish distribution to PyPI |
| uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 |
| |
| github-release: |
| name: Sign and upload GitHub Release |
| needs: |
| - publish-to-pypi |
| runs-on: ubuntu-latest |
| |
| permissions: |
| contents: write # IMPORTANT: mandatory for making GitHub Releases |
| id-token: write # IMPORTANT: mandatory for sigstore |
| |
| steps: |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 |
| - name: Download the dists |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 |
| with: |
| name: python-package-distributions |
| path: dist/ |
| - name: Sign with Sigstore |
| uses: sigstore/gh-action-sigstore-python@04cffa1d795717b140764e8b640de88853c92acc # v3.3.0 |
| with: |
| inputs: >- |
| ./dist/*.tar.gz |
| ./dist/*.whl |
| - name: Extract release notes from HISTORY.md |
| # The section headed "## <version> (<date>)" for the tagged version, |
| # without its heading. A release without a HISTORY entry is an error. |
| run: | |
| version="${GITHUB_REF_NAME#v}" |
| awk -v v="$version" '/^## /{p = ($2 == v)} p' HISTORY.md | tail -n +2 | sed '/./,$!d' > "$RUNNER_TEMP/release-notes.md" |
| if [ ! -s "$RUNNER_TEMP/release-notes.md" ]; then |
| echo "::error::No HISTORY.md section found for version $version" >&2 |
| exit 1 |
| fi |
| cat "$RUNNER_TEMP/release-notes.md" |
| - name: Create GitHub Release |
| env: |
| GITHUB_TOKEN: ${{ github.token }} |
| run: >- |
| gh release create |
| '${{ github.ref_name }}' |
| --repo '${{ github.repository }}' |
| --verify-tag |
| --notes-file "$RUNNER_TEMP/release-notes.md" |
| - name: Upload artifact signatures to GitHub Release |
| env: |
| GITHUB_TOKEN: ${{ github.token }} |
| # Upload to GitHub Release using the `gh` CLI. |
| # `dist/` contains the built packages, and the |
| # sigstore-produced signatures and certificates. |
| run: >- |
| gh release upload |
| '${{ github.ref_name }}' dist/** |
| --repo '${{ github.repository }}' |
| |
| publish-to-testpypi: |
| name: Publish to Test PyPI |
| # Only master, release branches (where the rcN pre-release commit lands |
| # before tagging) and version tags reach TestPyPI; topic and Dependabot |
| # branches still build and test but do not publish. |
| if: >- |
| github.ref == 'refs/heads/master' || |
| startsWith(github.ref, 'refs/heads/release-') || |
| startsWith(github.ref, 'refs/tags/v') |
| needs: |
| - build |
| runs-on: ubuntu-latest |
| |
| environment: |
| name: testpypi |
| url: https://test.pypi.org/p/idna |
| |
| permissions: |
| id-token: write # IMPORTANT: mandatory for trusted publishing |
| |
| steps: |
| - name: Download all the dists |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 |
| with: |
| name: python-package-distributions |
| path: dist/ |
| - name: Publish distribution to TestPyPI |
| uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 |
| with: |
| verbose: true |
| print-hash: true |
| repository-url: https://test.pypi.org/legacy/ |
| skip-existing: true |