diff --git a/fern/products/sdks/overview/typescript/publishing-to-npm.mdx b/fern/products/sdks/overview/typescript/publishing-to-npm.mdx index 1c24d9742..0ec156a52 100644 --- a/fern/products/sdks/overview/typescript/publishing-to-npm.mdx +++ b/fern/products/sdks/overview/typescript/publishing-to-npm.mdx @@ -3,12 +3,16 @@ title: Publishing to npm description: How to publish the Fern TypeScript SDK to npm. --- -Publish your public-facing Fern TypeScript SDK to the [npm +Publish your public-facing Fern TypeScript SDK to the [npmjs registry](https://www.npmjs.com/). After following the steps on this page, you'll have a versioned package published on npm. + +If you're currently using token-based authentication, npmjs is deprecating long-lived tokens in early 2025. See [Migrating from token-based to OpenID Connect (OIDC) publishing](#migrating-from-token-based-to-oidc-publishing) to upgrade to the more secure OIDC authentication. + + - Versioned package published on npm + Versioned package published on npmjs.com @@ -29,10 +33,10 @@ You'll need to update your `generators.yml` file to configure the package name, - In the `group` for your TypeScript SDK, change the output location from `local-file-system` (the default) to `npm` to indicate that Fern should publish your package directly to the npm registry: + In the `group` for your TypeScript SDK, change the output location from `local-file-system` (the default) to `npm` to indicate that Fern should publish your package directly to the npmjs registry: ```yaml {6-7} title="generators.yml" - groups: + groups: ts-sdk: # Group name for your TypeScript SDK generators: - name: fernapi/fern-typescript-sdk @@ -45,10 +49,10 @@ You'll need to update your `generators.yml` file to configure the package name, - Your package name must be unique in the npm repository, otherwise publishing your SDK to npm will fail. + Your package name must be unique in the npmjs registry, otherwise publishing your SDK will fail. ```yaml {8} title="generators.yml" - groups: + groups: ts-sdk: generators: - name: fernapi/fern-typescript-sdk @@ -62,10 +66,10 @@ You'll need to update your `generators.yml` file to configure the package name, - The `namespaceExport` option controls the name of the generated client. This is the name customers use to import your SDK (`import { your-client-name } from 'your-package-name';`). + The `namespaceExport` option controls the name of the generated client. This is the name customers use to import your SDK (`import { your-client-name } from 'your-package-name';`). ```yaml {9-10} title="generators.yml" - groups: + groups: ts-sdk: generators: - name: fernapi/fern-typescript-sdk @@ -81,259 +85,457 @@ You'll need to update your `generators.yml` file to configure the package name, -## Generate an npm token +## Configure GitHub publishing + +Fern can automatically publish your SDK to npmjs via GitHub Actions. Configure your GitHub repository and publishing mode: + + + +```yaml title="generators.yml" {11-14} +groups: + ts-sdk: + generators: + - name: fernapi/fern-typescript-sdk + version: + output: + location: npm + package-name: your-package-name + config: + namespaceExport: YourClientName + github: + repository: your-org/your-repository + mode: push # or "pull-request" + branch: your-branch-name # Required for mode: push +``` + +## Configure authentication + +Choose how you want to authenticate with npmjs when publishing. + + +**Starting in early 2025**, npmjs.org is deprecating long-lived authentication tokens for publishing from CI/CD workflows. **OpenID Connect (OIDC) authentication is strongly recommended** for security. + + + + + +OIDC-based publishing (also known as "trusted publishing") is the most secure way to publish. With OIDC, you don't need to manage authentication tokens - npmjs trusts your GitHub repository to publish directly. + + +- Fern TypeScript SDK generator version `3.12.0` or later +- Fern CLI version `0.94.0` or later (only required for local generation with `--local`) + + - + Add `token: OIDC` to the `output` section: - Log into [npm](https://www.npmjs.com/) or create a new account. + ```yaml title="generators.yml" {9} + groups: + ts-sdk: + generators: + - name: fernapi/fern-typescript-sdk + version: # Must be 3.12.0 or later + output: + location: npm + package-name: your-package-name + token: OIDC + config: + namespaceExport: YourClientName + github: + repository: your-org/your-repository + mode: push + branch: your-branch-name + ``` - + + + Generate your SDK to create the GitHub Actions workflow with OIDC configuration: + + ```bash + fern generate --group ts-sdk + ``` + + This creates a `.github/workflows/ci.yml` file that's configured to use OIDC for npmjs publishing. Alternatively, you can push your `generators.yml` changes and let the Fern GitHub Action generate the workflow for you. - 1. Click on your profile picture. - 1. Select **Edit Profile**. - 1. Select **Access Tokens**. + This creates a `.github/workflows/ci.yml` file that's configured to use OIDC for npm publishing. - + - Click on **Generate New Token**, then choose the appropriate token type. - - For more information on access tokens and which type to choose, see npm's [About access tokens](https://docs.npmjs.com/about-access-tokens) documentation. + Configure trusted publishing on npmjs.com to allow your GitHub repository to publish: - - + 1. Navigate to your package settings on npmjs.com + 1. Find the **Trusted Publisher** section and click **Add trusted publisher** + 1. Select **GitHub Actions** as your provider + 1. Fill in: + - **Organization or user**: Your GitHub username or organization + - **Repository**: Your TypeScript SDK repository name (e.g., `your-org/your-repository`) + - **Workflow filename**: `ci.yml` + - **Environment name**: Leave blank - 1. Select **Classic Token** - 1. Name your token and select **Automation** as the token type. - 1. Click **Generate Token**. + For more details, see npm's [trusted publishing documentation](https://docs.npmjs.com/trusted-publishers). - Save your new token – it won't be displayed after you leave the page. + + - - Creating NPM Automation Token - + - - - 1. Select **Granular Access Token**. - 1. Name your token. - 1. Set an expiration. - 1. Configure your token's access to packages and scopes. - 1. Configure your token's access to organizations. In order to fill this out, you must have at least one organization already configured in npm. See [Creating an organization](https://docs.npmjs.com/creating-an-organization) for more information. - 1. Optionally fill out additional permissions according to your organization's requirements. - 1. Click **Generate Token**. +**"Unable to authenticate" error** - Save your new token – it won't be displayed after you leave the page. +Common causes: +- Workflow filename doesn't match exactly (must be `ci.yml`) +- Trusted publisher configuration on npmjs.com doesn't match your repository settings +- Using self-hosted runners (not currently supported by npmjs.org) - - Creating Granular Access Token - +**Solution:** Double-check your trusted publisher configuration on npmjs.com matches your repository name and workflow filename exactly. - - - - +**Private repository limitations** - +Provenance attestations aren't generated for packages published from private repositories, even when using trusted publishing. This is a [known limitation](https://github.blog/changelog/2023-07-25-publishing-with-npm-provenance-from-private-source-repositories-is-no-longer-supported/). + + + + + + -## Configure npm publication + +**This method is being deprecated by npmjs.org in early 2025.** Long-lived authentication tokens can be exposed in logs, compromised, and are difficult to manage and rotate. [OIDC-based authentication is strongly recommended instead](#migrating-from-token-based-to-oidc-publishing). + - + - Add the path to the GitHub repository containing your TypeScript SDK: +1. Log into [npmjs.com](https://www.npmjs.com/) +1. Click on your profile picture and select **Edit Profile** +1. Select **Access Tokens** +1. Click **Generate New Token** and choose either **Classic Token** (select "Automation" type) or **Granular Access Token** +1. Save your token securely - it won't be displayed again - ```yaml {11-12} title="generators.yml" - groups: - ts-sdk: - generators: - - name: fernapi/fern-typescript-sdk - version: - output: - location: npm - package-name: your-package-name - config: - namespaceExport: YourClientName - github: - repository: your-org/company-typescript - ``` +For more information on access tokens, see npm's [About access tokens](https://docs.npmjs.com/about-access-tokens) documentation. - -Add `token: ${NPM_TOKEN}` to `generators.yml` to tell Fern to use the `NPM_TOKEN` environment variable for authentication when publishing to the npm registry. + + +Add `token: ${NPM_TOKEN}` to the `output` section: ```yaml title="generators.yml" {9} -groups: +groups: ts-sdk: generators: - name: fernapi/fern-typescript-sdk version: output: location: npm - package-name: name-of-your-package + package-name: your-package-name token: ${NPM_TOKEN} config: namespaceExport: YourClientName - github: + github: repository: your-org/your-repository + mode: push + branch: your-branch-name ``` + - - + -```yaml title="generators.yml" {14} -groups: - ts-sdk: - generators: - - name: fernapi/fern-typescript-sdk - version: - output: - location: npm - package-name: name-of-your-package - token: ${NPM_TOKEN} - config: - namespaceExport: YourClientName - github: - repository: your-org/your-repository - mode: push - branch: your-branch-name # Required for mode: push -``` +1. Open your repository on GitHub and go to **Settings** +1. Navigate to **Secrets and variables** > **Actions** +1. Click **New repository secret** +1. Name it `NPM_TOKEN` and paste your npm token +1. Click **Add secret** + + + + ## Publish your SDK -Decide how you want to publish your SDK to npm. You can use GitHub workflows for automated releases or publish directly via the CLI. +Your SDK will automatically be published to npmjs when you create a GitHub release with a version tag: + +1. Create a GitHub release with a version tag (for example, `v1.0.0`) +1. The CI workflow will run automatically and publish to npm +1. View your package on npmjs.com to confirm the new version + + + +If you prefer to trigger publishes manually, create a `.github/workflows/publish.yml` file: + +```yaml title=".github/workflows/publish.yml" +name: Publish TypeScript SDK + +on: + workflow_dispatch: + inputs: + version: + description: "Version to publish (e.g., 1.0.0)" + required: true + type: string + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install Fern + run: npm install -g fern-api + + - name: Generate and publish SDK + env: + FERN_TOKEN: ${{ secrets.FERN_TOKEN }} + run: fern generate --group ts-sdk --version ${{ inputs.version }} --log-level debug +``` + +Add your `FERN_TOKEN` as a repository secret (run `fern token` to generate one), then trigger the workflow from the **Actions** tab. + + + +--- + +## Migrating from token-based to OIDC publishing + +If you're currently using token-based authentication and need to migrate to OIDC, follow these steps: + +### Why migrate to OIDC + +npmjs is implementing trusted publishing to remove security risks associated with long-lived tokens, which can be: + +- Exposed in logs or configuration files +- Compromised and used persistently until manually revoked +- Difficult to manage and rotate + +OIDC-based publishing uses short-lived, cryptographically signed tokens that are specific to your workflow and can't be extracted or reused. + +### Prerequisites + +Before migrating, ensure you have: + +- A package published to [npmjs.org](https://npmjs.org) +- A GitHub repository with GitHub Actions configured +- Access to your package settings on [npmjs.com](https://npmjs.com) +- Fern CLI version `0.94.0` or later (for local generation) + +### Choose your migration path + +Select the approach that fits your situation: + - +This is the easiest path if you can upgrade to version 3.12.0 or later of the TypeScript SDK generator. -Set up a release workflow via [GitHub Actions](https://docs.github.com/en/actions/get-started/quickstart) so you can trigger new SDK releases directly from your source repository. +**When to use this path:** +- You're able to upgrade to Fern TypeScript SDK generator version 3.12.0 or later +- You haven't `.fernignore`'d your CI workflow file - + + + Follow npm's ["Add a trusted publisher on npmjs.com"](https://docs.npmjs.com/trusted-publishers#step-1-add-a-trusted-publisher-on-npmjscom) instructions: + + 1. Navigate to your package settings on [npmjs.com](https://npmjs.com) + 1. Find the **Trusted Publisher** section and click **Add trusted publisher** + 1. Select **GitHub Actions** as your provider + 1. Configure: + - **Organization or user**: Your GitHub username or organization + - **Repository**: Your TypeScript SDK repository name + - **Workflow filename**: `ci.yml` (the default Fern workflow file) + - **Environment name**: Leave blank (unless you use GitHub environments) + + + + + + Change the `output.token` field from `${NPM_TOKEN}` to `OIDC` and ensure you're using version `3.12.0` or later: + + ```yaml title="generators.yml" + groups: + ts-sdk: + generators: + - name: fernapi/fern-typescript-sdk + version: # Must be 3.12.0 or later + output: + location: npm + package-name: your-package-name + token: OIDC # Changed from ${NPM_TOKEN} + config: + namespaceExport: YourClientName + github: + repository: your-org/your-repository + ``` + + + + + + Regenerate your SDK with the updated CI configuration. You can do this either: + + **Locally:** - Open your source repository in GitHub. Click on the **Settings** tab. Then, under the **Security** section, open **Secrets and variables** > **Actions**. + ```bash + fern generate --group ts-sdk + ``` + + **Or via GitHub Actions:** - - Adding GitHub Repository Secret - + If you use the Fern GitHub Action to generate your SDK, simply push your updated `generators.yml` file and let the workflow regenerate the SDK for you. - You can also use the url `https://github.com//settings/secrets/actions`. + This will update your `.github/workflows/ci.yml` file with the required OIDC permissions. - - 1. Select **New repository secret**. - 1. Name your secret `NPM_TOKEN`. - 1. Add the corresponding token you generated above. - 1. Click **Add secret**. + - - NPM_TOKEN secret - + After verifying the migration works, remove the `NPM_TOKEN` secret from your GitHub repository settings to prevent accidental use. - + + + + + + +Use this path if you can't upgrade the generator or have customized your CI workflow. + +**When to use this path:** +- You can't upgrade due to breaking changes or bugs +- You've customized your CI workflow and added it to `.fernignore` +- Path 1 didn't update your workflow file - 1. Select **New repository secret**. - 1. Name your secret `FERN_TOKEN`. - 1. Add your Fern token. If you don't already have one, generate one by - running `fern token`. By default, the `fern_token` is generated for the - organization listed in `fern.config.json`. - 1. Click **Add secret**. + + + + Follow the same instructions as Path 1 to add your repository as a trusted publisher on npmjs.com. - - - Set up a CI workflow that you can manually trigger from the GitHub UI. In your repository, navigate to **Actions**. Select **New workflow**, then **Set up workflow yourself**. Add a workflow that's similar to this: - - ```yaml title=".github/workflows/publish.yml" maxLines=0 - name: Publish TypeScript SDK - - on: - workflow_dispatch: - inputs: - version: - description: "The version of the TypeScript SDK that you would like to release" - required: true - type: string - - jobs: - release: - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Install Fern CLI - run: npm install -g fern-api - - - name: Release TypeScript SDK - env: - FERN_TOKEN: ${{ secrets.FERN_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: | - fern generate --group ts-sdk --version ${{ inputs.version }} --log-level debug + + + + Open your `.github/workflows/ci.yml` file and make these changes to the `publish` job: + + ```yaml title=".github/workflows/ci.yml" + publish: + needs: [ compile, test ] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + permissions: + id-token: write # ADD THIS: Required for OIDC + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up node + uses: actions/setup-node@v4 + + # ADD THIS: Ensure npm 11.5.1 or later is installed for OIDC support + - name: Update npm + run: npm install -g npm@latest + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Install dependencies + run: pnpm install + + - name: Build + run: pnpm build + + # MODIFY THIS: Remove npm config set and env block + - name: Publish to npm + run: | + if [[ ${GITHUB_REF} == *alpha* ]]; then + npm publish --access public --tag alpha + elif [[ ${GITHUB_REF} == *beta* ]]; then + npm publish --access public --tag beta + else + npm publish --access public + fi + # Previously had: + # run: | + # npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN} + # if [[ ${GITHUB_REF} == *alpha* ]]; then + # npm publish --access public --tag alpha + # elif [[ ${GITHUB_REF} == *beta* ]]; then + # npm publish --access public --tag beta + # else + # npm publish --access public + # fi + # env: + # NPM_TOKEN: ${{ secrets.NPM_TOKEN }} ``` - - You can alternatively configure your workflow to execute `on: [push]`. See Vapi's [npm publishing GitHub Action](https://github.com/VapiAI/server-sdk-typescript/blob/main/.github/workflows/ci.yml) for an example. - - - + **Key changes:** + - Add `permissions` block with `id-token: write` to the publish job + - Add step to update npm to version 11.5.1 or later + - Remove the `npm config set` line from the publish step + - Remove the `env` block with `NPM_TOKEN` from the publish step - Navigate to the **Actions** tab, select the workflow you just created, specify a version number, and click **Run workflow**. This regenerates your SDK. + + + - - Running TS publish workflow - + If you haven't already, add your CI workflow to `.fernignore` to prevent future generator updates from overwriting your manual changes: - + ```text title=".fernignore" + .github/workflows/ci.yml + ``` - Once the workflow completes, you can view your new release by logging into npm and navigating to **Packages**. - + + + After verifying the migration works, remove the `NPM_TOKEN` secret from your GitHub repository settings. + + + - - +### Verify your migration - +After completing either migration path: -Set the `NPM_TOKEN` environment variable on your local machine: +1. **Trigger a workflow run** by creating a GitHub release with an alpha tag (for example, `v1.0.0-alpha`) +2. **Check the workflow logs** to verify the publish step succeeds +3. **Verify provenance** by visiting your package on [npmjs.com](https://npmjs.com) - you should see a provenance badge -```bash -export NPM_TOKEN=your-actual-npm-token -``` +### Migration troubleshooting - - + + -Regenerate your SDK, specifying the version: +**Common causes:** +- Workflow filename doesn't match exactly (must be `ci.yml` with the `.yml` extension) +- Missing `id-token: write` permission in workflow +- npm CLI version is older than 11.5.1 +- Using self-hosted runners (not currently supported) -```bash -fern generate --group ts-sdk --version -``` +**Solution:** Double-check your trusted publisher configuration on npmjs.com matches your actual workflow file name and verify all requirements are met. - + -Once the workflow completes, you can view your new release by logging into npm and navigating to **Packages**. + - +If your workflow continues using the old token-based authentication: + +- Verify you've removed the `npm config set` line and the `env: NPM_TOKEN` block from the publish step +- Check that npm CLI version 11.5.1+ is installed (add the update npm step) +- Ensure you're using generator version 3.12.0 or later (if using Path 1) +- When using `--local` generation, you need to use Fern CLI version 0.94.0 or later -