Skip to main content

Publish your SDKs

Once you are satisfied with the structure and naming conventions of your SDK, you can distribute them to end users by publishing them to package registries (for example, npm, PyPI, and maven). After a one-time setup, future Stainless SDKs flow from our private preview repository to your GitHub organization where they're picked up by the package registries.

The Stainless managed release flow handles versioning and changelogs expected in quality SDKs. You can even automate this process so subsequent changes to your OpenAPI spec trigger new SDK releases.

Identify or create your GitHub repositories

When getting started, we generate SDKs into private preview repositories under stainless-sdks. But your users expect them to be found in repos within your GitHub organization.

If you do not have a repository yet, you need to create one.

Be sure to select the correct organization as the owner and set a name that meaningfully identifies your SDK—for example, <my-brand>-python is a common choice for Python SDKs.

The repository can be public or private.

info

You need one GitHub repo for each language SDK (one for Node, one for Python, and so on).

Install the Stainless GitHub App

The Stainless GitHub App does the work of pushing preview SDKs to your repos. To install it:

  1. Navigate to the correct project in the Stainless dashboard.
  2. For one of your target languages, click Edit > Install GitHub App. (It does not matter from which language you trigger this process.)
  3. Follow the installation flow. When prompted, select which repositories the Stainless GitHub App can access.
  4. Once repositories have been selected and permissions have been reviewed, click Install & Authorize.
warning

By default All repositories is selected, but we recommend you only select the repositories you plan to use for Stainless SDKs.

GitHub App authorization screen

Connect package registries

Package registries make your packages readily available to your users. Once configured, Stainless automatically updates package registries on releases, but you have to complete a one-time setup and give Stainless permissions to update your packages on your behalf.

Node/TypeScript: npm

Get an Automation API token
  1. Log in or sign up at npm.
  2. Click on your profile picture on the top right to access a dropdown and then navigate to Access Tokens > Generate New Token.
  3. Choose a token version:
    • Granular Access Token, recommended if you’re publishing the SDK under a scope (for example, @<your-company-name>/sdk)
    • Classic Token, required if not publishing under a scope
  4. If creating a Classic Token, ensure you have selected Automation as the token type. Apart from that, the token settings you choose are up to you.
Add the API token to your Node GitHub repository
  1. Navigate to the actions secrets for your repository at Secrets and variables > Actions > New repository secret, the URL should look like https://github.com/<org>/<repo>/settings/secrets/actions/new.
  2. Add a new secret named NPM_TOKEN with your API token from the Get an Automation API token step.
Choose a package name and update the Stainless dashboard
  1. You have to choose an available name in the registry. Suggested names are:

    • <your-company-name>
    • @<your-company-name>/sdk (This requires you have the right npm organization.)

    You can check if the name is available.

  2. Update the Stainless dashboard with your package name. The SDK generation process restarts and pushes to NPM once it is done.

Deno: jsr

Create a package
  1. Go to JSR > Publish a package.
  2. Select an appropriate scope and package name, click Create.
(Option 1) Publish with GitHub OIDC
  1. Navigate to <Package> > Settings > GitHub Repository.

  2. Link your production GitHub repository to the package you created in the previous step.

  3. Navigate to your <Scope> > Settings > GitHub Actions security.

  4. Select Do not restrict publishing. This will let our bot publish even though it is not a member of your scope.

  5. Modify your stainless config:

    node:
    package_name: <package-name>
    publish:
    npm: true
    jsr:
    package_name: @<scope>/<package>
(Option 2) Use access token to publish
  1. Navigate to Account > Tokens > Personal access tokens > Create new token.

  2. Navigate through wizard to get your token:

    1. Publish packages
    2. A development machine
    3. Create a token
    4. Create a token for the package name.
  3. Modify your stainless config:

    node:
    package_name: <package-name>
    publish:
    npm: true
    jsr:
    package_name: @<scope>/<package>
    use_access_token: true
  4. Navigate to the actions secrets for your repository at Secrets and variables > Actions > New repository secret, the URL should look like https://github.com/<org>/<repo>/settings/secrets/actions/new.

  5. Add a new secret named JSR_TOKEN with your API token.

Python: PyPI

Get an API token
  1. Log in or sign up at PyPI.
  2. Navigate to your account settings.
  3. Scroll down to the API tokens section and click Add API Token. You may have to verify your email address and set up 2FA if you haven't done so already.
Add the API token to your Python GitHub repository
  1. Navigate to the actions secrets for your repository at Secrets and variables > Actions > New repository secret, the URL should look like https://github.com/<org>/<repo>/settings/secrets/actions/new.
  2. Add a new secret named PYPI_TOKEN with your API token from the Get an API token step.
Choose a package name and update the Stainless dashboard
  1. You have to choose an available name in the registry. Suggested names are:

    • <your-company-name>
    • <your-company-name>-client

    You can check whether the name is available by testing the link at https://pypi.org/project/<your-package-name>. If it is available, PyPI should say that the package is now available.

  2. Update the Stainless dashboard with your package name. The SDK generation process restarts and pushes to PyPI once it is done.

Ruby: RubyGems

