Skip to main content

A command-line tool for managing environment variables

Project description

EVM - Environment Variable Manager

A powerful command-line tool for managing environment variables on macOS and Linux systems.

Version: 2.4.0

Features

  • Set/Get/Delete: Manage environment variables
  • List/Search: View and find variables easily
  • Import/Export: JSON, .env, and shell script formats
  • Backup/Restore: Timestamps and merge support
  • Groups: Namespace-based organization
  • Execute: Run commands with custom environment
  • Load to Memory: Sync variables to system environment
  • Secrets: HKDF+HMAC-CTR encrypted storage (v3) with auto-migration from v1/v2
  • Templates: {{VAR}} reference expansion
  • Diff: Compare current state with backups
  • Dry-run: Preview changes before writing
  • Validate: Schema-based variable value validation (URL, email, port, etc.)
  • Schema: Define and enforce variable formats and constraints
  • History: Operation audit log with JSONL storage
  • Shell Completion: bash, zsh, fish completion script generation
  • Interactive Safety: Confirmation prompts for destructive operations (--force to skip)
  • JSON Output: --json flag for structured output (agent-friendly, stdout=data, stderr=errors)
  • Quiet Mode: --quiet suppresses all human-readable output
  • Granular Exit Codes: 11 distinct codes for programmatic error handling
  • Secure: Shell-safe export (key+value), chmod 600, atomic writes, shared lock file, HKDF key separation
  • Pure Python: No external dependencies, Python 3.9+

Platform Support

Supported Platforms:

  • ✅ macOS (Darwin)
  • ✅ Linux (POSIX-compliant systems)
  • ❌ Windows (not supported - uses fcntl for file locking)

Requirements:

  • Python 3.9 or higher
  • POSIX-compliant operating system (for file locking with fcntl)

Project Structure

evm/
├── evm/
│   ├── __init__.py           # Package init, version info
│   ├── __main__.py           # Module entry point
│   ├── cli.py                # CLI parsing and command dispatch
│   ├── manager.py            # Core business logic (CRUD, encryption, templates)
│   ├── _io.py                # IOMixin (import/export/backup/restore/diff)
│   ├── _groups.py            # GroupMixin (namespace management)
│   ├── _history.py           # HistoryMixin (operation logging)
│   ├── _schema.py            # SchemaMixin (format validation)
│   ├── _completion.py        # Shell completion generators (bash/zsh/fish)
│   ├── _json.py              # JSON output helpers (agent-friendly)
│   ├── _crypto.py            # HKDF + HMAC-CTR encryption module
│   ├── formatters.py         # Terminal output formatting
│   └── exceptions.py         # Custom exception hierarchy (17 classes)
├── examples/                 # Example scripts
├── tests/                    # Test suite (575 tests)
│   ├── test_main.py          # Unit + integration tests
│   ├── test_io_boundary.py   # _io.py boundary tests
│   ├── test_cli_boundary.py  # cli.py boundary tests
│   ├── test_v230_fixes.py    # v2.3.0 code review fix tests
│   ├── test_coverage_gap.py  # Coverage gap tests (98% target)
│   ├── test_formatters.py    # Formatter output tests
│   └── test_case/            # Test configuration files
├── docs/
│   ├── API_REFERENCE.md      # Python API reference
│   ├── CHANGELOG.md          # Version history
│   ├── DEVELOPMENT_REVIEW.md # Development review & roadmap
│   ├── ANALYSIS.md           # Project analysis report
│   ├── USER_GUIDE_CN.md      # 中文系统功能说明书
│   ├── AGENT_CLI_EVALUATION.md
│   ├── CODE_REVIEW.md
│   ├── CODE_REVIEW_v2.0.0_FINAL.md
│   └── CODE_REVIEW_v2.2.0.md
├── skill/                    # AI Agent Skill (evm-agent)
├── pyproject.toml            # PEP 621 project metadata & tool config
├── setup.py                  # Backward-compatible setup shim
├── requirements.txt          # Thin wrapper pointing to pyproject.toml
├── README.md                 # This file
├── LICENSE                   # MIT License
└── Makefile                  # Build automation

