Publishing

Beta
View as Markdown
Early access

The CLI generator is in early access. Reach out to get started.

Publish your generated CLI to npm. After following the steps on this page, each tagged release builds cross-platform binaries, attaches them to a GitHub Release, and publishes them to npm automatically. Homebrew and Scoop are opt-in channels on top of the same release. To keep generation on your own infrastructure, you can self-host the generator instead of using Fern’s cloud.

This page assumes that you have:

  • An initialized fern folder with generators.yml configured for the CLI generator. See Quickstart.
  • An existing GitHub repository for the generated CLI source, with the Fern GitHub App installed on it.

Configure output location

1

Set the output location to npm

In the group for your CLI, set the output location to npm:

generators.yml
1groups:
2 cli:
3 generators:
4 - name: fern-cli-generator
5 version: 0.38.4
6 output:
7 location: npm
8 config:
9 binaryName: my-cli
2

Add a unique package name

The package name must be unique in the npm registry.

generators.yml
1groups:
2 cli:
3 generators:
4 - name: fern-cli-generator
5 version: 0.38.4
6 output:
7 location: npm
8 package-name: "@myorg/my-cli"
9 config:
10 binaryName: my-cli

The npm package name is independent of the generated Rust crate’s identity. To publish the crate under your own name, license, repository, and authors, set packageIdentity in the generator’s config.

Configure GitHub publishing

Fern publishes your CLI via GitHub Actions. Configure your GitHub repository and publishing mode:

generators.yml
1groups:
2 cli:
3 generators:
4 - name: fern-cli-generator
5 version: 0.38.4
6 output:
7 location: npm
8 package-name: "@myorg/my-cli"
9 config:
10 binaryName: my-cli
11 github:
12 repository: my-org/my-cli
13 mode: release

The repository owner is independent of your Fern organization (the --organization value passed to fern init). Point it at the GitHub user or organization that owns the target repository.

ModeBehavior
releaseCommits to the default branch and tags a release. The CI workflow builds binaries and publishes automatically.
pull-requestOpens a PR with generated source for review. Merge the PR and create a GitHub release to trigger publishing.

Configure authentication

Choose how to authenticate with npm when publishing.

1

Generate an npm token

  1. Log into npmjs.com
  2. Click your profile picture and select Access Tokens
  3. Click Generate New Token and create a granular access token
  4. Give it Read and Write access to All packages
  5. Save your token securely
2

Add token to generators.yml

Set token: ${NPM_TOKEN} in the output section:

generators.yml
1groups:
2 cli:
3 generators:
4 - name: fern-cli-generator
5 version: 0.38.4
6 output:
7 location: npm
8 package-name: "@myorg/my-cli"
9 token: ${NPM_TOKEN}
10 config:
11 binaryName: my-cli
12 github:
13 repository: my-org/my-cli
14 mode: release
3

Add NPM_TOKEN as a GitHub Actions secret

  1. Open your CLI repository on GitHub and go to Settings
  2. Navigate to Secrets and variables > Actions
  3. Click New repository secret
  4. Name it NPM_TOKEN and paste your npm token
  5. Click Add secret

Configure distribution channels

Homebrew and Scoop are optional channels that install from the archives attached to each GitHub Release, so both require GitHub publishing. Add the channels you want under distribution in the generator’s config:

generators.yml
1groups:
2 cli:
3 generators:
4 - name: fern-cli-generator
5 version: 0.38.4
6 output:
7 location: npm
8 package-name: "@myorg/my-cli"
9 config:
10 binaryName: my-cli
11 distribution:
12 homebrew:
13 tap: my-org/homebrew-tap
14 formula: my-cli
15 scoop:
16 bucket: my-org/scoop-bucket
17 github:
18 repository: my-org/my-cli
19 mode: release

On the next generation, Fern adds a publish-homebrew-formula job, a publish-scoop job, or both to the release workflow, along with the matching install commands in the generated README. Prereleases are skipped: a tap and a bucket have no prerelease channel, so an RC never becomes the version users install.

Each channel needs a repository and credentials before that first release:

1

Create the tap and bucket repositories

Create a public GitHub repository for each channel you enable — conventionally homebrew-tap and scoop-bucket. They can be empty; the release workflow commits the formula and the manifest into them. A private tap or bucket breaks installation for users who lack access to it.

2

Add credentials with write access to them

The workflow’s built-in GITHUB_TOKEN is scoped to the CLI repository and can’t push to another one, so each channel authenticates with credentials you store as Actions secrets in the CLI repository, under Settings > Secrets and variables > Actions. A personal access token is the quickest to set up; a GitHub App keeps release access off any individual’s account.

One token per channel, each with write access to that channel’s repository:

SecretChannelOverridden by
HOMEBREW_TAP_TOKENHomebrewdistribution.homebrew.tokenEnvironmentVariable
SCOOP_BUCKET_TOKENScoopdistribution.scoop.tokenEnvironmentVariable

A token belongs to a person and doesn’t expire, so releases break once that person’s access ends.

Publish your CLI

How you trigger a publish depends on your mode:

  • In release mode, fern generate --group cli commits the updated source and tags a release. The tag triggers the CI workflow (no manual release step).
  • In pull-request mode, fern generate --group cli opens a PR. Merge it, then create a GitHub release with a version tag (e.g. v1.0.0) to trigger publishing.

The CI workflow then builds binaries for all platforms, attaches the archives and the curl | bash and PowerShell installers to the GitHub Release, publishes to npm, and updates any configured tap or bucket.

Build targets

The CI workflow produces statically linked binaries for:

TargetOSArchitecture
x86_64-unknown-linux-gnuLinuxx86_64
aarch64-unknown-linux-gnuLinuxARM64
x86_64-apple-darwinmacOSIntel
aarch64-apple-darwinmacOSApple Silicon
x86_64-pc-windows-msvcWindowsx86_64

The npm package wraps the native binary with a Node.js launcher. Platform-specific optional dependencies ensure only the correct binary downloads at install time.

Install instructions for users

After publishing, direct users to whichever channel they prefer. Every generated CLI supports npm and the installers attached to the GitHub Release:

$npm install -g @myorg/my-cli

With distribution.homebrew configured, a tap named my-org/homebrew-tap resolves as my-org/tap:

$brew install my-org/tap/my-cli

With distribution.scoop configured, users add the bucket once, then install:

$scoop bucket add my-org https://github.com/my-org/scoop-bucket
$scoop install my-cli

Scoop covers x86_64 Windows only; ARM64 Windows users install through npm or the PowerShell installer. self-update replaces the binary in place rather than deferring to brew upgrade or scoop update, so those package managers can report an older version than the one installed.