Skip to content
Merged
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
37 changes: 37 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Release

on:
push:
tags: ["v*"]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v6
with:
python-version: "3.13"
- run: uv sync
- run: uv run pytest -q -m "slow or not slow"
- run: uv build
- run: uvx twine check dist/*
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/project/simloop/
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@ dead-lettering) written in plain asyncio — tested end to end with
simloop. Its suite runs hundreds of seeds of partitions, crashes, and
poison jobs, and shows that removing any load-bearing safeguard produces
a violation the explorer finds and replays from a seed. The bug table
lives in [examples/jobqueue/README.md](examples/jobqueue/README.md).
lives in [examples/jobqueue/README.md](https://github.com/dhruvl/simloop/blob/main/examples/jobqueue/README.md).

## Honest limits

Code that goes through the event-loop API is supported; code that
bypasses it is fenced: threads and executors, raw sockets, subprocesses,
signals, TLS, and `getaddrinfo` raise `SimulationFenceError` rather than
silently breaking determinism. Write-side flow control is not simulated.
The full contract is in [docs/supported-api.md](docs/supported-api.md).
The full contract is in [docs/supported-api.md](https://github.com/dhruvl/simloop/blob/main/docs/supported-api.md).

## License

Expand Down
13 changes: 12 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ readme = "README.md"
requires-python = ">=3.12"
license = "MIT"
license-files = ["LICENSE"]
authors = [{ name = "Dhruv Kumar Singh", email = "dsingh32@binghamton.edu" }]
authors = [{ name = "Dhruv Kumar Singh", email = "singhdhruvk@gmail.com" }]
keywords = ["asyncio", "testing", "deterministic", "simulation", "dst"]
classifiers = [
"Development Status :: 3 - Alpha",
Expand All @@ -19,6 +19,11 @@ classifiers = [
]
dependencies = []

[project.urls]
Homepage = "https://github.com/dhruvl/simloop"
Repository = "https://github.com/dhruvl/simloop"
Issues = "https://github.com/dhruvl/simloop/issues"

[dependency-groups]
dev = ["pytest>=8", "mypy>=1.11"]

Expand All @@ -38,3 +43,9 @@ markers = ["slow: long-running seed sweeps and replay-stability checks"]
strict = true
files = ["src", "tests", "benchmarks", "examples/jobqueue"]
mypy_path = "examples/jobqueue"

[tool.hatch.build.targets.sdist]
only-include = ["src/simloop"]

[tool.hatch.build.targets.wheel]
packages = ["src/simloop"]
58 changes: 58 additions & 0 deletions tests/test_packaging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""The published artifacts must contain the package and nothing else."""

import subprocess
import tarfile
import zipfile
from pathlib import Path

import pytest

from simloop import __version__

REPO_ROOT = Path(__file__).resolve().parent.parent

SDIST_ALLOWED_FILES = {"PKG-INFO", "pyproject.toml", "README.md", "LICENSE", ".gitignore"}
PACKAGE_PREFIX = "src/simloop/"


def _build(tmp_path: Path, kind: str) -> Path:
subprocess.run(
["uv", "build", f"--{kind}", "--out-dir", str(tmp_path)],
cwd=REPO_ROOT,
check=True,
capture_output=True,
)
pattern = "*.tar.gz" if kind == "sdist" else "*.whl"
(artifact,) = tmp_path.glob(pattern)
return artifact


@pytest.mark.slow
def test_sdist_ships_only_the_package(tmp_path: Path) -> None:
sdist = _build(tmp_path, "sdist")
with tarfile.open(sdist) as tar:
names = [m.name for m in tar.getmembers() if not m.isdir()]
# every member is "<name>-<version>/<relpath>"
relpaths = [name.split("/", 1)[1] for name in names]
unexpected = [
p
for p in relpaths
if p not in SDIST_ALLOWED_FILES and not p.startswith(PACKAGE_PREFIX)
]
assert unexpected == []
assert "src/simloop/__init__.py" in relpaths


@pytest.mark.slow
def test_wheel_ships_only_the_package(tmp_path: Path) -> None:
wheel = _build(tmp_path, "wheel")
with zipfile.ZipFile(wheel) as zf:
names = [n for n in zf.namelist() if not n.endswith("/")]
dist_info_prefix = f"simloop-{__version__}.dist-info/"
unexpected = [
n
for n in names
if not n.startswith("simloop/") and not n.startswith(dist_info_prefix)
]
assert unexpected == []
assert "simloop/__init__.py" in names
Loading