Skip to content

Add PyPI release workflow (Trusted Publishing / OIDC) - #16

Merged
AnderRV merged 9 commits into
mainfrom
feat/pypi-release-workflow
Aug 21, 2026
Merged

Add PyPI release workflow (Trusted Publishing / OIDC)#16
AnderRV merged 9 commits into
mainfrom
feat/pypi-release-workflow

Conversation

@AnderRV

@AnderRV AnderRV commented Aug 21, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds .github/workflows/release.yml, publishing to PyPI on a published GitHub Release (or manual workflow_dispatch), via pypa/gh-action-pypi-publish with OIDC — no stored secret. This matches the Trusted Publisher you already configured on pypi.org (repository ZenRows/zenrows-python-sdk, workflow release.yml, environment (Any)) — that entry is inert until a run from this exact workflow file actually authenticates, which this PR provides.
  • Filename/location aren't a style choice: GitHub Actions only discovers workflows under .github/workflows/, and the trusted publisher is pinned to the literal filename release.yml.
  • Mirrors zenrows-node-sdk's existing release.yml: runs make check && make test before building, verifies the release tag matches pyproject.toml's version (fails fast on mismatch, same check node-sdk does against package.json), and adds workflow_dispatch as a second trigger so a failed run can be retried without cutting a new tag.
  • Base is feature/batch-api/rc, not main — same reasoning as the CI workflow in Add CI: lint, format check, typecheck, and tests on PR #15: pyproject.toml/uv only exist on this branch today.

Nothing publishes until this merges and an actual GitHub Release gets published — this PR alone has no side effects.

Test plan

  • python3 -c "import yaml; yaml.safe_load(open('.github/workflows/release.yml'))" — valid YAML
  • Verified the tag/version-comparison shell logic against the real pyproject.toml (version = "1.4.0") — extracts and compares correctly
  • Confirmed make check, make test, make build all exist as Makefile targets on this branch
  • Can't dry-run the actual publish step from here (no GitHub Release exists yet, and OIDC only works inside a real Actions run) — first real verification happens on the first published release

Note: no git tag convention exists yet in this repo (first-ever automated release). Assumed v-prefixed tags (v1.4.0) to match zenrows-node-sdk's convention — flag if you want a different scheme before the first release.

https://claude.ai/code/session_01DTXXBERPGEnYiYP4Mt3FjF

asyschikov and others added 8 commits July 13, 2026 12:14
Add ZenRowsBatchClient — a synchronous, typed client for the async-job
Batch API — and migrate the package to a src/ layout.

- Typestate resource handles: JobRef/JobHandle, RunRef/RunHandle,
  ExportRef/ExportHandle (id-only refs vs loaded handles with .data),
  with job.run.* (current run) and job.schedule.* facets that
  disambiguate the two "pause" endpoints.
- submit_regular / submit_open / submit_scheduled, typed schedule
  builders (At/Rate/Calendar), CSV file-input upload, and cursor-free
  scanners (iter_jobs / iter_runs / iter_results).
- Waiters (run / ingest / export) with jittered backoff; transport-level
  retries of transient failures (429/502/503/504 + network errors) on
  idempotent requests, honoring Retry-After.
- Downloads pulled straight from each result's presigned result_url (no
  /content endpoint): bulk download_to_dir / download_to_memory,
  server-side zip download_all_results, and per-task
  download_task_to_file / download_task_to_memory.
- Webhook management (get/put/delete job webhook, test_webhook), HMAC key
  lifecycle, and offline cost estimation (client.estimate_cost).
- RFC 7807 error mapping to BatchAPIError; pydantic v2 models generated
  from the OpenAPI spec; generated markdown API reference and runnable
  examples.
* Add fetch()/extract() onto the existing Batch RC branch, current logo

Branched from feature/batch-api/rc (tip e0359e1) rather than main -
that branch already had a complete, unreleased ZenRowsBatchClient
(submit_job/submit_regular/submit_open/submit_scheduled, pagination,
CSV upload, webhooks, HMAC rotation, all tested) that's more mature
than anything built for Node/Go this session. Building a second batch
client on main would have conflicted with this real, already-partially-
reviewed work (PRs #10/#11 are spec-sync patches on top of this exact
branch).

- fetch()/fetch_async() primary, get()/get_async() deprecated aliases.
  extract()/extract_async(mode=...) added.
- 18 new tests targeting return-value behavior (previous tests only
  asserted on call arguments, never on what the client returns):
  non-2xx responses pass through unchanged rather than raising,
  extract() mode is not validated (any string passes through, now
  documented via test), get()/fetch() produce identical requests,
  extract() doesn't mutate the caller's params dict, constructor/
  close()/context-manager behavior. client.py coverage 87%->97%.