Quick Start

Installation

# Core install (no third-party dependencies)
pip install .

# Development install (includes pytest, ruff, mypy)
pip install ".[dev]"

# Test-only install
pip install ".[test]"

# Lint-only install
pip install ".[lint]"

# From source (editable/development mode)
pip install -e ".[dev]"

# For current user only
pip install --user -e ".[dev]"

# Verify installation
evm --version
evm --help

Run as Module

python -m evm --help

Your First 5 Minutes with EVM

1. Basic Operations

# Set a variable
evm set API_KEY "abc123"

# Get the value back
evm get API_KEY
# Output: abc123

# List all variables
evm list
# Shows all stored variables in a table format

2. Use Isolated Storage (Recommended)

By default, EVM uses ~/.evm/env.json. For project-specific configs, use --env-file:

# Create a project-specific config
evm --env-file ./project.json set DATABASE_URL "postgresql://localhost/mydb"
evm --env-file ./project.json set API_KEY "project_secret"

# Only affects this file, not your global config
evm --env-file ./project.json list

3. JSON Output for Scripts & Agents

Use --json to get structured output (works before or after the command):

# Get as JSON (both work)
evm --env-file ./project.json --json get API_KEY
evm --env-file ./project.json get API_KEY --json
# stdout: {"status": "ok", "data": {"key": "API_KEY", "value": "project_secret"}}

# List all as JSON
evm --env-file ./project.json list --json
# stdout: {"status": "ok", "data": {"API_KEY": "project_secret", "DATABASE_URL": "..."}}

# Errors go to stderr with error codes
evm --env-file ./project.json get MISSING --json
# stderr: {"status": "error", "error": "Environment variable 'MISSING' not found", "error_code": 2}
# exit code: 2

4. Preview Changes with Dry-Run

# Preview what would happen without actually writing
evm --env-file ./project.json set NEW_KEY "value" --dry-run
# Output: [DRY-RUN] Would set: NEW_KEY=value
# (Nothing is actually written)

# Works with delete, clear, etc.
evm --env-file ./project.json delete API_KEY --dry-run
# Output: [DRY-RUN] Would delete: API_KEY

5. Manage Multiple Environments

Use groups to organize dev/staging/prod configurations:

# Set variables for different environments
evm --env-file ./project.json setg dev DATABASE_URL "localhost:5432/dev"
evm --env-file ./project.json setg prod DATABASE_URL "prod.example.com:5432/prod"

# List by group
evm --env-file ./project.json listg dev
# Shows only dev group variables

# List all groups
evm --env-file ./project.json groups
# Shows: dev (1 variable), prod (1 variable)

# Export a specific environment
evm --env-file ./project.json export --group prod --format env -o .env.prod

6. Encrypt Sensitive Data

# Store encrypted (HKDF + HMAC-CTR encryption)
evm --env-file ./project.json set --secret DB_PASSWORD "super_secret_password"

# Retrieve decrypted
evm --env-file ./project.json get --secret DB_PASSWORD
# Output: super_secret_password
# Warning: Decrypted secret displayed on terminal (visible in scrollback).

# Note: Encryption keys are machine-bound (hostname + uid + arch)
# Secrets cannot be migrated to different machines

7. Run Commands with Environment Variables

# Run a command with all EVM variables injected
evm --env-file ./project.json exec -- python app.py
# Your app can access DATABASE_URL, API_KEY, etc. from os.environ

# Exit codes are passed through from the child process
evm --env-file ./project.json exec -- sh -c 'exit 42'
echo $?  # Output: 42

8. Validate Configuration

# Define schemas for validation
evm --env-file ./project.json schema set DATABASE_URL --format url --required
evm --env-file ./project.json schema set API_KEY --pattern '^[a-zA-Z0-9]+$'

# Validate all variables
evm --env-file ./project.json validate
# Shows which variables pass/fail validation

# Validate a specific variable
evm --env-file ./project.json validate DATABASE_URL

9. Backup and Restore

# Create a backup
evm --env-file ./project.json backup --file backup.json

# Make some changes
evm --env-file ./project.json set NEW_VAR "value"

# Compare with backup
evm --env-file ./project.json diff backup.json
# Shows what was added/removed/changed

# Restore from backup
evm --env-file ./project.json restore backup.json

10. Quick Reference

# Essential flags
--env-file PATH    # Use custom storage file
--json             # Structured JSON output
--dry-run          # Preview changes
--quiet / -q       # Suppress output
--force            # Skip confirmation prompts

# Common commands
evm set KEY VALUE          # Set a variable
evm get KEY                # Get a variable
evm list                   # List all variables
evm delete KEY             # Delete a variable
evm setg GROUP KEY VALUE   # Set in a group
evm groups                 # List groups
evm exec -- COMMAND        # Run with env vars
evm backup                 # Create backup
evm validate               # Check schemas

Usage

Basic Commands

# Set a variable
evm set API_KEY your_secret_key
evm set DATABASE_URL "postgresql://localhost/mydb"

# Get a variable
evm get API_KEY

# List all variables
evm list

# Delete a variable
evm delete API_KEY

# Clear all
evm clear

Group Management

# Set variables in groups
evm setg dev API_URL http://localhost:3000
evm setg prod API_URL https://api.example.com

# List variables in a group
evm listg dev

# List all groups
evm groups

# Show variables grouped by namespace
evm list --show-groups

# Move variable to group
evm move-group API_KEY prod

# Delete entire group
evm delete-group test

Import/Export

# Export to different formats
evm export --format json -o config.json
evm export --format env -o .env
evm export --format sh -o export.sh

# Import from file (auto-detects format)
evm load config.json
evm load config.env

# Import with options
evm load config.json --replace    # Replace existing
evm load config.json --group dev  # Add to group
evm load config.json --nest       # Import nested JSON (first-level keys as groups)

Backup & Restore

# Create backup (auto-timestamped)
evm backup

# Backup to specific file
evm backup --file mybackup.json

# Restore
evm restore backup.json
evm restore backup.json --merge   # Merge with existing

Load to System Memory

# Load all variables to memory (with EVM: prefix)
evm loadmemory

# Load without prefix
evm loadmemory --no-prefix

# Load with filter
evm loadmemory --prefix DEMO_

# Check in Python
python -c "import os; print(os.environ.get('EVM:API_KEY'))"

Execute Commands

# Run with environment variables
evm exec -- python script.py
evm exec -- npm start

Search

# Search by key
evm search api

# Search by key and value
evm search localhost --value

Storage

Environment variables are stored as JSON in ~/.evm/env.json:

{
  "API_KEY": "secret123",
  "dev:DATABASE_URL": "localhost:5432",
  "prod:DATABASE_URL": "prod.example.com:5432"
}

Use custom storage:

evm --env-file /path/to/custom.json list

Secrets (Encrypted Variables)

⚠️ Machine-bound encryption: Encryption keys are derived from machine identity (hostname + uid + arch). Changing hostname, migrating to another machine, or rebuilding Docker containers will make secrets unrecoverable. Use a dedicated secrets manager (Vault, AWS Secrets Manager) for cross-machine scenarios.

# Store an encrypted secret
evm set --secret DB_PASSWORD "super_secret_password"

# Retrieve and decrypt
evm get --secret DB_PASSWORD

Template Expansion

# Use {{VAR}} references
evm set API_HOST "api.example.com"
evm set API_URL "https://{{API_HOST}}/v1"

# Expand templates
evm expand API_URL   # → https://api.example.com/v1

Diff

# Compare current state with a backup
evm diff backup_20260530_120000.json

