Skip to content

horihiro/runx

Repository files navigation

runx - Environment-Aware Command Proxy Manager

runx is an environment-aware command proxy manager—a cross-platform tool that creates command proxies (wrappers) that automatically load environment variables from files before executing commands. Useful for managing cloud profiles, API keys, runtime settings, or any directory-scoped configuration.

Features

  • 🔧 Cross-Platform: Works on Windows, Linux, and macOS
  • 📦 Single Binary, No Runtime Required: Just one executable, no separate runtime installation needed
  • 📁 Directory-Based Context: Automatically searches for environment files from current directory to root, then home directory
  • 🔄 Multiple Environment Files: Merge multiple .env files with later values overriding earlier ones
  • 🎯 Command Proxies: Create persistent command wrappers that automatically load the correct environment
  • 🐚 Shell Support: Bash, Zsh, Fish on Linux/macOS; CMD on Windows
  • 🔐 Windows PATH Management: Smart User/Machine PATH detection with automatic privilege escalation when needed
  • 🐛 Debug Mode: Set RUNX_DEBUG=1 or RUNX_DEBUG=2 for detailed environment file resolution tracing

Existing Tools and Where runx Fits

There are already several strong tools in this space, each with a proven workflow.

  • direnv: A widely trusted option that automatically loads and unloads environment variables when you enter or leave directories via shell hooks. It is excellent for full directory-level automation.
  • dotenv-cli / dotenvx style tools: Reliable and practical tools for explicit one-off command execution with env files (for example, dotenv -e .env -- cmd).

Compared to those approaches, runx focuses on a different operating model: persistent command proxies managed with add/list/remove, command-time environment application (instead of always-on directory hooks), consistent cross-platform behavior, and built-in Windows PATH handling.

Install

Download artifacts from GitHub Releases:

https://github.com/horihiro/runx/releases

Linux (deb package)

curl -LO https://github.com/horihiro/runx/releases/download/v<version>/runx_<version>_<arch>.deb
sudo apt install ./runx_<version>_<arch>.deb
runx --version

Uninstall:

sudo apt remove runx

Linux/macOS (tar.gz)

curl -LO https://github.com/horihiro/runx/releases/download/v<version>/runx-<os>-<arch>-v<version>.tar.gz
tar xzf runx-<os>-<arch>-v<version>.tar.gz
sudo install -m 0755 runx /usr/local/bin/runx
runx --version

Uninstall:

sudo rm -f /usr/local/bin/runx

Windows (winget manifest artifact)

You can install runx from the official winget community repository:

winget install --id horihiro.runx
runx --version

Uninstall:

winget uninstall --id horihiro.runx

If you want to test a release artifact manifest locally before it is published to the official winget repository, use the following flow:

curl -LO https://github.com/horihiro/runx/releases/download/v<version>/runx-winget-manifest-v<version>.zip
mkdir .\temp
tar xzf runx-winget-manifest-v<version>.zip -C .\temp
sudo winget settings --enable LocalManifestFiles
winget install --manifest .\temp
runx --version

Build

You can also build runx from source instead of using release artifacts.

Cross-compilation

# From any OS to Windows
GOOS=windows GOARCH=amd64 go build -o runx.exe main.go

# From any OS to Linux
GOOS=linux GOARCH=amd64 go build -o runx main.go

# From any OS to macOS
GOOS=darwin GOARCH=arm64 go build -o runx main.go

Quick Start

1. Create an Environment File

Create a .myenv file in your project directory:

# App/runtime settings
NODE_ENV=development
API_BASE_URL=https://dev-api.example.com
LOG_LEVEL=debug

Or any other name like .env, dev.env, etc.

2. Run Commands with Environment

# Create a persistent proxy
runx add node --envfile=.myenv

# Now 'node' automatically loads .myenv
node app.js

# Or

# One-time execution
runx exec --envfile=.myenv node app.js

Commands

runx add - Create Command Proxy

Create a command proxy that automatically loads specified environment files.

runx add ORIGINAL_COMMAND [--alias=PROXY_NAME] [--envfile=NAME ...] [--shell=bash|zsh|fish]

Examples:

# Windows
runx add terraform --envfile=.env

# Linux/macOS (auto-detects shell from $SHELL)
runx add terraform --envfile=.env

# Specify shell explicitly
runx add kubectl --envfile=k8s.env --shell=zsh

# Multiple environment files
runx add node --envfile=base.env --envfile=dev.env

# Alias proxy name (mytf executes original terraform)
runx add terraform --alias=mytf --envfile=.env

What happens on Windows:

  1. Checks if original command exists in Machine PATH or User PATH
  2. If in Machine PATH: Recommends creating a Machine proxy (requires admin privileges)
  3. If in User PATH or not found: Creates User proxy in %LOCALAPPDATA%\runx\proxy
  4. Automatically adds proxy directory to User PATH if needed
  5. Handles PATH priority conflicts intelligently

What happens on Linux/macOS:

  1. Creates a shell function in your shell config file (~/.bashrc, ~/.zshrc, or ~/.config/fish/config.fish)
  2. Function calls runx exec with specified environment files
  3. No PATH modification needed

runx exec - Execute with Environment Directly

runx add-created proxies use runx exec under the hood. You can also run runx exec directly for one-off execution without creating a proxy.

runx exec [--envfile=NAME ...] COMMAND [ARGS...]

Examples:

# One-off execution with a single environment file
runx exec --envfile=.myenv node app.js

