Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 215 additions & 1 deletion packages/cli/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,215 @@
# cli
# Bucket CLI

Command-line interface for interacting with Bucket services. The CLI allows you to manage apps, features, authentication, and generate TypeScript types for your Bucket features. With this tool, you can streamline your feature flagging workflow directly from your terminal.

## Quick Start

Get started quickly by running the CLI directly: initializing the CLI, creating a feature, and generate the types all at once.

```bash
# Initialize CLI (if not setup), create a feature, and generate types all at once
npx @bucketco/cli new
```

or install it locally

```bash
# npm
npm install --save-dev @bucketco/cli

# yarn
yarn add --dev @bucketco/cli
```

then

```bash
# npm
npx bucket new

# yarn
yarn bucket new
```

### Global installation

You can also install the CLI globally adding the it to your PATH allowing you to use the shorthand `bucket`

```bash
npm install -g @bucketco/cli

bucket <command>
```

### Individual commands

Instead of running `new` you can call each step individually.

```bash
# Initialize Bucket in your project (if not already setup)
bucket init

# Create a new feature
bucket features create "My Feature"

# Generate TypeScript types for your features
bucket features types
```

## Configuration

The CLI creates a `bucket.config.json` file in your project directory when you run `bucket init`. This file contains all the necessary settings for your Bucket integration.

### Configuration File Structure

Here's a comprehensive list of configuration options available in the `bucket.config.json` file:

```json
{
"$schema": "https://unpkg.com/@bucketco/cli@latest/schema.json",
"baseUrl": "https://app.bucket.co",
"apiUrl": "https://app.bucket.co/api",
"appId": "ap123456789",
"typesOutput": "gen/features.ts",
"keyFormat": "camelCase"
}
```

| Option | Description | Default |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| `$schema` | Autocompletion for the config. `latest` can be replaced with a specific version. | "https://unpkg.com/@bucketco/cli@latest/schema.json" |
| `baseUrl` | Base URL for Bucket services. | "https://app.bucket.co" |
| `apiUrl` | API URL for Bucket services (overrides baseUrl for API calls). | "https://app.bucket.co/api" |
| `appId` | Your Bucket application ID. | Required |
| `typesOutput` | Path where TypeScript types will be generated. | "gen/features.ts" |
| `keyFormat` | Format for feature keys (options: custom, pascalCase, camelCase, snakeCaseUpper, snakeCaseLower, kebabCaseUpper, kebabCaseLower). | "custom" |

You can override these settings using command-line options for individual commands.

## Commands

### `bucket init`

Initialize a new Bucket configuration in your project. This creates a `bucket.config.json` file with your settings and prompts for any required information not provided via options.

```bash
bucket init [--force]
```

Options:

- `--force`: Overwrite existing configuration file if one exists
- `--app-id <id>`: Set the application ID
- `--key-format <format>`: Set the key format for features

### `bucket new [featureName]`

All-in-one command to get started quickly. This command combines `init`, feature creation, and type generation in a single step. Use this for the fastest way to get up and running with Bucket.

```bash
bucket new "My Feature" [--key my-feature] [--app-id ap123456789] [--key-format custom] [--out gen/features.ts]
```

Options:

- `--key`: Specific key for the feature
- `--app-id`: App ID to use
- `--key-format`: Format for feature keys (custom, snake, camel, etc.)
- `--out`: Path to generate TypeScript types

If you prefer more control over each step, you can use the individual commands (`init`, `features create`, `features types`) instead.

### `bucket login`

Log in to your Bucket account. This will authenticate your CLI for subsequent operations and store credentials securely.

```bash
bucket login
```

### `bucket logout`

Log out from your Bucket account, removing stored credentials.

```bash
bucket logout
```

### `bucket features`

Manage your Bucket features with the following subcommands.

#### `bucket features create [featureName]`

Create a new feature in your Bucket app. The command guides you through the feature creation process with interactive prompts if options are not provided.

```bash
bucket features create "My Feature" [--key my-feature] [--app-id ap123456789] [--key-format custom]
```

Options:

- `--key`: Specific key for the feature
- `--app-id`: App ID to use
- `--key-format`: Format for feature keys

#### `bucket features list`

List all features for the current app. This helps you visualize what features are available and their current configurations.

```bash
bucket features list [--app-id ap123456789]
```

Options:

- `--app-id`: App ID to use

#### `bucket features types`

Generate TypeScript types for your features. This ensures type safety when using Bucket features in your TypeScript/JavaScript applications.

```bash
bucket features types [--app-id ap123456789] [--out gen/features.ts]
```

Options:

- `--app-id`: App ID to use
- `--out`: Path to generate TypeScript types

### `bucket apps`

Commands for managing Bucket apps.

## Global Options

These options can be used with any command:

- `--debug`: Enable debug mode for verbose output
- `--base-url <url>`: Set the base URL for Bucket API
- `--api-url <url>`: Set the API URL directly (overrides base URL)
- `--help`: Display help information for a command

## Development

```bash
# Build the CLI
yarn build

# Run the CLI locally
yarn bucket [command]

# Lint and format code
yarn lint
yarn format
```

## Requirements

- Node.js >=18.0.0

## License

> MIT License
> Copyright (c) 2025 Bucket ApS
10 changes: 5 additions & 5 deletions packages/cli/commands/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const listFeaturesAction = async () => {
};

