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 (
--forceto skip) - ✅ JSON Output:
--jsonflag for structured output (agent-friendly, stdout=data, stderr=errors) - ✅ Quiet Mode:
--quietsuppresses 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
fcntlfor 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bf8b6f85a6fb33fa6b5770fe2049428e9a0eec64d7e3a8c62723cb1396e87029
|
|
| MD5 |
f189c060f0c2ecf7735748f5007edd91
|
|
| BLAKE2b-256 |
c1834276c4cac6d79b8441f806df11e98175ed70e2cbfa85e12b88a147e8d151
|
Provenance
The following attestation bundles were made for evm_cli-2.4.0.tar.gz:
Publisher:
publish.yml on zxygithub/evm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
evm_cli-2.4.0.tar.gz -
Subject digest:
bf8b6f85a6fb33fa6b5770fe2049428e9a0eec64d7e3a8c62723cb1396e87029 - Sigstore transparency entry: 1675523964
- Sigstore integration time:
-
Permalink:
zxygithub/evm@311eb640b397a4b3151e19b2273e32e0850f1d4a -
Branch / Tag:
refs/tags/v2.4.0 - Owner: https://github.com/zxygithub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@311eb640b397a4b3151e19b2273e32e0850f1d4a -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
07cc1b33e3c841a6b553cde849875d7c1d35f08c1f2719c8300f5272e24b0765
|
|
| MD5 |
b9737c735d36f3ae30c2937bec1b66f3
|
|
| BLAKE2b-256 |
374e2a1b83bf4a89b58306539fdb24eea745c6ce8e3cba6862217db018a8ccb1
|
Provenance
The following attestation bundles were made for evm_cli-2.4.0-py3-none-any.whl:
Publisher:
publish.yml on zxygithub/evm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
evm_cli-2.4.0-py3-none-any.whl -
Subject digest:
07cc1b33e3c841a6b553cde849875d7c1d35f08c1f2719c8300f5272e24b0765 - Sigstore transparency entry: 1675523974
- Sigstore integration time:
-
Permalink:
zxygithub/evm@311eb640b397a4b3151e19b2273e32e0850f1d4a -
Branch / Tag:
refs/tags/v2.4.0 - Owner: https://github.com/zxygithub
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@311eb640b397a4b3151e19b2273e32e0850f1d4a -
Trigger Event:
push
-
Statement type: