
Speakeasy: how do we set up automated publishing to npm and PyPI from CI?
Quick Answer: Use Speakeasy’s OpenAPI-native workflow to generate your SDKs in CI, then wire your pipeline to publish the generated packages to npm and PyPI using registry tokens and standard release triggers (tags or main-branch releases).
Frequently Asked Questions
How do I automate publishing Speakeasy-generated SDKs to npm and PyPI from CI?
Short Answer: Generate your SDKs with Speakeasy in CI, then add language-specific publish steps that use npm and PyPI tokens stored in your CI secrets, typically triggered on tagged releases.
Expanded Explanation:
Speakeasy’s job is to keep your SDK source in sync with your OpenAPI. Your CI’s job is to turn that source into versioned artifacts and push them to registries. Once you’ve run speakeasy quickstart and checked in the generated SDK repo(s), the rest is standard CI: install Speakeasy, regenerate on each release, run tests, bump versions, and publish to npm (for TypeScript/JavaScript) and PyPI (for Python).
The key is separation of concerns: OpenAPI → Speakeasy → SDK repo is one workflow; SDK repo → CI → npm/PyPI is another. Connect them with a release strategy (tags, GitHub Releases, or a release branch) and registry credentials in CI, and you get “ship with every commit” without manual publish steps.
Key Takeaways:
- Speakeasy keeps SDK code up to date; your CI pipeline handles packaging and publishing.
- Use tagged releases and registry tokens in CI secrets to publish automatically to npm and PyPI.
What’s the high-level process to wire CI for npm and PyPI publishing?
Short Answer: Configure CI to regenerate your SDKs with Speakeasy, run tests, then publish to npm and PyPI on a release trigger using registry-specific auth.
Expanded Explanation:
Think in two loops:
- SDK generation loop: On API changes, you update your OpenAPI spec, Speakeasy regenerates SDKs (locally or via CI-generated PRs), and you merge those changes into each SDK repo.
- Release loop: On a version bump (often a Git tag like
v1.4.0), CI builds and publishes each SDK to its registry.
In practice this looks like a CI job that runs on tags: it installs dependencies, installs Speakeasy, runs speakeasy run (or your configured generation step) if needed, runs tests, then executes npm publish for your TS/JS SDK and twine upload for your Python package. Auth is provided via environment variables wired from your CI’s secret store.
Steps:
- Prepare SDK repos: Run
speakeasy quickstart, commit generated SDKs, and ensure each has validpackage.json(npm) orpyproject.toml/setup.cfg(PyPI). - Configure CI triggers: Set pipelines to run on tags (e.g.,
v*) or main-branch merges. - Add publish jobs: Install Speakeasy, build/test SDKs, and publish using
npm publishandtwine uploadwith registry credentials from secrets.
Should I publish directly from my main API repo or from dedicated SDK repos?
Short Answer: Use dedicated SDK repos for npm and PyPI; publish from those repos’ CI pipelines rather than from the main API repo.
Expanded Explanation:
You can technically generate and publish from a single monorepo, but decoupling SDKs into their own repositories matches how most consumers expect to work with SDKs and keeps your CI simpler. Speakeasy is designed to generate “live artifacts” into language-specific repos where you can customize with guardrails (overlays, hooks) and then run language-native release workflows.
When you separate repos:
- Your API repo owns the OpenAPI spec and triggers regeneration (often via Speakeasy’s CI-generated PRs).
- Each SDK repo (TypeScript, Python, etc.) owns its publishing pipeline to npm or PyPI.
- You can release SDKs independently, while still keeping them in lockstep with API changes.
Comparison Snapshot:
- Option A: Single monorepo (API + SDKs)
- Shared CI, but more complex pipelines, tighter coupling of release timelines.
- Option B: Dedicated SDK repos (recommended)
- Clear ownership, language-specific CI, easy to onboard consumers and internal teams.
- Best for: Teams that want repeatable, language-idiomatic release flows and clearer separation between API and SDK lifecycles.
What does a concrete CI setup for npm and PyPI look like?
Short Answer: Use your CI provider (e.g., GitHub Actions) to run jobs on tags: install Speakeasy, build your SDKs, then call npm publish and twine upload with credentials from secrets.
Expanded Explanation:
You can adapt this pattern to any CI (GitHub Actions, GitLab CI, CircleCI, Jenkins), but the pieces are the same: environment setup, Speakeasy auth, build, test, and publish. Below is a GitHub Actions-flavored outline you can translate as needed.
What You Need:
- Speakeasy API key
- Create an API key in the Speakeasy Platform and set
SPEAKEASY_API_KEYas a CI secret.
- Create an API key in the Speakeasy Platform and set
- Registry credentials
- npm:
NPM_TOKENwithpublishscope. - PyPI:
PYPI_USERNAMEandPYPI_PASSWORDorPYPI_API_TOKEN.
- npm:
Example: npm (TypeScript/JavaScript SDK)
name: Publish TypeScript SDK
on:
push:
tags:
- "ts-sdk-v*"
jobs:
publish-npm:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
registry-url: "https://registry.npmjs.org"
- name: Install dependencies
run: npm ci
- name: Install Speakeasy CLI
run: |
brew install speakeasy-api/tap/speakeasy || curl -fsSL https://cli.speakeasyapi.dev/install.sh | sh
- name: Regenerate SDK (optional if already checked in)
env:
SPEAKEASY_API_KEY: ${{ secrets.SPEAKEASY_API_KEY }}
run: |
speakeasy run
- name: Run tests
run: npm test
- name: Bump version from tag
run: |
VERSION=${GITHUB_REF#refs/tags/ts-sdk-v}
npm version "$VERSION" --no-git-tag-version
- name: Publish to npm
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publish --access public
Example: PyPI (Python SDK)
name: Publish Python SDK
on:
push:
tags:
- "py-sdk-v*"
jobs:
publish-pypi:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install build tools
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Install Speakeasy CLI
run: |
curl -fsSL https://cli.speakeasyapi.dev/install.sh | sh
- name: Regenerate SDK (optional if already checked in)
env:
SPEAKEASY_API_KEY: ${{ secrets.SPEAKEASY_API_KEY }}
run: |
speakeasy run
- name: Run tests
run: pytest
- name: Build package
run: python -m build
- name: Publish to PyPI
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} # or "__token__"
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} # or actual token
run: twine upload dist/*
Adjust the tag patterns and version bumping logic to match your release policy (e.g., a single v1.2.3 tag you parse in both jobs).
How do I align Speakeasy’s regeneration cycle with my npm/PyPI release strategy?
Short Answer: Use Speakeasy to continuously generate SDK changes via PRs, and only publish to npm/PyPI when you accept those changes and cut a new version (usually via git tags).
Expanded Explanation:
Speakeasy is built for “ship with every commit,” but publishing to registries should still follow your semantic versioning and stability rules. The pattern that works for most teams:
- API changes: You update your OpenAPI spec in the API repo.
- Speakeasy generation: Speakeasy runs in CI, regenerates SDKs, and opens PRs in each SDK repo with the updated code.
- Review & merge: You review those PRs (diffs are code, not templates), run language-level tests, and merge to
main. - Release & publish: You bump the version in each SDK repo and create tags. CI picks up the tag and publishes to npm/PyPI.
This gives you a clear control point: SDKs can move fast with the API, but registry releases are still a deliberate step tied to semantic versioning and your support policy.
Why It Matters:
- Keeps SDKs in lockstep with a fast-moving API without forcing you to publish every internal change.
- Gives you predictable, audit-friendly releases—critical when your SDKs are infrastructure for agents, Terraform, or production apps.
Quick Recap
Automated publishing to npm and PyPI with Speakeasy is a two-layer system: Speakeasy turns your OpenAPI into continuously updated SDK repos, and your CI pipelines publish those SDKs to registries when you cut releases. Store npm and PyPI credentials as CI secrets, wire jobs to run on tags, regenerate and test with Speakeasy in the pipeline, and you’ll get repeatable, language-idiomatic releases that stay in sync with your API without manual publish steps.