Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add workflow for Trusted Publishing #395

Merged
merged 14 commits into from
May 16, 2024
76 changes: 0 additions & 76 deletions .github/workflows/package.yml

This file was deleted.

147 changes: 147 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# 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:
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved

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@v4
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved
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@v4
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an idea: To make it more robust against future changes, maybe copy the test_* script to /tmp and delete all of src?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not enough because it relies on a few helper files. I think we'll have to change this either way if we ever change our file layout.


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@v4
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above.


publish-to-pypi:
name: >-
Publish Python distribution to PyPI
if: github.event_name == 'release' # only publish to PyPI on releases
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved
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: Publish distribution to PyPI
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved
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
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved
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__
hauntsaninja marked this conversation as resolved.
Show resolved Hide resolved
try:
subprocess.check_output(f'{sys.executable} -OO {file_path}',
stderr=subprocess.STDOUT,
Expand Down
Loading