feat: add PR validation workflow#365
Conversation
The Python SDK had no pull_request-triggered checks, so regen PRs (speakeasy-sdk-regen-*) carried an empty status-check rollup and the auto-merge flow would merge them with zero validation. The TypeScript SDK validates every regen PR via pr-validation.yaml; Python had no equivalent. Add a single-file PR Validation workflow that builds the package and runs the SDK's existing quality gates (mypy, pyright, pylint) via uv. No tests exist in this repo yet, so no API-key secret is needed, which also lets the workflow run on fork PRs. paths-ignore on .speakeasy/in.openapi.yaml mirrors TS so pure spec-bump PRs don't double-trigger. This registers a `validate` check that the auto-merge wait_for_checks step will gate on once both land.
There was a problem hiding this comment.
Perry's Review
Adds a PR validation workflow filling the gap identified in #364: regen PRs had no pull_request-triggered checks, so the auto-merge flow could publish to PyPI with zero validation. The single-file design mirrors the TypeScript SDK's approach, uses least-privilege permissions (contents: read), correctly ignores spec-only bumps via paths-ignore, and invokes the four quality gates (uv build, mypy, pyright, pylint) via uv's dev dependency group.
Verdict: 💬 LGTM — posted as COMMENT (maintainer app lacks pull_requests: write on OpenRouterTeam; APPROVE unavailable)
Details
Risk: 🟢 Low
CI: all passing ✅
Findings:
- nit: actions/checkout@v5 — consider pinning to a commit SHA; the auto-merge workflow uses an immutable SHA pin as a model
- nit: astral-sh/setup-uv@v5 — same consideration; floating major-version tags are consistent with the Speakeasy action style already in the repo
Research: N/A (small tier — deep research skipped)
Security: no concerns — permissions: contents: read, no secrets, no write paths
Test coverage: no tests/ directory; uv build + mypy/pyright/pylint is the appropriate validation tier for a generated SDK
Unresolved threads: none
Scope: first review (full)
Review: tier=small · model=claude-sonnet-latest · score=0.4
Why
The Python SDK has no
pull_request-triggered checks, so Speakeasy regen PRs (speakeasy-sdk-regen-*) carry an empty status-check rollup. The auto-merge flow (#364) therefore merges them — and publishes to PyPI — with zero validation. The TypeScript SDK validates every regen PR via itspr-validation.yaml; Python had no equivalent.Confirmed empirically: Python regen PRs #357/#352/#349/#347 all have
statusCheckRolluplength 0, while the TS regen PR #512 (sameapp/github-actionsbot author) carries avalidate / validatecheck frompull_request— so apull_requestworkflow does run on bot-authored regen PRs. The gap was purely the missing workflow.What
A single-file
PR Validationworkflow that builds the package and runs the SDK's existing quality gates viauv:uv builduv run --group dev mypy srcuv run --group dev pyright srcuv run --group dev pylint src --rcfile pylintrcAll four pass clean on current generated code (mypy: 660 files OK; pyright: 0 errors; pylint: 10.00/10).
Notes:
tests/, no pytest dep), so nothing here needs one. Keeping it secret-free also lets it run on fork PRs.paths-ignore: .speakeasy/in.openapi.yamlmirrors TS so pure spec-bump PRs (handled bysdk_generation_for_spec_change.yaml) don't double-trigger. Regen PRs change generatedsrc/**, so they are validated.pr-validation → validation-checks → composite actionindirection — there's no second caller, so the nesting isn't needed.Interaction with #364
Once both land, the auto-merge
wait_for_checks()step sees thevalidatecheck, waits for it, and only direct-merges on success — so validation genuinely gates auto-merge. No change to #364 required; no ordering dependency.Verification
actionlintclean.main.validatecheck should appear and pass on this PR itself (it touches.github/**, not the ignored spec path).