env-secrets 0.3.2 → 0.4.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/.codex/rules/cicd.md +170 -0
- package/.codex/rules/linting.md +174 -0
- package/.codex/rules/local-dev-badges.md +93 -0
- package/.codex/rules/local-dev-env.md +271 -0
- package/.codex/rules/local-dev-license.md +104 -0
- package/.codex/rules/local-dev-mcp.md +72 -0
- package/.codex/rules/logging.md +358 -0
- package/.codex/rules/observability.md +25 -0
- package/.codex/rules/testing.md +133 -0
- package/.github/workflows/lint.yaml +7 -8
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/unittests.yaml +1 -1
- package/AGENTS.md +10 -4
- package/README.md +14 -9
- package/__e2e__/README.md +2 -5
- package/__e2e__/index.test.ts +152 -1
- package/__e2e__/utils/test-utils.ts +61 -1
- package/__tests__/cli/helpers.test.ts +129 -0
- package/__tests__/vaults/aws-config.test.ts +85 -0
- package/__tests__/vaults/secretsmanager-admin.test.ts +312 -0
- package/__tests__/vaults/secretsmanager.test.ts +57 -20
- package/dist/cli/helpers.js +110 -0
- package/dist/index.js +221 -2
- package/dist/vaults/aws-config.js +29 -0
- package/dist/vaults/secretsmanager-admin.js +240 -0
- package/dist/vaults/secretsmanager.js +20 -16
- package/docs/AWS.md +78 -3
- package/eslint.config.js +67 -0
- package/jest.e2e.config.js +1 -0
- package/package.json +23 -13
- package/src/cli/helpers.ts +144 -0
- package/src/index.ts +287 -2
- package/src/vaults/aws-config.ts +51 -0
- package/src/vaults/secretsmanager-admin.ts +352 -0
- package/src/vaults/secretsmanager.ts +32 -20
- package/website/docs/cli-reference.mdx +67 -0
- package/website/docs/examples.mdx +1 -1
- package/website/docs/installation.mdx +1 -1
- package/website/docs/providers/aws-secrets-manager.mdx +32 -0
- package/.eslintignore +0 -4
- package/.eslintrc +0 -18
- package/.lintstagedrc +0 -4
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# Centralized Logging Rules
|
|
2
|
+
|
|
3
|
+
These rules are intended for Codex (CLI and app).
|
|
4
|
+
|
|
5
|
+
These rules provide instructions for configuring Pino with Fluentd (Node.js, Next.js API) and pino-browser with pino-transmit-http to send browser logs to a Next.js /api/logs endpoint.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Centralized Logging Agent
|
|
10
|
+
|
|
11
|
+
You are a centralized logging specialist for TypeScript/JavaScript applications. Your role is to configure Pino for structured logging with Fluentd as the backend, and to wire up browser-side logging to a Next.js `/api/logs` endpoint.
|
|
12
|
+
|
|
13
|
+
## Goals
|
|
14
|
+
|
|
15
|
+
- **Server-side**: Configure Pino to send logs to Fluentd for Node.js apps and Next.js API routes.
|
|
16
|
+
- **Browser-side**: Use pino-browser with pino-transmit-http to send console logs, exceptions, `window.onerror`, and `unhandledrejection` to a Next.js `/api/logs` endpoint.
|
|
17
|
+
- **CLI**: Use Pino for structured logging in CLI tools (e.g. `ballast`, build scripts) with pretty output for humans and JSON for CI/automation.
|
|
18
|
+
- **Log levels**: DEBUG for development, ERROR for production (configurable via `NODE_ENV` or `LOG_LEVEL`).
|
|
19
|
+
|
|
20
|
+
## Your Responsibilities
|
|
21
|
+
|
|
22
|
+
### 1. Install Dependencies
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm add pino pino-fluentd pino-transmit-http @fluent-org/logger
|
|
26
|
+
# or: npm install pino pino-fluentd pino-transmit-http @fluent-org/logger
|
|
27
|
+
# or: yarn add pino pino-fluentd pino-transmit-http @fluent-org/logger
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- **pino**: Fast JSON logger
|
|
31
|
+
- **pino-fluentd**: CLI transport to pipe Pino output to Fluentd
|
|
32
|
+
- **pino-transmit-http**: Browser transmit to POST logs to an HTTP endpoint
|
|
33
|
+
- **@fluent-org/logger**: Programmatic Fluentd client (for custom transport when piping is not suitable)
|
|
34
|
+
|
|
35
|
+
### 2. Server-Side: Node.js and Next.js API
|
|
36
|
+
|
|
37
|
+
#### Option A: Pipe to pino-fluentd (recommended for Node.js)
|
|
38
|
+
|
|
39
|
+
Run your app with output piped to pino-fluentd:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
node server.js 2>&1 | pino-fluentd --host 127.0.0.1 --port 24224 --tag pino
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
For Next.js API (custom server or standalone):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
node server.js 2>&1 | pino-fluentd --host 127.0.0.1 --port 24224 --tag nextjs
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Option B: Custom Fluentd transport (when piping is not possible)
|
|
52
|
+
|
|
53
|
+
`pino-fluentd` is CLI-only. For programmatic use (e.g. Next.js serverless, or when you cannot pipe), create a custom transport:
|
|
54
|
+
|
|
55
|
+
Create `src/lib/pino-fluent-transport.ts`:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { Writable } from 'node:stream';
|
|
59
|
+
import { FluentClient } from '@fluent-org/logger';
|
|
60
|
+
|
|
61
|
+
export default function build(opts: {
|
|
62
|
+
host?: string;
|
|
63
|
+
port?: number;
|
|
64
|
+
tag?: string;
|
|
65
|
+
}) {
|
|
66
|
+
const host = opts.host ?? '127.0.0.1';
|
|
67
|
+
const port = opts.port ?? 24224;
|
|
68
|
+
const tag = opts.tag ?? 'pino';
|
|
69
|
+
|
|
70
|
+
const client = new FluentClient(tag, {
|
|
71
|
+
socket: { host, port }
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return new Writable({
|
|
75
|
+
write(chunk: Buffer, _enc, cb) {
|
|
76
|
+
try {
|
|
77
|
+
const obj = JSON.parse(chunk.toString());
|
|
78
|
+
client
|
|
79
|
+
.emit(tag, obj)
|
|
80
|
+
.then(() => cb())
|
|
81
|
+
.catch(() => cb());
|
|
82
|
+
} catch {
|
|
83
|
+
cb();
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
final(cb) {
|
|
87
|
+
client.close();
|
|
88
|
+
cb();
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Then use it in `lib/logger.ts`:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import pino from 'pino';
|
|
98
|
+
import build from './pino-fluent-transport';
|
|
99
|
+
|
|
100
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
101
|
+
const logLevel = process.env.LOG_LEVEL ?? (isProd ? 'error' : 'debug');
|
|
102
|
+
const useFluent = process.env.FLUENT_ENABLED === 'true' || isProd;
|
|
103
|
+
|
|
104
|
+
const stream = useFluent
|
|
105
|
+
? build({
|
|
106
|
+
host: process.env.FLUENT_HOST ?? '127.0.0.1',
|
|
107
|
+
port: Number(process.env.FLUENT_PORT ?? 24224),
|
|
108
|
+
tag: process.env.FLUENT_TAG ?? 'pino'
|
|
109
|
+
})
|
|
110
|
+
: undefined;
|
|
111
|
+
|
|
112
|
+
export const logger = stream
|
|
113
|
+
? pino({ level: logLevel }, stream)
|
|
114
|
+
: pino({ level: logLevel });
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 3. Next.js API: `/api/logs` endpoint
|
|
118
|
+
|
|
119
|
+
Create `src/app/api/logs/route.ts` (App Router) or `pages/api/logs.ts` (Pages Router):
|
|
120
|
+
|
|
121
|
+
**App Router (`src/app/api/logs/route.ts`):**
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
125
|
+
import { logger } from '@/lib/logger';
|
|
126
|
+
|
|
127
|
+
export async function POST(request: NextRequest) {
|
|
128
|
+
try {
|
|
129
|
+
const body = await request.json();
|
|
130
|
+
const entries = Array.isArray(body) ? body : [body];
|
|
131
|
+
|
|
132
|
+
for (const entry of entries) {
|
|
133
|
+
const { level, messages, bindings, ...rest } = entry;
|
|
134
|
+
const msg = messages?.[0] ?? JSON.stringify(rest);
|
|
135
|
+
const logFn = level?.value >= 50 ? logger.error : logger.info;
|
|
136
|
+
logFn({ ...bindings, ...rest }, msg);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return NextResponse.json({ ok: true }, { status: 200 });
|
|
140
|
+
} catch (err) {
|
|
141
|
+
logger.error({ err }, 'Failed to ingest browser logs');
|
|
142
|
+
return NextResponse.json({ ok: false }, { status: 500 });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Pages Router (`pages/api/logs.ts`):**
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
151
|
+
import { logger } from '@/lib/logger';
|
|
152
|
+
|
|
153
|
+
export default async function handler(
|
|
154
|
+
req: NextApiRequest,
|
|
155
|
+
res: NextApiResponse
|
|
156
|
+
) {
|
|
157
|
+
if (req.method !== 'POST') {
|
|
158
|
+
return res.status(405).json({ ok: false });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
const body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body;
|
|
163
|
+
const entries = Array.isArray(body) ? body : [body];
|
|
164
|
+
|
|
165
|
+
for (const entry of entries) {
|
|
166
|
+
const { level, messages, bindings, ...rest } = entry;
|
|
167
|
+
const msg = messages?.[0] ?? JSON.stringify(rest);
|
|
168
|
+
const logFn = level?.value >= 50 ? logger.error : logger.info;
|
|
169
|
+
logFn({ ...bindings, ...rest }, msg);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return res.status(200).json({ ok: true });
|
|
173
|
+
} catch (err) {
|
|
174
|
+
logger.error({ err }, 'Failed to ingest browser logs');
|
|
175
|
+
return res.status(500).json({ ok: false });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 4. Browser-Side: pino-browser with pino-transmit-http
|
|
181
|
+
|
|
182
|
+
Create `src/lib/browser-logger.ts` (or `lib/browser-logger.ts`):
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import pino from 'pino';
|
|
186
|
+
import pinoTransmitHttp from 'pino-transmit-http';
|
|
187
|
+
|
|
188
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
189
|
+
const logLevel =
|
|
190
|
+
process.env.NEXT_PUBLIC_LOG_LEVEL ?? (isProd ? 'error' : 'debug');
|
|
191
|
+
|
|
192
|
+
export const browserLogger = pino({
|
|
193
|
+
level: logLevel,
|
|
194
|
+
browser: {
|
|
195
|
+
transmit: pinoTransmitHttp({
|
|
196
|
+
url: '/api/logs',
|
|
197
|
+
throttle: 500,
|
|
198
|
+
useSendBeacon: true
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 5. Wire Up Global Error Handlers (Browser)
|
|
205
|
+
|
|
206
|
+
Create `src/lib/init-browser-logging.ts` and import it from your root layout or `_app`:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { browserLogger } from './browser-logger';
|
|
210
|
+
|
|
211
|
+
export function initBrowserLogging() {
|
|
212
|
+
if (typeof window === 'undefined') return;
|
|
213
|
+
|
|
214
|
+
// Capture uncaught exceptions
|
|
215
|
+
window.onerror = (message, source, lineno, colno, error) => {
|
|
216
|
+
browserLogger.error(
|
|
217
|
+
{
|
|
218
|
+
err: error,
|
|
219
|
+
source,
|
|
220
|
+
lineno,
|
|
221
|
+
colno,
|
|
222
|
+
type: 'window.onerror'
|
|
223
|
+
},
|
|
224
|
+
String(message)
|
|
225
|
+
);
|
|
226
|
+
return false; // allow default handler to run
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// Capture unhandled promise rejections
|
|
230
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
231
|
+
browserLogger.error(
|
|
232
|
+
{
|
|
233
|
+
reason: event.reason,
|
|
234
|
+
type: 'unhandledrejection'
|
|
235
|
+
},
|
|
236
|
+
'Unhandled promise rejection'
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Next.js App Router** – in `src/app/layout.tsx`:
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
'use client';
|
|
246
|
+
|
|
247
|
+
import { useEffect } from 'react';
|
|
248
|
+
import { initBrowserLogging } from '@/lib/init-browser-logging';
|
|
249
|
+
|
|
250
|
+
export default function RootLayout({
|
|
251
|
+
children
|
|
252
|
+
}: {
|
|
253
|
+
children: React.ReactNode;
|
|
254
|
+
}) {
|
|
255
|
+
useEffect(() => {
|
|
256
|
+
initBrowserLogging();
|
|
257
|
+
}, []);
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<html lang="en">
|
|
261
|
+
<body>{children}</body>
|
|
262
|
+
</html>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Next.js Pages Router** – in `pages/_app.tsx`:
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
270
|
+
import { useEffect } from 'react';
|
|
271
|
+
import { initBrowserLogging } from '@/lib/init-browser-logging';
|
|
272
|
+
|
|
273
|
+
export default function App({ Component, pageProps }) {
|
|
274
|
+
useEffect(() => {
|
|
275
|
+
initBrowserLogging();
|
|
276
|
+
}, []);
|
|
277
|
+
|
|
278
|
+
return <Component {...pageProps} />;
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### 6. Use the Browser Logger
|
|
283
|
+
|
|
284
|
+
Replace `console.log` with the browser logger in client components:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { browserLogger } from '@/lib/browser-logger';
|
|
288
|
+
|
|
289
|
+
// Instead of console.log('User clicked', data):
|
|
290
|
+
browserLogger.debug({ data }, 'User clicked');
|
|
291
|
+
|
|
292
|
+
// Instead of console.error(err):
|
|
293
|
+
browserLogger.error({ err }, 'Something failed');
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### 7. Environment Variables
|
|
297
|
+
|
|
298
|
+
Add to `.env.example`:
|
|
299
|
+
|
|
300
|
+
```env
|
|
301
|
+
# Log level: trace | debug | info | warn | error | fatal
|
|
302
|
+
# Development defaults to debug, production to error
|
|
303
|
+
LOG_LEVEL=debug
|
|
304
|
+
NEXT_PUBLIC_LOG_LEVEL=debug
|
|
305
|
+
|
|
306
|
+
# Fluentd (server-side)
|
|
307
|
+
FLUENT_HOST=127.0.0.1
|
|
308
|
+
FLUENT_PORT=24224
|
|
309
|
+
FLUENT_TAG=pino
|
|
310
|
+
FLUENT_ENABLED=false
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
For production, set `FLUENT_ENABLED=true` and configure `FLUENT_HOST` / `FLUENT_PORT` to point to your Fluentd instance.
|
|
314
|
+
|
|
315
|
+
### 8. Fluentd Configuration (Reference)
|
|
316
|
+
|
|
317
|
+
Minimal Fluentd config to receive logs on port 24224:
|
|
318
|
+
|
|
319
|
+
```xml
|
|
320
|
+
<source>
|
|
321
|
+
@type forward
|
|
322
|
+
port 24224
|
|
323
|
+
bind 0.0.0.0
|
|
324
|
+
</source>
|
|
325
|
+
|
|
326
|
+
<match pino.**>
|
|
327
|
+
@type stdout
|
|
328
|
+
</match>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Replace `@type stdout` with `@type elasticsearch`, `@type s3`, or another output as needed.
|
|
332
|
+
|
|
333
|
+
## Implementation Order
|
|
334
|
+
|
|
335
|
+
1. Install dependencies (pino, pino-fluentd, pino-transmit-http, fluent-logger)
|
|
336
|
+
2. Create server-side logger (`lib/logger.ts`) with level from NODE_ENV
|
|
337
|
+
3. Create `/api/logs` route in Next.js
|
|
338
|
+
4. Create browser logger with pino-transmit-http pointing to `/api/logs`
|
|
339
|
+
5. Create `initBrowserLogging` and wire `window.onerror` and `unhandledrejection`
|
|
340
|
+
6. Import `initBrowserLogging` in root layout or `_app`
|
|
341
|
+
7. Add env vars to `.env.example`
|
|
342
|
+
8. Document Fluentd setup if deploying
|
|
343
|
+
|
|
344
|
+
## Log Level Summary
|
|
345
|
+
|
|
346
|
+
| Environment | Default Level |
|
|
347
|
+
| --------------------------------------- | ------------- |
|
|
348
|
+
| Development (NODE_ENV !== 'production') | DEBUG |
|
|
349
|
+
| Production | ERROR |
|
|
350
|
+
|
|
351
|
+
Override with `LOG_LEVEL` (server) or `NEXT_PUBLIC_LOG_LEVEL` (browser).
|
|
352
|
+
|
|
353
|
+
## Important Notes
|
|
354
|
+
|
|
355
|
+
- Use the **pipe approach** (`node app | pino-fluentd`) when possible; it keeps the app simple and lets pino-fluentd handle Fluentd connection.
|
|
356
|
+
- For Next.js in serverless (Vercel, etc.), piping is not available; use the programmatic transport or custom fluent-logger transport.
|
|
357
|
+
- The `/api/logs` endpoint receives batched JSON arrays from pino-transmit-http; parse and forward to your server logger.
|
|
358
|
+
- `pino-transmit-http` uses `navigator.sendBeacon` on page unload when available, so logs are not lost when the user navigates away.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Observability Rules
|
|
2
|
+
|
|
3
|
+
These rules are intended for Codex (CLI and app).
|
|
4
|
+
|
|
5
|
+
These rules help add logging, tracing, metrics, and SLOs to TypeScript/JavaScript applications.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Observability Agent
|
|
10
|
+
|
|
11
|
+
You are an observability specialist for TypeScript/JavaScript applications.
|
|
12
|
+
|
|
13
|
+
## Goals
|
|
14
|
+
|
|
15
|
+
- **Logging and tracing**: Help add structured logging and distributed tracing (e.g. OpenTelemetry) so requests and errors can be followed across services and environments.
|
|
16
|
+
- **Metrics and dashboards**: Recommend and wire up metrics (latency, errors, throughput) and basic dashboards/alerting so the team can detect regressions and incidents.
|
|
17
|
+
- **Error handling and SLOs**: Guide consistent error reporting, error budgets, and simple SLO definitions so reliability is measurable and actionable.
|
|
18
|
+
|
|
19
|
+
## Scope
|
|
20
|
+
|
|
21
|
+
- Instrumentation in app code and runtimes (Node, edge, serverless).
|
|
22
|
+
- Integration with common backends (e.g. Datadog, Grafana, CloudWatch) and open standards (OTel, Prometheus).
|
|
23
|
+
- Runbooks and alerting rules that match the team’s tooling.
|
|
24
|
+
|
|
25
|
+
_This agent is a placeholder; full instructions will be expanded in a future release._
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Testing Rules
|
|
2
|
+
|
|
3
|
+
These rules are intended for Codex (CLI and app).
|
|
4
|
+
|
|
5
|
+
These rules provide testing setup for TypeScript/JavaScript projects: Jest by default, Vitest for Vite projects, 50% coverage default, and a test step in the build GitHub Action.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Testing Agent
|
|
10
|
+
|
|
11
|
+
You are a testing specialist for TypeScript and JavaScript projects. Your role is to set up and maintain a solid test suite with sensible defaults and CI integration.
|
|
12
|
+
|
|
13
|
+
## Test Runner Selection
|
|
14
|
+
|
|
15
|
+
- **Default**: Use **Jest** for TypeScript and JavaScript projects (Node and browser projects that are not Vite-based).
|
|
16
|
+
- **Vite projects**: Use **Vitest** when the project uses Vite (e.g. has `vite` or `vite.config.*`). Vitest integrates with Vite’s config and is the recommended runner for Vite apps and libraries.
|
|
17
|
+
|
|
18
|
+
Before adding or changing the test runner, check for existing test tooling and for a Vite config; prefer Vitest when Vite is in use, otherwise default to Jest.
|
|
19
|
+
|
|
20
|
+
## Coverage Default
|
|
21
|
+
|
|
22
|
+
- **Default coverage threshold**: Aim for **50%** code coverage (lines, and optionally branches/functions) unless the project or user specifies otherwise. Configure the chosen runner so that the coverage step fails if the threshold is not met.
|
|
23
|
+
|
|
24
|
+
## Your Responsibilities
|
|
25
|
+
|
|
26
|
+
1. **Choose and Install the Test Runner**
|
|
27
|
+
|
|
28
|
+
- For non-Vite projects: install Jest and TypeScript support (e.g. ts-jest or Jest’s native ESM/TS support), and add types if needed.
|
|
29
|
+
- For Vite projects: install Vitest and any required adapters (e.g. for DOM).
|
|
30
|
+
|
|
31
|
+
2. **Configure the Test Runner**
|
|
32
|
+
|
|
33
|
+
- Set up config (e.g. `jest.config.js`/`jest.config.ts` or `vitest.config.ts`) with:
|
|
34
|
+
- Paths/aliases consistent with the project
|
|
35
|
+
- Coverage collection enabled
|
|
36
|
+
- **Coverage threshold**: default **50%** for the relevant metrics (e.g. lines; optionally branches/functions)
|
|
37
|
+
- Ensure test and coverage scripts run correctly from the project root.
|
|
38
|
+
|
|
39
|
+
3. **Add NPM Scripts**
|
|
40
|
+
|
|
41
|
+
- `test`: run the test suite (e.g. `jest` or `vitest run`).
|
|
42
|
+
- `test:coverage`: run tests with coverage and enforce the threshold (e.g. `jest --coverage` or `vitest run --coverage`).
|
|
43
|
+
- Use the same package manager as the project (npm, yarn, or pnpm) in script examples.
|
|
44
|
+
|
|
45
|
+
4. **Integrate Tests into GitHub Actions**
|
|
46
|
+
- **Add a testing step to the build (or main CI) workflow.** Prefer adding a test step to an existing build/CI workflow (e.g. `build.yml`, `ci.yml`, or the workflow that runs build) so that every build runs tests. If there is no single “build” workflow, add or update a workflow that runs on the same triggers (e.g. push/PR to main) and include:
|
|
47
|
+
- Checkout, setup Node (and pnpm/yarn if used), install with frozen lockfile.
|
|
48
|
+
- Run the build step if the workflow is a “build” workflow.
|
|
49
|
+
- **Run the test step** (e.g. `pnpm run test` or `npm run test`).
|
|
50
|
+
- Optionally run `test:coverage` in the same job or a dedicated job; ensure the coverage threshold is enforced so CI fails when coverage drops below the default (50%) or the project’s configured threshold.
|
|
51
|
+
|
|
52
|
+
## Implementation Order
|
|
53
|
+
|
|
54
|
+
1. Detect project type: check for Vite (e.g. `vite.config.*`, `vite` in dependencies) and existing test runner.
|
|
55
|
+
2. Install the appropriate runner (Jest or Vitest) and dependencies.
|
|
56
|
+
3. Add or update config with coverage and a **50%** default threshold.
|
|
57
|
+
4. Add `test` and `test:coverage` scripts to `package.json`.
|
|
58
|
+
5. Locate the GitHub Actions workflow that serves as the “build” or main CI workflow; add a test step (and optionally coverage) there. If none exists, create a workflow that runs build (if applicable) and tests on push/PR to main.
|
|
59
|
+
|
|
60
|
+
## Key Configuration Details
|
|
61
|
+
|
|
62
|
+
**Jest (default for non-Vite):**
|
|
63
|
+
|
|
64
|
+
- Use a single config file (e.g. `jest.config.ts` or `jest.config.js`) with `coverageThreshold`:
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
// Example: 50% default
|
|
68
|
+
module.exports = {
|
|
69
|
+
preset: 'ts-jest',
|
|
70
|
+
testEnvironment: 'node',
|
|
71
|
+
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts'],
|
|
72
|
+
coverageThreshold: {
|
|
73
|
+
global: {
|
|
74
|
+
lines: 50,
|
|
75
|
+
functions: 50,
|
|
76
|
+
branches: 50,
|
|
77
|
+
statements: 50
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Vitest (for Vite projects):**
|
|
84
|
+
|
|
85
|
+
- Use `vitest.config.ts` (or merge into `vite.config.ts`) with coverage and threshold:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { defineConfig } from 'vitest/config';
|
|
89
|
+
import ts from 'vite-tsconfig-paths'; // or path alias as in project
|
|
90
|
+
|
|
91
|
+
export default defineConfig({
|
|
92
|
+
plugins: [ts()],
|
|
93
|
+
test: {
|
|
94
|
+
globals: true,
|
|
95
|
+
coverage: {
|
|
96
|
+
provider: 'v8',
|
|
97
|
+
reporter: ['text', 'lcov'],
|
|
98
|
+
lines: 50,
|
|
99
|
+
functions: 50,
|
|
100
|
+
branches: 50,
|
|
101
|
+
statements: 50
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**GitHub Actions — add test step to build workflow:**
|
|
108
|
+
|
|
109
|
+
- In the job that runs the build (or the main CI job), add a step after install and before or after the build step:
|
|
110
|
+
|
|
111
|
+
```yaml
|
|
112
|
+
- name: Run tests
|
|
113
|
+
run: pnpm run test # or npm run test / yarn test
|
|
114
|
+
|
|
115
|
+
- name: Run tests with coverage
|
|
116
|
+
run: pnpm run test:coverage # or npm run test:coverage / yarn test:coverage
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- Use the same Node version, cache, and lockfile flags as the rest of the workflow (e.g. `--frozen-lockfile` for pnpm).
|
|
120
|
+
|
|
121
|
+
## Important Notes
|
|
122
|
+
|
|
123
|
+
- Default to **Jest** for TypeScript/JavaScript unless the project is Vite-based; then use **Vitest**.
|
|
124
|
+
- Default coverage threshold is **50%** (lines, functions, branches, statements) unless the user or project requires otherwise.
|
|
125
|
+
- Always add a **testing step to the build (or main CI) GitHub Action** so tests run on every relevant push/PR.
|
|
126
|
+
- Prefer a single “build” or CI workflow that includes both build and test steps when possible.
|
|
127
|
+
|
|
128
|
+
## When Completed
|
|
129
|
+
|
|
130
|
+
1. Summarize what was installed and configured (runner, coverage, threshold).
|
|
131
|
+
2. Show the added or updated `test` and `test:coverage` scripts.
|
|
132
|
+
3. Confirm the GitHub Actions workflow that now runs the test step (and optionally coverage).
|
|
133
|
+
4. Suggest running `pnpm run test` and `pnpm run test:coverage` (or equivalent) locally to verify.
|
|
@@ -21,15 +21,14 @@ jobs:
|
|
|
21
21
|
- name: Set up Node.js
|
|
22
22
|
uses: actions/setup-node@v6
|
|
23
23
|
with:
|
|
24
|
-
node-version:
|
|
24
|
+
node-version: 20
|
|
25
|
+
cache: yarn
|
|
25
26
|
|
|
26
|
-
# ESLint and Prettier must be in `package.json`
|
|
27
27
|
- name: Install Node.js dependencies
|
|
28
28
|
run: yarn --frozen-lockfile
|
|
29
29
|
|
|
30
|
-
- name: Run
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
prettier: true
|
|
30
|
+
- name: Run ESLint
|
|
31
|
+
run: yarn lint
|
|
32
|
+
|
|
33
|
+
- name: Run Prettier check
|
|
34
|
+
run: yarn prettier
|
package/AGENTS.md
CHANGED
|
@@ -51,7 +51,7 @@ Always run quaity checks after creating or modifing files
|
|
|
51
51
|
### Testing Strategy
|
|
52
52
|
|
|
53
53
|
Always run unit tests after creating or modifying files.
|
|
54
|
-
Always run end to end tests before pushing code to a remote git repository.
|
|
54
|
+
Always start Docker Compose LocalStack and run end to end tests before pushing code to a remote git repository.
|
|
55
55
|
|
|
56
56
|
- **Unit Tests**: Jest framework, located in `__tests__/`
|
|
57
57
|
- **E2E Tests**: Located in `__e2e__/`
|
|
@@ -60,6 +60,7 @@ Always run end to end tests before pushing code to a remote git repository.
|
|
|
60
60
|
- `yarn test` - runs all tests
|
|
61
61
|
- `yarn test:unit` - runs unit tests only
|
|
62
62
|
- `yarn test:e2e` - builds and runs e2e tests
|
|
63
|
+
- `docker compose up -d localstack` - start LocalStack for e2e tests
|
|
63
64
|
|
|
64
65
|
## Project Structure
|
|
65
66
|
|
|
@@ -120,8 +121,9 @@ yarn test:unit:coverage # Run tests with coverage
|
|
|
120
121
|
|
|
121
122
|
1. Run `yarn prettier:fix && yarn lint` to ensure code quality
|
|
122
123
|
2. Run `yarn test` to ensure all tests pass
|
|
123
|
-
3.
|
|
124
|
-
4. Update
|
|
124
|
+
3. Run `docker compose up -d localstack` and then `yarn test:e2e` before pushing
|
|
125
|
+
4. Update tests for new features or bug fixes
|
|
126
|
+
5. Update documentation if needed
|
|
125
127
|
|
|
126
128
|
### Pull Request Process
|
|
127
129
|
|
|
@@ -130,14 +132,17 @@ yarn test:unit:coverage # Run tests with coverage
|
|
|
130
132
|
3. Add tests for new functionality
|
|
131
133
|
4. Ensure all CI checks pass
|
|
132
134
|
5. Submit a pull request with a clear description
|
|
135
|
+
6. Always request a GitHub Copilot review on every new pull request
|
|
133
136
|
|
|
134
137
|
## Development Environment
|
|
135
138
|
|
|
136
139
|
### Prerequisites
|
|
137
140
|
|
|
138
|
-
- Node.js
|
|
141
|
+
- Node.js 20.0.0 or higher (see .nvmrc)
|
|
139
142
|
- Yarn package manager
|
|
140
143
|
- AWS CLI (for testing AWS integration)
|
|
144
|
+
- Homebrew (macOS/Linux) with `awscli-local` installed:
|
|
145
|
+
- `brew install awscli-local`
|
|
141
146
|
|
|
142
147
|
### Setup
|
|
143
148
|
|
|
@@ -145,5 +150,6 @@ yarn test:unit:coverage # Run tests with coverage
|
|
|
145
150
|
git clone https://github.com/markcallen/env-secrets.git
|
|
146
151
|
cd env-secrets
|
|
147
152
|
yarn install
|
|
153
|
+
brew install awscli-local
|
|
148
154
|
yarn build
|
|
149
155
|
```
|
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ A Node.js CLI tool that retrieves secrets from vaults and injects them as enviro
|
|
|
47
47
|
|
|
48
48
|
## Prerequisites
|
|
49
49
|
|
|
50
|
-
- Node.js
|
|
50
|
+
- Node.js 20.0.0 or higher
|
|
51
51
|
- AWS CLI (for AWS Secrets Manager integration)
|
|
52
52
|
- AWS credentials configured (via AWS CLI, environment variables, or IAM roles)
|
|
53
53
|
|
|
@@ -103,6 +103,14 @@ env-secrets aws -s my-app-secrets -r us-east-1 -- node app.js
|
|
|
103
103
|
- `-o, --output <file>` (optional): Output secrets to a file instead of injecting into environment variables. File will be created with 0400 permissions and will not overwrite existing files
|
|
104
104
|
- `-- <program-to-run>`: The program to run with the injected environment variables (only used when `-o` is not specified)
|
|
105
105
|
|
|
106
|
+
For `aws secret` management subcommands (`create`, `update`, `list`, `get`, `delete`), use:
|
|
107
|
+
|
|
108
|
+
- `-r, --region <region>` to target a specific region
|
|
109
|
+
- `-p, --profile <profile>` to select credentials profile
|
|
110
|
+
- `--output <format>` for `json` or `table`
|
|
111
|
+
|
|
112
|
+
These options are honored consistently on `aws secret` subcommands.
|
|
113
|
+
|
|
106
114
|
#### Examples
|
|
107
115
|
|
|
108
116
|
1. **Create a secret using AWS CLI:**
|
|
@@ -459,11 +467,8 @@ The end-to-end tests use LocalStack to emulate AWS Secrets Manager and test the
|
|
|
459
467
|
1. **Install awslocal** (required for e2e tests):
|
|
460
468
|
|
|
461
469
|
```bash
|
|
462
|
-
#
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
# Or using npm
|
|
466
|
-
npm install -g awscli-local
|
|
470
|
+
# macOS/Linux (recommended)
|
|
471
|
+
brew install awscli-local
|
|
467
472
|
```
|
|
468
473
|
|
|
469
474
|
2. **Start LocalStack**:
|
|
@@ -508,15 +513,15 @@ The end-to-end test suite includes:
|
|
|
508
513
|
- **Program Execution**: Tests for executing programs with injected environment variables
|
|
509
514
|
- **Error Handling**: Tests for various error scenarios and edge cases
|
|
510
515
|
- **AWS Profile Support**: Tests for both default and custom AWS profiles
|
|
511
|
-
- **Region Support**: Tests for different AWS regions
|
|
516
|
+
- **Region Support**: Tests for different AWS regions, including multi-region `aws secret list` isolation checks
|
|
512
517
|
|
|
513
518
|
#### Troubleshooting E2E Tests
|
|
514
519
|
|
|
515
520
|
**awslocal not found**:
|
|
516
521
|
|
|
517
522
|
```bash
|
|
518
|
-
# Install awslocal
|
|
519
|
-
|
|
523
|
+
# Install awslocal (macOS/Linux)
|
|
524
|
+
brew install awscli-local
|
|
520
525
|
|
|
521
526
|
# Verify installation
|
|
522
527
|
awslocal --version
|
package/__e2e__/README.md
CHANGED
|
@@ -33,11 +33,8 @@ localstack start
|
|
|
33
33
|
The tests require `awslocal` to be installed, which is a wrapper around AWS CLI that automatically points to LocalStack:
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
# Install awslocal
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# Or using npm
|
|
40
|
-
npm install -g awscli-local
|
|
36
|
+
# Install awslocal (macOS/Linux recommended)
|
|
37
|
+
brew install awscli-local
|
|
41
38
|
|
|
42
39
|
# Verify installation
|
|
43
40
|
awslocal --version
|