- Reworded 2 lowercase "scraper API" mentions in docs/openapi.yaml.
- Added the missing logo header + Extract section to this branch's
  README (predates the main-branch logo fix, different branch/file).

Known gap, not addressed here: this branch's batch client has 58
public methods vs. 13 in the new Node/Go reference clients - full
parity (scheduling, webhook CRUD, HMAC, CSV, exports, pagination
iterators, retry/backoff) is a separate, larger piece of work, not
attempted in this session.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTXXBERPGEnYiYP4Mt3FjF

* Fix: brand spelling is Zenrows, not ZenRows in this session's new prose

None of this repo's own code had wrong casing (it was already correct
pre-session), but the fetch()/extract() docstrings and README sections
I added this session used "ZenRows" in prose. Confirmed the correct
spelling via zenrows-web commit 9f1f00c6d (PR #1847, merged
2026-08-11): "Brand spelling is Zenrows (capital Z, lowercase r)."

Fixed: README title/intro/Extract-section/Batch-intro, and client.py's
module/class/method docstrings for fetch()/extract(). Left
docs/openapi.yaml's "ZenRows Batch API" title untouched — it mirrors
zenrows-web's own docs/api/conveyor/api.md, which itself still says
"ZenRows" and hasn't been swept yet; matching the current authoritative
source spec matters more here than applying the brand-copy rule to a
generated/copied artifact. Class names (ZenRowsClient,
ZenRowsBatchClient) are unaffected either way — published exports keep
their existing casing per that same PR's own precedent.

Re-verified: 117/117 tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTXXBERPGEnYiYP4Mt3FjF

* Fall back to Autoparse when Extract's auto mode hits AUTH010

extract(mode="auto") (the default) is a domain-gated open beta - when
the target domain isn't enabled yet, the API returns 402 with code
AUTH010. Catch that and retry once with autoparse=True instead of
raising, matching the CLI's own extract adapter. Opt out with
fallback_to_autoparse=False on both extract() and extract_async().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTXXBERPGEnYiYP4Mt3FjF

* Fix wording: Extract/Batch are beta, not invite-gated

"Private beta" implied an invite/enablement step for the whole
product. Neither Extract nor Batch actually gate that way — confirmed
live, a plain API key can call both immediately. Extract does gate
per-domain (AUTH010), which is a separate, narrower fact already
covered by the fallback logic above; it isn't "you need an invite to
use Extract at all."

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTXXBERPGEnYiYP4Mt3FjF

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Targets that need js_render/premium_proxy used to fail extract()
with a 400 REQS002 unless the caller passed those manually - Zoopla
listing pages are a real example. adaptive_stealth=True (default)
sends mode="auto" at the wire-param level (unrelated to this
method's own `mode` argument, which picks the extract contract) on
both the extract attempt and the Autoparse fallback. Pass
adaptive_stealth=False to disable and set js_render/premium_proxy
yourself.

Verified live: extract(zoopla_url) now succeeds with zero extra
params, and mode=auto doesn't interfere with AUTH010 detection on
domains that are still gated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTXXBERPGEnYiYP4Mt3FjF
No GitHub Actions existed in this repo at all before this. Runs
make check (ruff lint + format --check, ty typecheck) and make test
(pytest) on every PR and on push to main/feature/batch-api/rc.

Deliberately scoped to lint/checks/tests only - no publish
automation. See the separate investigation into what a PyPI release
workflow would need before that gets built.


Claude-Session: https://claude.ai/code/session_01DTXXBERPGEnYiYP4Mt3FjF

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Publishes to PyPI on a published GitHub Release (or manual
workflow_dispatch), via pypa/gh-action-pypi-publish with OIDC —
no stored token, matching the trusted publisher already configured
on pypi.org for this repo (repository ZenRows/zenrows-python-sdk,
workflow release.yml).

Runs make check && make test before uv build, and fails fast if the
release tag doesn't match pyproject.toml's version, mirroring
zenrows-node-sdk's release.yml pattern.

Based on feature/batch-api/rc, not main, since that's where
pyproject.toml/uv currently live (same reasoning as the CI workflow
in #15).
Base automatically changed from feature/batch-api/rc to main August 21, 2026 07:57
@AnderRV AnderRV self-assigned this Aug 21, 2026
@AnderRV
AnderRV merged commit 6d9cd95 into main Aug 21, 2026
1 check passed
@AnderRV
AnderRV deleted the feat/pypi-release-workflow branch August 21, 2026 08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants