diff --git a/pyproject.toml b/pyproject.toml index e27a5a9..29c7116 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,6 +99,19 @@ build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["openadapt_capture"] +[tool.hatch.build.targets.sdist] +ignore-vcs = true +only-include = [ + "openadapt_capture", + "CHANGELOG.md", + "SECURITY.md", + "docs", +] +exclude = [ + "/docs/images/demo.gif", + "/docs/whisper-integration-plan.md", +] + [tool.ruff] line-length = 100 target-version = "py310" diff --git a/scripts/verify_distribution.py b/scripts/verify_distribution.py index 71e845f..ebf8e03 100644 --- a/scripts/verify_distribution.py +++ b/scripts/verify_distribution.py @@ -10,6 +10,16 @@ FORBIDDEN_DEPENDENCIES = ("oa-atomacos", "pynput") FORBIDDEN_SOURCE_TOKENS = ("oa_atomacos", "pynput") +FORBIDDEN_ARCHIVE_PATHS = ( + ".env.example", + ".github/", + "CLAUDE.md", + "chrome_extension/", + "docs/images/demo.gif", + "docs/whisper-integration-plan.md", + "scripts/", + "tests/", +) def _archive_files(path: Path) -> dict[str, bytes]: @@ -31,12 +41,37 @@ def _archive_files(path: Path) -> dict[str, bytes]: raise ValueError(f"Unsupported distribution archive: {path}") +def _relative_archive_name(name: str) -> str: + """Remove the versioned root directory used by source distributions.""" + parts = Path(name).parts + if len(parts) > 1 and parts[0].startswith("openadapt_capture-"): + return Path(*parts[1:]).as_posix() + return Path(name).as_posix() + + def verify_distribution(path: Path) -> None: files = _archive_files(path) + relative_names = {_relative_archive_name(name) for name in files} assert any(Path(name).name == "LICENSE" for name in files), ( f"{path}: MIT LICENSE file is missing" ) + for name in relative_names: + assert not any( + name == forbidden.rstrip("/") or name.startswith(forbidden) + for forbidden in FORBIDDEN_ARCHIVE_PATHS + ), f"{path}: repository-only path is in the release archive: {name}" + + if path.name.endswith(".tar.gz"): + required_source_files = { + "LICENSE", + "README.md", + "pyproject.toml", + "openadapt_capture/__init__.py", + } + missing = required_source_files - relative_names + assert not missing, f"{path}: required source files are missing: {sorted(missing)}" + metadata_files = [ content.decode("utf-8") for name, content in files.items()