Dry-run

# Preview changes without writing
evm --dry-run set NEW_KEY value
evm --dry-run delete EXISTING_KEY
evm --dry-run clear

Schema & Validate

# Define schemas for variables
evm schema set API_URL --format url --required
evm schema set PORT --format port
evm schema set EMAIL --format email --description "Admin email"

# Available formats: url, email, port, integer, boolean, path, ipv4, ipv6
# Custom regex also supported:
evm schema set CODE --pattern '^[A-Z]{3}-\d{4}$'

# List all schemas
evm schema list

# Validate a specific variable
evm validate API_URL

# Validate all variables with schemas
evm validate

# Delete a schema
evm schema delete API_URL

History

# View operation history (latest first)
evm history

# Show more entries
evm history --limit 50

# Clear history
evm history --clear

Shell Completion

# Generate and install bash completion
evm completion bash > ~/.evm-completion.bash
echo 'source ~/.evm-completion.bash' >> ~/.bashrc

# zsh
evm completion zsh > ~/.evm-completion.zsh
echo 'source ~/.evm-completion.zsh' >> ~/.zshrc

# fish
evm completion fish > ~/.config/fish/completions/evm.fish

Interactive Safety

# clear and delete-group now prompt for confirmation
evm clear                        # Asks: "This will clear all N variables. Continue? [y/N]"
evm delete-group dev             # Asks: "This will delete group 'dev'... Continue? [y/N]"

# Skip confirmation with --force
evm --force clear
evm --force delete-group dev

Agent-Friendly Usage

EVM is designed to be easily called by AI agents and scripts:

JSON Output (--json)

All commands support structured JSON output. stdout contains data, stderr contains errors:

# Get a variable as JSON
evm get API_KEY --json
# stdout: {"status": "ok", "data": {"key": "API_KEY", "value": "secret123"}}

# List all variables
evm list --json
# stdout: {"status": "ok", "data": {"API_KEY": "secret123", "DB_URL": "..."}}

# Errors go to stderr as JSON
evm get MISSING --json
# stderr: {"status": "error", "error": "Environment variable 'MISSING' not found", "error_code": 2}
# exit code: 2

Quiet Mode (--quiet / -q)

Suppress all human-readable output. Combined with --json, only structured data on stdout:

evm --quiet set KEY value        # No output, exit code 0
evm --quiet get MISSING          # No output, exit code 2
evm --json --quiet list          # No stdout, only exit code

Granular Exit Codes

Code Meaning Exception Type
0 Success
1 General error / cancelled OperationCancelledError
2 Variable not found KeyNotFoundError, KeyAlreadyExistsError
3 Storage error StorageError, CorruptedStorageError, LockTimeoutError
4 Import/export format error ImportError, ExportError
5 Decryption error DecryptionError
6 Validation/schema error ValidationError, SchemaError
7 Group error GroupNotFoundError, GroupOperationError
8 Backup error BackupError
9 Editor error EditorError
10 Command not found CommandNotFoundError

Agent Usage Patterns

# Read a value (parse JSON from stdout)
VALUE=$(evm get API_KEY --json | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['value'])")

# Check if variable exists
if evm get KEY --quiet 2>/dev/null; then echo "exists"; fi

# Conditional set with dry-run preview
evm --dry-run set KEY value --json    # Preview without writing

# Execute with env vars (exit code is passed through)
evm exec -- python script.py
echo "Script exited with: $?"

# Use --env-file for isolated storage (no interference with user config)
evm --env-file /tmp/agent_env.json set KEY value
evm --env-file /tmp/agent_env.json --json list

Python API

EVM can also be used as a Python library. See API Reference for full documentation.

from evm import EnvironmentManager, EVMError, KeyNotFoundError

manager = EnvironmentManager()                    # default: ~/.evm/env.json
# manager = EnvironmentManager('/path/to/env.json')  # custom storage

# Basic operations
manager.set('API_KEY', 'secret123')
value = manager.get('API_KEY')
manager.set_grouped('dev', 'DEBUG', 'true')

# Encrypted secrets (HKDF+HMAC-CTR v3, auto-migration from v1/v2)
manager.set_secret('DB_PASS', 'encrypted_value')
plain = manager.get_secret('DB_PASS')

# Schema validation
manager.set_schema('API_URL', format='url', required=True)
result = manager.validate('API_URL')       # {'valid': True, 'errors': [], 'warnings': []}
all_results = manager.validate_all()

# Operation history
history = manager.get_history(limit=10)

# Template expansion
manager.set('HOST', 'example.com')
manager.set('URL', 'https://{{HOST}}/api')
expanded = manager.expand('URL')           # → https://example.com/api

# Import / Export / Backup
manager.load('.env')
manager.export(format_type='env', output_file='.env')
manager.backup()

# Error handling — all exceptions inherit EVMError
try:
    manager.get('MISSING')
except KeyNotFoundError as e:
    print(f"Not found: {e.key}")
except EVMError as e:
    print(f"EVM error: {e}")

Development

# Install for development (includes pytest, ruff, mypy)
pip install -e ".[dev]"

# Run tests
python -m pytest tests/ -v

# Run tests with coverage
python -m pytest tests/ --cov=evm --cov-report=term-missing

# Lint with ruff
ruff check evm/ tests/

# Auto-fix lint issues
ruff check --fix evm/ tests/

# Type check with mypy
mypy evm/

# Run demo
make demo

Examples

Development Workflow

# Setup dev environment
evm setg dev NODE_ENV development
evm setg dev API_URL http://localhost:3000
evm setg dev DEBUG true

# Export for team
evm export --format env -o .env

# Run application
evm exec -- npm start

Multi-Environment Management

# Setup environments
evm setg dev DATABASE_URL "localhost:5432/dev"
evm setg test DATABASE_URL "test-server:5432/test"
evm setg prod DATABASE_URL "prod-server:5432/prod"

# View all
evm list --show-groups

# Export specific environment
evm listg dev
evm export --group dev --format env

Requirements

  • Python 3.9+
  • No external dependencies (uses only standard library)

License

MIT License

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues and questions, please open an issue on GitHub.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

evm_cli-2.4.0.tar.gz (86.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

evm_cli-2.4.0-py3-none-any.whl (43.6 kB view details)

Uploaded Python 3

File details

Details for the file evm_cli-2.4.0.tar.gz.

File metadata

  • Download URL: evm_cli-2.4.0.tar.gz
  • Upload date:
  • Size: 86.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for evm_cli-2.4.0.tar.gz
Algorithm Hash digest
SHA256 bf8b6f85a6fb33fa6b5770fe2049428e9a0eec64d7e3a8c62723cb1396e87029
MD5 f189c060f0c2ecf7735748f5007edd91
BLAKE2b-256 c1834276c4cac6d79b8441f806df11e98175ed70e2cbfa85e12b88a147e8d151

See more details on using hashes here.

Provenance

The following attestation bundles were made for evm_cli-2.4.0.tar.gz:

Publisher: publish.yml on zxygithub/evm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file evm_cli-2.4.0-py3-none-any.whl.

File metadata

  • Download URL: evm_cli-2.4.0-py3-none-any.whl
  • Upload date:
  • Size: 43.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for evm_cli-2.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 07cc1b33e3c841a6b553cde849875d7c1d35f08c1f2719c8300f5272e24b0765
MD5 b9737c735d36f3ae30c2937bec1b66f3
BLAKE2b-256 374e2a1b83bf4a89b58306539fdb24eea745c6ce8e3cba6862217db018a8ccb1

See more details on using hashes here.

Provenance

The following attestation bundles were made for evm_cli-2.4.0-py3-none-any.whl:

Publisher: publish.yml on zxygithub/evm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page