From c6c3729d95ecc0ab5005c72fc8d65b0f1494233f Mon Sep 17 00:00:00 2001 From: 0xFantomMenace <93462858+0xFantomMenace@users.noreply.github.com> Date: Sat, 19 Sep 2026 20:31:50 +0000 Subject: [PATCH 1/2] Scope the sdist to the package The wheel has always been scoped to the oxarchive package, but the sdist had no scope at all, so the build backend included every non-ignored file in the working directory. Version 1.8.0's sdist is 1.67 MB against an 87 KB wheel because of it. List what ships instead of relying on the default, and ignore agent working directories so they cannot be swept in again. A rebuilt sdist is 84 KB and 30 files. --- .gitignore | 5 +++++ pyproject.toml | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/.gitignore b/.gitignore index 06fed4f..3793469 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,8 @@ dmypy.json # OS .DS_Store Thumbs.db + +# Agent working directories. These are not source and must never be packaged; +# an unscoped sdist published one to PyPI once already. +.claude/ +.codex/ diff --git a/pyproject.toml b/pyproject.toml index d2f4898..e194ad5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,19 @@ Issues = "https://github.com/0xArchiveIO/sdk-python/issues" [tool.hatch.build.targets.wheel] packages = ["oxarchive"] +# The wheel is scoped to the package, but the sdist had no scope at all, so +# hatchling swept in every non-gitignored file in the directory. That published +# an agent working directory, and with it a copy of the private backend, inside +# oxarchive 1.8.0's sdist. List what ships instead of trusting the default. +[tool.hatch.build.targets.sdist] +include = [ + "/oxarchive", + "/README.md", + "/CHANGELOG.md", + "/LICENSE", + "/pyproject.toml", +] + [tool.ruff] line-length = 100 target-version = "py39" From 3c804a156c8526dc6334f314ee3f89488f8ae742 Mon Sep 17 00:00:00 2001 From: 0xFantomMenace <93462858+0xFantomMenace@users.noreply.github.com> Date: Sat, 19 Sep 2026 20:52:45 +0000 Subject: [PATCH 2/2] Fail the build if the sdist grows beyond the package The include list fixes today's build. This stops it regressing: the job plants a stray directory, builds the sdist, and fails if anything outside the package ends up inside it. --- .github/workflows/package-contents.yml | 31 ++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/package-contents.yml diff --git a/.github/workflows/package-contents.yml b/.github/workflows/package-contents.yml new file mode 100644 index 0000000..8aa8ed9 --- /dev/null +++ b/.github/workflows/package-contents.yml @@ -0,0 +1,31 @@ +name: Package contents + +# The sdist once shipped an entire agent working directory because it had no +# include list and the build backend defaults to "everything not ignored". +# This fails the build if anything outside the package ever ships again. +on: + pull_request: + push: + branches: [master, main] + +jobs: + sdist-scope: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - run: pip install build + - name: Plant a stray directory the way an agent would + run: mkdir -p .claude/worktrees/probe && echo leak > .claude/worktrees/probe/leak.rs + - run: python -m build --sdist --outdir dist-check + - name: The sdist must contain only the package and its metadata + run: | + set -euo pipefail + tar tzf dist-check/*.tar.gz | sed 's|^[^/]*/||' | grep -v '^$' \ + | awk -F/ '{print $1}' | sort -u > /tmp/top.txt + echo "top-level entries in the sdist:"; cat /tmp/top.txt + if grep -qE '^\.claude|^\.codex|^backend|^crates|^migrations' /tmp/top.txt; then + echo "::error::the sdist contains files from outside the package"; exit 1 + fi