A command-line tool to generate a JSON Schema from a YAML values.yaml file, optionally merging an overrides file. Useful for Helm chart values and other YAML-based configurations.
- Valet: Helm Values to JSON Schema
Valet automatically generates JSON Schema definitions from Helm chart values.yaml files:
- Infers types from YAML values
- Preserves defaults from your values files
- Handles components with enabled flags intelligently
- Supports overrides via separate YAML files
- Speeds up development by providing schema validation for Helm charts
- Beautiful CLI experience powered by Charm's Fang library
%%{
init: {
'theme': 'dark',
'themeVariables': {
'primaryColor': '#3e4451',
'primaryTextColor': '#abb2bf',
'primaryBorderColor': '#56b6c2',
'lineColor': '#61afef',
'secondaryColor': '#2c313c',
'tertiaryColor': '#3b4048'
}
}
}%%
graph TD
Main[main.go] --> |entry point| Cmd[cmd package]
Cmd --> RootCmd[cmd/root.go]
RootCmd --> GenerateCmd[cmd/generate.go]
RootCmd --> VersionCmd[cmd/version.go]
GenerateCmd --> Config[internal/config]
GenerateCmd --> |schema generation| SchemaGen[Schema Generator]
GenerateCmd --> Telemetry[internal/telemetry]
Config --> |config loading| YAML[YAML Config Files]
subgraph "Core Functionality"
SchemaGen --> TypeInference[Type Inference]
SchemaGen --> ComponentHandling[Component Processing]
SchemaGen --> OverrideMerging[Override Merging]
end
subgraph "CLI Interface"
Main --> |wrapped by| Fang[Fang CLI Framework]
Fang --> Cmd
Cmd
RootCmd
GenerateCmd
VersionCmd
end
subgraph "Configuration"
Config
YAML
end
subgraph "Observability"
Telemetry --> Tracing[OpenTelemetry Tracing]
Telemetry --> Metrics[Metrics Collection]
Telemetry --> Logging[Structured Logging]
Tracing --> OTLP[OTLP Exporter]
Metrics --> OTLP
Logging --> |zap integration| Tracing
end
classDef core fill:#c678dd,stroke:#61afef,stroke-width:1px,color:#efefef;
classDef cli fill:#61afef,stroke:#56b6c2,stroke-width:1px,color:#efefef;
classDef config fill:#98c379,stroke:#56b6c2,stroke-width:1px,color:#282c34;
classDef fang fill:#e06c75,stroke:#56b6c2,stroke-width:2px,color:#efefef;
classDef telemetry fill:#56b6c2,stroke:#61afef,stroke-width:1px,color:#efefef;
class SchemaGen,TypeInference,ComponentHandling,OverrideMerging core;
class Main,Cmd,RootCmd,GenerateCmd,VersionCmd cli;
class Config,YAML config;
class Fang fang;
class Telemetry,Tracing,Metrics,Logging,OTLP telemetry;
Clone the repository and build:
git clone https://github.com/mkm29/valet.git
cd valet
go build -o bin/valet main.goInstall directly using Go modules:
go install github.com/mkm29/valet@latestGenerate a JSON Schema from a values.yaml in the given <context-dir> using the generate command:
valet [global options] generate [flags] <context-dir>
Global options:
--config-file string config file path (default: .valet.yaml)
-d, --debug enable debug logging
--telemetry-enabled enable telemetry
--telemetry-exporter string telemetry exporter type (none, stdout, otlp) (default: none)
--telemetry-endpoint string OTLP endpoint for telemetry (default: localhost:4317)
--telemetry-insecure use insecure connection for OTLP (default: false)
--telemetry-sample-rate float trace sampling rate (0.0 to 1.0) (default: 1.0)
Generate flags:
-f, --overrides string path (relative to context dir) to an overrides YAML file (optional)
-o, --output string output file (default: values.schema.json)The tool writes a values.schema.json (or custom output file) in the <context-dir>.
Valet supports configuration through multiple sources, with precedence in the following order:
- CLI flags (highest priority)
- Environment variables
- Configuration file
- Default values (lowest priority)
The CLI supports a YAML configuration file (default: .valet.yaml) in the current directory. Use the --config-file flag to specify a custom path. The following keys are supported:
context: directory containingvalues.yamloverrides: path to an overrides YAML fileoutput: name of the output schema file (default:values.schema.json)debug: enable debug logging (boolean)telemetry: telemetry configuration (object)enabled: enable telemetry (boolean)serviceName: service name for telemetry (default:valet)serviceVersion: service version for telemetry (default:0.1.0)exporterType: type of exporter (none,stdout,otlp)otlpEndpoint: OTLP endpoint for traces and metricsinsecure: use insecure connection for OTLPsampleRate: trace sampling rate (0.0 to 1.0)headers: additional headers for OTLP requests (map)
Configuration can also be set via environment variables:
VALET_CONTEXTVALET_OVERRIDESVALET_OUTPUTVALET_DEBUG
Generate schema from a directory containing values.yaml:
./bin/valet generate charts/mychartGenerate schema merging an override file:
./bin/valet generate --overrides override.yaml charts/mychartPrint version/build information:
./bin/valet versiongithub.com/mkm29/valet@v0.1.1 (commit 9153c14b9ffddeaccba93268a0851d5da0ae8cbf)
Valet includes comprehensive observability capabilities through OpenTelemetry integration, providing distributed tracing, metrics, and structured logging for monitoring and debugging.
Enable telemetry using CLI flags or configuration:
# Enable with stdout exporter (for development)
valet generate --telemetry-enabled --telemetry-exporter stdout charts/mychart
# Enable with OTLP exporter (for production)
valet generate --telemetry-enabled --telemetry-exporter otlp \
--telemetry-endpoint localhost:4317 \
--telemetry-insecure charts/mychartTelemetry can be configured via:
-
CLI Flags:
--telemetry-enabled: Enable telemetry (default: false)--telemetry-exporter: Exporter type:none,stdout,otlp(default: none)--telemetry-endpoint: OTLP endpoint (default: localhost:4317)--telemetry-insecure: Use insecure connection for OTLP (default: false for better security)--telemetry-sample-rate: Trace sampling rate 0.0-1.0 (default: 1.0)
-
Configuration File (
.valet.yaml):
telemetry:
enabled: true
serviceName: valet
exporterType: otlp
otlpEndpoint: localhost:4317
insecure: false
sampleRate: 1.0
headers:
api-key: your-api-key- Environment Variables:
VALET_TELEMETRYVALET_TELEMETRY_EXPORTERVALET_TELEMETRY_ENDPOINTVALET_TELEMETRY_INSECUREVALET_TELEMETRY_SAMPLE_RATE
Valet creates detailed traces for all operations:
- Command execution: Root span for the entire command
- File operations: Loading values.yaml, overrides, writing schema
- Schema generation: Type inference, merging, validation
- Component processing: Individual spans for complex operations
Example trace structure:
generate.command
├── load.values_yaml
├── load.overrides_yaml (if applicable)
├── merge.yaml_files
├── generate.schema
├── marshal.json
└── write.schema_fileThe following metrics are collected:
-
Command Metrics:
valet.command.executions: Total command executions (counter)valet.command.duration: Command execution duration (histogram)valet.command.errors: Total command errors (counter)
-
File Operation Metrics:
valet.file.reads: File read operations (counter)valet.file.writes: File write operations (counter)valet.file.size: File size distribution (histogram)
-
Schema Generation Metrics:
valet.schema.generations: Total schema generations (counter)valet.schema.fields: Number of fields in schemas (histogram)valet.schema.generation_duration: Schema generation time (histogram)
All file path attributes in metrics are sanitized to protect sensitive information - only the filename and immediate parent directory are included in telemetry data.
Valet uses Uber's zap for high-performance structured logging with OpenTelemetry integration:
- Zero-allocation logging: Zap's design ensures minimal performance overhead
- Structured fields: All log data is structured for easy parsing and querying
- OpenTelemetry integration: Log entries automatically include trace and span IDs
- Span events: All logs are also recorded as events in the active span
- Level control: Info level by default, Debug level when
--debugflag is set - JSON encoding: Logs are emitted as JSON for compatibility with log aggregation systems
Example log output:
{
"timestamp": "2024-01-20T10:15:30.123Z",
"level": "debug",
"logger": "valet",
"caller": "generate.go:459",
"message": "Original YAML values loaded",
"trace_id": "7d3e8f9a1b2c3d4e5f6a7b8c9d0e1f2a",
"span_id": "1a2b3c4d5e6f7890",
"file": "charts/mychart/values.yaml",
"top_level_keys": 15
}Valet's OTLP exporter can send telemetry data to any OpenTelemetry-compatible backend:
- Jaeger: For distributed tracing
- Prometheus: For metrics collection
- Grafana: For visualization
- Elastic APM: For application performance monitoring
- New Relic, Datadog, etc.: Via OTLP support
Example docker-compose setup for local observability:
services:
otel-collector:
image: otel/opentelemetry-collector:latest
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
volumes:
- ./otel-config.yaml:/etc/otel-collector-config.yaml
command: ["--config=/etc/otel-collector-config.yaml"]
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686" # Jaeger UI
- "14250:14250" # Jaeger gRPCGiven a values.yaml:
replicaCount: 3
image:
repository: nginx
tag: stable
env:
- name: LOG_LEVEL
value: debugRunning the generate command:
./bin/valet generate .Produces values.schema.json with contents:
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"replicaCount": {
"type": "integer",
"default": 3
},
"image": {
"type": "object",
"properties": {
"repository": {
"type": "string",
"default": "nginx"
},
"tag": {
"type": "string",
"default": "stable"
}
},
"default": {}
},
"env": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"default": "LOG_LEVEL"
},
"value": {
"type": "string",
"default": "debug"
}
},
"default": {}
},
"default": []
}
},
"required": ["replicaCount", "image", "env"]
}- Load configuration from the file specified by
--config-file(default:.valet.yaml), environment variables, and CLI flags - Load
values.yamlin the specified directory - Merge an overrides YAML if the
--overridesflag is provided - Recursively infer JSON Schema types and defaults
- Post-process the schema to intelligently handle:
- Components with
enabled: falsefield (skipping required fields) - Empty default values (strings, arrays, maps)
- Nested component structures
- Components with
- Write
values.schema.json(or custom output file) in the same directory
The tool includes several smart features:
- Component detection: Automatically detects components with an
enabledfield and handles their required fields intelligently - Empty value handling: Fields with empty default values aren't marked as required
- Type conversion: Maps and complex types are properly represented in the schema
- Nested processing: Recursively processes properties at all levels of nesting
- Go 1.23 or later
A Makefile is provided with common development tasks:
make help: Show available commands (default when runningmake).make build: Build the CLI (outputsbin/valet).make test: Run tests, generatecover.outandcover.html.make check-coverage: Install and rungo-test-coverageto enforce coverage thresholds defined in.testcoverage.yml.make clean: Remove build artifacts (bin/andvalet).
Make sure you have GNU Make installed.
The project uses Testify as its testing framework, with all tests organized in the tests directory using the ValetTestSuite test suite.
You can use the Makefile to run tests and check coverage:
make test
make check-coverageTo run the test suite:
go test ./...To run tests with verbose output:
go test ./tests/... -vTo generate a coverage report:
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.outTo view an HTML coverage report:
go tool cover -html=coverage.outAll tests are located in the tests directory and use the ValetTestSuite struct which provides:
- Setup and teardown functionality
- Helper methods like
CopyDirfor test fixtures - Consistent assertion methods via Testify
The project maintains high test coverage standards:
- 70% minimum coverage for each file
- 80% minimum coverage for each package
- 85% minimum total coverage
These thresholds are enforced in CI via the coverage workflow.
This project uses GoReleaser to automate builds and releases. Binaries for Linux and macOS (amd64 and arm64) are built when tags (e.g., v0.1.0) are pushed.
-
A GitHub Actions workflow (
.github/workflows/release.yml) runs GoReleaser on push tags and via manual dispatch. -
Note: The release workflow sets
permissions.contents: writeso that theGITHUB_TOKENhas sufficient permissions to create releases. -
To run a local release:
go install github.com/goreleaser/goreleaser@latest goreleaser release --rm-dist
Contributions are welcome! Feel free to open issues and submit pull requests.
