evm-cli 2.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- evm/__init__.py +55 -0
- evm/__main__.py +9 -0
- evm/_completion.py +253 -0
- evm/_crypto.py +177 -0
- evm/_groups.py +127 -0
- evm/_history.py +141 -0
- evm/_io.py +421 -0
- evm/_json.py +52 -0
- evm/_schema.py +265 -0
- evm/_typing.py +40 -0
- evm/cli.py +961 -0
- evm/exceptions.py +150 -0
- evm/formatters.py +285 -0
- evm/manager.py +644 -0
- evm_cli-2.4.0.dist-info/METADATA +748 -0
- evm_cli-2.4.0.dist-info/RECORD +20 -0
- evm_cli-2.4.0.dist-info/WHEEL +5 -0
- evm_cli-2.4.0.dist-info/entry_points.txt +2 -0
- evm_cli-2.4.0.dist-info/licenses/LICENSE +21 -0
- evm_cli-2.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: evm-cli
|
|
3
|
+
Version: 2.4.0
|
|
4
|
+
Summary: A command-line tool for managing environment variables
|
|
5
|
+
Author-email: EVM Tool <evm@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/zxygithub/evm
|
|
8
|
+
Project-URL: Repository, https://github.com/zxygithub/evm
|
|
9
|
+
Project-URL: Issues, https://github.com/zxygithub/evm/issues
|
|
10
|
+
Keywords: environment,variables,cli,devops,configuration
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Topic :: System :: Systems Administration
|
|
24
|
+
Classifier: Topic :: Utilities
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
32
|
+
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
33
|
+
Provides-Extra: test
|
|
34
|
+
Requires-Dist: pytest>=8.0.0; extra == "test"
|
|
35
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == "test"
|
|
36
|
+
Provides-Extra: lint
|
|
37
|
+
Requires-Dist: ruff>=0.4.0; extra == "lint"
|
|
38
|
+
Requires-Dist: mypy>=1.10.0; extra == "lint"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# EVM - Environment Variable Manager
|
|
42
|
+
|
|
43
|
+
A powerful command-line tool for managing environment variables on macOS and Linux systems.
|
|
44
|
+
|
|
45
|
+
**Version**: 2.4.0
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
- ✅ **Set/Get/Delete**: Manage environment variables
|
|
50
|
+
- ✅ **List/Search**: View and find variables easily
|
|
51
|
+
- ✅ **Import/Export**: JSON, .env, and shell script formats
|
|
52
|
+
- ✅ **Backup/Restore**: Timestamps and merge support
|
|
53
|
+
- ✅ **Groups**: Namespace-based organization
|
|
54
|
+
- ✅ **Execute**: Run commands with custom environment
|
|
55
|
+
- ✅ **Load to Memory**: Sync variables to system environment
|
|
56
|
+
- ✅ **Secrets**: HKDF+HMAC-CTR encrypted storage (v3) with auto-migration from v1/v2
|
|
57
|
+
- ✅ **Templates**: `{{VAR}}` reference expansion
|
|
58
|
+
- ✅ **Diff**: Compare current state with backups
|
|
59
|
+
- ✅ **Dry-run**: Preview changes before writing
|
|
60
|
+
- ✅ **Validate**: Schema-based variable value validation (URL, email, port, etc.)
|
|
61
|
+
- ✅ **Schema**: Define and enforce variable formats and constraints
|
|
62
|
+
- ✅ **History**: Operation audit log with JSONL storage
|
|
63
|
+
- ✅ **Shell Completion**: bash, zsh, fish completion script generation
|
|
64
|
+
- ✅ **Interactive Safety**: Confirmation prompts for destructive operations (`--force` to skip)
|
|
65
|
+
- ✅ **JSON Output**: `--json` flag for structured output (agent-friendly, stdout=data, stderr=errors)
|
|
66
|
+
- ✅ **Quiet Mode**: `--quiet` suppresses all human-readable output
|
|
67
|
+
- ✅ **Granular Exit Codes**: 11 distinct codes for programmatic error handling
|
|
68
|
+
- ✅ **Secure**: Shell-safe export (key+value), chmod 600, atomic writes, shared lock file, HKDF key separation
|
|
69
|
+
- ✅ **Pure Python**: No external dependencies, Python 3.9+
|
|
70
|
+
|
|
71
|
+
## Platform Support
|
|
72
|
+
|
|
73
|
+
**Supported Platforms:**
|
|
74
|
+
- ✅ macOS (Darwin)
|
|
75
|
+
- ✅ Linux (POSIX-compliant systems)
|
|
76
|
+
- ❌ Windows (not supported - uses `fcntl` for file locking)
|
|
77
|
+
|
|
78
|
+
**Requirements:**
|
|
79
|
+
- Python 3.9 or higher
|
|
80
|
+
- POSIX-compliant operating system (for file locking with `fcntl`)
|
|
81
|
+
|
|
82
|
+
## Project Structure
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
evm/
|
|
86
|
+
├── evm/
|
|
87
|
+
│ ├── __init__.py # Package init, version info
|
|
88
|
+
│ ├── __main__.py # Module entry point
|
|
89
|
+
│ ├── cli.py # CLI parsing and command dispatch
|
|
90
|
+
│ ├── manager.py # Core business logic (CRUD, encryption, templates)
|
|
91
|
+
│ ├── _io.py # IOMixin (import/export/backup/restore/diff)
|
|
92
|
+
│ ├── _groups.py # GroupMixin (namespace management)
|
|
93
|
+
│ ├── _history.py # HistoryMixin (operation logging)
|
|
94
|
+
│ ├── _schema.py # SchemaMixin (format validation)
|
|
95
|
+
│ ├── _completion.py # Shell completion generators (bash/zsh/fish)
|
|
96
|
+
│ ├── _json.py # JSON output helpers (agent-friendly)
|
|
97
|
+
│ ├── _crypto.py # HKDF + HMAC-CTR encryption module
|
|
98
|
+
│ ├── formatters.py # Terminal output formatting
|
|
99
|
+
│ └── exceptions.py # Custom exception hierarchy (17 classes)
|
|
100
|
+
├── examples/ # Example scripts
|
|
101
|
+
├── tests/ # Test suite (575 tests)
|
|
102
|
+
│ ├── test_main.py # Unit + integration tests
|
|
103
|
+
│ ├── test_io_boundary.py # _io.py boundary tests
|
|
104
|
+
│ ├── test_cli_boundary.py # cli.py boundary tests
|
|
105
|
+
│ ├── test_v230_fixes.py # v2.3.0 code review fix tests
|
|
106
|
+
│ ├── test_coverage_gap.py # Coverage gap tests (98% target)
|
|
107
|
+
│ ├── test_formatters.py # Formatter output tests
|
|
108
|
+
│ └── test_case/ # Test configuration files
|
|
109
|
+
├── docs/
|
|
110
|
+
│ ├── API_REFERENCE.md # Python API reference
|
|
111
|
+
│ ├── CHANGELOG.md # Version history
|
|
112
|
+
│ ├── DEVELOPMENT_REVIEW.md # Development review & roadmap
|
|
113
|
+
│ ├── ANALYSIS.md # Project analysis report
|
|
114
|
+
│ ├── USER_GUIDE_CN.md # 中文系统功能说明书
|
|
115
|
+
│ ├── AGENT_CLI_EVALUATION.md
|
|
116
|
+
│ ├── CODE_REVIEW.md
|
|
117
|
+
│ ├── CODE_REVIEW_v2.0.0_FINAL.md
|
|
118
|
+
│ └── CODE_REVIEW_v2.2.0.md
|
|
119
|
+
├── skill/ # AI Agent Skill (evm-agent)
|
|
120
|
+
├── pyproject.toml # PEP 621 project metadata & tool config
|
|
121
|
+
├── setup.py # Backward-compatible setup shim
|
|
122
|
+
├── requirements.txt # Thin wrapper pointing to pyproject.toml
|
|
123
|
+
├── README.md # This file
|
|
124
|
+
├── LICENSE # MIT License
|
|
125
|
+
└── Makefile # Build automation
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Quick Start
|
|
129
|
+
|
|
130
|
+
### Installation
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Core install (no third-party dependencies)
|
|
134
|
+
pip install .
|
|
135
|
+
|
|
136
|
+
# Development install (includes pytest, ruff, mypy)
|
|
137
|
+
pip install ".[dev]"
|
|
138
|
+
|
|
139
|
+
# Test-only install
|
|
140
|
+
pip install ".[test]"
|
|
141
|
+
|
|
142
|
+
# Lint-only install
|
|
143
|
+
pip install ".[lint]"
|
|
144
|
+
|
|
145
|
+
# From source (editable/development mode)
|
|
146
|
+
pip install -e ".[dev]"
|
|
147
|
+
|
|
148
|
+
# For current user only
|
|
149
|
+
pip install --user -e ".[dev]"
|
|
150
|
+
|
|
151
|
+
# Verify installation
|
|
152
|
+
evm --version
|
|
153
|
+
evm --help
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Run as Module
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
python -m evm --help
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Your First 5 Minutes with EVM
|
|
163
|
+
|
|
164
|
+
**1. Basic Operations**
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Set a variable
|
|
168
|
+
evm set API_KEY "abc123"
|
|
169
|
+
|
|
170
|
+
# Get the value back
|
|
171
|
+
evm get API_KEY
|
|
172
|
+
# Output: abc123
|
|
173
|
+
|
|
174
|
+
# List all variables
|
|
175
|
+
evm list
|
|
176
|
+
# Shows all stored variables in a table format
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**2. Use Isolated Storage (Recommended)**
|
|
180
|
+
|
|
181
|
+
By default, EVM uses `~/.evm/env.json`. For project-specific configs, use `--env-file`:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
# Create a project-specific config
|
|
185
|
+
evm --env-file ./project.json set DATABASE_URL "postgresql://localhost/mydb"
|
|
186
|
+
evm --env-file ./project.json set API_KEY "project_secret"
|
|
187
|
+
|
|
188
|
+
# Only affects this file, not your global config
|
|
189
|
+
evm --env-file ./project.json list
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**3. JSON Output for Scripts & Agents**
|
|
193
|
+
|
|
194
|
+
Use `--json` to get structured output (works before or after the command):
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# Get as JSON (both work)
|
|
198
|
+
evm --env-file ./project.json --json get API_KEY
|
|
199
|
+
evm --env-file ./project.json get API_KEY --json
|
|
200
|
+
# stdout: {"status": "ok", "data": {"key": "API_KEY", "value": "project_secret"}}
|
|
201
|
+
|
|
202
|
+
# List all as JSON
|
|
203
|
+
evm --env-file ./project.json list --json
|
|
204
|
+
# stdout: {"status": "ok", "data": {"API_KEY": "project_secret", "DATABASE_URL": "..."}}
|
|
205
|
+
|
|
206
|
+
# Errors go to stderr with error codes
|
|
207
|
+
evm --env-file ./project.json get MISSING --json
|
|
208
|
+
# stderr: {"status": "error", "error": "Environment variable 'MISSING' not found", "error_code": 2}
|
|
209
|
+
# exit code: 2
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**4. Preview Changes with Dry-Run**
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# Preview what would happen without actually writing
|
|
216
|
+
evm --env-file ./project.json set NEW_KEY "value" --dry-run
|
|
217
|
+
# Output: [DRY-RUN] Would set: NEW_KEY=value
|
|
218
|
+
# (Nothing is actually written)
|
|
219
|
+
|
|
220
|
+
# Works with delete, clear, etc.
|
|
221
|
+
evm --env-file ./project.json delete API_KEY --dry-run
|
|
222
|
+
# Output: [DRY-RUN] Would delete: API_KEY
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**5. Manage Multiple Environments**
|
|
226
|
+
|
|
227
|
+
Use groups to organize dev/staging/prod configurations:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
# Set variables for different environments
|
|
231
|
+
evm --env-file ./project.json setg dev DATABASE_URL "localhost:5432/dev"
|
|
232
|
+
evm --env-file ./project.json setg prod DATABASE_URL "prod.example.com:5432/prod"
|
|
233
|
+
|
|
234
|
+
# List by group
|
|
235
|
+
evm --env-file ./project.json listg dev
|
|
236
|
+
# Shows only dev group variables
|
|
237
|
+
|
|
238
|
+
# List all groups
|
|
239
|
+
evm --env-file ./project.json groups
|
|
240
|
+
# Shows: dev (1 variable), prod (1 variable)
|
|
241
|
+
|
|
242
|
+
# Export a specific environment
|
|
243
|
+
evm --env-file ./project.json export --group prod --format env -o .env.prod
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**6. Encrypt Sensitive Data**
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# Store encrypted (HKDF + HMAC-CTR encryption)
|
|
250
|
+
evm --env-file ./project.json set --secret DB_PASSWORD "super_secret_password"
|
|
251
|
+
|
|
252
|
+
# Retrieve decrypted
|
|
253
|
+
evm --env-file ./project.json get --secret DB_PASSWORD
|
|
254
|
+
# Output: super_secret_password
|
|
255
|
+
# Warning: Decrypted secret displayed on terminal (visible in scrollback).
|
|
256
|
+
|
|
257
|
+
# Note: Encryption keys are machine-bound (hostname + uid + arch)
|
|
258
|
+
# Secrets cannot be migrated to different machines
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**7. Run Commands with Environment Variables**
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# Run a command with all EVM variables injected
|
|
265
|
+
evm --env-file ./project.json exec -- python app.py
|
|
266
|
+
# Your app can access DATABASE_URL, API_KEY, etc. from os.environ
|
|
267
|
+
|
|
268
|
+
# Exit codes are passed through from the child process
|
|
269
|
+
evm --env-file ./project.json exec -- sh -c 'exit 42'
|
|
270
|
+
echo $? # Output: 42
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**8. Validate Configuration**
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
# Define schemas for validation
|
|
277
|
+
evm --env-file ./project.json schema set DATABASE_URL --format url --required
|
|
278
|
+
evm --env-file ./project.json schema set API_KEY --pattern '^[a-zA-Z0-9]+$'
|
|
279
|
+
|
|
280
|
+
# Validate all variables
|
|
281
|
+
evm --env-file ./project.json validate
|
|
282
|
+
# Shows which variables pass/fail validation
|
|
283
|
+
|
|
284
|
+
# Validate a specific variable
|
|
285
|
+
evm --env-file ./project.json validate DATABASE_URL
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**9. Backup and Restore**
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
# Create a backup
|
|
292
|
+
evm --env-file ./project.json backup --file backup.json
|
|
293
|
+
|
|
294
|
+
# Make some changes
|
|
295
|
+
evm --env-file ./project.json set NEW_VAR "value"
|
|
296
|
+
|
|
297
|
+
# Compare with backup
|
|
298
|
+
evm --env-file ./project.json diff backup.json
|
|
299
|
+
# Shows what was added/removed/changed
|
|
300
|
+
|
|
301
|
+
# Restore from backup
|
|
302
|
+
evm --env-file ./project.json restore backup.json
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**10. Quick Reference**
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
# Essential flags
|
|
309
|
+
--env-file PATH # Use custom storage file
|
|
310
|
+
--json # Structured JSON output
|
|
311
|
+
--dry-run # Preview changes
|
|
312
|
+
--quiet / -q # Suppress output
|
|
313
|
+
--force # Skip confirmation prompts
|
|
314
|
+
|
|
315
|
+
# Common commands
|
|
316
|
+
evm set KEY VALUE # Set a variable
|
|
317
|
+
evm get KEY # Get a variable
|
|
318
|
+
evm list # List all variables
|
|
319
|
+
evm delete KEY # Delete a variable
|
|
320
|
+
evm setg GROUP KEY VALUE # Set in a group
|
|
321
|
+
evm groups # List groups
|
|
322
|
+
evm exec -- COMMAND # Run with env vars
|
|
323
|
+
evm backup # Create backup
|
|
324
|
+
evm validate # Check schemas
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Usage
|
|
328
|
+
|
|
329
|
+
### Basic Commands
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
# Set a variable
|
|
333
|
+
evm set API_KEY your_secret_key
|
|
334
|
+
evm set DATABASE_URL "postgresql://localhost/mydb"
|
|
335
|
+
|
|
336
|
+
# Get a variable
|
|
337
|
+
evm get API_KEY
|
|
338
|
+
|
|
339
|
+
# List all variables
|
|
340
|
+
evm list
|
|
341
|
+
|
|
342
|
+
# Delete a variable
|
|
343
|
+
evm delete API_KEY
|
|
344
|
+
|
|
345
|
+
# Clear all
|
|
346
|
+
evm clear
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Group Management
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# Set variables in groups
|
|
353
|
+
evm setg dev API_URL http://localhost:3000
|
|
354
|
+
evm setg prod API_URL https://api.example.com
|
|
355
|
+
|
|
356
|
+
# List variables in a group
|
|
357
|
+
evm listg dev
|
|
358
|
+
|
|
359
|
+
# List all groups
|
|
360
|
+
evm groups
|
|
361
|
+
|
|
362
|
+
# Show variables grouped by namespace
|
|
363
|
+
evm list --show-groups
|
|
364
|
+
|
|
365
|
+
# Move variable to group
|
|
366
|
+
evm move-group API_KEY prod
|
|
367
|
+
|
|
368
|
+
# Delete entire group
|
|
369
|
+
evm delete-group test
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Import/Export
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
# Export to different formats
|
|
376
|
+
evm export --format json -o config.json
|
|
377
|
+
evm export --format env -o .env
|
|
378
|
+
evm export --format sh -o export.sh
|
|
379
|
+
|
|
380
|
+
# Import from file (auto-detects format)
|
|
381
|
+
evm load config.json
|
|
382
|
+
evm load config.env
|
|
383
|
+
|
|
384
|
+
# Import with options
|
|
385
|
+
evm load config.json --replace # Replace existing
|
|
386
|
+
evm load config.json --group dev # Add to group
|
|
387
|
+
evm load config.json --nest # Import nested JSON (first-level keys as groups)
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Backup & Restore
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
# Create backup (auto-timestamped)
|
|
394
|
+
evm backup
|
|
395
|
+
|
|
396
|
+
# Backup to specific file
|
|
397
|
+
evm backup --file mybackup.json
|
|
398
|
+
|
|
399
|
+
# Restore
|
|
400
|
+
evm restore backup.json
|
|
401
|
+
evm restore backup.json --merge # Merge with existing
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Load to System Memory
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
# Load all variables to memory (with EVM: prefix)
|
|
408
|
+
evm loadmemory
|
|
409
|
+
|
|
410
|
+
# Load without prefix
|
|
411
|
+
evm loadmemory --no-prefix
|
|
412
|
+
|
|
413
|
+
# Load with filter
|
|
414
|
+
evm loadmemory --prefix DEMO_
|
|
415
|
+
|
|
416
|
+
# Check in Python
|
|
417
|
+
python -c "import os; print(os.environ.get('EVM:API_KEY'))"
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Execute Commands
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
# Run with environment variables
|
|
424
|
+
evm exec -- python script.py
|
|
425
|
+
evm exec -- npm start
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Search
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
# Search by key
|
|
432
|
+
evm search api
|
|
433
|
+
|
|
434
|
+
# Search by key and value
|
|
435
|
+
evm search localhost --value
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## Storage
|
|
439
|
+
|
|
440
|
+
Environment variables are stored as JSON in `~/.evm/env.json`:
|
|
441
|
+
|
|
442
|
+
```json
|
|
443
|
+
{
|
|
444
|
+
"API_KEY": "secret123",
|
|
445
|
+
"dev:DATABASE_URL": "localhost:5432",
|
|
446
|
+
"prod:DATABASE_URL": "prod.example.com:5432"
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
Use custom storage:
|
|
451
|
+
```bash
|
|
452
|
+
evm --env-file /path/to/custom.json list
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Secrets (Encrypted Variables)
|
|
456
|
+
|
|
457
|
+
> ⚠️ **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.
|
|
458
|
+
|
|
459
|
+
```bash
|
|
460
|
+
# Store an encrypted secret
|
|
461
|
+
evm set --secret DB_PASSWORD "super_secret_password"
|
|
462
|
+
|
|
463
|
+
# Retrieve and decrypt
|
|
464
|
+
evm get --secret DB_PASSWORD
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Template Expansion
|
|
468
|
+
|
|
469
|
+
```bash
|
|
470
|
+
# Use {{VAR}} references
|
|
471
|
+
evm set API_HOST "api.example.com"
|
|
472
|
+
evm set API_URL "https://{{API_HOST}}/v1"
|
|
473
|
+
|
|
474
|
+
# Expand templates
|
|
475
|
+
evm expand API_URL # → https://api.example.com/v1
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Diff
|
|
479
|
+
|
|
480
|
+
```bash
|
|
481
|
+
# Compare current state with a backup
|
|
482
|
+
evm diff backup_20260530_120000.json
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### Dry-run
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
# Preview changes without writing
|
|
489
|
+
evm --dry-run set NEW_KEY value
|
|
490
|
+
evm --dry-run delete EXISTING_KEY
|
|
491
|
+
evm --dry-run clear
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Schema & Validate
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
# Define schemas for variables
|
|
498
|
+
evm schema set API_URL --format url --required
|
|
499
|
+
evm schema set PORT --format port
|
|
500
|
+
evm schema set EMAIL --format email --description "Admin email"
|
|
501
|
+
|
|
502
|
+
# Available formats: url, email, port, integer, boolean, path, ipv4, ipv6
|
|
503
|
+
# Custom regex also supported:
|
|
504
|
+
evm schema set CODE --pattern '^[A-Z]{3}-\d{4}$'
|
|
505
|
+
|
|
506
|
+
# List all schemas
|
|
507
|
+
evm schema list
|
|
508
|
+
|
|
509
|
+
# Validate a specific variable
|
|
510
|
+
evm validate API_URL
|
|
511
|
+
|
|
512
|
+
# Validate all variables with schemas
|
|
513
|
+
evm validate
|
|
514
|
+
|
|
515
|
+
# Delete a schema
|
|
516
|
+
evm schema delete API_URL
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### History
|
|
520
|
+
|
|
521
|
+
```bash
|
|
522
|
+
# View operation history (latest first)
|
|
523
|
+
evm history
|
|
524
|
+
|
|
525
|
+
# Show more entries
|
|
526
|
+
evm history --limit 50
|
|
527
|
+
|
|
528
|
+
# Clear history
|
|
529
|
+
evm history --clear
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### Shell Completion
|
|
533
|
+
|
|
534
|
+
```bash
|
|
535
|
+
# Generate and install bash completion
|
|
536
|
+
evm completion bash > ~/.evm-completion.bash
|
|
537
|
+
echo 'source ~/.evm-completion.bash' >> ~/.bashrc
|
|
538
|
+
|
|
539
|
+
# zsh
|
|
540
|
+
evm completion zsh > ~/.evm-completion.zsh
|
|
541
|
+
echo 'source ~/.evm-completion.zsh' >> ~/.zshrc
|
|
542
|
+
|
|
543
|
+
# fish
|
|
544
|
+
evm completion fish > ~/.config/fish/completions/evm.fish
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### Interactive Safety
|
|
548
|
+
|
|
549
|
+
```bash
|
|
550
|
+
# clear and delete-group now prompt for confirmation
|
|
551
|
+
evm clear # Asks: "This will clear all N variables. Continue? [y/N]"
|
|
552
|
+
evm delete-group dev # Asks: "This will delete group 'dev'... Continue? [y/N]"
|
|
553
|
+
|
|
554
|
+
# Skip confirmation with --force
|
|
555
|
+
evm --force clear
|
|
556
|
+
evm --force delete-group dev
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
## Agent-Friendly Usage
|
|
560
|
+
|
|
561
|
+
EVM is designed to be easily called by AI agents and scripts:
|
|
562
|
+
|
|
563
|
+
### JSON Output (`--json`)
|
|
564
|
+
|
|
565
|
+
All commands support structured JSON output. stdout contains data, stderr contains errors:
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
# Get a variable as JSON
|
|
569
|
+
evm get API_KEY --json
|
|
570
|
+
# stdout: {"status": "ok", "data": {"key": "API_KEY", "value": "secret123"}}
|
|
571
|
+
|
|
572
|
+
# List all variables
|
|
573
|
+
evm list --json
|
|
574
|
+
# stdout: {"status": "ok", "data": {"API_KEY": "secret123", "DB_URL": "..."}}
|
|
575
|
+
|
|
576
|
+
# Errors go to stderr as JSON
|
|
577
|
+
evm get MISSING --json
|
|
578
|
+
# stderr: {"status": "error", "error": "Environment variable 'MISSING' not found", "error_code": 2}
|
|
579
|
+
# exit code: 2
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### Quiet Mode (`--quiet` / `-q`)
|
|
583
|
+
|
|
584
|
+
Suppress all human-readable output. Combined with `--json`, only structured data on stdout:
|
|
585
|
+
|
|
586
|
+
```bash
|
|
587
|
+
evm --quiet set KEY value # No output, exit code 0
|
|
588
|
+
evm --quiet get MISSING # No output, exit code 2
|
|
589
|
+
evm --json --quiet list # No stdout, only exit code
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Granular Exit Codes
|
|
593
|
+
|
|
594
|
+
| Code | Meaning | Exception Type |
|
|
595
|
+
|------|---------|---------------|
|
|
596
|
+
| 0 | Success | — |
|
|
597
|
+
| 1 | General error / cancelled | OperationCancelledError |
|
|
598
|
+
| 2 | Variable not found | KeyNotFoundError, KeyAlreadyExistsError |
|
|
599
|
+
| 3 | Storage error | StorageError, CorruptedStorageError, LockTimeoutError |
|
|
600
|
+
| 4 | Import/export format error | ImportError, ExportError |
|
|
601
|
+
| 5 | Decryption error | DecryptionError |
|
|
602
|
+
| 6 | Validation/schema error | ValidationError, SchemaError |
|
|
603
|
+
| 7 | Group error | GroupNotFoundError, GroupOperationError |
|
|
604
|
+
| 8 | Backup error | BackupError |
|
|
605
|
+
| 9 | Editor error | EditorError |
|
|
606
|
+
| 10 | Command not found | CommandNotFoundError |
|
|
607
|
+
|
|
608
|
+
### Agent Usage Patterns
|
|
609
|
+
|
|
610
|
+
```bash
|
|
611
|
+
# Read a value (parse JSON from stdout)
|
|
612
|
+
VALUE=$(evm get API_KEY --json | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['value'])")
|
|
613
|
+
|
|
614
|
+
# Check if variable exists
|
|
615
|
+
if evm get KEY --quiet 2>/dev/null; then echo "exists"; fi
|
|
616
|
+
|
|
617
|
+
# Conditional set with dry-run preview
|
|
618
|
+
evm --dry-run set KEY value --json # Preview without writing
|
|
619
|
+
|
|
620
|
+
# Execute with env vars (exit code is passed through)
|
|
621
|
+
evm exec -- python script.py
|
|
622
|
+
echo "Script exited with: $?"
|
|
623
|
+
|
|
624
|
+
# Use --env-file for isolated storage (no interference with user config)
|
|
625
|
+
evm --env-file /tmp/agent_env.json set KEY value
|
|
626
|
+
evm --env-file /tmp/agent_env.json --json list
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
## Python API
|
|
630
|
+
|
|
631
|
+
EVM can also be used as a Python library. See [**API Reference**](docs/API_REFERENCE.md) for full documentation.
|
|
632
|
+
|
|
633
|
+
```python
|
|
634
|
+
from evm import EnvironmentManager, EVMError, KeyNotFoundError
|
|
635
|
+
|
|
636
|
+
manager = EnvironmentManager() # default: ~/.evm/env.json
|
|
637
|
+
# manager = EnvironmentManager('/path/to/env.json') # custom storage
|
|
638
|
+
|
|
639
|
+
# Basic operations
|
|
640
|
+
manager.set('API_KEY', 'secret123')
|
|
641
|
+
value = manager.get('API_KEY')
|
|
642
|
+
manager.set_grouped('dev', 'DEBUG', 'true')
|
|
643
|
+
|
|
644
|
+
# Encrypted secrets (HKDF+HMAC-CTR v3, auto-migration from v1/v2)
|
|
645
|
+
manager.set_secret('DB_PASS', 'encrypted_value')
|
|
646
|
+
plain = manager.get_secret('DB_PASS')
|
|
647
|
+
|
|
648
|
+
# Schema validation
|
|
649
|
+
manager.set_schema('API_URL', format='url', required=True)
|
|
650
|
+
result = manager.validate('API_URL') # {'valid': True, 'errors': [], 'warnings': []}
|
|
651
|
+
all_results = manager.validate_all()
|
|
652
|
+
|
|
653
|
+
# Operation history
|
|
654
|
+
history = manager.get_history(limit=10)
|
|
655
|
+
|
|
656
|
+
# Template expansion
|
|
657
|
+
manager.set('HOST', 'example.com')
|
|
658
|
+
manager.set('URL', 'https://{{HOST}}/api')
|
|
659
|
+
expanded = manager.expand('URL') # → https://example.com/api
|
|
660
|
+
|
|
661
|
+
# Import / Export / Backup
|
|
662
|
+
manager.load('.env')
|
|
663
|
+
manager.export(format_type='env', output_file='.env')
|
|
664
|
+
manager.backup()
|
|
665
|
+
|
|
666
|
+
# Error handling — all exceptions inherit EVMError
|
|
667
|
+
try:
|
|
668
|
+
manager.get('MISSING')
|
|
669
|
+
except KeyNotFoundError as e:
|
|
670
|
+
print(f"Not found: {e.key}")
|
|
671
|
+
except EVMError as e:
|
|
672
|
+
print(f"EVM error: {e}")
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
## Development
|
|
676
|
+
|
|
677
|
+
```bash
|
|
678
|
+
# Install for development (includes pytest, ruff, mypy)
|
|
679
|
+
pip install -e ".[dev]"
|
|
680
|
+
|
|
681
|
+
# Run tests
|
|
682
|
+
python -m pytest tests/ -v
|
|
683
|
+
|
|
684
|
+
# Run tests with coverage
|
|
685
|
+
python -m pytest tests/ --cov=evm --cov-report=term-missing
|
|
686
|
+
|
|
687
|
+
# Lint with ruff
|
|
688
|
+
ruff check evm/ tests/
|
|
689
|
+
|
|
690
|
+
# Auto-fix lint issues
|
|
691
|
+
ruff check --fix evm/ tests/
|
|
692
|
+
|
|
693
|
+
# Type check with mypy
|
|
694
|
+
mypy evm/
|
|
695
|
+
|
|
696
|
+
# Run demo
|
|
697
|
+
make demo
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
## Examples
|
|
701
|
+
|
|
702
|
+
### Development Workflow
|
|
703
|
+
|
|
704
|
+
```bash
|
|
705
|
+
# Setup dev environment
|
|
706
|
+
evm setg dev NODE_ENV development
|
|
707
|
+
evm setg dev API_URL http://localhost:3000
|
|
708
|
+
evm setg dev DEBUG true
|
|
709
|
+
|
|
710
|
+
# Export for team
|
|
711
|
+
evm export --format env -o .env
|
|
712
|
+
|
|
713
|
+
# Run application
|
|
714
|
+
evm exec -- npm start
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
### Multi-Environment Management
|
|
718
|
+
|
|
719
|
+
```bash
|
|
720
|
+
# Setup environments
|
|
721
|
+
evm setg dev DATABASE_URL "localhost:5432/dev"
|
|
722
|
+
evm setg test DATABASE_URL "test-server:5432/test"
|
|
723
|
+
evm setg prod DATABASE_URL "prod-server:5432/prod"
|
|
724
|
+
|
|
725
|
+
# View all
|
|
726
|
+
evm list --show-groups
|
|
727
|
+
|
|
728
|
+
# Export specific environment
|
|
729
|
+
evm listg dev
|
|
730
|
+
evm export --group dev --format env
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
## Requirements
|
|
734
|
+
|
|
735
|
+
- **Python 3.9+**
|
|
736
|
+
- **No external dependencies** (uses only standard library)
|
|
737
|
+
|
|
738
|
+
## License
|
|
739
|
+
|
|
740
|
+
MIT License
|
|
741
|
+
|
|
742
|
+
## Contributing
|
|
743
|
+
|
|
744
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
745
|
+
|
|
746
|
+
## Support
|
|
747
|
+
|
|
748
|
+
For issues and questions, please open an issue on GitHub.
|