# Flagbearer CLI

Create feature flags and roll them out from a terminal or a CI job. Requires
Node.js 22 or later.

```sh
npx @flagbearer/cli login
npx @flagbearer/cli flags set new-checkout --env development --rollout 100
```

To skip the `npx` prefix, install it globally with
`npm install --global @flagbearer/cli`, then run `flagbearer`.

## Sign in

On your own machine, sign in through the browser:

```sh
flagbearer login
```

Flagbearer opens in your browser. Pick the organization the CLI can use and its
access, **Editor** or **Viewer**, and approve it. The sign-in is saved in
`~/.config/flagbearer/credentials.json`, readable only by you, and refreshes on
its own. `flagbearer logout` removes it from the machine. To end it everywhere,
disconnect the Flagbearer CLI on the organization's **API tokens** tab.

In CI, use a personal API token instead:

1. Open your organization's **API tokens** tab and create a token. Pick
   **Editor** so it can create and change flags. Give it a
   name like "GitHub Actions": flag history shows its changes under that name.
2. Store the secret in your CI's secrets and pass it as `FLAGBEARER_TOKEN`.

When `FLAGBEARER_TOKEN` is set, the CLI uses it instead of a saved sign-in, so
a CI job never acts as whoever last signed in on the machine.

## Pick an application

Flag commands work on one application. Pass its ID, slug or name as `--app`, or
set `FLAGBEARER_APP`. To see them:

```sh
flagbearer apps list
```

```
ID  SLUG        NAME        ORG
12  storefront  Storefront  3
```

A name can match apps in two organizations. Then the command lists them and
asks for the ID. In CI, prefer the ID: it doesn't change when the app is
renamed.

## Commands

| Command                         | What it does                                                 |
| ------------------------------- | ------------------------------------------------------------ |
| `flagbearer orgs list`          | The organizations you belong to                              |
| `flagbearer apps list`          | Applications in every organization, or one with `--org <id>` |
| `flagbearer flags list`         | Each flag, on or off with its rollout, in every environment  |
| `flagbearer flags create <key>` | Creates a flag, off in every environment                     |
| `flagbearer flags set <key>`    | Changes a flag in one environment                            |
| `flagbearer generate types`     | Writes the flag keys for TypeScript, or as JSON              |

`flags create` takes `--name` and `--description`. The name defaults to the key.
Quote values with spaces, like `--name "New checkout"`.

## Change a flag

`flags set` changes one environment, chosen with `--env` and its key, like
`development` or `production`:

```sh
flagbearer flags set new-checkout --env staging --rollout 25
```

- `--rollout <percent>` turns the flag on for that percentage of users, from 0
  to 100. At 0, only the environment's testers get it.
- `--on` turns the flag on and keeps the rollout it had.
- `--off` turns it off.

Changing `production` asks you to confirm. Where nobody is at the terminal, as
in CI, pass `--yes` instead, or the command refuses.

## Typed flag keys

`generate types` writes a file listing the application's flags, so TypeScript
checks flag keys and editors autocomplete them:

```sh
flagbearer generate types --app storefront --out src/flagbearer.d.ts
```

```ts
// src/flagbearer.d.ts
// Generated by `flagbearer generate types --app storefront --out src/flagbearer.d.ts`.
// Don't edit it. Run that command again after adding or removing flags.
import "@flagbearer/web";

declare module "@flagbearer/web" {
  interface Flags {
    "dark-mode": true;
    "new-checkout": true;
  }
}
```

```ts
flags.isEnabled("new-checkout", false); // ok
flags.isEnabled("new-chekout", false); // type error: not a flag in this app
```

Commit the file. Without it, any string is a flag key.

- The file covers each Flagbearer SDK in the nearest `package.json`. To choose,
  pass `--sdk web`, `--sdk node` or `--sdk react-native`, more than once for
  several.
- `--check` writes nothing and exits 1 when the file is out of date, so CI
  catches a deleted flag that code still uses.
- `--format json` writes the sorted keys as a JSON array, for other languages
  and tools.

## In CI

A GitHub Actions step that rolls a flag out to a quarter of production users
after a deploy:

```yaml
- run: npx @flagbearer/cli flags set new-checkout --env production --rollout 25 --yes
  env:
    FLAGBEARER_TOKEN: ${{ secrets.FLAGBEARER_TOKEN }}
    FLAGBEARER_APP: "12"
```

Other CI systems work the same way: set the two variables and run the command.

To fail a build when the typed flag keys are out of date:

```yaml
- run: npx @flagbearer/cli generate types --app storefront --out src/flagbearer.d.ts --check
  env:
    FLAGBEARER_TOKEN: ${{ secrets.FLAGBEARER_TOKEN }}
```

## Options and output

Every command takes:

- `--app <app>`: the application's ID, slug or name, instead of
  `FLAGBEARER_APP`.
- `--json`: prints JSON instead of text, for scripts.
- `--url <url>`: a Flagbearer server other than `https://flagbearer.dev`,
  instead of `FLAGBEARER_URL`.

The exit code says how it went:

- `0`: done.
- `1`: Flagbearer refused the change or couldn't be reached. The message says
  why.
- `2`: the command was wrong, or you're not signed in.

An API token can make 60 changes a minute. Past that, commands exit 1 and say
to wait.
