envx-cli 1.0.0 → 1.1.0
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.
- package/LICENSE +1 -1
- package/README.md +104 -14
- package/dist/commands/create.d.ts +1 -1
- package/dist/commands/create.js +54 -53
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/decrypt.d.ts.map +1 -1
- package/dist/commands/decrypt.js +142 -49
- package/dist/commands/decrypt.js.map +1 -1
- package/dist/commands/encrypt.d.ts.map +1 -1
- package/dist/commands/encrypt.js +124 -40
- package/dist/commands/encrypt.js.map +1 -1
- package/dist/commands/interactive.d.ts.map +1 -1
- package/dist/commands/interactive.js +4 -8
- package/dist/commands/interactive.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/schemas/index.d.ts +43 -5
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +80 -14
- package/dist/schemas/index.js.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/exec.d.ts.map +1 -1
- package/dist/utils/exec.js +17 -13
- package/dist/utils/exec.js.map +1 -1
- package/dist/utils/file.d.ts +1 -1
- package/dist/utils/file.js +30 -30
- package/dist/utils/interactive.d.ts +2 -2
- package/dist/utils/interactive.js +101 -101
- package/dist/utils/interactive.js.map +1 -1
- package/package.json +35 -4
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -29,19 +29,23 @@ Before using EnvX, ensure you have:
|
|
|
29
29
|
### Installing GPG
|
|
30
30
|
|
|
31
31
|
#### macOS
|
|
32
|
+
|
|
32
33
|
```bash
|
|
33
34
|
brew install gnupg
|
|
34
35
|
```
|
|
35
36
|
|
|
36
37
|
#### Ubuntu/Debian
|
|
38
|
+
|
|
37
39
|
```bash
|
|
38
40
|
sudo apt-get install gnupg
|
|
39
41
|
```
|
|
40
42
|
|
|
41
43
|
#### Windows
|
|
44
|
+
|
|
42
45
|
Download from [https://gnupg.org/download/](https://gnupg.org/download/)
|
|
43
46
|
|
|
44
47
|
#### Verify Installation
|
|
48
|
+
|
|
45
49
|
```bash
|
|
46
50
|
gpg --version
|
|
47
51
|
```
|
|
@@ -49,46 +53,54 @@ gpg --version
|
|
|
49
53
|
## Installation
|
|
50
54
|
|
|
51
55
|
### Global Installation (Recommended)
|
|
56
|
+
|
|
52
57
|
```bash
|
|
53
|
-
npm install -g envx
|
|
58
|
+
npm install -g envx-cli
|
|
54
59
|
```
|
|
55
60
|
|
|
56
61
|
### Local Installation
|
|
62
|
+
|
|
57
63
|
```bash
|
|
58
|
-
npm install envx
|
|
64
|
+
npm install envx-cli
|
|
59
65
|
npx envx --help
|
|
60
66
|
```
|
|
61
67
|
|
|
62
68
|
## Quick Start
|
|
63
69
|
|
|
64
70
|
1. **Initialize EnvX in your project:**
|
|
71
|
+
|
|
65
72
|
```bash
|
|
66
73
|
envx init
|
|
67
74
|
```
|
|
68
75
|
|
|
69
76
|
2. **Create environment files:**
|
|
77
|
+
|
|
70
78
|
```bash
|
|
71
79
|
envx create -e development
|
|
72
80
|
envx create -e production
|
|
73
81
|
```
|
|
74
82
|
|
|
75
83
|
3. **Set up secrets for encryption:**
|
|
84
|
+
|
|
76
85
|
```bash
|
|
77
86
|
envx interactive
|
|
78
87
|
```
|
|
79
88
|
|
|
80
89
|
4. **Encrypt your environment files:**
|
|
90
|
+
|
|
81
91
|
```bash
|
|
82
92
|
envx encrypt -e production
|
|
83
93
|
```
|
|
84
94
|
|
|
85
95
|
5. **Commit encrypted files to git:**
|
|
96
|
+
|
|
86
97
|
```bash
|
|
87
98
|
git add *.gpg
|
|
88
99
|
git commit -m "Add encrypted environment files"
|
|
89
100
|
```
|
|
90
101
|
|
|
91
102
|
6. **Decrypt when needed:**
|
|
103
|
+
|
|
92
104
|
```bash
|
|
93
105
|
envx decrypt -e production
|
|
94
106
|
```
|
|
@@ -96,6 +108,7 @@ envx decrypt -e production
|
|
|
96
108
|
## Commands
|
|
97
109
|
|
|
98
110
|
### `envx init`
|
|
111
|
+
|
|
99
112
|
Initialize EnvX in a new project with guided setup.
|
|
100
113
|
|
|
101
114
|
```bash
|
|
@@ -103,6 +116,7 @@ envx init
|
|
|
103
116
|
```
|
|
104
117
|
|
|
105
118
|
### `envx create`
|
|
119
|
+
|
|
106
120
|
Create new environment files.
|
|
107
121
|
|
|
108
122
|
```bash
|
|
@@ -120,6 +134,7 @@ envx create -e staging --overwrite
|
|
|
120
134
|
```
|
|
121
135
|
|
|
122
136
|
**Options:**
|
|
137
|
+
|
|
123
138
|
- `-e, --environment <env>` - Environment name
|
|
124
139
|
- `-t, --template <path>` - Template file path
|
|
125
140
|
- `-i, --interactive` - Interactive mode
|
|
@@ -127,52 +142,71 @@ envx create -e staging --overwrite
|
|
|
127
142
|
- `-c, --cwd <path>` - Working directory
|
|
128
143
|
|
|
129
144
|
### `envx encrypt`
|
|
145
|
+
|
|
130
146
|
Encrypt environment files using GPG.
|
|
131
147
|
|
|
132
148
|
```bash
|
|
133
149
|
# Encrypt specific environment
|
|
134
150
|
envx encrypt -e production
|
|
135
151
|
|
|
152
|
+
# Encrypt all environments at once
|
|
153
|
+
envx encrypt --all
|
|
154
|
+
|
|
136
155
|
# Use custom secret from .envrc
|
|
137
156
|
envx encrypt -e production -s CUSTOM_SECRET
|
|
138
157
|
|
|
139
|
-
# Interactive file selection
|
|
158
|
+
# Interactive file selection (for single environment)
|
|
140
159
|
envx encrypt -e staging -i
|
|
141
160
|
|
|
161
|
+
# Encrypt all with specific passphrase
|
|
162
|
+
envx encrypt --all -p "your-passphrase"
|
|
163
|
+
|
|
142
164
|
# Specify passphrase directly (not recommended)
|
|
143
165
|
envx encrypt -e development -p "your-passphrase"
|
|
144
166
|
```
|
|
145
167
|
|
|
146
168
|
**Options:**
|
|
147
|
-
|
|
169
|
+
|
|
170
|
+
- `-e, --environment <env>` - Environment name (required unless using --all)
|
|
171
|
+
- `-a, --all` - Process all available environments
|
|
148
172
|
- `-p, --passphrase <pass>` - Encryption passphrase
|
|
149
173
|
- `-s, --secret <secret>` - Secret variable name from .envrc
|
|
150
|
-
- `-i, --interactive` - Interactive file selection
|
|
174
|
+
- `-i, --interactive` - Interactive file selection (disabled with --all)
|
|
151
175
|
- `-c, --cwd <path>` - Working directory
|
|
152
176
|
|
|
153
177
|
### `envx decrypt`
|
|
178
|
+
|
|
154
179
|
Decrypt environment files.
|
|
155
180
|
|
|
156
181
|
```bash
|
|
157
182
|
# Decrypt specific environment
|
|
158
183
|
envx decrypt -e production
|
|
159
184
|
|
|
185
|
+
# Decrypt all environments at once
|
|
186
|
+
envx decrypt --all
|
|
187
|
+
|
|
160
188
|
# Overwrite existing files without confirmation
|
|
161
189
|
envx decrypt -e development --overwrite
|
|
162
190
|
|
|
163
|
-
#
|
|
191
|
+
# Decrypt all with overwrite flag
|
|
192
|
+
envx decrypt --all --overwrite
|
|
193
|
+
|
|
194
|
+
# Interactive file selection (for single environment)
|
|
164
195
|
envx decrypt -e staging -i
|
|
165
196
|
```
|
|
166
197
|
|
|
167
198
|
**Options:**
|
|
168
|
-
|
|
199
|
+
|
|
200
|
+
- `-e, --environment <env>` - Environment name (required unless using --all)
|
|
201
|
+
- `-a, --all` - Process all available environments
|
|
169
202
|
- `-p, --passphrase <pass>` - Decryption passphrase
|
|
170
203
|
- `-s, --secret <secret>` - Secret variable name from .envrc
|
|
171
|
-
- `-i, --interactive` - Interactive file selection
|
|
204
|
+
- `-i, --interactive` - Interactive file selection (disabled with --all)
|
|
172
205
|
- `--overwrite` - Overwrite existing files without confirmation
|
|
173
206
|
- `-c, --cwd <path>` - Working directory
|
|
174
207
|
|
|
175
208
|
### `envx interactive`
|
|
209
|
+
|
|
176
210
|
Interactive setup for `.envrc` file with secrets.
|
|
177
211
|
|
|
178
212
|
```bash
|
|
@@ -187,11 +221,13 @@ envx interactive --generate
|
|
|
187
221
|
```
|
|
188
222
|
|
|
189
223
|
**Options:**
|
|
224
|
+
|
|
190
225
|
- `--overwrite` - Overwrite existing .envrc file
|
|
191
226
|
- `--generate` - Generate random secrets
|
|
192
227
|
- `-c, --cwd <path>` - Working directory
|
|
193
228
|
|
|
194
229
|
### `envx list`
|
|
230
|
+
|
|
195
231
|
List all environment files and their status.
|
|
196
232
|
|
|
197
233
|
```bash
|
|
@@ -203,9 +239,11 @@ envx ls
|
|
|
203
239
|
```
|
|
204
240
|
|
|
205
241
|
**Options:**
|
|
242
|
+
|
|
206
243
|
- `-c, --cwd <path>` - Working directory
|
|
207
244
|
|
|
208
245
|
### `envx status`
|
|
246
|
+
|
|
209
247
|
Show project encryption status and recommendations.
|
|
210
248
|
|
|
211
249
|
```bash
|
|
@@ -213,6 +251,7 @@ envx status
|
|
|
213
251
|
```
|
|
214
252
|
|
|
215
253
|
**Options:**
|
|
254
|
+
|
|
216
255
|
- `-c, --cwd <path>` - Working directory
|
|
217
256
|
|
|
218
257
|
## Workflow Examples
|
|
@@ -220,12 +259,14 @@ envx status
|
|
|
220
259
|
### Basic Workflow
|
|
221
260
|
|
|
222
261
|
1. **Create environment files:**
|
|
262
|
+
|
|
223
263
|
```bash
|
|
224
264
|
envx create -e development
|
|
225
265
|
envx create -e production
|
|
226
266
|
```
|
|
227
267
|
|
|
228
268
|
2. **Edit your environment files:**
|
|
269
|
+
|
|
229
270
|
```bash
|
|
230
271
|
# Edit .env.development
|
|
231
272
|
echo "DATABASE_URL=postgresql://localhost:5432/myapp_dev" >> .env.development
|
|
@@ -237,16 +278,19 @@ echo "API_KEY=prod-api-key-secret" >> .env.production
|
|
|
237
278
|
```
|
|
238
279
|
|
|
239
280
|
3. **Set up encryption secrets:**
|
|
281
|
+
|
|
240
282
|
```bash
|
|
241
283
|
envx interactive
|
|
242
284
|
```
|
|
243
285
|
|
|
244
286
|
4. **Encrypt production secrets:**
|
|
287
|
+
|
|
245
288
|
```bash
|
|
246
289
|
envx encrypt -e production
|
|
247
290
|
```
|
|
248
291
|
|
|
249
292
|
5. **Add to version control:**
|
|
293
|
+
|
|
250
294
|
```bash
|
|
251
295
|
echo ".env.*" >> .gitignore
|
|
252
296
|
echo "!*.gpg" >> .gitignore
|
|
@@ -257,6 +301,7 @@ git commit -m "Add encrypted production environment"
|
|
|
257
301
|
### Team Workflow
|
|
258
302
|
|
|
259
303
|
1. **Clone repository and decrypt:**
|
|
304
|
+
|
|
260
305
|
```bash
|
|
261
306
|
git clone <your-repo>
|
|
262
307
|
cd <your-repo>
|
|
@@ -264,6 +309,7 @@ envx decrypt -e production
|
|
|
264
309
|
```
|
|
265
310
|
|
|
266
311
|
2. **Make changes and re-encrypt:**
|
|
312
|
+
|
|
267
313
|
```bash
|
|
268
314
|
# Edit .env.production
|
|
269
315
|
envx encrypt -e production
|
|
@@ -271,6 +317,38 @@ git add .env.production.gpg
|
|
|
271
317
|
git commit -m "Update production configuration"
|
|
272
318
|
```
|
|
273
319
|
|
|
320
|
+
### Batch Operations
|
|
321
|
+
|
|
322
|
+
**Process all environments at once:**
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
# Encrypt all environment files
|
|
326
|
+
envx encrypt --all
|
|
327
|
+
|
|
328
|
+
# Decrypt all environment files
|
|
329
|
+
envx decrypt --all
|
|
330
|
+
|
|
331
|
+
# Decrypt all with overwrite protection disabled
|
|
332
|
+
envx decrypt --all --overwrite
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Benefits of using `--all`:**
|
|
336
|
+
|
|
337
|
+
- ✅ **Efficiency**: Process multiple environments in one command
|
|
338
|
+
- ✅ **Consistency**: Same passphrase/secret handling across all environments
|
|
339
|
+
- ✅ **Automation**: Perfect for CI/CD pipelines and scripts
|
|
340
|
+
- ✅ **Safety**: Each environment is processed independently - failures in one don't stop others
|
|
341
|
+
- ✅ **Reporting**: Comprehensive summary showing results for each environment
|
|
342
|
+
|
|
343
|
+
**Key Features of `--all` Flag:**
|
|
344
|
+
|
|
345
|
+
- **Sequential Processing**: Environments are processed one by one to avoid resource conflicts
|
|
346
|
+
- **Independent Operations**: Failure in one environment doesn't stop processing of others
|
|
347
|
+
- **Smart Passphrase Resolution**: Uses provided passphrase, environment-specific secrets, or prompts as needed
|
|
348
|
+
- **Comprehensive Reporting**: Shows detailed results for each environment plus overall summary
|
|
349
|
+
- **Safety Checks**: Validates compatibility with other flags (incompatible with `--environment` and `--interactive`)
|
|
350
|
+
- **Flexible Configuration**: Works with all existing options like `--passphrase`, `--secret`, `--cwd`, and `--overwrite`
|
|
351
|
+
|
|
274
352
|
## Configuration
|
|
275
353
|
|
|
276
354
|
### `.envrc` File
|
|
@@ -289,6 +367,7 @@ export PRODUCTION_SECRET="your-production-secret"
|
|
|
289
367
|
EnvX follows the convention: `<STAGE>_SECRET`
|
|
290
368
|
|
|
291
369
|
Examples:
|
|
370
|
+
|
|
292
371
|
- `DEVELOPMENT_SECRET`
|
|
293
372
|
- `STAGING_SECRET`
|
|
294
373
|
- `PRODUCTION_SECRET`
|
|
@@ -342,6 +421,7 @@ your-project/
|
|
|
342
421
|
EnvX works great with [direnv](https://direnv.net/) for automatic environment loading:
|
|
343
422
|
|
|
344
423
|
1. **Install direnv:**
|
|
424
|
+
|
|
345
425
|
```bash
|
|
346
426
|
# macOS
|
|
347
427
|
brew install direnv
|
|
@@ -351,6 +431,7 @@ sudo apt install direnv
|
|
|
351
431
|
```
|
|
352
432
|
|
|
353
433
|
2. **Add to your shell profile:**
|
|
434
|
+
|
|
354
435
|
```bash
|
|
355
436
|
# For bash
|
|
356
437
|
echo 'eval "$(direnv hook bash)"' >> ~/.bashrc
|
|
@@ -360,6 +441,7 @@ echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc
|
|
|
360
441
|
```
|
|
361
442
|
|
|
362
443
|
3. **Allow direnv in your project:**
|
|
444
|
+
|
|
363
445
|
```bash
|
|
364
446
|
direnv allow
|
|
365
447
|
```
|
|
@@ -371,17 +453,20 @@ Now your secrets will be automatically loaded when you enter the project directo
|
|
|
371
453
|
### GPG Issues
|
|
372
454
|
|
|
373
455
|
**Problem:** `gpg: command not found`
|
|
456
|
+
|
|
374
457
|
```bash
|
|
375
458
|
# Install GPG (see Prerequisites section)
|
|
376
459
|
```
|
|
377
460
|
|
|
378
461
|
**Problem:** `gpg: decryption failed: Bad session key`
|
|
462
|
+
|
|
379
463
|
```bash
|
|
380
464
|
# Wrong passphrase - try again or check your .envrc file
|
|
381
465
|
envx decrypt -e production
|
|
382
466
|
```
|
|
383
467
|
|
|
384
468
|
**Problem:** `gpg: can't connect to the agent`
|
|
469
|
+
|
|
385
470
|
```bash
|
|
386
471
|
# Restart GPG agent
|
|
387
472
|
gpgconf --kill gpg-agent
|
|
@@ -391,6 +476,7 @@ gpgconf --launch gpg-agent
|
|
|
391
476
|
### Permission Issues
|
|
392
477
|
|
|
393
478
|
**Problem:** `EACCES: permission denied`
|
|
479
|
+
|
|
394
480
|
```bash
|
|
395
481
|
# Check file permissions
|
|
396
482
|
ls -la .env.*
|
|
@@ -400,6 +486,7 @@ chmod 644 .env.*
|
|
|
400
486
|
### File Not Found
|
|
401
487
|
|
|
402
488
|
**Problem:** `Template file not found`
|
|
489
|
+
|
|
403
490
|
```bash
|
|
404
491
|
# Check if template exists
|
|
405
492
|
ls -la .env.example
|
|
@@ -460,16 +547,18 @@ The test suite prioritizes **essential functionality** over comprehensive covera
|
|
|
460
547
|
|
|
461
548
|
### Test Coverage
|
|
462
549
|
|
|
463
|
-
**Current Status**: ✅
|
|
550
|
+
**Current Status**: ✅ 120 tests passing, ~11s execution time
|
|
464
551
|
|
|
465
|
-
- **Core Tests**:
|
|
552
|
+
- **Core Tests**: 104 tests covering essential functionality
|
|
466
553
|
- Schema validation: 25 tests (command input validation)
|
|
467
554
|
- File utilities: 39 tests (path manipulation, secret generation)
|
|
468
|
-
- Command logic:
|
|
555
|
+
- Command logic: 25 tests (workflow patterns and decision logic)
|
|
556
|
+
- All-flag functionality: 15 tests (batch operations, error handling)
|
|
469
557
|
|
|
470
|
-
- **Integration Tests**:
|
|
558
|
+
- **Integration Tests**: 16 tests covering real CLI usage
|
|
471
559
|
- Help/version commands
|
|
472
560
|
- Create command functionality
|
|
561
|
+
- All-flag compatibility testing
|
|
473
562
|
- Error handling scenarios
|
|
474
563
|
- Environment validation
|
|
475
564
|
|
|
@@ -480,7 +569,8 @@ __tests__/
|
|
|
480
569
|
├── core/ # Essential functionality tests
|
|
481
570
|
│ ├── schemas.test.ts # Input validation for all commands
|
|
482
571
|
│ ├── file.test.ts # File utilities and path manipulation
|
|
483
|
-
│
|
|
572
|
+
│ ├── commands.test.ts # Command workflow logic patterns
|
|
573
|
+
│ └── all-flag.test.ts # Batch operations and --all flag functionality
|
|
484
574
|
└── integration/ # End-to-end CLI tests
|
|
485
575
|
└── cli.test.ts # Real CLI execution scenarios
|
|
486
576
|
```
|
|
@@ -547,4 +637,4 @@ MIT © [rahulretnan](https://github.com/rahulretnan)
|
|
|
547
637
|
|
|
548
638
|
**Made with ❤️ by developers, for developers.**
|
|
549
639
|
|
|
550
|
-
|
|
640
|
+
_Remember: Security is not a feature, it's a requirement. EnvX helps you maintain security without sacrificing developer experience._
|
package/dist/commands/create.js
CHANGED
|
@@ -12,14 +12,14 @@ const exec_1 = require("../utils/exec");
|
|
|
12
12
|
const file_1 = require("../utils/file");
|
|
13
13
|
const interactive_1 = require("../utils/interactive");
|
|
14
14
|
const createCreateCommand = () => {
|
|
15
|
-
const command = new commander_1.Command(
|
|
15
|
+
const command = new commander_1.Command('create');
|
|
16
16
|
command
|
|
17
|
-
.description(
|
|
18
|
-
.option(
|
|
19
|
-
.option(
|
|
20
|
-
.option(
|
|
21
|
-
.option(
|
|
22
|
-
.option(
|
|
17
|
+
.description('Create new environment files')
|
|
18
|
+
.option('-e, --environment <env>', 'Environment name (e.g., development, staging, production)')
|
|
19
|
+
.option('-t, --template <path>', 'Template file path to use as base')
|
|
20
|
+
.option('-c, --cwd <path>', 'Working directory path')
|
|
21
|
+
.option('-i, --interactive', 'Interactive mode for creating multiple environments')
|
|
22
|
+
.option('--overwrite', 'Overwrite existing files without confirmation')
|
|
23
23
|
.action(async (options) => {
|
|
24
24
|
try {
|
|
25
25
|
await executeCreate(options);
|
|
@@ -33,11 +33,11 @@ const createCreateCommand = () => {
|
|
|
33
33
|
};
|
|
34
34
|
exports.createCreateCommand = createCreateCommand;
|
|
35
35
|
async function executeCreate(rawOptions) {
|
|
36
|
-
exec_1.CliUtils.header(
|
|
36
|
+
exec_1.CliUtils.header('Environment File Creation');
|
|
37
37
|
const cwd = rawOptions.cwd || exec_1.ExecUtils.getCurrentDir();
|
|
38
38
|
const existingEnvironments = await file_1.FileUtils.findAllEnvironments(cwd);
|
|
39
39
|
if (existingEnvironments.length > 0) {
|
|
40
|
-
exec_1.CliUtils.info(`Existing environments: ${existingEnvironments.map(
|
|
40
|
+
exec_1.CliUtils.info(`Existing environments: ${existingEnvironments.map(env => exec_1.CliUtils.formatEnvironment(env)).join(', ')}`);
|
|
41
41
|
}
|
|
42
42
|
let environment = rawOptions.environment;
|
|
43
43
|
let template = rawOptions.template;
|
|
@@ -46,31 +46,32 @@ async function executeCreate(rawOptions) {
|
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
if (!environment) {
|
|
49
|
-
environment = await interactive_1.InteractiveUtils.promptEnvironmentName(
|
|
49
|
+
environment = await interactive_1.InteractiveUtils.promptEnvironmentName('Enter environment name:');
|
|
50
50
|
}
|
|
51
51
|
if (!file_1.FileUtils.isValidEnvironmentName(environment)) {
|
|
52
|
-
throw new Error(
|
|
52
|
+
throw new Error('Environment name can only contain letters, numbers, hyphens, and underscores');
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
(0, schemas_1.validateCreateOptions)({
|
|
55
55
|
environment,
|
|
56
56
|
template,
|
|
57
57
|
cwd,
|
|
58
|
+
overwrite: rawOptions.overwrite,
|
|
58
59
|
});
|
|
59
60
|
if (existingEnvironments.includes(environment)) {
|
|
60
61
|
exec_1.CliUtils.warning(`Environment '${environment}' already exists`);
|
|
61
62
|
const envFiles = await file_1.FileUtils.findEnvFiles(environment, cwd);
|
|
62
63
|
if (envFiles.length > 0) {
|
|
63
|
-
console.log(
|
|
64
|
-
envFiles.forEach(
|
|
64
|
+
console.log('Existing files:');
|
|
65
|
+
envFiles.forEach(file => {
|
|
65
66
|
const displayPath = file.encrypted
|
|
66
67
|
? file_1.FileUtils.getEncryptedPath(file.path)
|
|
67
68
|
: file.path;
|
|
68
69
|
console.log(` • ${exec_1.CliUtils.formatPath(displayPath, cwd)}`);
|
|
69
70
|
});
|
|
70
71
|
if (!rawOptions.overwrite) {
|
|
71
|
-
const confirm = await interactive_1.InteractiveUtils.confirmOperation(
|
|
72
|
+
const confirm = await interactive_1.InteractiveUtils.confirmOperation('Do you want to create additional files for this environment?', false);
|
|
72
73
|
if (!confirm) {
|
|
73
|
-
exec_1.CliUtils.info(
|
|
74
|
+
exec_1.CliUtils.info('Operation cancelled.');
|
|
74
75
|
return;
|
|
75
76
|
}
|
|
76
77
|
}
|
|
@@ -90,7 +91,7 @@ async function executeCreate(rawOptions) {
|
|
|
90
91
|
if ((await file_1.FileUtils.fileExists(envFilePath)) && !rawOptions.overwrite) {
|
|
91
92
|
const confirm = await interactive_1.InteractiveUtils.confirmOperation(`File ${chalk_1.default.cyan(relativePath)} already exists. Overwrite?`, false);
|
|
92
93
|
if (!confirm) {
|
|
93
|
-
exec_1.CliUtils.info(
|
|
94
|
+
exec_1.CliUtils.info('Operation cancelled.');
|
|
94
95
|
return;
|
|
95
96
|
}
|
|
96
97
|
}
|
|
@@ -100,10 +101,10 @@ async function executeCreate(rawOptions) {
|
|
|
100
101
|
if (result.success) {
|
|
101
102
|
exec_1.CliUtils.success(`Created: ${chalk_1.default.cyan(relativePath)}`);
|
|
102
103
|
console.log();
|
|
103
|
-
exec_1.CliUtils.info(
|
|
104
|
+
exec_1.CliUtils.info('Next steps:');
|
|
104
105
|
console.log(chalk_1.default.gray(`• Edit ${relativePath} and add your environment variables`));
|
|
105
106
|
console.log(chalk_1.default.gray(`• Use "envx encrypt -e ${environment}" to encrypt the file`));
|
|
106
|
-
console.log(chalk_1.default.gray(
|
|
107
|
+
console.log(chalk_1.default.gray('• Add the encrypted .gpg file to version control'));
|
|
107
108
|
}
|
|
108
109
|
else {
|
|
109
110
|
exec_1.CliUtils.error(result.message);
|
|
@@ -117,36 +118,36 @@ async function executeCreate(rawOptions) {
|
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
120
|
async function executeInteractiveCreate(cwd, existingEnvironments, template, overwrite) {
|
|
120
|
-
exec_1.CliUtils.subheader(
|
|
121
|
+
exec_1.CliUtils.subheader('Interactive Environment Creation');
|
|
121
122
|
const commonEnvironments = [
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
'development',
|
|
124
|
+
'staging',
|
|
125
|
+
'production',
|
|
126
|
+
'local',
|
|
127
|
+
'test',
|
|
127
128
|
];
|
|
128
|
-
const suggestedEnvironments = commonEnvironments.filter(
|
|
129
|
+
const suggestedEnvironments = commonEnvironments.filter(env => !existingEnvironments.includes(env));
|
|
129
130
|
if (suggestedEnvironments.length > 0) {
|
|
130
|
-
exec_1.CliUtils.info(
|
|
131
|
-
suggestedEnvironments.forEach(
|
|
131
|
+
exec_1.CliUtils.info('Suggested environments:');
|
|
132
|
+
suggestedEnvironments.forEach(env => {
|
|
132
133
|
console.log(` • ${exec_1.CliUtils.formatEnvironment(env)}`);
|
|
133
134
|
});
|
|
134
135
|
console.log();
|
|
135
136
|
}
|
|
136
|
-
const { createMode } = (await interactive_1.InteractiveUtils.confirmOperation(
|
|
137
|
-
? { createMode:
|
|
138
|
-
: { createMode:
|
|
137
|
+
const { createMode } = (await interactive_1.InteractiveUtils.confirmOperation('Do you want to select from suggested environments?'))
|
|
138
|
+
? { createMode: 'suggested' }
|
|
139
|
+
: { createMode: 'custom' };
|
|
139
140
|
let environmentsToCreate = [];
|
|
140
|
-
if (createMode ===
|
|
141
|
-
environmentsToCreate = await interactive_1.InteractiveUtils.selectMultipleEnvironments(suggestedEnvironments,
|
|
141
|
+
if (createMode === 'suggested' && suggestedEnvironments.length > 0) {
|
|
142
|
+
environmentsToCreate = await interactive_1.InteractiveUtils.selectMultipleEnvironments(suggestedEnvironments, 'Select environments to create:');
|
|
142
143
|
}
|
|
143
|
-
const { addCustom } = (await interactive_1.InteractiveUtils.confirmOperation(
|
|
144
|
+
const { addCustom } = (await interactive_1.InteractiveUtils.confirmOperation('Do you want to add custom environments?', createMode === 'custom'))
|
|
144
145
|
? { addCustom: true }
|
|
145
146
|
: { addCustom: false };
|
|
146
147
|
if (addCustom) {
|
|
147
148
|
let addingMore = true;
|
|
148
149
|
while (addingMore) {
|
|
149
|
-
const newEnv = await interactive_1.InteractiveUtils.promptEnvironmentName(
|
|
150
|
+
const newEnv = await interactive_1.InteractiveUtils.promptEnvironmentName('Enter environment name:');
|
|
150
151
|
if (!environmentsToCreate.includes(newEnv) &&
|
|
151
152
|
!existingEnvironments.includes(newEnv)) {
|
|
152
153
|
environmentsToCreate.push(newEnv);
|
|
@@ -154,7 +155,7 @@ async function executeInteractiveCreate(cwd, existingEnvironments, template, ove
|
|
|
154
155
|
}
|
|
155
156
|
else if (existingEnvironments.includes(newEnv)) {
|
|
156
157
|
exec_1.CliUtils.warning(`Environment ${exec_1.CliUtils.formatEnvironment(newEnv)} already exists`);
|
|
157
|
-
const confirm = await interactive_1.InteractiveUtils.confirmOperation(
|
|
158
|
+
const confirm = await interactive_1.InteractiveUtils.confirmOperation('Do you want to create files for this environment anyway?');
|
|
158
159
|
if (confirm) {
|
|
159
160
|
environmentsToCreate.push(newEnv);
|
|
160
161
|
}
|
|
@@ -162,22 +163,22 @@ async function executeInteractiveCreate(cwd, existingEnvironments, template, ove
|
|
|
162
163
|
else {
|
|
163
164
|
exec_1.CliUtils.warning(`Environment ${exec_1.CliUtils.formatEnvironment(newEnv)} already in creation list`);
|
|
164
165
|
}
|
|
165
|
-
const { continueAdding } = (await interactive_1.InteractiveUtils.confirmOperation(
|
|
166
|
+
const { continueAdding } = (await interactive_1.InteractiveUtils.confirmOperation('Do you want to add another environment?', false))
|
|
166
167
|
? { continueAdding: true }
|
|
167
168
|
: { continueAdding: false };
|
|
168
169
|
addingMore = continueAdding;
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
if (environmentsToCreate.length === 0) {
|
|
172
|
-
exec_1.CliUtils.warning(
|
|
173
|
+
exec_1.CliUtils.warning('No environments selected for creation.');
|
|
173
174
|
return;
|
|
174
175
|
}
|
|
175
176
|
if (!template) {
|
|
176
|
-
const { useTemplate } = (await interactive_1.InteractiveUtils.confirmOperation(
|
|
177
|
+
const { useTemplate } = (await interactive_1.InteractiveUtils.confirmOperation('Do you want to use a template file?', false))
|
|
177
178
|
? { useTemplate: true }
|
|
178
179
|
: { useTemplate: false };
|
|
179
180
|
if (useTemplate) {
|
|
180
|
-
const commonTemplates = [
|
|
181
|
+
const commonTemplates = ['.env.example', '.env.template', '.env.sample'];
|
|
181
182
|
const foundTemplates = [];
|
|
182
183
|
for (const templateName of commonTemplates) {
|
|
183
184
|
const templatePath = path_1.default.join(cwd, templateName);
|
|
@@ -186,9 +187,9 @@ async function executeInteractiveCreate(cwd, existingEnvironments, template, ove
|
|
|
186
187
|
}
|
|
187
188
|
}
|
|
188
189
|
if (foundTemplates.length > 0) {
|
|
189
|
-
const { templateChoice } = (await interactive_1.InteractiveUtils.selectFiles([...foundTemplates,
|
|
190
|
-
if (templateChoice ===
|
|
191
|
-
const { customTemplate } = (await interactive_1.InteractiveUtils.promptEnvironmentName(
|
|
190
|
+
const { templateChoice } = (await interactive_1.InteractiveUtils.selectFiles([...foundTemplates, 'Enter custom path'], 'Select template file:'));
|
|
191
|
+
if (templateChoice === 'Enter custom path') {
|
|
192
|
+
const { customTemplate } = (await interactive_1.InteractiveUtils.promptEnvironmentName('Enter template file path:'));
|
|
192
193
|
template = customTemplate;
|
|
193
194
|
}
|
|
194
195
|
else {
|
|
@@ -196,7 +197,7 @@ async function executeInteractiveCreate(cwd, existingEnvironments, template, ove
|
|
|
196
197
|
}
|
|
197
198
|
}
|
|
198
199
|
else {
|
|
199
|
-
const { customTemplate } = (await interactive_1.InteractiveUtils.promptEnvironmentName(
|
|
200
|
+
const { customTemplate } = (await interactive_1.InteractiveUtils.promptEnvironmentName('Enter template file path:'));
|
|
200
201
|
template = customTemplate;
|
|
201
202
|
}
|
|
202
203
|
if (template) {
|
|
@@ -215,20 +216,20 @@ async function executeInteractiveCreate(cwd, existingEnvironments, template, ove
|
|
|
215
216
|
}
|
|
216
217
|
}
|
|
217
218
|
console.log();
|
|
218
|
-
exec_1.CliUtils.subheader(
|
|
219
|
+
exec_1.CliUtils.subheader('Creation Summary');
|
|
219
220
|
console.log(`Will create ${environmentsToCreate.length} environment file(s):`);
|
|
220
|
-
environmentsToCreate.forEach(
|
|
221
|
+
environmentsToCreate.forEach(env => {
|
|
221
222
|
console.log(` • .env.${exec_1.CliUtils.formatEnvironment(env)}`);
|
|
222
223
|
});
|
|
223
224
|
if (template) {
|
|
224
225
|
console.log(`Using template: ${exec_1.CliUtils.formatPath(template, cwd)}`);
|
|
225
226
|
}
|
|
226
|
-
const confirm = await interactive_1.InteractiveUtils.confirmOperation(
|
|
227
|
+
const confirm = await interactive_1.InteractiveUtils.confirmOperation('Create these environment files?');
|
|
227
228
|
if (!confirm) {
|
|
228
|
-
exec_1.CliUtils.info(
|
|
229
|
+
exec_1.CliUtils.info('Operation cancelled.');
|
|
229
230
|
return;
|
|
230
231
|
}
|
|
231
|
-
exec_1.CliUtils.subheader(
|
|
232
|
+
exec_1.CliUtils.subheader('Creating Files');
|
|
232
233
|
let successCount = 0;
|
|
233
234
|
let errorCount = 0;
|
|
234
235
|
for (const environment of environmentsToCreate.sort()) {
|
|
@@ -240,7 +241,7 @@ async function executeInteractiveCreate(cwd, existingEnvironments, template, ove
|
|
|
240
241
|
if ((await file_1.FileUtils.fileExists(envFilePath)) && !overwrite) {
|
|
241
242
|
const fileConfirm = await interactive_1.InteractiveUtils.confirmOperation(`File already exists. Overwrite ${relativePath}?`, false);
|
|
242
243
|
if (!fileConfirm) {
|
|
243
|
-
exec_1.CliUtils.warning(
|
|
244
|
+
exec_1.CliUtils.warning('Skipped existing file');
|
|
244
245
|
continue;
|
|
245
246
|
}
|
|
246
247
|
}
|
|
@@ -260,7 +261,7 @@ async function executeInteractiveCreate(cwd, existingEnvironments, template, ove
|
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
263
|
console.log();
|
|
263
|
-
exec_1.CliUtils.subheader(
|
|
264
|
+
exec_1.CliUtils.subheader('Creation Results');
|
|
264
265
|
if (successCount > 0) {
|
|
265
266
|
exec_1.CliUtils.success(`Successfully created ${successCount} file(s)`);
|
|
266
267
|
}
|
|
@@ -269,8 +270,8 @@ async function executeInteractiveCreate(cwd, existingEnvironments, template, ove
|
|
|
269
270
|
}
|
|
270
271
|
if (successCount > 0) {
|
|
271
272
|
console.log();
|
|
272
|
-
exec_1.CliUtils.info(
|
|
273
|
-
console.log(chalk_1.default.gray(
|
|
273
|
+
exec_1.CliUtils.info('Next steps:');
|
|
274
|
+
console.log(chalk_1.default.gray('• Edit the created files and add your environment variables'));
|
|
274
275
|
console.log(chalk_1.default.gray('• Use "envx encrypt" to encrypt sensitive files'));
|
|
275
276
|
console.log(chalk_1.default.gray('• Use "envx interactive" to set up .envrc with secrets'));
|
|
276
277
|
}
|