Skip to content

Commit 9069bad

Browse files
jacalataclaude
andcommitted
fix: use exclude instead of include in packages.find to fix broken wheel
The include=["tableauserverclient*"] glob caused setuptools to strip the tableauserverclient/ prefix from all subpackages, producing a wheel where top_level.txt listed bin/helpers/models/server instead of tableauserverclient. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 897d088 commit 9069bad

4 files changed

Lines changed: 81 additions & 14 deletions

File tree

.github/workflows/publish-pypi.yml

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
name: Publish to PyPi
22

3-
# This will build a package with a version set by versioneer from the most recent tag matching v____
4-
# It will publish to TestPyPi, and to real Pypi *if* run on master where head has a release tag
5-
# For a live run, this should only need to be triggered by a newly published repo release.
6-
# This can also be run manually for testing
3+
# Builds the package, checks wheel structure, publishes to TestPyPI, runs smoke
4+
# tests against TestPyPI, then publishes to real PyPI only if all prior steps pass.
75
on:
86
release:
97
types: [published]
@@ -13,31 +11,76 @@ on:
1311
- 'v*.*.*'
1412

1513
jobs:
16-
build-n-publish:
17-
name: Build dist files for PyPi
14+
build:
15+
name: Build and check wheel
1816
runs-on: ubuntu-latest
1917
steps:
2018
- uses: actions/checkout@v4
2119
with:
2220
fetch-depth: 0
2321
- uses: actions/setup-python@v5
2422
with:
25-
python-version: 3.13
23+
python-version: '3.13'
2624
- name: Build dist files
2725
run: |
2826
python -m pip install --upgrade pip
2927
python -m pip install -e .[test] build
3028
python -m build
3129
git describe --tag --dirty --always
32-
33-
- name: Publish distribution 📦 to Test PyPI # always run
30+
- name: Check wheel structure
31+
run: python -m pytest test/test_packaging.py -m packaging -v
32+
- uses: actions/upload-artifact@v4
33+
with:
34+
name: dist
35+
path: dist/
36+
37+
publish-test-pypi:
38+
name: Publish to Test PyPI
39+
needs: build
40+
runs-on: ubuntu-latest
41+
steps:
42+
- uses: actions/download-artifact@v4
43+
with:
44+
name: dist
45+
path: dist/
46+
- name: Publish to Test PyPI
3447
uses: pypa/gh-action-pypi-publish@release/v1 # license BSD-2
3548
with:
3649
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
3750
repository_url: https://test.pypi.org/legacy/
38-
39-
- name: Publish distribution 📦 to PyPI
40-
if: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v') }}
51+
skip_existing: true
52+
53+
smoke-test-pypi:
54+
name: Smoke test from Test PyPI
55+
needs: publish-test-pypi
56+
strategy:
57+
fail-fast: false
58+
matrix:
59+
os: [ubuntu-latest, macos-latest, windows-latest]
60+
python-version: ['3.x']
61+
runs-on: ${{ matrix.os }}
62+
steps:
63+
- uses: actions/setup-python@v5
64+
with:
65+
python-version: ${{ matrix.python-version }}
66+
- name: Install from Test PyPI
67+
run: |
68+
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ tableauserverclient
69+
- name: Smoke test
70+
run: |
71+
python -c "import tableauserverclient as TSC; server = TSC.Server('http://example.com', use_server_version=False)"
72+
73+
publish-pypi:
74+
name: Publish to PyPI
75+
needs: smoke-test-pypi
76+
if: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v') }}
77+
runs-on: ubuntu-latest
78+
steps:
79+
- uses: actions/download-artifact@v4
80+
with:
81+
name: dist
82+
path: dist/
83+
- name: Publish to PyPI
4184
uses: pypa/gh-action-pypi-publish@release/v1 # license BSD-2
4285
with:
4386
password: ${{ secrets.PYPI_API_TOKEN }}

.github/workflows/pypi-smoke-tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ name: Pypi smoke tests
55

66
on:
77
workflow_dispatch:
8+
release:
9+
types: [published]
810
schedule:
911
- cron: 0 11 * * * # Every day at 11AM UTC (7AM EST)
1012

pyproject.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ tableauserverclient = ["*"]
4242

4343
[tool.setuptools.packages.find]
4444
where = ["."]
45-
include = ["tableauserverclient*"]
45+
exclude = ["test*", "samples*", "test_e2e*", "docs*", "dist*", "build*"]
4646

4747
[tool.setuptools.dynamic]
4848
version = {attr = "versioneer.get_version"}
@@ -68,7 +68,10 @@ exclude = ['/bin/']
6868
[tool.pytest.ini_options]
6969
testpaths = ["test"]
7070
addopts = "--junitxml=./test.junit.xml -n auto"
71-
markers = ["e2e: mark test as end-to-end (requires a real Tableau server)"]
71+
markers = [
72+
"e2e: mark test as end-to-end (requires a real Tableau server)",
73+
"packaging: mark test as requiring a built wheel in dist/",
74+
]
7275

7376
[tool.versioneer]
7477
VCS = "git"

test/test_packaging.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import zipfile
2+
from pathlib import Path
3+
import pytest
4+
5+
pytestmark = pytest.mark.packaging
6+
7+
8+
def _find_wheel():
9+
wheels = list(Path("dist").glob("tableauserverclient-*.whl"))
10+
if not wheels:
11+
pytest.skip("No wheel in dist/ -- run 'python -m build --wheel' first")
12+
return max(wheels, key=lambda p: p.stat().st_mtime)
13+
14+
15+
def test_wheel_only_tableauserverclient_at_root():
16+
with zipfile.ZipFile(_find_wheel()) as whl:
17+
top_dirs = {n.split("/")[0] for n in whl.namelist() if "/" in n}
18+
non_dist_info = {d for d in top_dirs if not d.endswith(".dist-info") and not d.endswith(".data")}
19+
assert non_dist_info == {"tableauserverclient"}, f"Unexpected top-level entries: {non_dist_info}"

0 commit comments

Comments
 (0)