Variant2
Turn your bash scripts into a modern, single-executable CLI app today
Install / Use
/learn @mumoshu/Variant2README
Variant 2
This repository contains a development branch of the second major version of Variant.
See https://github.com/mumoshu/variant for more information on the first version.
Once finished, this repository will eventually take over the
masterbranch of the original variant repository.
Features
- HCL-based DSL: Terraform-like strongly-typed DSL on top of HCL to define your command. See
Configuration Languagebelow. - Concurrency and Workflow: Embedded workflow engine with concurrency. See
Concurrencybelow. Example: concurrency - Auto-prompt: Variant prompts for missing arguments to your command so that your user is only needed to provide missing arguments on the fly, instead of rerunning the whole command by repeating every argument.
- Configs: Deep-merging YAML configuration files. Example: config
- Secrets: Deep-merging secret values from Vault, AWS SecretsManager, SOPS, etc. powered by vals. Example: secret
- Testing: Test framework with
go test-compatible test runner. Example: simple - Embeddable: Easy embedding in any Golang application
- Easy distribution: Build a single-executable of your command with Golang
- Dependency Management: Dependent files, executable binaries and docker-run shims can be automatically installed and updated with the variantdev/mod integration. Example: module
- Run as a Kubernetes controller: You can easily turn your Variant command into a Kubernetes controller. See examples/controller
- Integrations: Integrates nicely with Slack, GitHub. You can run Variant command in response to Slack message, GitHub issue comment, commit push, etc.
Getting Started
Create an variant file that contains the following:
examples/getting-started/getting-started.variant:
option "namespace" {
description = "Namespace to interact with"
type = string
default = "default"
short = "n"
}
job "kubectl" {
parameter "dir" {
type = string
}
exec {
command = "kubectl"
args = ["-n", opt.namespace, "-f", param.dir]
}
}
job "helm" {
parameter "release" {
type = string
}
parameter "chart" {
type = string
}
option "values" {
type = list(string)
}
exec {
command = "helm"
args = ["upgrade", "--install", "-n", opt.namespace, param.release, param.chart]
}
}
job "deploy" {
description = "Deploys our application and the infrastructure onto the K8s cluster"
step "deploy infra" {
run "helm" {
release = "app1"
chart = "app1"
}
}
step "deploy apps" {
run "kubectl" {
dir = "deploy/environments/${opt.env}/manifests"
}
}
}
Now you can run it with variant:
variant run -h will show you that all the jobs are available via sub-commands:
$ variant run -h
Usage:
variant run [flags]
variant run [command]
Available Commands:
deploy
helm
kubectl
Flags:
-h, --help help for run
-n, --namespace string Namespace to interact with
Use "variant run [command] --help" for more information about a command.
And variant run deploy -h for the usage for the specific job = sub-command named deploy:
Deploys our application and the infrastructure onto the K8s cluster
Usage:
variant run deploy [flags]
Flags:
-h, --help help for deploy
Global Flags:
-n, --namespace string Namespace to interact with
As you've seen in the help output, variant run deploy runs the deploy job, which in turn runs kubectl and helm to install your apps onto the K8s cluster:
$ variant run deploy
Once you're finished developing the command, let's build a single executable binary of the command for easy distribution:
$ variant export binary ./ build/myapp
The exported executable binary accepts the same arguments as variant run:
$ ./build/myapp -h
$ ./build/myapp run deploy
Congratulations! You're now ready to dive deep and solve your own problems with Variant.
Still curious how Variant helps developing your own command as it grows?
Head over to the following per-topic sections for more features:
- Writing Commands to learn the Variant DSL for writing commands
- Debugging Commands to learn how to debug your Variant command
- Writing Tests to learn the Variant DSL for writing tests
- Generating Shims to make your Variant command look native
- Compiling Command to export Go source or an executable binary
- Running Command From Other Directory
- Concurrency section to make
kubectlandhelmconcurrent so that the installation time becomes minimal - Log Collection to filter and forward log of commands and the arguments passed to them along with their outputs
- Use Split, Merge and Import to split, compose and tidy Variant commands
- JSON Configuration Syntax can be used as an alternative to HCL2-based one
- Slack integration to turn your command into a Slack bot
Generating Shims
If you're distributing this command with your teammates, do use variant generate shim to create a shim to make it look like a native command:
$ variant generate shim examples/getting-started/
$ cat ./examples/getting-started/getting-started
#!/usr/bin/env variant
import = "."
$ ./examples/getting-started/getting-started
Usage:
getting-started [flags]
getting-started [command]
Available Commands:
deploy Deploys our application and the infrastructure onto the K8s cluster
helm
help Help about any command
kubectl
Flags:
-h, --help help for getting-started
-n, --namespace string Namespace to interact with
Use "getting-started [command] --help" for more information about a command.
Compiling Command
As we've covered in the Getting Started guide, variant export sub-commands can be used to export
your command in various formats.
variant export binary SRC CMD generates an executable binary at the path CMD from your command defined under the
directory SRC:
$ variant export binary ./ build/myapp
The exported executable binary accepts the same arguments as variant run.
So myapp -h corresponds to cd $SRC; variant run -h, while
myapp run deploy corresponds to cd $SRC; variant run deploy.
variant export go SRC/CMD PKG generates a directory at PKG/CMD that contains Go source files that can be built by
running go build PKG/CMD.
Assuming you already have go installed, you can run variint export go src/myapp build, then edit code under
build/myapp to make any customization that can't be done with shims, and finally build
an executable with go build -o myapp ./build/myapp.
Running Command From Other Directory
Usually, when your command has been defined under the directory path/to/your/command, variant run requires you to chdir to
it before running.
To be clear, cd $SRC; variant run can be used to run it from another directory. However with that you command cannot
access the actual current directory, as you've already cded.
You can also use a shim or an exported binary to make it runnable from any
directory. But it takes some time so probably you'd like to run it from any directory without a variant export step
while developing?
The VARIANT_DIR environment variable might be the solution. When variant recognizes it, it reads the command from the directory
specified by it.
Just run:
VARIANT_DIR=$SRC variant run
Your command can now be run without cd and still has access to the current directory.
Split, Merge and Import
Do you have a huge yourcmd.variant that needs to be split for readability?
path/to/yourcmd.variant:
job "foo" {
# snip
}
job "bar" {
# snip
}
job "baz" {
# snip
}
Variant usually works per-directory basis. That is, it loads and merges all the .variant files in a directory to form a single command.
That is, you can just split the file into three .variant files in the same directory to split the huge file:
path/to/yourcmd_foo.variant:
job "foo" {
parameter "param1" {
# snip
}
# snip
}
path/to/yourcmd_bar.variant:
job "bar" {
# snip
}
path/to/yourcmd_baz.variant:
job "baz" {
# snip
}
Okay that works. But you ended up too many files in a single directory?
A "parent" variant file containing import or imports can be used to load all the *.variant files in the directory into the current job.
path/to/yourcmd.variant:
job "foo" {
import = "./foo"
}
job "bar" {
import = "./bar"
}
path/to/foo/foo.variant:
parameter "param1" {
# snip
}
# snip
Note that imports is the newer variant of import that supports multiple sources to be imported.
Also, you can import following sources:
- Relative path to local directory (A local path that doesn't start with
/, likefoo/bar) - Absolute path to local directory (An absolute path that starts with
/, like `/variant/modules/example.co
Related Skills
node-connect
353.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
353.3kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
353.3kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
