Skip to content

Commit 0025843

Browse files
committed
chore: Verify the sdist in CI and make its file list an allowlist
The exclude list had the wrong default. Every new repository file shipped unless somebody remembered to exclude it. An allowlist inverts that, but its failure mode is a needed file going missing, which nothing would notice. So the allowlist comes with a job that proves it. The job builds the sdist, unpacks it outside the checkout, and runs the unit tests, the type checks and both contract suites from the unpacked tree. The test-count check is what makes the job meaningful. An sdist that shipped half its tests would still pass the tests it did ship, so the job counts collected tests in the checkout and in the sdist and fails if the sdist collects fewer. The contract-test steps cover the other half, since a missing contract-tests directory changes no test count. One job on one Python version. The matrix already covers the checkout. The job runs the contract suites without persistence tests, because it starts no database services; the linux job still covers those. Hatchling force-includes pyproject.toml, README.md, LICENSE.txt, PKG-INFO and .gitignore whatever the file list says, so an allowlist cannot make the sdist unbuildable or uninstallable. Verified locally. The allowlist produces the same 262-file sdist as the exclude list did, 1,983 tests collect from it, and 1,628 pass with 355 database tests skipped, matching make test.
1 parent f945823 commit 0025843

2 files changed

Lines changed: 86 additions & 13 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,77 @@ jobs:
9393
version: v3
9494
enable_persistence_tests: "true"
9595

96+
# Proves the sdist is complete and functional, since the sdist file list is an
97+
# allowlist and an omission there is otherwise invisible. The test-count check
98+
# is what makes this meaningful: without it, an sdist missing half its tests
99+
# would still pass the tests it did ship. The contract-test steps cover the
100+
# other half, since a missing contract-tests directory changes no test count.
101+
sdist:
102+
name: sdist (functional)
103+
runs-on: ubuntu-latest
104+
105+
steps:
106+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
107+
108+
- name: Set up uv
109+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
110+
with:
111+
python-version: "3.13"
112+
113+
- name: Count the tests in the checkout
114+
run: |
115+
uv sync --all-extras
116+
count=$(uv run pytest --collect-only -q | grep -oE '[0-9]+ tests? collected' | grep -oE '^[0-9]+')
117+
echo "The checkout collects $count tests."
118+
echo "checkout_tests=$count" >> "$GITHUB_ENV"
119+
120+
- name: Build and unpack the sdist
121+
run: |
122+
uv build --sdist --out-dir dist
123+
mkdir -p "$RUNNER_TEMP/sdist"
124+
tar -xzf dist/*.tar.gz -C "$RUNNER_TEMP/sdist" --strip-components=1
125+
126+
- name: Check that the sdist ships every test
127+
working-directory: ${{ runner.temp }}/sdist
128+
run: |
129+
uv sync --all-extras
130+
count=$(uv run pytest --collect-only -q | grep -oE '[0-9]+ tests? collected' | grep -oE '^[0-9]+')
131+
echo "The sdist collects $count tests; the checkout collected $checkout_tests."
132+
if [ "$count" -lt "$checkout_tests" ]; then
133+
echo "::error::The sdist is missing tests. Add the missing paths to the include list in [tool.hatch.build.targets.sdist]."
134+
exit 1
135+
fi
136+
137+
- name: Run the tests from the sdist
138+
working-directory: ${{ runner.temp }}/sdist
139+
run: make test
140+
141+
- name: Verify typehints from the sdist
142+
working-directory: ${{ runner.temp }}/sdist
143+
run: make lint
144+
145+
- name: Start the contract test service from the sdist
146+
working-directory: ${{ runner.temp }}/sdist
147+
run: make start-contract-test-service-bg
148+
149+
- name: Run contract tests against the sdist
150+
uses: launchdarkly/gh-actions/actions/contract-tests@contract-tests-v1
151+
with:
152+
test_service_port: 9000
153+
enable_persistence_tests: "false"
154+
token: ${{ secrets.GITHUB_TOKEN }}
155+
156+
- name: Start the async contract test service from the sdist
157+
working-directory: ${{ runner.temp }}/sdist
158+
run: make start-async-contract-test-service-bg
159+
160+
- name: Run async contract tests against the sdist
161+
uses: launchdarkly/gh-actions/actions/contract-tests@contract-tests-v1
162+
with:
163+
test_service_port: 9001
164+
enable_persistence_tests: "false"
165+
token: ${{ secrets.GITHUB_TOKEN }}
166+
96167
windows:
97168
runs-on: windows-latest
98169

‎pyproject.toml‎

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,20 @@ build-backend = "hatchling.build"
101101
packages = ["ldclient"]
102102
exclude = ["ldclient/testing"]
103103

104-
# The sdist keeps what a consumer or a downstream redistributor needs: the
105-
# package, the tests, the contract-test service, the docs source, the Makefile
106-
# and PROVENANCE.md. Repository-management files are of no use outside this
107-
# repository. Hatchling force-includes .gitignore so the sdist can be rebuilt
108-
# with the same exclusions, so it cannot be listed here.
104+
# An allowlist, so that a new repository-management file cannot leak into the
105+
# sdist by default. The CI sdist job proves the list is complete: it builds the
106+
# sdist, unpacks it, and fails if fewer tests collect there than in the
107+
# checkout. Hatchling force-includes pyproject.toml, README.md, LICENSE.txt,
108+
# PKG-INFO and .gitignore whatever this says, so the sdist always builds.
109109
[tool.hatch.build.targets.sdist]
110-
exclude = [
111-
"/.github",
112-
"/.codeclimate.yml",
113-
"/.readthedocs.yml",
114-
"/.sdk_metadata.json",
115-
"/CODEOWNERS",
116-
"/release-please-config.json",
117-
"/.release-please-manifest.json",
110+
include = [
111+
"/ldclient",
112+
"/contract-tests",
113+
"/docs",
114+
"/Makefile",
115+
"/setup.cfg",
116+
"/CHANGELOG.md",
117+
"/CONTRIBUTING.md",
118+
"/SECURITY.md",
119+
"/PROVENANCE.md",
118120
]

0 commit comments

Comments
 (0)