npm package: @river-agency/cli
Description: The river CLI tool provides a central service for creating, managing and configuring projects at RIVER.
Latest version: 1.4.0

# RIVER CLI

The river CLI tool provides a central service for creating, managing and configuring projects at RIVER.

## Usage

### Available Commands

- `new`: Create a new Laravel project using the default branch of the river-laravel-template repository
- `version`: Display the CLI version

#### Laravel

- `env:init`: Generate Laravel's `.env` file

#### Terraform

- `terraform:init`: Generate the terraform configuration
- `terraform:apply`: Apply the terraform configuration
- `terraform:download`: Download the AWS configuration file
- `terraform:install`: Run all terraform commands

#### Bitbucket Pipelines

- `pipelines:init`: Generate the `bitbucket-pipelines.yml` configuration
- `pipelines:add`: Add a new deployment to the `bitbucket-pipelines.yml` configuration
- `pipelines:remove`: Remove a deployment from the `bitbucket-pipelines.yml` configuration

#### Lando

- `lando:init`: Generate the `.lando.yml` configuration

## Development

To develop on the CLI locally, follow these steps:

**Install dependencies**

```bash
npm install
npm install -g ts-node  # typescript version of `node`
```

**\*Note**: Node 16 or later is required.\*

**Run Commands**

```bash
# Invoke the CLI application
ts-node src/index.ts

# Show the help menu explicitly
ts-node src/index.ts --help

# Run a command
ts-node src/index.ts new
```

##### A note on performance

If you are using an IDE with Typescript support, you can skip the compile time type-checking and get native JS speeds when running commands. Simply pass the `transpileOnly` flag when running the command.

```bash
ts-node -T src/index.ts
```

#### Adding new Commands

Commands are automatically loaded from the `src/commands` directory. This is not recursive.

All commands must extend the `Command` class exported from `src/command.ts`

#### Working with the CLI during development

Create a new project locally, using `example` as the production domain (this is ignored via `.gitignore`)

```bash
ts-node src/index.ts new
```

Change into the project directory and call the CLI using relative paths, not the global install.

```bash
cd example
ts-node ../src/index.ts env:init
```


## Deployment

To publish the package, ensure you are logged into the RIVER agency NPM account.

##### Steps

```bash
# Start from a clean working directory
git add .
git commit -m "your message"
git push

# Tag a new version
# 	major: Major release / Breaking changes (e.g. 2.0.0)
# 	minor: Feature release / Non breaking changes
# 	patch: Bug fix releasee / Non breaking changes
npm version minor


# Publish the package
npm publish
```

**Note**: The `prepublishOnly` and `postpublish` NPM scripts have been configured to automate the discrete build steps

## Documentation

#### Command API

- `name`: The name of the command (required)
- `description`: An optional description for the command
- `options`: An object representing any flag that can be passed to the command. The key will be used as the flag and the value the description.

```ts
protected options = {
	'--force': "Force the command"

	// Optionally, define a shortcut
	'--force': ["-f", "Force the command"]
}
```

Flags can be accessed from the `handle` method (described below).

```ts
const isForced = this.flag("force", true /* optional default */);
```

- `handle`: The async function to run when the command is invoked.

```ts
public async handle() {
	return 0;
}
```

Note, if invoking the command from another command, returning a value will not exit the process. To exit manually, you can call the `exit` method.

```ts
public async handle() {
	//...

	this.exit(); // Same as this.exit(0);
}
```

#### Return values

- `0` (integer) = Exit without errors
- `1` (integer) = Exit with errors
- any other value = Exit without errors

#### Invoking commands programatically

You may call any registered command by "dispatching" it via the application container.

```ts
public async handle() {
	const result = this.app.dispatch("other-command")

	// You may also provide flags
	const result = this.app.dispatch("other-command", {
		'--force': true, 	// By flag
		'-f': true, 		// By flag shortcut,
		'force': true, 		// By flag name,
    })
}
```

#### Writing Console Output

```ts
public async handle()
{
	this.info("Hello world") 	 // Prints green tex
	this.warn("Hello world") 	 // Prints yellow textt
	this.error("Hello world") 	 // Prints red text
	this.line("Hello world") 	 // Prints gray text
	this.jsonLine("Hello world") // Prints formmated json (gray)
	this.newLine() 				 // Prints 1 new line
	this.newLine(3) 			 // Prints 3 new lines
	this.loading("Hello world")  // Display a loading spinner with a message
	this.loading(false)			 // Stop the last loading spinner
}
```

#### Accepting input

```ts
public async handle()
{
	// Accept text input
	const name = await this.ask("Question", "default value");

	// Accept true/false input
	const runInProd = await this.confirm("Run in production?", false);

	// Pick from a list of options
	const mode = await this.prompt(
		"Which mode to run?",
		["Long", "Short"],
		"Short"
	);
}
```