Skip to content

Commit

Permalink
Add workflow for Trusted Publishing (#395)
Browse files Browse the repository at this point in the history
Co-authored-by: Sebastian Rittau <srittau@rittau.biz>
  • Loading branch information
JelleZijlstra and srittau authored May 16, 2024
1 parent 074d053 commit 63d8277
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 79 deletions.
76 changes: 0 additions & 76 deletions .github/workflows/package.yml

This file was deleted.

149 changes: 149 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Based on
# https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/

name: Test builds and publish Python distribution to PyPI

on:
release:
types: [published]
push:
branches: [main]
pull_request:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
build:
name: Build distribution
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Check package metadata
run: python scripts/check_package.py ${{ github.ref }}
- name: Install pypa/build
run: |
# Be wary of running `pip install` here, since it becomes easy for us to
# accidentally pick up typing_extensions as installed by a dependency
python -m pip install --upgrade build
python -m pip list
- name: Build a binary wheel and a source tarball
run: python -m build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/

test-wheel:
name: Test wheel
needs:
- build
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Install wheel
run: |
export path_to_file=$(find dist -type f -name "typing_extensions-*.whl")
echo "::notice::Installing wheel: $path_to_file"
python -m pip install --user $path_to_file
python -m pip list
- name: Run typing_extensions tests against installed package
run: rm src/typing_extensions.py && python src/test_typing_extensions.py

test-sdist:
name: Test source distribution
needs:
- build
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Unpack and test source distribution
run: |
export path_to_file=$(find dist -type f -name "typing_extensions-*.tar.gz")
echo "::notice::Unpacking source distribution: $path_to_file"
tar xzf $path_to_file -C dist/
cd ${path_to_file%.tar.gz}
python src/test_typing_extensions.py
test-sdist-installed:
name: Test installed source distribution
needs:
- build
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.x"
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Install source distribution
run: |
export path_to_file=$(find dist -type f -name "typing_extensions-*.tar.gz")
echo "::notice::Installing source distribution: $path_to_file"
python -m pip install --user $path_to_file
python -m pip list
- name: Run typing_extensions tests against installed package
run: rm src/typing_extensions.py && python src/test_typing_extensions.py

publish-to-pypi:
name: >-
Publish Python distribution to PyPI
if: github.event_name == 'release' # only publish to PyPI on releases
needs:
- test-sdist
- test-sdist-installed
- test-wheel
- build
runs-on: ubuntu-latest
environment:
name: publish
url: https://pypi.org/p/typing-extensions
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing

steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Ensure exactly one sdist and one wheel have been downloaded
run: test $(ls *.tar.gz | wc -l) = 1 && test $(ls *.whl | wc -l) = 1
- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development",
]

Expand Down
60 changes: 60 additions & 0 deletions scripts/check_package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import argparse
import re
import sys
import tomllib
from pathlib import Path


class ValidationError(Exception):
pass


def check(github_ref: str | None) -> None:
pyproject = Path(__file__).parent.parent / "pyproject.toml"
if not pyproject.exists():
raise ValidationError("pyproject.toml not found")
with pyproject.open("rb") as f:
data = tomllib.load(f)
pyproject_version = data["project"]["version"]

if github_ref is not None and github_ref.startswith("refs/tags/"):
version = github_ref.removeprefix("refs/tags/")
if version != pyproject_version:
raise ValidationError(
f"Version mismatch: GitHub ref is {version}, "
f"but pyproject.toml is {pyproject_version}"
)

requires_python = data["project"]["requires-python"]
assert sys.version_info[0] == 3, "Rewrite this script when Python 4 comes out"
match = re.fullmatch(r">=3\.(\d+)", requires_python)
if not match:
raise ValidationError(f"Invalid requires-python: {requires_python!r}")
lowest_minor = int(match.group(1))

description = data["project"]["description"]
if not description.endswith(f"3.{lowest_minor}+"):
raise ValidationError(f"Description should mention Python 3.{lowest_minor}+")

classifiers = set(data["project"]["classifiers"])
for should_be_supported in range(lowest_minor, sys.version_info[1] + 1):
if (
f"Programming Language :: Python :: 3.{should_be_supported}"
not in classifiers
):
raise ValidationError(
f"Missing classifier for Python 3.{should_be_supported}"
)


if __name__ == "__main__":
parser = argparse.ArgumentParser("Script to check the package metadata")
parser.add_argument(
"github_ref", type=str, help="The current GitHub ref", nargs="?"
)
args = parser.parse_args()
try:
check(args.github_ref)
except ValidationError as e:
print(e)
sys.exit(1)
4 changes: 1 addition & 3 deletions src/test_typing_extensions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import sys
import os
import abc
import gc
import io
Expand Down Expand Up @@ -5718,8 +5717,7 @@ def test_typing_extensions_defers_when_possible(self):
getattr(typing, item))

def test_typing_extensions_compiles_with_opt(self):
file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),
'typing_extensions.py')
file_path = typing_extensions.__file__
try:
subprocess.check_output(f'{sys.executable} -OO {file_path}',
stderr=subprocess.STDOUT,
Expand Down

0 comments on commit 63d8277

Please sign in to comment.