export const generateTypesAction = async () => {
const { baseUrl, appId, typesPath } = configStore.getConfig();
const { baseUrl, appId, typesOutput } = configStore.getConfig();

let spinner: Ora | undefined;
let featureKeys: string[] = [];
Expand All @@ -103,9 +103,9 @@ export const generateTypesAction = async () => {
spinner = ora("Generating feature types...").start();
const types = genDTS(featureKeys);
const projectPath = configStore.getProjectPath();
const outPath = isAbsolute(typesPath)
? typesPath
: join(projectPath, typesPath);
const outPath = isAbsolute(typesOutput)
? typesOutput
: join(projectPath, typesOutput);
await mkdir(dirname(outPath), { recursive: true });
await writeFile(outPath, types);
spinner.succeed(
Expand Down Expand Up @@ -147,7 +147,7 @@ export function registerFeatureCommands(cli: Command) {
// Update the config with the cli override values
featuresCommand.hook("preAction", (_, command) => {
const { appId, keyFormat, out } = command.opts();
configStore.setConfig({ appId, keyFormat, typesPath: out });
configStore.setConfig({ appId, keyFormat, typesOutput: out });
});

cli.addCommand(featuresCommand);
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ora, { Ora } from "ora";

import { App, listApps } from "../services/bootstrap.js";
import { configStore } from "../stores/config.js";
import { chalkBrand, DEFAULT_TYPES_PATH } from "../utils/constants.js";
import { chalkBrand, DEFAULT_TYPES_OUTPUT } from "../utils/constants.js";
import { handleError } from "../utils/errors.js";
import { initOverrideOption } from "../utils/options.js";

Expand Down Expand Up @@ -69,16 +69,16 @@ export const initAction = async (args: InitArgs = {}) => {
apps.find((app) => app.id === appId)?.featureKeyFormat ?? "custom";

// Get types output path
const typesPath = await input({
const typesOutput = await input({
message: "Where should we generate the types?",
default: DEFAULT_TYPES_PATH,
default: DEFAULT_TYPES_OUTPUT,
});

// Update config
configStore.setConfig({
appId,
keyFormat,
typesPath,
typesOutput,
});

// Create config file
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/commands/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ export function registerNewCommand(cli: Command) {
// Update the config with the cli override values
cli.hook("preAction", (command) => {
const { appId, keyFormat, out } = command.opts();
configStore.setConfig({ appId, keyFormat, typesPath: out });
configStore.setConfig({ appId, keyFormat, typesOutput: out });
});
}
12 changes: 11 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
{
"name": "@bucketco/cli",
"version": "0.0.1",
"version": "0.1.0",
"packageManager": "yarn@4.1.1",
"description": "CLI for Bucket service",
"main": "./dist/index.js",
"type": "module",
"license": "MIT",
"author": "Bucket.",
"homepage": "https://docs.bucket.co/",
"repository": {
"type": "git",
"url": "https://github.com/bucketco/bucket-javascript-sdk.git"
},
"engines": {
"node": ">=18.0.0"
},
Expand All @@ -15,6 +22,9 @@
"dist",
"schema.json"
],
"exports": {
".": "./dist/index.js"
},
"scripts": {
"build": "tsc",
"bucket": "yarn build && node dist/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"appId": {
"type": "string"
},
"typesPath": {
"typesOutput": {
"type": "string"
},
"keyFormat": {
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/stores/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
CONFIG_FILE_NAME,
DEFAULT_API_URL,
DEFAULT_BASE_URL,
DEFAULT_TYPES_PATH,
DEFAULT_TYPES_OUTPUT,
SCHEMA_URL,
} from "../utils/constants.js";
import { ConfigValidationError, handleError } from "../utils/errors.js";
Expand All @@ -30,7 +30,7 @@ type Config = {
baseUrl: string;
apiUrl: string;
appId: string | undefined;
typesPath: string;
typesOutput: string;
keyFormat: KeyFormat;
};

Expand All @@ -39,7 +39,7 @@ const defaultConfig: Config = {
baseUrl: DEFAULT_BASE_URL,
apiUrl: DEFAULT_API_URL,
appId: undefined,
typesPath: DEFAULT_TYPES_PATH,
typesOutput: DEFAULT_TYPES_OUTPUT,
keyFormat: "custom",
};

Expand Down
8 changes: 2 additions & 6 deletions packages/cli/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import { createRequire } from "module";
import { join } from "path";
import chalk from "chalk";

// https://github.com/nodejs/node/issues/51347#issuecomment-2111337854
const packageJson = createRequire(import.meta.url)("../../package.json");

export const CONFIG_FILE_NAME = "bucket.config.json";
export const AUTH_FILE = join(
process.env.HOME ?? process.env.USERPROFILE ?? "",
".bucket-auth",
);
export const SCHEMA_URL = `https://unpkg.com/@bucketco/cli@${packageJson.version}/schema.json`;
export const SCHEMA_URL = `https://unpkg.com/@bucketco/cli@latest/schema.json`;

export const DEFAULT_BASE_URL = "https://app.bucket.co";
export const DEFAULT_API_URL = `${DEFAULT_BASE_URL}/api`;
export const DEFAULT_TYPES_PATH = join("gen", "features.ts");
export const DEFAULT_TYPES_OUTPUT = join("gen", "features.ts");

export const chalkBrand = chalk.hex("#847CFB");

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const initOverrideOption = new Option(

export const typesOutOption = new Option(
"-o, --out [path]",
`Output path for generated feature types. Falls back to typesPath value in ${CONFIG_FILE_NAME}.`,
`Output path for generated feature types. Falls back to typesOutput value in ${CONFIG_FILE_NAME}.`,
);

export const keyFormatOption = new Option(
Expand Down