Get an API token
  1. Log in or sign up at RubyGems.
  2. Navigate to your settings tab and click on "API keys" link towards bottom of page.
  3. Create a new API key with "push rubygems" privileges, make sure MFA is disabled.
Add the API token to your Ruby GitHub repository
  1. Navigate to the actions secrets for your repository at Secrets and variables > Actions > New repository secret, the URL should look like https://github.com/<org>/<repo>/settings/secrets/actions/new.
  2. Add a new secret named GEM_HOST_API_KEY with your API token from the Get an API token step.
Choose a package name and update the Stainless dashboard
  1. You have to choose an available name in the registry. Suggested names are:

    • <your-company-name>
    • <your-company-name>-client

    You can check whether the name is available by testing the link at https://rubygems.org/gems/<your-package-name>. If it is available, RubyGems should say that the package is now available.

  2. Update the Stainless dashboard with your package name. The SDK generation process restarts and pushes to RubyGems once it is done.

Java & Kotlin: Sonatype Maven Central

We publish JVM packages to Sonatype. Sonatype has both a legacy (OSSRH) publishing flow and a new Central Portal publishing flow.

If you do not already publish to OSSRH, you will most likely have to use the new Central Portal publishing flow. If you are already publishing to OSSRH, you can continue to do so.

warning

Setting up Sonatype can be an involved process. We recommend you go through this process after getting your SDKs into a satisfactory state.

Modify Stainless configuration (Central Portal only)

For backwards compatibility, currently the default sonatype_platform is ossrh. If you are using the Central Portal, you need to update the Stainless configuration.

In the Stainless dashboard, under targets.{java,kotlin}.publish.maven, add sonatype_platform: portal.

targets:
java:
production_repo: ...
reverse_domain: com.example.api
publish:
maven:
sonatype_platform: portal
Add namespace (Central Portal only)
  1. Add a namespace on Sonatype Portal

    The namespace will be part of your package's name. It will be the inverse of the domain that you own. i.e. tld.domain.subdomain

  2. Copy down the Verification Key

    You will be able to prove your ownership of the namespace by adding a TXT record of the verification key to your domain.

Prove ownership over your domain

Add a temporary TXT record to the DNS for your primary domain (the reverse of your groupId) to verify that the person registering this Java group actually controls the domain.

If using the Central Portal flow, the contents of the TXT record is the verification key provided by Sonatype.

If using the OSSRH flow, the contents of the TXT record is the ID of the JIRA ticket created in the previous step (for example, OSSRH-12345).

Generate user token (Central Portal only)

From the Sonatype dashboard, generate a user token XML file.

Extract <username>…</username> and <password>…</password> from the XML file and store them somewhere secure.

<server>
<id>${server}</id>
<username>//P7KNy5</username>
<password>+8sEiCiUrSoODM/S4yFhiKqUWJzS1lDbcac5I7hVzPS4</password>
</server>
Set up PGP package signing
  1. Install GnuPG.

  2. In a terminal, run gpg --gen-key. You are prompted to enter your name, email, and a passphrase for the keypair.

  3. Run gpg --list-keys --keyid-format short:

    % gpg --list-keys --keyid-format short
    [keyboxd]
    ---------
    pub ed25519/7FAD9FAA 2023-08-02 [SC] [expires: 2026-08-01]
    418D41921938A753CAAE57985114AD037FAD9FAA
    uid [ultimate] Your Organization <you@yourorg.com>
    sub cv25519/07EB38A7 2023-08-02 [E] [expires: 2026-08-01]
  4. Export the key in ASCII-armored format (7FAD9FAA is the short key ID in this example).

    $ gpg --export-secret-keys --armor 7FAD9FAA
    -----BEGIN PGP PRIVATE KEY BLOCK-----

    lIYEZMpnEhYJKwYBBAHaRw8BAQdA1FQMKz+wwliKNdLehegZP0QiaKrZJqADNyVn
    VCUzIrD+BwMCwZrqVbVPfSr8NwxEh3M6kWtMGmnLMOk/NWVe7dtCxDxo37l/Ncxj
    Mm9EZiH6WXwoXXq20nOW354oNOVz/UPvDU+oaRDDUM9SYs392i69WLQjWW91ciBP
    cmdhbml6YXRpb24gPHlvdUB5b3Vyb3JnLmNvbT6ImQQTFgoAQRYhBEGNQZIZOKdT
    yq5XmFEUrQN/rZ+qBQJkymcSAhsDBQkFo5qABQsJCAcCAiICBhUKCQgLAgQWAgMB
    Ah4HAheAAAoJEFEUrQN/rZ+qgtsBAMCBuTqYEJljxStRO7SsMLWOc47CIIXD0Yid
    CbySBX0ZAP9DXuuGVYbHFONvHxNKszu2hY9A1BbRuNjeGeWuVOw9ApyLBGTKZxIS
    CisGAQQBl1UBBQEBB0AFxkZ+ZdEN7Epwri/w5ETAf+MOqdwAP2sS6TccSjEiXQMB
    CAf+BwMCtKG1TBUSC/z8tn761I5j+ifVIuMqdQPYIhZtjvIyC+NyrBi0j1ZtUG4A
    DAeDKNyM63uyb7omOH8+Lu0J71SGhVnZWszUOf3rrT/TA5MktYh+BBgWCgAmFiEE
    QY1Bkhk4p1PKrleYURStA3+tn6oFAmTKZxICGwwFCQWjmoAACgkQURStA3+tn6qa
    XAD/W2ucVURngmCUiUtdjQAZz36yQYPQmBhcdabZMyXKHz0A/itwYkuRbD2mp4p/
    xJk/QLXs/2/xBA6s0ROVVspy6MEA
    =p/g/
    -----END PGP PRIVATE KEY BLOCK-----
  5. Publish your gpg public key. (Using the same 7FAD9FAA from above as key ID example).

    $ gpg --keyserver keyserver.ubuntu.com --send-keys 7FAD9FAA

    If you get the following error:

    gpg: sending key XXXXXX to hkp://keyserver.ubuntu.com
    gpg: keyserver send failed: No route to host

    Then your computer might be trying to use IPv6 and failing. Run the following to get keyserver.ubuntu.com's IPv4 addresses:

    $ host keyserver.ubuntu.com
    keyserver.ubuntu.com has address XXX.XXX.XXX.XX
    keyserver.ubuntu.com has address XXX.XXX.XXX.XX
    keyserver.ubuntu.com has IPv6 address XXXX:XX:XXXX:XXXX::XXX
    keyserver.ubuntu.com has IPv6 address XXXX:XX:XXXX:XXXX::XXX

    Then rerun the gpg command with one of the IPv4 addresses:

    $ gpg --keyserver XXX.XXX.XXX.XX --send-keys 7FAD9FAA
