kongcue

package module
v0.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 8, 2025 License: Apache-2.0 Imports: 16 Imported by: 1

README

kongcue

A Go library that bridges Kong CLI parsing with CUE-based configuration files.

Installation

go get github.com/brianm/kongcue

Quick Start

Embed kongcue.Config in your CLI struct to automatically load config files and resolve flag values:

package main

import (
	"fmt"

	"github.com/alecthomas/kong"
	"github.com/brianm/kongcue"
)

type cli struct {
	Name   string         `default:"world"`
	Config kongcue.Config `default:"./config.{yml,yaml,cue,json}" sep:";"`
}

func (c *cli) Run() error {
	fmt.Printf("Hello, %s\n", c.Name)
	return nil
}

func main() {
	var c cli
	ktx := kong.Parse(&c, kongcue.Options())
	err := ktx.Run()
	ktx.FatalIfErrorf(err)
}

With config.yml:

name: "Brian"
./myapp              # Hello, Brian (from config)
./myapp --name Alice # Hello, Alice (CLI overrides config)

Note: kongcue.Options() is required when using kongcue.Config or kongcue.ConfigDoc. If you use kongcue.AllowUnknownFields(), it includes Options() automatically.

Features

  • Multiple file formats: YAML, JSON, and CUE files
  • Glob patterns: Load configs with patterns like ~/.myapp/*.{yaml,yml}
  • Config unification: Multiple config files are merged; conflicts produce errors
  • Automatic name mapping: CLI flags (--ca-url) map to config keys (ca_url)
  • Command hierarchy: Flags resolve based on subcommand context
  • Schema validation: Config keys are validated against CLI flags

Glob Patterns

You can use bash style glob patterns (provided by doublestar).

Note that if you use {yml,yaml,cue,json} style brace expansion and a default value, you will need to tell kong to use a seperator other than , or it will split inside the braces, so use something like:

Config kongcue.Config `default:"./config.{yml,yaml,cue,json}" sep:";"`

This tells kong to use a ; as the seperator between values, so you could do:

Config kongcue.Config `default:"/etc/foo.cue;~/.config/foo/config.{yaml,cue,json}" sep:";"`

To tell it to look in /etc/foo.cue and ~/.config/foo/config.{yaml,cue,json}.

Schema Validation

By default, config files are validated against your CLI struct. Unknown keys that don't correspond to any CLI flag will cause an error:

error: unknown configuration key: typo_field: field not allowed
       Hint: Check that all config keys correspond to valid CLI flags

This catches typos and stale config keys early.

To allow extra fields in config files (useful if configs are shared with other tools), use AllowUnknownFields():

ctx := kong.Parse(&cli, kongcue.AllowUnknownFields())

Schema Documentation Command

Add a command that prints the CUE schema for your CLI's configuration:

type cli struct {
	Name      string            `help:"Name to greet"`
	Server    serverCmd         `cmd:"" help:"Run the server"`
	ConfigDoc kongcue.ConfigDoc `cmd:"config-doc" help:"Print config schema"`
	Config    kongcue.Config    `default:"./config.yaml"`
}

Running ./myapp config-doc outputs a CUE schema:

// Configuration schema for validating config files.
//
// This schema is written in CUE, a configuration language that
// validates and defines data. Learn more at https://cuelang.org
//
// To validate your config file against this schema:
//   1. Save this schema to a file (e.g., schema.cue)
//   2. Run: cue vet -d '#Root' schema.cue your-config.yaml
//
// Fields marked with ? are optional. Fields without ? are required.
#Root: close({
	// Name to greet
	name?: string
	// Run the server
	server?: #Server
})
#Server: close({
	// Server port
	port?: int
})

The schema includes:

  • Help text as comments: Kong help:"..." tags become CUE documentation
  • Required field markers: Fields with required:"" don't have ? and must be present
  • Nested definitions: Each subcommand gets its own #Definition

Users can validate their config files using the CUE CLI:

./myapp config-doc > schema.cue
cue vet -d '#Root' schema.cue config.yaml

Configuration Formats

All formats are parsed using CUE, which means you get CUE's type checking and unification:

YAML (config.yaml):

verbose: 2
agent:
  ca_url: "https://ca.example.com"

JSON (config.json):

{
  "verbose": 2,
  "agent": {
    "ca_url": "https://ca.example.com"
  }
}

CUE (config.cue):

verbose: 2
agent: {
  ca_url: "https://ca.example.com"
}

Naming Convention

CLI flags use kebab-case, config files use snake_case:

CLI Flag Config Key
--ca-url ca_url
--log-file log_file

This avoids quoted field names in CUE (where - is the subtraction operator).

Command Path Resolution

Flags are resolved using the command hierarchy. For a CLI like:

type cli struct {
    Verbose int `name:"verbose"`
    Agent struct {
        CaURL string `name:"ca-url"`
    } `cmd:""`
}
  • --verbose resolves to verbose in config
  • agent --ca-url resolves to agent.ca_url in config

Multiple Config Files

Specify multiple config files with repeated flags:

./myapp --config base.yaml --config overrides.yaml

Files are unified in order. Conflicting values (same key, different values) produce an error.

Low-Level API

For more control, use the loader and resolver directly:

config, err := kongcue.LoadAndUnifyPaths([]string{
    "~/.myapp/config.yaml",
    "./local.yaml",
})
if err != nil {
    log.Fatal(err)
}

ctx := kong.Parse(&cli, kong.Resolvers(kongcue.NewResolver(config)))

License

Apache-2.0

Documentation

Overview

Package kongcue provides CUE-based configuration loading with Kong CLI integration. It supports YAML, JSON, and CUE file formats using CUE as the underlying parser, and provides a Kong resolver for seamless CLI flag and config file merging.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AllowUnknownFields added in v0.1.2

func AllowUnknownFields(paths ...string) kong.Option

AllowUnknownFields returns a Kong option that allows unknown config keys. With no arguments, unknown fields are allowed everywhere (backwards compatible). With path arguments, unknown fields are only allowed at those paths and their descendants.

Paths use dot notation matching the config structure (e.g., "server", "server.tls").

Usage:

kong.Parse(&cli, kongcue.AllowUnknownFields())                    // allow everywhere
kong.Parse(&cli, kongcue.AllowUnknownFields("extra", "legacy"))   // allow at specific paths

func GenerateSchema added in v0.1.2

func GenerateSchema(ctx *cue.Context, app *kong.Application, opts *schemaOptions) (cue.Value, error)

GenerateSchema creates a CUE schema from a Kong application model. The schema uses named definitions (#Root, #CommandName) and closed structs to reject unknown config keys unless allowUnknownFields is set in options. Returns the #Root definition as a cue.Value for validation.

func GenerateSchemaFile added in v0.1.4

func GenerateSchemaFile(app *kong.Application, opts *schemaOptions) *ast.File

GenerateSchemaFile creates a CUE AST file with named definitions. Each command becomes a separate definition (e.g., #Agent, #Server). The root schema is in #Root and references command definitions.

Example output:

#Root: close({
    verbose?: int
    agent?: #Agent
})
#Agent: close({
    ca_url?: string
})

func LoadAndUnifyPaths

func LoadAndUnifyPaths(patterns []string) (cue.Value, error)

LoadAndUnifyPaths loads multiple config files and unifies them into a single CUE value. Supports glob patterns and mixed file types (.cue, .yaml, .yml, .json). Missing files are silently skipped. Returns error if files have conflicting values.

The ~ character is expanded to the user's home directory.

func NewResolver

func NewResolver(value cue.Value) kong.Resolver

NewResolver creates a Kong resolver backed by a CUE value. The resolver automatically maps CLI flag names (kebab-case) to config keys (snake_case).

Config paths are built from the command hierarchy. For example, a flag "--ca-url" on command "agent" will look up "agent.ca_url" in the CUE value. Global flags (not under a subcommand) use just the flag name.

Example:

config, _ := LoadAndUnifyPaths([]string{"~/.myapp/*.yaml"})
ctx := kong.Parse(&cli, kong.Resolvers(NewResolver(config)))

func Options added in v0.1.4

func Options() kong.Option

Options returns a Kong option that sets up kongcue's schema validation. This must be included when using kongcue.Config or kongcue.ConfigDoc. If you want to allow unknown fields, use AllowUnknownFields() instead.

Usage:

kong.Parse(&cli, kongcue.Options())

func ValidateConfig added in v0.1.2

func ValidateConfig(schema cue.Value, config cue.Value) error

ValidateConfig validates that config conforms to the schema. Returns an error if config contains unknown fields or type mismatches.

Types

type Config

type Config []string

func (Config) BeforeResolve

func (r Config) BeforeResolve(k *kong.Kong, ctx *kong.Context, trace *kong.Path, schemaOpts *SchemaOptions) error

type ConfigDoc added in v0.1.4

type ConfigDoc struct {
	// Output is the writer for schema output. Defaults to os.Stdout.
	// Exposed for testing.
	Output io.Writer `kong:"-"`
}

ConfigDoc is a Kong command that outputs the CUE schema for the CLI. Embed this in your CLI struct to add a command that prints the config schema.

The generated schema uses named CUE definitions (#Root, #CommandName, etc.) and can be used to validate configuration files.

Usage:

type cli struct {
    Agent     agentCmd          `cmd:""`
    ConfigDoc kongcue.ConfigDoc `cmd:"config-doc" help:"Print CUE schema for config file"`
}

Running `./myapp config-doc` outputs the schema to stdout.

func (*ConfigDoc) BeforeApply added in v0.1.4

func (c *ConfigDoc) BeforeApply(app *kong.Kong, schemaOpts *SchemaOptions) error

BeforeApply is called by Kong before validation. Using BeforeApply (instead of AfterApply) allows this command to run without requiring other flags to be set, similar to --help.

type SchemaOptions added in v0.1.4

type SchemaOptions struct {
	AllowUnknownPaths []string
	AllowAll          bool
}

SchemaOptions holds configuration for schema generation. Exported so it can be received via Kong's dependency injection in hooks.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL