An open API service indexing awesome lists of open source software.

https://github.com/gdvalle/bbcue

An experimental way to use CUE
https://github.com/gdvalle/bbcue

cue cuelang

Last synced: 12 days ago
JSON representation

An experimental way to use CUE

Awesome Lists containing this project

README

          

# bbcue

**bbcue** turns [CUE](https://cuelang.org) into a low-boilerplate config file generator. Drop a `bb.cue` file in any directory, run `bbcue`, and get JSON, YAML, TOML, or plain text files, with full access to CUE's type system, imports, and a subset of tool tasks.

It's basically an opinionated wrapper with a predefined `cue cmd` task to write config out by convention, without having to recall the `cue export` and `cue import` syntax.

## Install

Download a prebuilt binary from the [Releases page](https://github.com/gdvalle/bbcue/releases).

Or, install with Go:

```sh
go install github.com/gdvalle/bbcue/cmd/bbcue@latest
```

## Quick start

If you have existing config, import it into `bb.cue`:
```sh
bbcue import config.yaml
```

Or, create `bb.cue` yourself:

```cue
bbcue: "config.yaml": {
content: {
database: {
host: "localhost"
port: 5432
}
}
}
```

Run `bbcue`:

```sh
$ bbcue
. -> config.yaml
wrote 1 file(s) from 1 source(s) in 1ms

```

Out comes `config.yaml`:

```yaml
# Generated by bbcue. DO NOT EDIT.

database:
host: localhost
port: 5432
```

Now you can take advantage of CUE features incrementally.

## How it works

bbcue recursively finds `bb.cue` files starting from the current directory. Each file defines a `bbcue` struct whose keys are output filenames and whose values describe the content to write.

The output format is inferred from the file extension (`.json`, `.yaml`/`.yml`, `.toml`, `.txt`) or set explicitly with a `format` field.

### Multiple outputs

A single `bb.cue` can produce multiple files:

```cue
bbcue: "a.json": {
content: { x: 1 }
}
bbcue: "b.yaml": {
content: { y: 2 }
}
bbcue: "c.toml": {
content: { z: 3 }
}
```

## Tool tasks

bbcue reimplements a subset of [CUE tool imports](https://pkg.go.dev/cuelang.org/go/pkg/tool). See [API docs](https://pkg.go.dev/cuelang.org/go/pkg/tool).
(We reimplement because these are not public APIs).
* `exec.Run`
* `file.*`
* `http.Do`
* `os.Environ`
* `os.Getenv`

Note these are powerful tools that break the hermetic seal around CUE evaluation and are typically isolated to CUE `_tool`-suffixed files.

For the tools that interact with the filesystem, they execute relative to the directory containing the `bb.cue` file, regardless of where `bbcue` was invoked.

### Reading files with `tool/file`

Read a file's contents and embed it in the output:

```cue
import "tool/file"

readMe: file.Read & {
filename: "input.txt"
contents: string
}

bbcue: "result.json": {
content: {
data: readMe.contents
}
}
```

### Running commands with `tool/exec`

Capture command output:

```cue
import "tool/exec"

run: exec.Run & {
cmd: ["echo", "hello world"]
stdout: string
}

bbcue: "result.json": {
content: {
message: run.stdout
}
}
```

Handle commands that might fail with `mustSucceed: false`:

```cue
import "tool/exec"

run: exec.Run & {
cmd: ["false"]
mustSucceed: false
stdout: string
}

bbcue: "result.json": {
content: {
ok: run.success
}
}
```

### Reading environment variables with `tool/os`

Read specific variables with `os.Getenv`:

```cue
import "tool/os"

getenv: os.Getenv & {
BBCUE_TEST_VAR: string
}

bbcue: "result.json": {
content: {
val: getenv.BBCUE_TEST_VAR
}
}
```

Or read from the full environment with `os.Environ`:

```cue
import "tool/os"

environ: os.Environ

bbcue: "result.json": {
content: {
val: environ.BBCUE_ENV_TEST
}
}
```

## CUE packages and schemas

All `.cue` files in a directory are loaded together, so you can split schemas and values across files using CUE packages.

bbcue strictly scopes evaluation to the directory containing the `bb.cue` file (it does not automatically unify with parent directories). However, it does walk parent directories to discover `cue.mod` roots for import resolution.

```cue
// schema.cue
package bb

#Config: {
host: string
port: int
}
```

```cue
// bb.cue
package bb

bbcue: "config.json": {
content: #Config & {
host: "localhost"
port: 5432
}
}
```

## Importing data files

The `import` subcommand converts existing JSON, YAML, and TOML files into `bb.cue` entries:

```sh
bbcue import config.json settings.yaml
```

This appends entries to (or creates) `bb.cue` in the current directory. Each file becomes a field under the `bbcue` struct, keyed by filename and wrapped in a `content` field:

```cue
bbcue: "config.json": content: {
host: "localhost"
port: 5432
}
bbcue: "settings.yaml": content: {
key: "value"
}
```

If `bb.cue` already contains an entry for a filename, that file is skipped with a warning. Multi-document YAML files are not supported.

## CLI usage

bbcue uses subcommands. When invoked without a subcommand, `gen` is the default.

```
bbcue [flags] [args...]
```

| Flag | Description |
|------|-------------|
| `--version`, `-v` | Print version and build information |
| `--help`, `-h` | Show usage |

### `cue` (passthrough)

Run the embedded CUE interpreter. All arguments, flags, and environment variables are passed directly to the standard CUE CLI.

```sh
bbcue cue version
```

### `gen` (default)

Discover `bb.cue` files and write configured outputs.

```
bbcue [gen] [flags] [paths...]
```

With no arguments, bbcue checks the current directory for a `bb.cue` file.

| Flag | Description |
|------|-------------|
| `--recurse`, `-r` | Recurse into subdirectories |
| `--no-recurse`, `-R` | Do not recurse (default; overrides `-r` if both given) |
| `--version`, `-v` | Print version and build information |

When paths are specified:
- **Directories** are used as roots (checked directly by default, or recursed with `-r`)
- **Files** use their parent directory as the root

### `import`

Import data files (JSON, YAML, TOML) as CUE values into `bb.cue`.

```
bbcue import
```

### `fmt`

Format CUE files.

```
bbcue fmt [flags] [files...]
```

With no arguments, bbcue discovers and formats all `.cue` files in the current directory (and subdirectories if recurse is enabled). Files are always simplified.

| Flag | Description |
|------|-------------|
| `--recurse`, `-r` | Recurse into subdirectories |
| `--no-recurse`, `-R` | Do not recurse (default; overrides `-r` if both given) |

Example:

```cue
// Before
package example
bbcue: "output.txt": { content: "hello" }
```

```cue
// After
package example

bbcue: "output.txt": content: "hello"
```

### Examples

```sh
# Generate from current directory
bbcue

# Recursively generate from current directory
bbcue -r

# Recursively generate from specific directories
bbcue gen -r ./services ./infra

# Import data files
bbcue import config.json settings.yaml

# Print version
bbcue --version
```

## Directory skipping

When recursing, bbcue automatically skips:

- Directories starting with `.`
- `node_modules`
- `cue.mod`

## Path safety

Output paths are validated to stay within the `bb.cue` file's directory. Absolute paths and `../` traversal are rejected.