@romaintaillandier1978/dotenv-never-lies 0.3.0 → 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/README.md +328 -69
- package/dist/cli/commands/assert.d.ts +4 -2
- package/dist/cli/commands/assert.d.ts.map +1 -1
- package/dist/cli/commands/assert.js +7 -9
- package/dist/cli/commands/explain.d.ts +6 -1
- package/dist/cli/commands/explain.d.ts.map +1 -1
- package/dist/cli/commands/explain.js +9 -16
- package/dist/cli/commands/export.d.ts +34 -0
- package/dist/cli/commands/export.d.ts.map +1 -0
- package/dist/cli/commands/export.js +263 -0
- package/dist/cli/commands/generate.d.ts +7 -2
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +9 -7
- package/dist/cli/commands/reverseEnv.d.ts +8 -2
- package/dist/cli/commands/reverseEnv.d.ts.map +1 -1
- package/dist/cli/commands/reverseEnv.js +14 -7
- package/dist/cli/index.js +265 -78
- package/dist/cli/utils/exitCodes.d.ts +8 -0
- package/dist/cli/utils/exitCodes.d.ts.map +1 -0
- package/dist/cli/utils/exitCodes.js +8 -0
- package/dist/cli/utils/load-schema.d.ts.map +1 -1
- package/dist/cli/utils/load-schema.js +19 -12
- package/dist/cli/utils/resolve-schema.d.ts.map +1 -1
- package/dist/cli/utils/resolve-schema.js +7 -3
- package/dist/cli/utils/toFile.d.ts +2 -0
- package/dist/cli/utils/toFile.d.ts.map +1 -0
- package/dist/cli/utils/toFile.js +8 -0
- package/dist/core.js +2 -2
- package/dist/errors.d.ts +29 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +33 -4
- package/dist/romaintaillandier1978-dotenv-never-lies-0.3.0.tgz +0 -0
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
**dotenv-never-lies** valide, type et documente tes variables d’environnement à partir d’un schéma TypeScript / Zod.
|
|
6
6
|
Il échoue **vite**, **fort**, et **avant la prod**.
|
|
7
7
|
|
|
8
|
-
---
|
|
9
|
-
|
|
10
8
|
## Pourquoi ?
|
|
11
9
|
|
|
12
10
|
Parce que tout ça arrive **tout le temps** :
|
|
@@ -54,7 +52,7 @@ Ce package a un périmètre volontairement **limité**.
|
|
|
54
52
|
Deno, Bun, Cloudflare Workers, edge runtimes : hors scope (pour l’instant).
|
|
55
53
|
|
|
56
54
|
- ❌ **Ce n’est pas un framework de configuration global**
|
|
57
|
-
Il ne gère ni les fichiers YAML/JSON, ni les
|
|
55
|
+
Il ne gère ni les fichiers YAML/JSON, ni les profils dynamiques,
|
|
58
56
|
ni les overrides magiques par environnement.
|
|
59
57
|
|
|
60
58
|
- ❌ **Ce n’est pas permissif**
|
|
@@ -67,30 +65,26 @@ qui préfèrent **échouer proprement au démarrage** plutôt que **bugger silen
|
|
|
67
65
|
|
|
68
66
|
---
|
|
69
67
|
|
|
70
|
-
## Dependency warnings
|
|
71
|
-
|
|
72
|
-
⚠️ Important
|
|
73
|
-
dotenv-never-lies expose des schémas Zod dans son API publique.
|
|
74
|
-
Zod v4 est requis.
|
|
75
|
-
Mélanger les versions cassera l’inférence de types (et oui, ça fait mal).
|
|
76
|
-
|
|
77
68
|
## Installation
|
|
78
69
|
|
|
79
70
|
```bash
|
|
80
|
-
|
|
71
|
+
npm install @romaintaillandier1978/dotenv-never-lies
|
|
81
72
|
# ou
|
|
82
|
-
|
|
73
|
+
yarn add @romaintaillandier1978/dotenv-never-lies
|
|
83
74
|
```
|
|
84
75
|
|
|
85
|
-
##
|
|
76
|
+
## Dépendances et compatibilité
|
|
77
|
+
|
|
78
|
+
**[`zod`](https://www.npmjs.com/package/zod)**, dotenv-never-lies expose des schémas Zod dans son API publique.
|
|
86
79
|
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
⚠️ _Important : Zod **v4.2.1** minimum est requis._
|
|
81
|
+
Utiliser Zod v3 entraînera des erreurs de typage ou d’inférence.
|
|
89
82
|
|
|
90
|
-
|
|
91
|
-
sans duplication ni copier-coller fragile.
|
|
83
|
+
**[`dotenv`](https://www.npmjs.com/package/dotenv)** permet à dotenv-never-lies de gérer automatiquement le parsing des fichiers env
|
|
92
84
|
|
|
93
|
-
|
|
85
|
+
**[`dotenv-expand`](https://www.npmjs.com/package/dotenv-expand)** permet à dotenv-never-lies de gérer automatiquement l’expansion des variables d’environnement. Cela permet de définir des variables composées à partir d’autres variables, sans duplication ni copier-coller fragile.
|
|
86
|
+
|
|
87
|
+
**Exemple**
|
|
94
88
|
|
|
95
89
|
```env
|
|
96
90
|
FRONT_A=https://a.site.com
|
|
@@ -100,13 +94,38 @@ FRONT_C=https://c.site.com
|
|
|
100
94
|
NODE_CORS_ORIGIN="${FRONT_A};${FRONT_B};${FRONT_C}"
|
|
101
95
|
```
|
|
102
96
|
|
|
103
|
-
##
|
|
97
|
+
## Schéma DNL
|
|
98
|
+
|
|
99
|
+
Le schéma DNL est ta nouvelle source de vérité.
|
|
100
|
+
|
|
101
|
+
(`dnl reverse-env` t'aidera à faire le premier squelette)
|
|
102
|
+
|
|
103
|
+
### emplacement du schéma
|
|
104
104
|
|
|
105
|
-
env.dnl.ts
|
|
105
|
+
Recommandé : env.dnl.ts
|
|
106
|
+
|
|
107
|
+
Supporté dans cet ordre pour toutes les commandes CLI :
|
|
108
|
+
|
|
109
|
+
1. --schema path/to/my-dnl.ts
|
|
110
|
+
2. déclaré dans le package.json :
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
...
|
|
115
|
+
"dotenv-never-lies": {
|
|
116
|
+
"schema": "path/to/my-dnl.ts"
|
|
117
|
+
}
|
|
118
|
+
...
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
3. un des "env.dnl.ts", "env.dnl.js", "dnl.config.ts", "dnl.config.js"
|
|
123
|
+
|
|
124
|
+
### définir un schéma
|
|
106
125
|
|
|
107
126
|
```typescript
|
|
108
127
|
import { z } from "zod";
|
|
109
|
-
import { define } from "dotenv-never-lies";
|
|
128
|
+
import { define } from "@romaintaillandier1978/dotenv-never-lies";
|
|
110
129
|
|
|
111
130
|
export default define({
|
|
112
131
|
NODE_ENV: {
|
|
@@ -116,35 +135,14 @@ export default define({
|
|
|
116
135
|
|
|
117
136
|
NODE_PORT: {
|
|
118
137
|
description: "Port de l’API",
|
|
119
|
-
schema: z.coerce.number(),
|
|
120
|
-
},
|
|
121
|
-
|
|
122
|
-
FRONT_A: {
|
|
123
|
-
description: "Mon site A",
|
|
124
|
-
schema: z.url(),
|
|
138
|
+
schema: z.coerce.number().default(3000),
|
|
125
139
|
},
|
|
126
140
|
|
|
127
|
-
|
|
128
|
-
description: "Mon site
|
|
141
|
+
FRONT_URL: {
|
|
142
|
+
description: "Mon site",
|
|
129
143
|
schema: z.url(),
|
|
130
144
|
},
|
|
131
145
|
|
|
132
|
-
FRONT_C: {
|
|
133
|
-
description: "Mon site C",
|
|
134
|
-
schema: z.url(),
|
|
135
|
-
},
|
|
136
|
-
|
|
137
|
-
NODE_CORS_ORIGIN: {
|
|
138
|
-
description: "URLs frontend autorisées à appeler cette API",
|
|
139
|
-
schema: z.string().transform((v) =>
|
|
140
|
-
v
|
|
141
|
-
.split(";")
|
|
142
|
-
.map((s) => s.trim())
|
|
143
|
-
.filter(Boolean)
|
|
144
|
-
.map((url) => z.url().parse(url))
|
|
145
|
-
),
|
|
146
|
-
},
|
|
147
|
-
|
|
148
146
|
JWT_SECRET: {
|
|
149
147
|
description: "JWT Secret",
|
|
150
148
|
schema: z.string(),
|
|
@@ -153,6 +151,50 @@ export default define({
|
|
|
153
151
|
});
|
|
154
152
|
```
|
|
155
153
|
|
|
154
|
+
## Gestion des secrets
|
|
155
|
+
|
|
156
|
+
Rappel : dotenv-never-lies n’est pas un secret manager.
|
|
157
|
+
|
|
158
|
+
### déclaration dans le schéma DNL
|
|
159
|
+
|
|
160
|
+
Une variable est considérée comme secrète _si et seulement si elle est marquée explicitement_ dans le schéma avec `secret: true`. (`secret : undefined` est équivalent à `secret: false`)
|
|
161
|
+
Cette règle est volontairement stricte.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
JWT_SECRET: {
|
|
165
|
+
description: "Clé de signature des JWT",
|
|
166
|
+
schema: z.string(),
|
|
167
|
+
secret: true,
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Secrets et commandes CLI
|
|
172
|
+
|
|
173
|
+
assert : valide les secrets comme n’importe quelle variable
|
|
174
|
+
|
|
175
|
+
reverse-env : lors de la génération du schéma, la commande tente d’identifier automatiquement les variables sensibles (ex. SECRET, KEY, TOKEN, PASSWORD).
|
|
176
|
+
**Cette détection est heuristique et doit toujours être vérifiée et corrigée manuellement.**
|
|
177
|
+
|
|
178
|
+
export : adapte le comportement selon le format cible (env, docker, CI, Kubernetes…). Voir le tableau ci-dessous pour le détail par format.
|
|
179
|
+
|
|
180
|
+
### Lors de l’export
|
|
181
|
+
|
|
182
|
+
Les variables marquées `secret: true` dans le schéma sont traitées différemment selon le format d’export.
|
|
183
|
+
|
|
184
|
+
| Format | Secrets inclus par défaut | Masquables (`--hide-secret`) | Excluables (`--exclude-secret`) | Remarques |
|
|
185
|
+
| ------------- | ------------------------- | ---------------------------- | ------------------------------- | ---------------------------- |
|
|
186
|
+
| env | oui | oui | oui | .env classique |
|
|
187
|
+
| docker-env | oui | oui | oui | Pour --env-file |
|
|
188
|
+
| docker-args | oui | oui | oui | Pour docker run -e |
|
|
189
|
+
| json | oui | oui | oui | Debug / tooling |
|
|
190
|
+
| ts | oui | oui | oui | Export typé |
|
|
191
|
+
| js | oui | oui | oui | Export runtime |
|
|
192
|
+
| github-env | oui | oui | oui | visibles dans les logs |
|
|
193
|
+
| github-secret | secrets uniquement | non | oui | Via gh secret set |
|
|
194
|
+
| gitlab-env | oui | oui | oui | Variables CI GitLab |
|
|
195
|
+
| k8s-configmap | oui | oui | oui | warning si secret non masqué |
|
|
196
|
+
| k8s-secret | secrets uniquement | oui | oui | Kubernetes Secret |
|
|
197
|
+
|
|
156
198
|
## Utilisation runtime
|
|
157
199
|
|
|
158
200
|
```typescript
|
|
@@ -160,20 +202,26 @@ import envDef from "./env.dnl";
|
|
|
160
202
|
|
|
161
203
|
export const ENV = envDef.load();
|
|
162
204
|
|
|
163
|
-
if(
|
|
164
|
-
|
|
205
|
+
// if (process.env.NODE_ENV && process.env.NODE_ENV === "test") {
|
|
206
|
+
if (ENV.NODE_ENV === "test") {
|
|
207
|
+
doAdditionalTest();
|
|
208
|
+
}
|
|
165
209
|
|
|
210
|
+
const server = http.createServer(app);
|
|
211
|
+
//server.listen(process.env.NODE_PORT||3000, () => {
|
|
212
|
+
server.listen(ENV.NODE_PORT, () => {
|
|
213
|
+
console.log(`Server started on ${ENV.NODE_PORT}`);
|
|
214
|
+
});
|
|
166
215
|
```
|
|
167
216
|
|
|
168
217
|
Résultat :
|
|
169
218
|
|
|
170
219
|
- ENV.NODE_ENV est un enum
|
|
171
220
|
- ENV.NODE_PORT est un number
|
|
172
|
-
-
|
|
173
|
-
- ENV.NODE_CORS_ORIGIN est un string[] contenant des URLs valides
|
|
221
|
+
- FRONT_URL est une URL valides
|
|
174
222
|
- ENV.JWT_SECRET est une string
|
|
175
223
|
|
|
176
|
-
Si une variable est absente ou invalide → le process s’arrête immédiatement.
|
|
224
|
+
Si une variable est absente ou invalide → le process s’arrête immédiatement.
|
|
177
225
|
C’est volontaire.
|
|
178
226
|
|
|
179
227
|
## Éviter `process.env` dans le code applicatif
|
|
@@ -187,7 +235,7 @@ Cela garantit :
|
|
|
187
235
|
- des valeurs validées
|
|
188
236
|
- un point d’entrée unique pour la configuration
|
|
189
237
|
|
|
190
|
-
Pour identifier les usages résiduels de `process.env` dans
|
|
238
|
+
Pour identifier les usages résiduels de `process.env` dans ta base de code, un simple outil de recherche suffit :
|
|
191
239
|
|
|
192
240
|
```bash
|
|
193
241
|
grep -R "process\.env" src
|
|
@@ -197,7 +245,7 @@ Le choix de corriger (ou non) ces usages dépend du contexte et reste volontaire
|
|
|
197
245
|
|
|
198
246
|
## CLI
|
|
199
247
|
|
|
200
|
-
Le CLI permet de valider, charger, générer et documenter les variables d’environnement à partir d’un schéma `dotenv-never-lies`.
|
|
248
|
+
Le CLI permet de valider, charger, générer, exporter et documenter les variables d’environnement à partir d’un schéma `dotenv-never-lies`.
|
|
201
249
|
|
|
202
250
|
Il est conçu pour être utilisé :
|
|
203
251
|
|
|
@@ -205,31 +253,36 @@ Il est conçu pour être utilisé :
|
|
|
205
253
|
- en CI (sans surprise)
|
|
206
254
|
- avant que l’application ne démarre (et pas après)
|
|
207
255
|
|
|
208
|
-
###
|
|
256
|
+
### Exit codes
|
|
257
|
+
|
|
258
|
+
`dotenv-never-lies` utilise des codes de sortie explicites, pensés pour la CI :
|
|
259
|
+
|
|
260
|
+
| Code | Signification |
|
|
261
|
+
| ---: | ------------------------------------- |
|
|
262
|
+
| 0 | Succès |
|
|
263
|
+
| 1 | Erreur d'usage ou erreur interne |
|
|
264
|
+
| 2 | Schéma DNL introuvable |
|
|
265
|
+
| 3 | Validation de l'environnement échouée |
|
|
266
|
+
| 4 | Erreur lors de l'export |
|
|
267
|
+
|
|
268
|
+
### assert : Valider un fichier `.env` (CI-friendly)
|
|
209
269
|
|
|
210
270
|
Valide les variables sans les injecter dans `process.env`.
|
|
211
271
|
|
|
212
272
|
```bash
|
|
213
|
-
dnl
|
|
273
|
+
dnl assert --source .env --schema env.dnl.ts
|
|
214
274
|
```
|
|
215
275
|
|
|
276
|
+
Sans --source, `dnl assert` valide `process.env`.
|
|
277
|
+
C'est le mode recommandé lorsque les variables sont injectées par le runtime ou la CI.
|
|
278
|
+
|
|
216
279
|
→ échoue si :
|
|
217
280
|
|
|
218
281
|
- une variable est manquante
|
|
219
282
|
- une valeur est invalide
|
|
220
283
|
- le schéma n’est pas respecté
|
|
221
284
|
|
|
222
|
-
###
|
|
223
|
-
|
|
224
|
-
Charge et valide les variables dans `process.env`.
|
|
225
|
-
|
|
226
|
-
```bash
|
|
227
|
-
dnl load --schema env.dnl.ts
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
Usage typique : scripts de démarrage, tooling local.
|
|
231
|
-
|
|
232
|
-
### Générer un fichier .env à partir du schéma
|
|
285
|
+
### generate : Générer un fichier .env à partir du schéma
|
|
233
286
|
|
|
234
287
|
Génère un .env documenté à partir du schéma.
|
|
235
288
|
|
|
@@ -243,7 +296,7 @@ Utile pour :
|
|
|
243
296
|
- partager un template
|
|
244
297
|
- éviter les .env.example obsolètes
|
|
245
298
|
|
|
246
|
-
### Générer un schéma depuis un .env existant
|
|
299
|
+
### reverse-env : Générer un schéma depuis un .env existant
|
|
247
300
|
|
|
248
301
|
Crée un fichier env.dnl.ts à partir d’un .env.
|
|
249
302
|
|
|
@@ -256,12 +309,12 @@ Utile pour :
|
|
|
256
309
|
- migrer un projet existant
|
|
257
310
|
- documenter a posteriori une configuration legacy
|
|
258
311
|
|
|
259
|
-
### Afficher la documentation des variables
|
|
312
|
+
### explain : Afficher la documentation des variables
|
|
260
313
|
|
|
261
314
|
Affiche la liste des variables connues et leur description.
|
|
262
315
|
|
|
263
316
|
```bash
|
|
264
|
-
dnl
|
|
317
|
+
dnl explain
|
|
265
318
|
```
|
|
266
319
|
|
|
267
320
|
Exemple de sortie :
|
|
@@ -275,10 +328,69 @@ JWT_SECRET: JWT Secret
|
|
|
275
328
|
|
|
276
329
|
```
|
|
277
330
|
|
|
278
|
-
|
|
331
|
+
### export : Exporter les variables vers d’autres formats
|
|
332
|
+
|
|
333
|
+
La commande export permet de transformer les variables validées par le schéma
|
|
334
|
+
en formats directement exploitables par d’autres outils (Docker, CI, Kubernetes, scripts…).
|
|
335
|
+
|
|
336
|
+
Le schéma reste la source de vérité.
|
|
337
|
+
Les valeurs sont validées avant export.
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
dnl export <format>
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Par défaut, les valeurs sont lues depuis process.env.
|
|
344
|
+
Un fichier .env peut être fourni via --source.
|
|
345
|
+
|
|
346
|
+
Exemples :
|
|
347
|
+
Exporter les variables d'environnement au format JSON depuis un fichier .env
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
dnl export json --source .env
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Nettoyer un fichier .env (retirer commentaires et lignes inutiles)
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
dnl export env --source .env --out .env.clean
|
|
357
|
+
dnl export env --source .env --out .env --force
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Exporter les variables au format docker-args
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
dnl export docker-args --source .env
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Résultat :
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
-e "NODE_ENV=production" -e "NODE_PORT=3000"
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Exporter pour GitHub Actions (variables)
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
dnl export github-env
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Résultat :
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
echo "NODE_ENV=production" >> $GITHUB_ENV
|
|
382
|
+
echo "NODE_PORT=3000" >> $GITHUB_ENV
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Il existe encore quelques autres formats et options (voir la doc CLI `dnl export --help`)
|
|
279
386
|
|
|
280
387
|
## Usages dans la vraie vie
|
|
281
388
|
|
|
389
|
+
### GitIgnore
|
|
390
|
+
|
|
391
|
+
dotenv-never-lies crée des fichiers temporaires dans ton répertoire projet.
|
|
392
|
+
Ajoute `.dnl/` à ton `.gitignore`.
|
|
393
|
+
|
|
282
394
|
### Git
|
|
283
395
|
|
|
284
396
|
#### Git hooks recommandés
|
|
@@ -317,3 +429,150 @@ EOF
|
|
|
317
429
|
|
|
318
430
|
chmod +x .githooks/pre-commit .githooks/post-merge
|
|
319
431
|
```
|
|
432
|
+
|
|
433
|
+
### Gitlab CI
|
|
434
|
+
|
|
435
|
+
Step de validation des variables d'environnement.
|
|
436
|
+
|
|
437
|
+
```yaml
|
|
438
|
+
# .gitlab-ci.yml
|
|
439
|
+
check-env:
|
|
440
|
+
stage: test
|
|
441
|
+
image: node:20-alpine
|
|
442
|
+
script:
|
|
443
|
+
- corepack enable
|
|
444
|
+
- yarn install --frozen-lockfile
|
|
445
|
+
- yarn dnl assert --source $DOT_ENV_FILE
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### GitHub Actions
|
|
449
|
+
|
|
450
|
+
```yaml
|
|
451
|
+
# .github/workflows/check-env.yml
|
|
452
|
+
name: Check environment
|
|
453
|
+
|
|
454
|
+
on: [push, pull_request]
|
|
455
|
+
|
|
456
|
+
jobs:
|
|
457
|
+
check-env:
|
|
458
|
+
runs-on: ubuntu-latest
|
|
459
|
+
steps:
|
|
460
|
+
- uses: actions/checkout@v4
|
|
461
|
+
|
|
462
|
+
- uses: actions/setup-node@v4
|
|
463
|
+
with:
|
|
464
|
+
node-version: 20
|
|
465
|
+
|
|
466
|
+
- run: corepack enable
|
|
467
|
+
- run: yarn install --frozen-lockfile
|
|
468
|
+
|
|
469
|
+
# Exemple avec un fichier .env fourni par un secret
|
|
470
|
+
- run: yarn dnl assert --source .env
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
Le fichier .env peut être généré à partir d’un secret GitHub ou monté dynamiquement.
|
|
474
|
+
|
|
475
|
+
```yaml
|
|
476
|
+
- run: echo "$ENV_FILE_CONTENT" > .env
|
|
477
|
+
env:
|
|
478
|
+
ENV_FILE_CONTENT: ${{ secrets.ENV_FILE }}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Quelles commandes utiliser ?
|
|
482
|
+
|
|
483
|
+
| Situation | Commande à utiliser |
|
|
484
|
+
| ----------------------------------------: | ------------------------------ |
|
|
485
|
+
| Nouveau projet | generate |
|
|
486
|
+
| Projet existant avec un .env | reverse-env |
|
|
487
|
+
| Valider la configuration en CI | assert |
|
|
488
|
+
| Valider la config injectée par le runtime | assert |
|
|
489
|
+
| Documenter les variables | explain |
|
|
490
|
+
| Générer un .env propre | export env |
|
|
491
|
+
| Préparer un build Docker | export docker-\* |
|
|
492
|
+
| Injecter des variables en CI | export github-env / gitlab-env |
|
|
493
|
+
| Kubernetes (ConfigMap / Secret) | export k8s-\* |
|
|
494
|
+
|
|
495
|
+
Règle simple :
|
|
496
|
+
|
|
497
|
+
> Le schéma est toujours la source de vérité.
|
|
498
|
+
> Les commandes ne font que valider, documenter ou transformer.
|
|
499
|
+
|
|
500
|
+
## FAQ / Choix de design
|
|
501
|
+
|
|
502
|
+
### Pourquoi être aussi strict ?
|
|
503
|
+
|
|
504
|
+
Parce que les erreurs de configuration sont des bugs, pas des warnings.
|
|
505
|
+
|
|
506
|
+
Si une variable est manquante ou invalide :
|
|
507
|
+
|
|
508
|
+
- l’application ne doit pas démarrer
|
|
509
|
+
- l’erreur doit être immédiate et explicite
|
|
510
|
+
|
|
511
|
+
Tolérer une config invalide revient à déplacer le bug en production.
|
|
512
|
+
|
|
513
|
+
### Pourquoi Node.js uniquement ?
|
|
514
|
+
|
|
515
|
+
Parce que le runtime cible est clair :
|
|
516
|
+
|
|
517
|
+
- APIs
|
|
518
|
+
- workers
|
|
519
|
+
- jobs
|
|
520
|
+
- CI
|
|
521
|
+
|
|
522
|
+
Les runtimes edge (Deno, Bun, Cloudflare Workers…) ont :
|
|
523
|
+
|
|
524
|
+
- des modèles d’environnement différents
|
|
525
|
+
- des contraintes différentes
|
|
526
|
+
- des attentes différentes
|
|
527
|
+
|
|
528
|
+
Ils sont volontairement hors scope.
|
|
529
|
+
|
|
530
|
+
### Pourquoi Zod ?
|
|
531
|
+
|
|
532
|
+
Parce que Zod fournit :
|
|
533
|
+
|
|
534
|
+
- un typage TypeScript fiable
|
|
535
|
+
- une validation runtime cohérente
|
|
536
|
+
- des transformations expressives
|
|
537
|
+
|
|
538
|
+
Le schéma est à la fois :
|
|
539
|
+
|
|
540
|
+
- documentation
|
|
541
|
+
- contrat
|
|
542
|
+
- validation
|
|
543
|
+
- source de typage
|
|
544
|
+
|
|
545
|
+
Aucun autre outil ne couvre ces quatre points aussi proprement aujourd’hui.
|
|
546
|
+
|
|
547
|
+
### Pourquoi ne pas utiliser dotenv-safe / env-schema / autre ?
|
|
548
|
+
|
|
549
|
+
Ces outils :
|
|
550
|
+
|
|
551
|
+
- valident partiellement
|
|
552
|
+
- typent peu ou mal
|
|
553
|
+
- ne documentent pas vraiment
|
|
554
|
+
- n’offrent pas de CLI cohérent
|
|
555
|
+
|
|
556
|
+
dotenv-never-lies assume un périmètre plus strict,
|
|
557
|
+
mais fournit une chaîne complète :
|
|
558
|
+
schéma → validation → typage → CI → export.
|
|
559
|
+
|
|
560
|
+
### Pourquoi ne pas gérer les secrets ?
|
|
561
|
+
|
|
562
|
+
Parce que ce n’est pas le bon niveau.
|
|
563
|
+
|
|
564
|
+
dotenv-never-lies :
|
|
565
|
+
|
|
566
|
+
- identifie les secrets
|
|
567
|
+
- peut les exclure, masquer ou exporter
|
|
568
|
+
|
|
569
|
+
Mais :
|
|
570
|
+
|
|
571
|
+
- ne chiffre rien
|
|
572
|
+
- ne stocke rien
|
|
573
|
+
|
|
574
|
+
Il s’intègre avec les outils existants, il ne les concurrence pas.
|
|
575
|
+
|
|
576
|
+
# Conclusion :
|
|
577
|
+
|
|
578
|
+
> dotenv-never-lies ne cherche pas à être flexible. Il cherche à être fiable, explicite et prévisible.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
type AssertCliOptions = {
|
|
2
2
|
schema: string;
|
|
3
3
|
source: string;
|
|
4
|
-
}
|
|
4
|
+
};
|
|
5
|
+
export declare const assertCommand: (opts?: AssertCliOptions | undefined) => Promise<void>;
|
|
6
|
+
export {};
|
|
5
7
|
//# sourceMappingURL=assert.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/assert.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/assert.ts"],"names":[],"mappings":"AAOA,KAAK,gBAAgB,GAAG;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,aAAa,GAAU,OAAO,gBAAgB,GAAG,SAAS,KAAG,OAAO,CAAC,IAAI,CAsBrF,CAAC"}
|
|
@@ -3,24 +3,22 @@ import dnl from "../../index.js";
|
|
|
3
3
|
import { loadDef as loadDef } from "../utils/load-schema.js";
|
|
4
4
|
import { resolveSchemaPath } from "../utils/resolve-schema.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
+
import { ValidationError } from "../../errors.js";
|
|
6
7
|
export const assertCommand = async (opts) => {
|
|
7
|
-
const schemaPath = resolveSchemaPath(opts
|
|
8
|
+
const schemaPath = resolveSchemaPath(opts?.schema);
|
|
8
9
|
const envDef = (await loadDef(schemaPath));
|
|
9
10
|
try {
|
|
10
11
|
envDef.assert({
|
|
11
|
-
source: opts
|
|
12
|
+
source: opts?.source ? dnl.readEnvFile(path.resolve(process.cwd(), opts.source)) : process.env,
|
|
12
13
|
});
|
|
13
14
|
console.log("✅ Environment is valid");
|
|
14
15
|
}
|
|
15
16
|
catch (error) {
|
|
16
17
|
if (error instanceof z.ZodError) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
console.error(` → ${issue.message}`);
|
|
22
|
-
}
|
|
23
|
-
process.exit(1);
|
|
18
|
+
throw new ValidationError("Invalid environment variables", error.issues.map((issue) => ({
|
|
19
|
+
key: issue.path.join("."),
|
|
20
|
+
message: issue.message,
|
|
21
|
+
})));
|
|
24
22
|
}
|
|
25
23
|
throw error;
|
|
26
24
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import { Explanation } from "../utils/printer.js";
|
|
1
2
|
type ExplainCliOptions = {
|
|
2
3
|
schema?: string | undefined;
|
|
3
4
|
keys?: string[] | undefined;
|
|
4
5
|
format?: "human" | "json" | undefined;
|
|
5
6
|
};
|
|
6
|
-
export declare const explainCommand: (options?: ExplainCliOptions) => Promise<
|
|
7
|
+
export declare const explainCommand: (options?: ExplainCliOptions) => Promise<{
|
|
8
|
+
format: "human" | "json";
|
|
9
|
+
result: Explanation[];
|
|
10
|
+
}>;
|
|
11
|
+
export declare const printHuman: (result: Explanation[]) => void;
|
|
7
12
|
export {};
|
|
8
13
|
//# sourceMappingURL=explain.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/explain.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/explain.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAiB,MAAM,qBAAqB,CAAC;AAGjE,KAAK,iBAAiB,GAAG;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,UAAU,iBAAiB,KAAG,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,EAAE,CAAA;CAAE,CA2B7H,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,QAAQ,WAAW,EAAE,SAqB/C,CAAC"}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { loadDef } from "../utils/load-schema.js";
|
|
2
2
|
import { resolveSchemaPath } from "../utils/resolve-schema.js";
|
|
3
3
|
import { toExplanation } from "../utils/printer.js";
|
|
4
|
+
import { UsageError } from "../../errors.js";
|
|
4
5
|
export const explainCommand = async (options) => {
|
|
6
|
+
const format = options?.format ?? "human";
|
|
7
|
+
if (format !== "human" && format !== "json") {
|
|
8
|
+
throw new UsageError(`Invalid format: ${format}`);
|
|
9
|
+
}
|
|
5
10
|
const schemaPath = resolveSchemaPath(options?.schema);
|
|
6
11
|
const envDef = (await loadDef(schemaPath));
|
|
7
|
-
const format = options?.format ?? "human";
|
|
8
12
|
const keysToSerialize = new Array();
|
|
9
13
|
if (options?.keys) {
|
|
10
14
|
keysToSerialize.push(...options.keys);
|
|
@@ -17,25 +21,14 @@ export const explainCommand = async (options) => {
|
|
|
17
21
|
result.push(toExplanation(key, value));
|
|
18
22
|
}
|
|
19
23
|
if (result.length === 0) {
|
|
20
|
-
|
|
21
|
-
return 1;
|
|
22
|
-
}
|
|
23
|
-
switch (format) {
|
|
24
|
-
case "json":
|
|
25
|
-
console.log(JSON.stringify(result, null, 2));
|
|
26
|
-
return 0;
|
|
27
|
-
case "human":
|
|
28
|
-
printHuman(result);
|
|
29
|
-
return 0;
|
|
30
|
-
default:
|
|
31
|
-
console.error(`Invalid format: ${format}`);
|
|
32
|
-
return 1;
|
|
24
|
+
throw new UsageError("No matching environment variables found");
|
|
33
25
|
}
|
|
26
|
+
return { format, result };
|
|
34
27
|
};
|
|
35
|
-
const printHuman = (result) => {
|
|
28
|
+
export const printHuman = (result) => {
|
|
36
29
|
if (result.length > 1) {
|
|
37
30
|
for (const item of result) {
|
|
38
|
-
console.log(`${item.key}: ${item.description}
|
|
31
|
+
console.log(`${item.key}: ${item.description} \t\tfor more details : dnl explain ${item.key}`);
|
|
39
32
|
}
|
|
40
33
|
return;
|
|
41
34
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import dnl from "../../index.js";
|
|
2
|
+
export declare const exportFormatsNames: readonly ["docker-env", "docker-args", "env", "k8s-configmap", "k8s-secret", "github-env", "github-secret", "gitlab-env", "json", "ts", "js"];
|
|
3
|
+
export type ExportFormat = (typeof exportFormatsNames)[number];
|
|
4
|
+
export type ExportResult = {
|
|
5
|
+
content: string;
|
|
6
|
+
warnings: string[];
|
|
7
|
+
out?: string;
|
|
8
|
+
};
|
|
9
|
+
export type ExportCliOptions = {
|
|
10
|
+
format: ExportFormat;
|
|
11
|
+
schema?: string | undefined;
|
|
12
|
+
source?: string | undefined;
|
|
13
|
+
hideSecret?: boolean;
|
|
14
|
+
excludeSecret?: boolean;
|
|
15
|
+
includeComments?: boolean;
|
|
16
|
+
out?: string | undefined;
|
|
17
|
+
force?: boolean;
|
|
18
|
+
k8sName?: string | undefined;
|
|
19
|
+
githubOrg?: string | undefined;
|
|
20
|
+
};
|
|
21
|
+
export declare const exportCommand: (options: ExportCliOptions) => Promise<ExportResult>;
|
|
22
|
+
export declare const contentByFormat: (format: ExportFormat, envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
|
|
23
|
+
export declare const exportJson: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
|
|
24
|
+
export declare const exportEnv: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
|
|
25
|
+
export declare const exportDockerArgs: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
|
|
26
|
+
export declare const exportDockerEnv: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
|
|
27
|
+
export declare const exportK8sConfigmap: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
|
|
28
|
+
export declare const exportK8sSecret: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
|
|
29
|
+
export declare const exportTs: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
|
|
30
|
+
export declare const exportJs: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
|
|
31
|
+
export declare const exportGithubEnv: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
|
|
32
|
+
export declare const exportGithubSecret: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
|
|
33
|
+
export declare const exportGitlabEnv: (envDef: dnl.EnvDefinitionHelper<any>, options: ExportCliOptions, warnings: string[]) => string;
|
|
34
|
+
//# sourceMappingURL=export.d.ts.map
|