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
- Host: GitHub
- URL: https://github.com/gdvalle/bbcue
- Owner: gdvalle
- License: apache-2.0
- Created: 2026-02-15T01:23:51.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-05-22T06:46:13.000Z (19 days ago)
- Last Synced: 2026-05-22T15:09:50.788Z (19 days ago)
- Topics: cue, cuelang
- Language: Go
- Homepage:
- Size: 12.2 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
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.