Add Sonatype credentials and PGP secret key to Java/Kotlin GitHub repository
  • In your GitHub repository, navigate to Settings > Secrets and Variables > Actions.
  • Click New Repository Secret.

github-secrets

  • Set Name to {MY_ORG}_SONATYPE_GPG_SIGNING_KEY (replace {MY_ORG} with your organization name).
  • In Secret, paste the PGP private key block from gpg --export-secret-keys --armor <keyid>.
  • Click Add Secret.

github-new-secret

  • Following the same process, add these secrets:

    {MY_ORG}_SONATYPE_GPG_SIGNING_PASSWORD

    • The passphrase you entered when creating the keypair.

    {MY_ORG}_SONATYPE_USERNAME

    {MY_ORG}_SONATYPE_PASSWORD

    • Your username and password for your Sonatype account (OSSRH) or your Sonatype user token (Central Portal).

github-secrets-2

Go: GitHub

Go installs packages from source, so nothing other than the GitHub repository is required for setup. We automatically request updates from the Go package index which updates your godoc on a release.

Versioning and releases

Whenever we generate a new change for your SDK, we create and merge a PR into the next branch in your production repository. Each commit should describe the related change using the Conventional Commits format.

Public Repository
📓 acme/acme-node
Public Repository...
main
main
release 0.1.0
release 0.1.0
feat(api): add get /user endpoint
feat(api): add get /user endpoint
feat(api): add post /user endpoint
feat(api): add post /user endpoint
release 0.2.0
release 0.2.0
next
next
Release Pull Request
Release Pull Request

A Release PR is then created against the main branch, which collects individual changes until you are ready to release them. The PR waits for a code owner's review and merges automatically upon approval.

The Release PR aggregates changes due to both your API spec changes and changes to Stainless' codegen engine. Each granular change is added as a separate commit, which you can review individually.

Your first release will be Release 0.1.0-alpha.1, meaning that the first release published to your package manager will have an alpha tag.

Stainless will increment the version used in each subsequent release PR based on the prior one. We use the semantic information of the commits in the release to find the next available version. We highly recommend using Conventional Commits to ensure versioning is correct and automatic:

  • Breaking changes increment the major version number (if and only if the version is already at least 1.0.0)
  • Features increment the minor version number
  • Fixes increment the patch number

You can override our suggested version by modifying the PR title. Doing so will propagate your provided version (including any alpha or beta tags) to the release in Github and your package manager. We recommend:

  • Staying in the alpha or beta stage while your SDKs still have diagnostic errors
  • Staying in the 0.0.x range while your SDKs still have diagnostic warnings
  • Staying in the 0.x.y range while your SDKs still have unacknowledged diagnostic notes
  • Moving to 1.0.0 once your SDK has none of the above

We use semver-valid version numbers for releases.

Development and release flow

Stainless pushes code to various GitHub repos:

  1. Stainless config repo
  2. Stainless SDK repos
  3. Your production repos

Release flow

Release flow

Stainless uses a few branches in your production repository to manage the release process:

  • main/master: The target branch of your repository. This defaults to main but can be configured per language using production_repo.
  • next: This branch is used to collect changes for the next release. Stainless will create Release PRs based on the changes in this branch.
  • generated: The branch where Stainless pushes generated code from the Studio. This branch represents the latest generated code before any custom code changes are applied.
  • release-please--*: Temporary release branches created and managed by our GitHub App. These branches are based on next and are used to create release PRs.