Motivation
When using kubectl or Terraform in team development, you typically face these problems:
- You want to use different versions of kubectl for different projects
- Team members have different versions installed
- It’s tedious to explain “please install the correct version” every time
- Even after explaining, there’s no guarantee the correct version is actually installed
To solve these problems, I created a collection of wrapper scripts that control tool versions via environment variables.
Overview
versenv is a collection of wrapper scripts for CLI tools like kubectl, Terraform, and Helm.
Each script automatically downloads and caches the executable for the version specified by environment variables, then behaves identically to the original command.
# Specify version via environment variable and run
KUBECTL_VERSION=1.23.5 ./kubectl version --client
On the first run, the specified version is downloaded. Subsequent runs use the cached binary.
Supported Tools
Major tools:
| Tool | Environment Variable |
|---|---|
| direnv | DIRENV_VERSION |
| kubectl | KUBECTL_VERSION |
| terraform | TERRAFORM_VERSION |
| helm | HELM_VERSION |
| packer | PACKER_VERSION |
| aws (v2) | AWSCLI_VERSION |
| protoc | PROTOC_VERSION |
| sops | SOPS_VERSION |
| gitleaks | GITLEAKS_VERSION |
Additionally, over 20 tools are supported including buf, tflint, and fzf.
Installation
Individual Download
curl --tlsv1.2 -#fLR https://raw.githubusercontent.com/versenv/versenv/HEAD/bin/kubectl -o ./kubectl && chmod +x ./kubectl
Bulk Installation
curl --tlsv1.2 -fLRSs https://raw.githubusercontent.com/versenv/versenv/HEAD/install.sh | VERSENV_PATH=./bin bash
To install only specific tools, specify VERSENV_SCRIPTS.
curl --tlsv1.2 -fLRSs https://raw.githubusercontent.com/versenv/versenv/HEAD/install.sh | \
VERSENV_SCRIPTS=kubectl,terraform VERSENV_PATH=./bin bash
Usage
Basic Usage
# Run with a specific version
KUBECTL_VERSION=1.23.5 ./kubectl version --client
# Specifying latest or stable uses the latest stable version
KUBECTL_VERSION=latest ./kubectl version --client
# Without an environment variable, the latest stable version is automatically selected
./kubectl version --client
Combined with direnv (Recommended)
By combining with direnv, you can pin tool versions per project.
# Create a bin directory at the project root and place the scripts
cd "$(git rev-parse --show-toplevel)"
curl --tlsv1.2 -fLRSs https://raw.githubusercontent.com/versenv/versenv/HEAD/install.sh | \
VERSENV_SCRIPTS=kubectl,terraform VERSENV_PATH=./bin bash
# Pin versions in .envrc
cat <<'EOF' > .envrc
export PATH="$(git rev-parse --show-toplevel)/bin:$PATH"
export KUBECTL_VERSION=1.23.5
export TERRAFORM_VERSION=1.1.8
EOF
direnv allow .
Now when you enter this project directory, the specified versions are automatically used. By committing .envrc to Git, everyone on the team uses the same versions.
Special Features
Context Switching via KUBECTL_CONTEXT
For kubectl, helm, and stern, you can automatically switch contexts using the KUBECTL_CONTEXT environment variable.
KUBECTL_CONTEXT=gke_myproject_us-central1_alpha kubectl get pods
# => Runs as: kubectl --context=gke_myproject_us-central1_alpha get pods
self-update
You can update the script itself to the latest version.
./kubectl versenv self-update
Listing Available Versions
# Show all versions
./kubectl versenv versions
# Show only stable versions
./kubectl versenv stables
How It Works
Each wrapper script operates as follows:
- Get the version from environment variables (if unset, automatically resolve to the latest stable version)
- Check if the binary exists at
~/.cache/versenv/{tool-name}/{version}/bin/ - If not, download from the official site and cache it
- Execute the cached binary with
exec
The only dependency is curl (or wget), and it runs as a single Bash script.
Why Use versenv
The greatest strength of versenv is that it’s just a thin shell script.
By committing the scripts to your project’s bin/ directory and adding it to PATH via .envrc, anyone who clones the repository can use the same environment. There’s no need to have them install external tools like asdf or mise beforehand.
your-project/
├── bin/
│ ├── kubectl # Committed to Git
│ └── terraform # Committed to Git
└── .envrc # Sets PATH and VERSION
When a new member joins the project, all they need to do is git clone and direnv allow. No need to explain “first install asdf, then add the plugins…”
Differences from existing version management tools (asdf, mise, etc.):
- No pre-installation required: Just clone since it’s included in the repository
- Minimal dependencies: Works with just curl and bash
- Transparent operation: Works exactly like the original commands
- CI/CD friendly: Easy to use in CI environments since versions are controlled via environment variables
It’s convenient when you want to eliminate the hassle of “having team members install something.”