env-secrets 0.5.2 → 0.5.4
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/.claude/rules/cicd.md +189 -0
- package/.claude/rules/docs.md +96 -0
- package/.claude/rules/git-hooks.md +43 -0
- package/.claude/rules/local-dev-badges.md +91 -0
- package/.claude/rules/local-dev-env.md +382 -0
- package/.claude/rules/local-dev-license.md +104 -0
- package/.claude/rules/local-dev-mcp.md +70 -0
- package/.claude/rules/observability.md +23 -0
- package/.claude/rules/publishing-api.md +158 -0
- package/.claude/rules/publishing-apps.md +204 -0
- package/.claude/rules/publishing-apt.md +146 -0
- package/.claude/rules/publishing-brew.md +110 -0
- package/.claude/rules/publishing-cli.md +238 -0
- package/.claude/rules/publishing-libraries.md +115 -0
- package/.claude/rules/publishing-sdks.md +109 -0
- package/.claude/rules/publishing-web.md +185 -0
- package/.claude/rules/typescript-linting.md +141 -0
- package/.claude/rules/typescript-logging.md +356 -0
- package/.claude/rules/typescript-testing.md +185 -0
- package/.claude/settings.json +18 -0
- package/.claude/skills/github-health-check.skill +0 -0
- package/.codex/rules/cicd.md +21 -0
- package/.codex/rules/docs.md +98 -0
- package/.codex/rules/git-hooks.md +43 -0
- package/.codex/rules/github-health-check.md +440 -0
- package/.codex/rules/local-dev-env.md +47 -0
- package/.codex/rules/local-dev-license.md +3 -1
- package/.codex/rules/publishing-api.md +160 -0
- package/.codex/rules/publishing-apps.md +206 -0
- package/.codex/rules/publishing-apt.md +148 -0
- package/.codex/rules/publishing-brew.md +112 -0
- package/.codex/rules/publishing-cli.md +240 -0
- package/.codex/rules/publishing-libraries.md +117 -0
- package/.codex/rules/publishing-sdks.md +111 -0
- package/.codex/rules/publishing-web.md +187 -0
- package/.codex/rules/typescript-linting.md +143 -0
- package/.codex/rules/typescript-logging.md +358 -0
- package/.codex/rules/typescript-testing.md +187 -0
- package/.github/workflows/deploy-docs.yml +2 -2
- package/.github/workflows/unittests.yaml +1 -1
- package/.rulesrc.json +20 -0
- package/AGENTS.md +34 -0
- package/CLAUDE.md +58 -0
- package/README.md +17 -3
- package/__e2e__/aws-exec-args.test.ts +97 -1
- package/__e2e__/aws-secret-value-args.test.ts +142 -0
- package/__e2e__/utils/test-utils.ts +78 -0
- package/__tests__/cli/helpers.test.ts +35 -0
- package/__tests__/index.test.ts +208 -58
- package/dist/cli/helpers.js +13 -1
- package/dist/index.js +94 -44
- package/docker-compose.yaml +1 -1
- package/docs/AWS.md +42 -13
- package/package.json +6 -6
- package/src/cli/helpers.ts +16 -0
- package/src/index.ts +117 -52
package/__tests__/index.test.ts
CHANGED
|
@@ -39,6 +39,24 @@ const mockObjectToExport = objectToExport as jest.MockedFunction<
|
|
|
39
39
|
typeof objectToExport
|
|
40
40
|
>;
|
|
41
41
|
|
|
42
|
+
// Build a ChildProcess-like mock that records event handlers
|
|
43
|
+
function makeChildMock() {
|
|
44
|
+
const handlers: Record<string, ((...args: unknown[]) => void)[]> = {};
|
|
45
|
+
const child = {
|
|
46
|
+
stdio: 'inherit',
|
|
47
|
+
on: jest.fn((event: string, cb: (...args: unknown[]) => void) => {
|
|
48
|
+
if (!handlers[event]) handlers[event] = [];
|
|
49
|
+
handlers[event].push(cb);
|
|
50
|
+
return child;
|
|
51
|
+
}),
|
|
52
|
+
emit(event: string, ...args: unknown[]) {
|
|
53
|
+
(handlers[event] ?? []).forEach((cb) => cb(...args));
|
|
54
|
+
}
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
+
} as any;
|
|
57
|
+
return child;
|
|
58
|
+
}
|
|
59
|
+
|
|
42
60
|
describe('index.ts CLI functionality', () => {
|
|
43
61
|
beforeEach(() => {
|
|
44
62
|
jest.clearAllMocks();
|
|
@@ -94,43 +112,67 @@ describe('index.ts CLI functionality', () => {
|
|
|
94
112
|
);
|
|
95
113
|
});
|
|
96
114
|
|
|
97
|
-
it('should spawn
|
|
115
|
+
it('should spawn using shell (default) with joined command string', async () => {
|
|
98
116
|
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
99
117
|
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
100
118
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
stdio: 'inherit'
|
|
104
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
|
-
} as any;
|
|
106
|
-
mockSpawn.mockReturnValue(mockChildProcess);
|
|
119
|
+
const child = makeChildMock();
|
|
120
|
+
mockSpawn.mockReturnValue(child);
|
|
107
121
|
|
|
108
122
|
const program = ['node', 'script.js', 'arg1', 'arg2'];
|
|
109
|
-
const options = { secret: 'my-secret' };
|
|
123
|
+
const options = { secret: 'my-secret', shell: true };
|
|
110
124
|
|
|
111
|
-
// Simulate the action logic
|
|
112
125
|
let env = await mockSecretsmanager(options);
|
|
113
126
|
env = Object.assign({}, process.env, env);
|
|
114
127
|
|
|
115
128
|
if (program && program.length > 0) {
|
|
116
|
-
mockSpawn(program
|
|
129
|
+
mockSpawn(program.join(' '), [], {
|
|
117
130
|
stdio: 'inherit',
|
|
118
131
|
shell: true,
|
|
119
132
|
env
|
|
120
133
|
});
|
|
121
134
|
}
|
|
122
135
|
|
|
123
|
-
expect(mockSpawn).toHaveBeenCalledWith(
|
|
124
|
-
'
|
|
125
|
-
|
|
126
|
-
{
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
136
|
+
expect(mockSpawn).toHaveBeenCalledWith('node script.js arg1 arg2', [], {
|
|
137
|
+
stdio: 'inherit',
|
|
138
|
+
shell: true,
|
|
139
|
+
env: expect.objectContaining({
|
|
140
|
+
SECRET_KEY: 'secret_value'
|
|
141
|
+
})
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should spawn without shell when --no-shell is passed', async () => {
|
|
146
|
+
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
147
|
+
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
148
|
+
|
|
149
|
+
const child = makeChildMock();
|
|
150
|
+
mockSpawn.mockReturnValue(child);
|
|
151
|
+
|
|
152
|
+
const program = ['node', 'script.js', 'arg1'];
|
|
153
|
+
const options = { secret: 'my-secret', shell: false };
|
|
154
|
+
|
|
155
|
+
let env = await mockSecretsmanager(options);
|
|
156
|
+
env = Object.assign({}, process.env, env);
|
|
157
|
+
|
|
158
|
+
if (program && program.length > 0) {
|
|
159
|
+
if (options.shell) {
|
|
160
|
+
mockSpawn(program.join(' '), [], {
|
|
161
|
+
stdio: 'inherit',
|
|
162
|
+
shell: true,
|
|
163
|
+
env
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
mockSpawn(program[0], program.slice(1), { stdio: 'inherit', env });
|
|
132
167
|
}
|
|
133
|
-
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
expect(mockSpawn).toHaveBeenCalledWith('node', ['script.js', 'arg1'], {
|
|
171
|
+
stdio: 'inherit',
|
|
172
|
+
env: expect.objectContaining({
|
|
173
|
+
SECRET_KEY: 'secret_value'
|
|
174
|
+
})
|
|
175
|
+
});
|
|
134
176
|
});
|
|
135
177
|
|
|
136
178
|
it('should not spawn a program when no program is provided', async () => {
|
|
@@ -143,7 +185,7 @@ describe('index.ts CLI functionality', () => {
|
|
|
143
185
|
|
|
144
186
|
const program: string[] = [];
|
|
145
187
|
if (program && program.length > 0) {
|
|
146
|
-
mockSpawn(program
|
|
188
|
+
mockSpawn(program.join(' '), [], {
|
|
147
189
|
stdio: 'inherit',
|
|
148
190
|
shell: true,
|
|
149
191
|
env
|
|
@@ -163,7 +205,7 @@ describe('index.ts CLI functionality', () => {
|
|
|
163
205
|
|
|
164
206
|
const program: string[] = [];
|
|
165
207
|
if (program && program.length > 0) {
|
|
166
|
-
mockSpawn(program
|
|
208
|
+
mockSpawn(program.join(' '), [], {
|
|
167
209
|
stdio: 'inherit',
|
|
168
210
|
shell: true,
|
|
169
211
|
env
|
|
@@ -173,26 +215,21 @@ describe('index.ts CLI functionality', () => {
|
|
|
173
215
|
expect(mockSpawn).not.toHaveBeenCalled();
|
|
174
216
|
});
|
|
175
217
|
|
|
176
|
-
it('should handle single program argument', async () => {
|
|
218
|
+
it('should handle single program argument (shell mode)', async () => {
|
|
177
219
|
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
178
220
|
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
179
221
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
stdio: 'inherit'
|
|
183
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
184
|
-
} as any;
|
|
185
|
-
mockSpawn.mockReturnValue(mockChildProcess);
|
|
222
|
+
const child = makeChildMock();
|
|
223
|
+
mockSpawn.mockReturnValue(child);
|
|
186
224
|
|
|
187
225
|
const program = ['echo'];
|
|
188
|
-
const options = { secret: 'my-secret' };
|
|
226
|
+
const options = { secret: 'my-secret', shell: true };
|
|
189
227
|
|
|
190
|
-
// Simulate the action logic
|
|
191
228
|
let env = await mockSecretsmanager(options);
|
|
192
229
|
env = Object.assign({}, process.env, env);
|
|
193
230
|
|
|
194
231
|
if (program && program.length > 0) {
|
|
195
|
-
mockSpawn(program
|
|
232
|
+
mockSpawn(program.join(' '), [], {
|
|
196
233
|
stdio: 'inherit',
|
|
197
234
|
shell: true,
|
|
198
235
|
env
|
|
@@ -219,13 +256,12 @@ describe('index.ts CLI functionality', () => {
|
|
|
219
256
|
|
|
220
257
|
mockSecretsmanager.mockResolvedValue(mockSecrets);
|
|
221
258
|
|
|
222
|
-
// Simulate the action logic
|
|
223
259
|
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
224
260
|
env = Object.assign({}, process.env, env);
|
|
225
261
|
|
|
226
262
|
const program = ['echo'];
|
|
227
263
|
if (program && program.length > 0) {
|
|
228
|
-
mockSpawn(program
|
|
264
|
+
mockSpawn(program.join(' '), [], {
|
|
229
265
|
stdio: 'inherit',
|
|
230
266
|
shell: true,
|
|
231
267
|
env
|
|
@@ -247,13 +283,12 @@ describe('index.ts CLI functionality', () => {
|
|
|
247
283
|
it('should handle secretsmanager returning empty object', async () => {
|
|
248
284
|
mockSecretsmanager.mockResolvedValue({});
|
|
249
285
|
|
|
250
|
-
// Simulate the action logic
|
|
251
286
|
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
252
287
|
env = Object.assign({}, process.env, env);
|
|
253
288
|
|
|
254
289
|
const program = ['echo'];
|
|
255
290
|
if (program && program.length > 0) {
|
|
256
|
-
mockSpawn(program
|
|
291
|
+
mockSpawn(program.join(' '), [], {
|
|
257
292
|
stdio: 'inherit',
|
|
258
293
|
shell: true,
|
|
259
294
|
env
|
|
@@ -276,6 +311,128 @@ describe('index.ts CLI functionality', () => {
|
|
|
276
311
|
'AWS connection failed'
|
|
277
312
|
);
|
|
278
313
|
});
|
|
314
|
+
|
|
315
|
+
describe('ChildProcess error and exit handling', () => {
|
|
316
|
+
beforeEach(() => {
|
|
317
|
+
jest.spyOn(process, 'exit').mockImplementation(() => {
|
|
318
|
+
throw new Error('process.exit called');
|
|
319
|
+
});
|
|
320
|
+
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
afterEach(() => {
|
|
324
|
+
jest.restoreAllMocks();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should print error message and exit 1 on child process error', async () => {
|
|
328
|
+
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
329
|
+
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
330
|
+
|
|
331
|
+
const child = makeChildMock();
|
|
332
|
+
mockSpawn.mockReturnValue(child);
|
|
333
|
+
|
|
334
|
+
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
335
|
+
env = Object.assign({}, process.env, env);
|
|
336
|
+
|
|
337
|
+
const program = ['nonexistent-cmd'];
|
|
338
|
+
mockSpawn(program.join(' '), [], {
|
|
339
|
+
stdio: 'inherit',
|
|
340
|
+
shell: true,
|
|
341
|
+
env
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Simulate attaching handlers as src/index.ts does
|
|
345
|
+
child.on('error', (err: Error) => {
|
|
346
|
+
// eslint-disable-next-line no-console
|
|
347
|
+
console.error(`Failed to start process: ${err.message}`);
|
|
348
|
+
process.exit(1);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
expect(() => child.emit('error', new Error('spawn ENOENT'))).toThrow(
|
|
352
|
+
'process.exit called'
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
// eslint-disable-next-line no-console
|
|
356
|
+
expect(console.error).toHaveBeenCalledWith(
|
|
357
|
+
'Failed to start process: spawn ENOENT'
|
|
358
|
+
);
|
|
359
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('should propagate child exit code 0', async () => {
|
|
363
|
+
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
364
|
+
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
365
|
+
|
|
366
|
+
const child = makeChildMock();
|
|
367
|
+
mockSpawn.mockReturnValue(child);
|
|
368
|
+
|
|
369
|
+
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
370
|
+
env = Object.assign({}, process.env, env);
|
|
371
|
+
|
|
372
|
+
mockSpawn(['node', '-e', '"process.exit(0)"'].join(' '), [], {
|
|
373
|
+
stdio: 'inherit',
|
|
374
|
+
shell: true,
|
|
375
|
+
env
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
child.on('exit', (code: number | null, signal: string | null) => {
|
|
379
|
+
process.exit(signal ? 1 : code ?? 0);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
expect(() => child.emit('exit', 0, null)).toThrow(
|
|
383
|
+
'process.exit called'
|
|
384
|
+
);
|
|
385
|
+
expect(process.exit).toHaveBeenCalledWith(0);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('should propagate non-zero child exit code', async () => {
|
|
389
|
+
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
390
|
+
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
391
|
+
|
|
392
|
+
const child = makeChildMock();
|
|
393
|
+
mockSpawn.mockReturnValue(child);
|
|
394
|
+
|
|
395
|
+
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
396
|
+
env = Object.assign({}, process.env, env);
|
|
397
|
+
|
|
398
|
+
mockSpawn(['node', '-e', '"process.exit(42)"'].join(' '), [], {
|
|
399
|
+
stdio: 'inherit',
|
|
400
|
+
shell: true,
|
|
401
|
+
env
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
child.on('exit', (code: number | null, signal: string | null) => {
|
|
405
|
+
process.exit(signal ? 1 : code ?? 0);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
expect(() => child.emit('exit', 42, null)).toThrow(
|
|
409
|
+
'process.exit called'
|
|
410
|
+
);
|
|
411
|
+
expect(process.exit).toHaveBeenCalledWith(42);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('should exit 1 when child is killed by a signal', async () => {
|
|
415
|
+
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
416
|
+
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
417
|
+
|
|
418
|
+
const child = makeChildMock();
|
|
419
|
+
mockSpawn.mockReturnValue(child);
|
|
420
|
+
|
|
421
|
+
let env = await mockSecretsmanager({ secret: 'my-secret' });
|
|
422
|
+
env = Object.assign({}, process.env, env);
|
|
423
|
+
|
|
424
|
+
mockSpawn('sleep 10', [], { stdio: 'inherit', shell: true, env });
|
|
425
|
+
|
|
426
|
+
child.on('exit', (code: number | null, signal: string | null) => {
|
|
427
|
+
process.exit(signal ? 1 : code ?? 0);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
expect(() => child.emit('exit', null, 'SIGTERM')).toThrow(
|
|
431
|
+
'process.exit called'
|
|
432
|
+
);
|
|
433
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
434
|
+
});
|
|
435
|
+
});
|
|
279
436
|
});
|
|
280
437
|
|
|
281
438
|
describe('Debug logging', () => {
|
|
@@ -314,12 +471,8 @@ describe('index.ts CLI functionality', () => {
|
|
|
314
471
|
const mockEnv = { SECRET_KEY: 'secret_value' };
|
|
315
472
|
mockSecretsmanager.mockResolvedValue(mockEnv);
|
|
316
473
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
stdio: 'inherit'
|
|
320
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
321
|
-
} as any;
|
|
322
|
-
mockSpawn.mockReturnValue(mockChildProcess);
|
|
474
|
+
const child = makeChildMock();
|
|
475
|
+
mockSpawn.mockReturnValue(child);
|
|
323
476
|
|
|
324
477
|
const program = ['node', 'script.js', 'arg1'];
|
|
325
478
|
|
|
@@ -331,7 +484,7 @@ describe('index.ts CLI functionality', () => {
|
|
|
331
484
|
// Simulate debug logging
|
|
332
485
|
mockDebugInstance(`${program[0]} ${program.slice(1).join(' ')}`);
|
|
333
486
|
|
|
334
|
-
mockSpawn(program
|
|
487
|
+
mockSpawn(program.join(' '), [], {
|
|
335
488
|
stdio: 'inherit',
|
|
336
489
|
shell: true,
|
|
337
490
|
env
|
|
@@ -438,16 +591,12 @@ describe('index.ts CLI functionality', () => {
|
|
|
438
591
|
const mockSecrets = { SECRET_KEY: 'secret_value' };
|
|
439
592
|
mockSecretsmanager.mockResolvedValue(mockSecrets);
|
|
440
593
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
const options: { secret: string; output?: string } = {
|
|
449
|
-
secret: 'my-secret'
|
|
450
|
-
// No output option
|
|
594
|
+
const child = makeChildMock();
|
|
595
|
+
mockSpawn.mockReturnValue(child);
|
|
596
|
+
|
|
597
|
+
const options: { secret: string; output?: string; shell: boolean } = {
|
|
598
|
+
secret: 'my-secret',
|
|
599
|
+
shell: true
|
|
451
600
|
};
|
|
452
601
|
|
|
453
602
|
const program = ['echo', 'hello'];
|
|
@@ -474,7 +623,7 @@ describe('index.ts CLI functionality', () => {
|
|
|
474
623
|
const env = Object.assign({}, process.env, secrets);
|
|
475
624
|
|
|
476
625
|
if (program && program.length > 0) {
|
|
477
|
-
mockSpawn(program
|
|
626
|
+
mockSpawn(program.join(' '), [], {
|
|
478
627
|
stdio: 'inherit',
|
|
479
628
|
shell: true,
|
|
480
629
|
env
|
|
@@ -483,7 +632,7 @@ describe('index.ts CLI functionality', () => {
|
|
|
483
632
|
}
|
|
484
633
|
|
|
485
634
|
expect(mockSecretsmanager).toHaveBeenCalledWith(options);
|
|
486
|
-
expect(mockSpawn).toHaveBeenCalledWith('echo', [
|
|
635
|
+
expect(mockSpawn).toHaveBeenCalledWith('echo hello', [], {
|
|
487
636
|
stdio: 'inherit',
|
|
488
637
|
shell: true,
|
|
489
638
|
env: expect.objectContaining({
|
|
@@ -538,9 +687,10 @@ describe('index.ts CLI functionality', () => {
|
|
|
538
687
|
mockObjectToExport.mockReturnValue(mockEnvContent);
|
|
539
688
|
mockExistsSync.mockReturnValue(false);
|
|
540
689
|
|
|
541
|
-
const options: { secret: string; output: string } = {
|
|
690
|
+
const options: { secret: string; output: string; shell: boolean } = {
|
|
542
691
|
secret: 'my-secret',
|
|
543
|
-
output: '/tmp/secrets.env'
|
|
692
|
+
output: '/tmp/secrets.env',
|
|
693
|
+
shell: true
|
|
544
694
|
};
|
|
545
695
|
|
|
546
696
|
const program = ['echo', 'hello'];
|
|
@@ -566,7 +716,7 @@ describe('index.ts CLI functionality', () => {
|
|
|
566
716
|
const env = Object.assign({}, process.env, secrets);
|
|
567
717
|
|
|
568
718
|
if (program && program.length > 0) {
|
|
569
|
-
mockSpawn(program
|
|
719
|
+
mockSpawn(program.join(' '), [], {
|
|
570
720
|
stdio: 'inherit',
|
|
571
721
|
shell: true,
|
|
572
722
|
env
|
package/dist/cli/helpers.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.resolveAwsScope = exports.parseEnvSecretsFile = exports.parseEnvSecrets = exports.resolveSecretValue = exports.readStdin = exports.parseRecoveryDays = exports.printData = exports.renderTable = exports.asOutputFormat = void 0;
|
|
12
|
+
exports.resolveAwsScope = exports.resolveOutputFormat = exports.parseEnvSecretsFile = exports.parseEnvSecrets = exports.resolveSecretValue = exports.readStdin = exports.parseRecoveryDays = exports.printData = exports.renderTable = exports.asOutputFormat = void 0;
|
|
13
13
|
const promises_1 = require("node:fs/promises");
|
|
14
14
|
const asOutputFormat = (value) => {
|
|
15
15
|
if (value !== 'json' && value !== 'table') {
|
|
@@ -156,6 +156,18 @@ const parseEnvSecretsFile = (path) => __awaiter(void 0, void 0, void 0, function
|
|
|
156
156
|
return (0, exports.parseEnvSecrets)(content);
|
|
157
157
|
});
|
|
158
158
|
exports.parseEnvSecretsFile = parseEnvSecretsFile;
|
|
159
|
+
const resolveOutputFormat = (options, command) => {
|
|
160
|
+
var _a, _b;
|
|
161
|
+
if (options.output) {
|
|
162
|
+
return (0, exports.asOutputFormat)(options.output);
|
|
163
|
+
}
|
|
164
|
+
const globalOutput = (_b = (_a = command === null || command === void 0 ? void 0 : command.optsWithGlobals) === null || _a === void 0 ? void 0 : _a.call(command)) === null || _b === void 0 ? void 0 : _b.output;
|
|
165
|
+
if (globalOutput === 'json' || globalOutput === 'table') {
|
|
166
|
+
return globalOutput;
|
|
167
|
+
}
|
|
168
|
+
return 'table';
|
|
169
|
+
};
|
|
170
|
+
exports.resolveOutputFormat = resolveOutputFormat;
|
|
159
171
|
const resolveAwsScope = (options, command) => {
|
|
160
172
|
var _a;
|
|
161
173
|
const globalOptions = ((_a = command === null || command === void 0 ? void 0 : command.optsWithGlobals) === null || _a === void 0 ? void 0 : _a.call(command)) || {};
|
package/dist/index.js
CHANGED
|
@@ -91,6 +91,7 @@ const awsCommand = program
|
|
|
91
91
|
.option('-p, --profile <profile>', 'profile to use')
|
|
92
92
|
.option('-r, --region <region>', 'region to use')
|
|
93
93
|
.option('-o, --output <file>', 'output secrets to file instead of environment variables')
|
|
94
|
+
.option('--no-shell', 'run program directly without a shell (disables shell expansion)')
|
|
94
95
|
.action((program, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
95
96
|
if (!options.secret) {
|
|
96
97
|
exitWithError(new Error('Missing required option --secret for this command.'));
|
|
@@ -123,10 +124,20 @@ const awsCommand = program
|
|
|
123
124
|
console.log(JSON.stringify(env));
|
|
124
125
|
return;
|
|
125
126
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
const child = options.shell
|
|
128
|
+
? (0, node_child_process_1.spawn)(program.join(' '), [], {
|
|
129
|
+
stdio: 'inherit',
|
|
130
|
+
shell: true,
|
|
131
|
+
env
|
|
132
|
+
})
|
|
133
|
+
: (0, node_child_process_1.spawn)(program[0], program.slice(1), { stdio: 'inherit', env });
|
|
134
|
+
child.on('error', (err) => {
|
|
135
|
+
// eslint-disable-next-line no-console
|
|
136
|
+
console.error(`Failed to start process: ${err.message}`);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
});
|
|
139
|
+
child.on('exit', (code, signal) => {
|
|
140
|
+
process.exit(signal ? 1 : code !== null && code !== void 0 ? code : 0);
|
|
130
141
|
});
|
|
131
142
|
}
|
|
132
143
|
}
|
|
@@ -148,13 +159,9 @@ secretCommand
|
|
|
148
159
|
.option('-r, --region <region>', 'region to use')
|
|
149
160
|
.option('--output <format>', 'output format: json|table')
|
|
150
161
|
.action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
|
|
151
|
-
var _a;
|
|
152
162
|
try {
|
|
153
163
|
const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
|
|
154
|
-
const
|
|
155
|
-
const output = (_a = options.output) !== null && _a !== void 0 ? _a : (typeof globalOptions.output === 'string'
|
|
156
|
-
? globalOptions.output
|
|
157
|
-
: 'table');
|
|
164
|
+
const output = (0, helpers_1.resolveOutputFormat)(options, command);
|
|
158
165
|
const value = yield (0, helpers_1.resolveSecretValue)(options.value, options.valueStdin, options.file);
|
|
159
166
|
if (!value) {
|
|
160
167
|
throw new Error('Secret value is required. Provide --value, --value-stdin, or --file.');
|
|
@@ -192,13 +199,9 @@ secretCommand
|
|
|
192
199
|
.option('-r, --region <region>', 'region to use')
|
|
193
200
|
.option('--output <format>', 'output format: json|table')
|
|
194
201
|
.action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
|
|
195
|
-
var _b;
|
|
196
202
|
try {
|
|
197
203
|
const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
|
|
198
|
-
const
|
|
199
|
-
const output = (_b = options.output) !== null && _b !== void 0 ? _b : (typeof globalOptions.output === 'string'
|
|
200
|
-
? globalOptions.output
|
|
201
|
-
: 'table');
|
|
204
|
+
const output = (0, helpers_1.resolveOutputFormat)(options, command);
|
|
202
205
|
const value = yield (0, helpers_1.resolveSecretValue)(options.value, options.valueStdin, options.file);
|
|
203
206
|
if (!value && !options.description && !options.kmsKeyId) {
|
|
204
207
|
throw new Error('Nothing to update. Provide --value/--value-stdin/--file, --description, or --kms-key-id.');
|
|
@@ -234,13 +237,9 @@ secretCommand
|
|
|
234
237
|
.option('-r, --region <region>', 'region to use')
|
|
235
238
|
.option('--output <format>', 'output format: json|table')
|
|
236
239
|
.action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
|
|
237
|
-
var _c;
|
|
238
240
|
try {
|
|
239
241
|
const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
|
|
240
|
-
const
|
|
241
|
-
const output = (_c = options.output) !== null && _c !== void 0 ? _c : (typeof globalOptions.output === 'string'
|
|
242
|
-
? globalOptions.output
|
|
243
|
-
: 'table');
|
|
242
|
+
const output = (0, helpers_1.resolveOutputFormat)(options, command);
|
|
244
243
|
const parsed = yield (0, helpers_1.parseEnvSecretsFile)(options.file);
|
|
245
244
|
if (parsed.entries.length === 0) {
|
|
246
245
|
throw new Error('No env entries found. Include lines like KEY=value or export KEY=value.');
|
|
@@ -346,13 +345,9 @@ secretCommand
|
|
|
346
345
|
.option('-r, --region <region>', 'region to use')
|
|
347
346
|
.option('--output <format>', 'output format: json|table')
|
|
348
347
|
.action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
|
|
349
|
-
var _d;
|
|
350
348
|
try {
|
|
351
349
|
const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
|
|
352
|
-
const
|
|
353
|
-
const output = (_d = options.output) !== null && _d !== void 0 ? _d : (typeof globalOptions.output === 'string'
|
|
354
|
-
? globalOptions.output
|
|
355
|
-
: 'table');
|
|
350
|
+
const output = (0, helpers_1.resolveOutputFormat)(options, command);
|
|
356
351
|
const value = yield (0, helpers_1.resolveSecretValue)(options.value, options.valueStdin, options.file);
|
|
357
352
|
if (!value) {
|
|
358
353
|
throw new Error('Append value is required. Provide --value, --value-stdin, or --file.');
|
|
@@ -393,13 +388,9 @@ secretCommand
|
|
|
393
388
|
.option('-r, --region <region>', 'region to use')
|
|
394
389
|
.option('--output <format>', 'output format: json|table')
|
|
395
390
|
.action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
|
|
396
|
-
var _e;
|
|
397
391
|
try {
|
|
398
392
|
const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
|
|
399
|
-
const
|
|
400
|
-
const output = (_e = options.output) !== null && _e !== void 0 ? _e : (typeof globalOptions.output === 'string'
|
|
401
|
-
? globalOptions.output
|
|
402
|
-
: 'table');
|
|
393
|
+
const output = (0, helpers_1.resolveOutputFormat)(options, command);
|
|
403
394
|
const keys = options.key;
|
|
404
395
|
const current = yield (0, secretsmanager_admin_1.getSecretString)({
|
|
405
396
|
name: options.name,
|
|
@@ -450,13 +441,9 @@ secretCommand
|
|
|
450
441
|
.option('-r, --region <region>', 'region to use')
|
|
451
442
|
.option('--output <format>', 'output format: json|table')
|
|
452
443
|
.action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
|
|
453
|
-
var _f;
|
|
454
444
|
try {
|
|
455
445
|
const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
|
|
456
|
-
const
|
|
457
|
-
const output = (_f = options.output) !== null && _f !== void 0 ? _f : (typeof globalOptions.output === 'string'
|
|
458
|
-
? globalOptions.output
|
|
459
|
-
: 'table');
|
|
446
|
+
const output = (0, helpers_1.resolveOutputFormat)(options, command);
|
|
460
447
|
const result = yield (0, secretsmanager_admin_1.listSecrets)({
|
|
461
448
|
prefix: options.prefix,
|
|
462
449
|
tags: options.tag,
|
|
@@ -486,13 +473,9 @@ secretCommand
|
|
|
486
473
|
.option('-r, --region <region>', 'region to use')
|
|
487
474
|
.option('--output <format>', 'output format: json|table')
|
|
488
475
|
.action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
|
|
489
|
-
var _g;
|
|
490
476
|
try {
|
|
491
477
|
const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
|
|
492
|
-
const
|
|
493
|
-
const output = (_g = options.output) !== null && _g !== void 0 ? _g : (typeof globalOptions.output === 'string'
|
|
494
|
-
? globalOptions.output
|
|
495
|
-
: 'table');
|
|
478
|
+
const output = (0, helpers_1.resolveOutputFormat)(options, command);
|
|
496
479
|
const result = yield (0, secretsmanager_admin_1.getSecretMetadata)({
|
|
497
480
|
name: options.name,
|
|
498
481
|
profile,
|
|
@@ -521,6 +504,77 @@ secretCommand
|
|
|
521
504
|
exitWithError(error);
|
|
522
505
|
}
|
|
523
506
|
}));
|
|
507
|
+
secretCommand
|
|
508
|
+
.command('value')
|
|
509
|
+
.description('get the values of a secret')
|
|
510
|
+
.requiredOption('-n, --name <name>', 'secret name')
|
|
511
|
+
.option('--reveal', 'reveal secret values in table output (values are masked by default)', false)
|
|
512
|
+
.option('-p, --profile <profile>', 'profile to use')
|
|
513
|
+
.option('-r, --region <region>', 'region to use')
|
|
514
|
+
.option('--output <format>', 'output format: json|table')
|
|
515
|
+
.action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
|
|
516
|
+
try {
|
|
517
|
+
const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
|
|
518
|
+
const output = (0, helpers_1.resolveOutputFormat)(options, command);
|
|
519
|
+
let secretString;
|
|
520
|
+
try {
|
|
521
|
+
secretString = yield (0, secretsmanager_admin_1.getSecretString)({
|
|
522
|
+
name: options.name,
|
|
523
|
+
profile,
|
|
524
|
+
region
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
529
|
+
if (msg.includes('cannot be edited with append/remove')) {
|
|
530
|
+
throw new Error(`Secret "${options.name}" is stored as binary and cannot be displayed as text.`);
|
|
531
|
+
}
|
|
532
|
+
throw error;
|
|
533
|
+
}
|
|
534
|
+
let jsonEntries;
|
|
535
|
+
try {
|
|
536
|
+
const parsed = JSON.parse(secretString);
|
|
537
|
+
if (parsed && !Array.isArray(parsed) && typeof parsed === 'object') {
|
|
538
|
+
jsonEntries = Object.entries(parsed).map(([key, value]) => ({ key, value }));
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
jsonEntries = [{ key: options.name, value: secretString }];
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
catch (_a) {
|
|
545
|
+
jsonEntries = [{ key: options.name, value: secretString }];
|
|
546
|
+
}
|
|
547
|
+
if (output === 'json') {
|
|
548
|
+
if (process.stdout.isTTY) {
|
|
549
|
+
// eslint-disable-next-line no-console
|
|
550
|
+
console.error('Warning: displaying sensitive secret values.');
|
|
551
|
+
}
|
|
552
|
+
const result = Object.fromEntries(jsonEntries.map(({ key, value }) => [key, value]));
|
|
553
|
+
// eslint-disable-next-line no-console
|
|
554
|
+
console.log(JSON.stringify(result, null, 2));
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (options.reveal) {
|
|
558
|
+
// eslint-disable-next-line no-console
|
|
559
|
+
console.error('Warning: displaying sensitive secret values.');
|
|
560
|
+
}
|
|
561
|
+
const rows = jsonEntries.map(({ key, value }) => ({
|
|
562
|
+
key,
|
|
563
|
+
value: options.reveal
|
|
564
|
+
? typeof value === 'string'
|
|
565
|
+
? value
|
|
566
|
+
: JSON.stringify(value)
|
|
567
|
+
: '****'
|
|
568
|
+
}));
|
|
569
|
+
(0, helpers_1.printData)((0, helpers_1.asOutputFormat)(output), [
|
|
570
|
+
{ key: 'key', label: 'Key' },
|
|
571
|
+
{ key: 'value', label: 'Value' }
|
|
572
|
+
], rows);
|
|
573
|
+
}
|
|
574
|
+
catch (error) {
|
|
575
|
+
exitWithError(error);
|
|
576
|
+
}
|
|
577
|
+
}));
|
|
524
578
|
secretCommand
|
|
525
579
|
.command('delete')
|
|
526
580
|
.description('delete a secret in AWS Secrets Manager')
|
|
@@ -532,13 +586,9 @@ secretCommand
|
|
|
532
586
|
.option('-r, --region <region>', 'region to use')
|
|
533
587
|
.option('--output <format>', 'output format: json|table')
|
|
534
588
|
.action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
|
|
535
|
-
var _h;
|
|
536
589
|
try {
|
|
537
590
|
const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
|
|
538
|
-
const
|
|
539
|
-
const output = (_h = options.output) !== null && _h !== void 0 ? _h : (typeof globalOptions.output === 'string'
|
|
540
|
-
? globalOptions.output
|
|
541
|
-
: 'table');
|
|
591
|
+
const output = (0, helpers_1.resolveOutputFormat)(options, command);
|
|
542
592
|
if (!options.yes) {
|
|
543
593
|
throw new Error('Delete requires --yes confirmation.');
|
|
544
594
|
}
|