# Multiple files (merged in order, later overrides earlier)
runx exec --envfile=base.env --envfile=dev.env node app.js

# No environment files (just pass through)
runx exec echo "Hello"

runx env - Preview Resolved Environment Variables

Preview which environment variables are set from resolved env files at the current location.

runx env [COMMAND_OR_ALIAS] [--envfile=NAME ...] [--shell=bash|zsh|fish]

Notes:

  • If COMMAND_OR_ALIAS is specified, envfiles registered by runx add are loaded first
  • Additional --envfile values can be combined and are applied after registered envfiles
  • Command name/alias is optional
  • Prints merged KEY=VALUE entries resolved from the final envfile list
  • Prints (none) if no entries are resolved

Examples:

# Use envfiles registered for command alias `az`
runx env az

# Check merged entries from layered env files
runx env --envfile=base.env --envfile=dev.env

# Combine registered envfiles and ad-hoc overrides
runx env az --envfile=override.env

# Minimal form (current directory tree + home resolution)
runx env --envfile=.env

runx remove - Remove Command Proxy

Remove a previously created command proxy.

runx remove COMMAND [--shell=bash|zsh|fish]

Examples:

# Windows
runx remove az

# Linux/macOS
runx remove az --shell=bash

runx list - List Command Proxies

List all command proxies created by runx.

runx list [--shell=bash|zsh|fish]

Examples:

# Windows
runx list

# Linux/macOS
runx list --shell=zsh

Environment File Format

Environment files use simple KEY=VALUE format:

# Comments start with #
AWS_PROFILE=staging
AWS_REGION=us-east-1

# Quotes are optional
API_BASE_URL=https://api.example.com
API_KEY="your-api-key-here"

# export prefix is supported (bash compatibility)
export NODE_ENV=production

Envfile Argument Rules

  • Supported: file name (.env, dev.env, myconfig) or absolute path (/home/user/.env, C:\config\app.env)
  • Not supported: relative paths with separators (../parent.env, configs/app.env)

Search Order

When you specify a file name (for example --envfile=.env), runx searches in this order:

  1. Current directory: ./.env
  2. Parent directories: Searches up to filesystem root
  3. Home directory: ~/.env

First match wins. This allows project-specific configs to override global defaults.

When you specify an absolute path (for example --envfile=/path/to/app.env), runx checks only that file.

Merge Behavior

When multiple --envfile options are provided, runx merges variables in the order given.

  • Later files override earlier files for the same key.
  • Merged values override the current process environment for matching keys.
  • File names and absolute paths can be mixed.

Example:

runx exec --envfile=base.env --envfile=/abs/path/override.env node app.js

If both files define API_URL, the value from /abs/path/override.env is used because /abs/path/override.env is later.

Debug Mode

Set RUNX_DEBUG environment variable to see detailed information:

# Level 1: Show resolved envfiles and merged variables
export RUNX_DEBUG=1
runx exec --envfile=.env terraform plan

# Level 2: Also show file search trace
export RUNX_DEBUG=2
runx exec --envfile=.env terraform plan

Output example:

[runx][debug] resolved envfiles:
[runx][debug]   .env: /home/user/project/.env
[runx][debug] envfile search trace:
[runx][debug]   .env:
[runx][debug]     - /home/user/project/subdir/.env
[runx][debug]     - /home/user/project/.env
[runx][debug] merged environment entries:
[runx][debug]   - AWS_PROFILE=staging (from: .env)
[runx][debug]   - AWS_REGION=us-east-1 (from: .env)

Usage Examples

Terraform with Per-Project Environments

# Create .env files per project
$ cat ~/project-a/.env
TF_WORKSPACE=project-a
AWS_PROFILE=project-a

$ cat ~/project-b/.env
TF_WORKSPACE=project-b
AWS_PROFILE=project-b

# Create terraform proxy
$ runx add terraform --envfile=.env

# 'terraform' now picks env from the current directory tree
$ cd ~/project-a && terraform plan  # Uses project-a env
$ cd ~/project-b && terraform plan  # Uses project-b env

AWS CLI with Profile Switching

$ cat .awsenv
AWS_PROFILE=production
AWS_REGION=us-east-1

$ runx add aws --envfile=.awsenv
$ aws s3 ls  # Uses production profile

Layered Environment Files (Merge)

# base.env - shared configuration
NODE_ENV=production
LOG_LEVEL=info
API_URL=https://api.example.com

# secrets.env - sensitive overrides
API_KEY=secret-key
DB_PASSWORD=secret-password

# later file wins for duplicate keys
$ runx add node --envfile=base.env --envfile=secrets.env
$ node app.js

Alias Proxy Name

# Create project-specific proxy name that executes terraform
runx add terraform --alias=mytf --envfile=.env

# Original 'terraform' still works normally
# 'mytf' runs original 'terraform' with environment loaded by runx

Shell-Specific Proxies

runx add kubectl --envfile=k8s.env --shell=bash
runx add kubectl --envfile=k8s.env --shell=zsh
runx add kubectl --envfile=k8s.env --shell=fish

One-Off Execution (No Proxy)

$ runx exec --envfile=test.env pytest
$ runx exec --envfile=staging.env curl https://api.example.com

Architecture

See architecture details for Windows/Linux proxy behavior, including User vs Machine proxy selection on Windows:

Troubleshooting

See detailed troubleshooting guide:

About

runx - Environment-Aware Command Proxy Manager

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors