feat(ci): add post-publish PyPI smoke test - #134
Conversation
Adds a smoke-test job to the publish workflow that installs docvet from PyPI after release and verifies docvet --version and docvet check --help both succeed.
Epic breakdown for next batch of improvements: publish reliability, docs navigation, advanced exclude patterns, and CLI progress output.
Prevents runaway billing if pip install hangs or PyPI is slow.
Fix stale AC-to-Test line numbers shifted by timeout-minutes commit, fill Code Review section with findings and verification checklist.
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
Adds a post-release verification step to the release pipeline so maintainers get automated confirmation that the PyPI artifact installs and the CLI runs correctly after publishing.
Changes:
- Add a
smoke-testjob to.github/workflows/publish.ymlthat waits for PyPI propagation, installs the just-published version, and runs basic CLI commands. - Add/update BMAD planning + implementation artifacts documenting Epic 17 (Publish Reliability) and current sprint status.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/publish.yml |
Adds a post-publish PyPI smoke-test job chained after build-and-publish. |
_bmad-output/planning-artifacts/implementation-readiness-report-2026-02-25.md |
New implementation readiness report artifact (generated planning output). |
_bmad-output/planning-artifacts/epics-next.md |
New “next epics” planning doc that includes the smoke-test requirement (plus other future work). |
_bmad-output/implementation-artifacts/sprint-status.yaml |
Updates sprint/epic tracking status (marks Epic 17 done, adds Epics 18–20 backlog). |
_bmad-output/implementation-artifacts/17-1-post-publish-pypi-smoke-test.md |
New story implementation artifact describing the smoke-test workflow change. |
_bmad-output/implementation-artifacts/16-2-integration-verification-and-documentation.md |
Marks story 16.2 status as done. |
|
|
||
| NFR1: The smoke test shall wait for PyPI propagation (~60s) before attempting install. | ||
|
|
||
| NFR2: The smoke test job shall use `needs: [publish]` to depend on successful publication. |
There was a problem hiding this comment.
NFR2 states the smoke test should use needs: [publish], but the actual workflow job it needs to depend on (and the story/implementation) is build-and-publish. Keeping the job name consistent in the requirements doc will avoid confusion when implementing/maintaining the workflow.
| NFR2: The smoke test job shall use `needs: [publish]` to depend on successful publication. | |
| NFR2: The smoke test job shall use `needs: [build-and-publish]` to depend on successful publication. |
There was a problem hiding this comment.
Addressed in latest push — updated NFR2 to reference build-and-publish to match the actual workflow job name.
|
|
||
| ### Additional Requirements | ||
|
|
||
| - From architecture: `_is_excluded` lives in `src/docvet/discovery.py:106-129`, uses `fnmatch.fnmatch` for both component-level and path-level matching. Python 3.12+ `pathlib.PurePath.full_match()` supports `**` natively. |
There was a problem hiding this comment.
pathlib.PurePath.full_match() is referenced here, but PurePath doesn’t have a full_match method in Python’s pathlib API. This should point to the correct mechanism (e.g., PurePath.match() for glob-style matching, or fnmatch/glob semantics) so the implementation guidance is accurate.
| - From architecture: `_is_excluded` lives in `src/docvet/discovery.py:106-129`, uses `fnmatch.fnmatch` for both component-level and path-level matching. Python 3.12+ `pathlib.PurePath.full_match()` supports `**` natively. | |
| - From architecture: `_is_excluded` lives in `src/docvet/discovery.py:106-129`, uses `fnmatch.fnmatch` for both component-level and path-level matching. Python's `pathlib.PurePath.match()` supports `**` patterns natively. |
There was a problem hiding this comment.
Keeping as-is — PurePath.full_match() was added in Python 3.12 (see docs). Since docvet requires python >= 3.12, this is the correct API. full_match is preferred over .match() because it provides explicit full-path glob matching with ** support.
|
|
||
| **Given** an exclude pattern containing `**` (e.g., `**/test_*.py`) | ||
| **When** `_is_excluded` evaluates a file path | ||
| **Then** it matches paths across any directory depth using `pathlib.PurePath.full_match()` (e.g., `src/tests/test_foo.py` matches, `src/foo.py` does not) |
There was a problem hiding this comment.
Acceptance criteria references pathlib.PurePath.full_match(), but that method doesn’t exist on PurePath in Python’s pathlib API. Update the AC to use the correct API/approach (e.g., PurePath.match() or an explicit glob/fnmatch strategy) so future implementation and tests aren’t based on a nonexistent method.
| **Then** it matches paths across any directory depth using `pathlib.PurePath.full_match()` (e.g., `src/tests/test_foo.py` matches, `src/foo.py` does not) | |
| **Then** it matches paths across any directory depth using glob-style matching (e.g., `pathlib.PurePath.match("**/test_*.py")`) (e.g., `src/tests/test_foo.py` matches, `src/foo.py` does not) |
There was a problem hiding this comment.
Keeping as-is — same rationale as above. PurePath.full_match() exists in Python 3.12+ and is the intended API for this AC. See Python 3.12 pathlib docs.
| smoke-test: | ||
| runs-on: ubuntu-latest | ||
| needs: [build-and-publish] | ||
| timeout-minutes: 5 | ||
| steps: | ||
| - name: Extract version from tag | ||
| id: version | ||
| run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Wait for PyPI propagation | ||
| run: sleep 60 | ||
|
|
||
| - name: Install docvet from PyPI | ||
| run: pip install --no-cache-dir docvet==${{ steps.version.outputs.VERSION }} | ||
|
|
There was a problem hiding this comment.
smoke-test uses pip install without pinning/setting up a Python version. The package declares requires-python = ">=3.12" (pyproject.toml), but ubuntu-latest’s default pip may be tied to an older Python (e.g., 3.10/3.11), which would make this job fail with a Python version constraint error. Add an explicit Python setup step (e.g., actions/setup-python to 3.12) and invoke installs via that interpreter (e.g., python -m pip ...) so the smoke test runs under a supported runtime.
There was a problem hiding this comment.
Addressed in latest push — added actions/setup-python@v5 with python-version: '3.12' and switched to python -m pip install to guarantee the correct interpreter.
🤖 I have created a release *beep* *boop* --- ## [1.4.0](v1.3.0...v1.4.0) (2026-02-26) ### Features * **ci:** add post-publish PyPI smoke test ([#134](#134)) ([5b89f3d](5b89f3d)) * **cli:** add per-check timing and total execution time ([#140](#140)) ([56c6c62](56c6c62)), closes [#24](#24) * **cli:** add progress bar for file processing ([#139](#139)) ([0f5e5dd](0f5e5dd)), closes [#24](#24) * **discovery:** add trailing-slash and double-star pattern support ([3696e6a](3696e6a)) * **discovery:** add trailing-slash and double-star pattern support ([#137](#137)) ([3696e6a](3696e6a)) * **docs:** add breadcrumb back-links to rule pages ([#136](#136)) ([17e985b](17e985b)) ### Bug Fixes * **docs:** remove redundant f-string in rule_header macro ([17e985b](17e985b)) ### Documentation * **cli:** add story 20.2 implementation record ([56c6c62](56c6c62)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
After publishing to PyPI, there was no automated verification that the released package actually installs and runs correctly. Maintainers had to manually verify each release.
smoke-testjob topublish.ymlthat runs afterbuild-and-publishpip install --no-cache-dirdocvet --versionanddocvet check --helpboth exit 0timeout-minutes: 5to prevent runaway billingTest: CI only (workflow runs on
release: [published]events)PR Review
Checklist
uv run pytest)uv run ruff check .)uv run ty check)!in title andBREAKING CHANGE:in bodyReview Focus
The smoke-test job in
.github/workflows/publish.yml— verify dependency chain (needs: [build-and-publish]), version extraction, and thatupdate-tagsdependency is unchanged.Related
Epic 17: Publish Reliability