Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Publish release to PyPI

on:
push:
tags:
- "v*"

jobs:
publish:
runs-on: ubuntu-latest
environment:
name: pypi
permissions:
id-token: write
contents: read

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
version: "0.11.28"

- name: Install Python
run: uv python install 3.14

- name: Build sdist and wheel
run: uv build

- name: Check dist versions match tag
run: python _ensure_dist_files_use_version.py "${GITHUB_REF_NAME#v}" dist/*.whl dist/*.tar.gz

- name: Smoke test wheel
run: >
uv run --isolated --no-project --with dist/*.whl
python -c "from dotmap import DotMap; assert DotMap(a=1).a == 1"

- name: Smoke test sdist
run: >
uv run --isolated --no-project --with dist/*.tar.gz
python -c "from dotmap import DotMap; assert DotMap(a=1).a == 1"

- name: Publish to PyPI
run: uv publish --trusted-publishing always
52 changes: 52 additions & 0 deletions _ensure_dist_files_use_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import email
import pathlib
import sys
import tarfile
import zipfile


def _wheel_version(path: str) -> str:
with zipfile.ZipFile(path) as zf:
metadata_name = next(
name for name in zf.namelist() if name.endswith(".dist-info/METADATA")
)
return email.message_from_bytes(zf.read(metadata_name))["Version"]


def _sdist_version(path: str) -> str:
with tarfile.open(path, "r:gz") as tf:
pkg_info = next(
member
for member in tf.getmembers()
if pathlib.Path(member.name).name == "PKG-INFO"
)
return email.message_from_bytes(tf.extractfile(pkg_info).read())["Version"]


def main() -> int:
if len(sys.argv) < 3:
raise SystemExit(
"usage: python _ensure_dist_files_use_version.py <expected-version> <dist-file>..."
)

expected = sys.argv[1]
files = sys.argv[2:]
validated = []

for path in files:
if path.endswith(".whl"):
actual = _wheel_version(path)
elif path.endswith(".tar.gz"):
actual = _sdist_version(path)
else:
raise AssertionError(f"unsupported distribution file: {path}")

assert actual == expected, f"{path} has version {actual!r} != expected {expected!r}"
validated.append(f"{path}={actual}")

print("validated dist versions:", ", ".join(validated))
return 0


if __name__ == "__main__":
sys.exit(main())