@romaintaillandier1978/dotenv-never-lies 0.3.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +437 -160
  2. package/dist/cli/commands/assert.d.ts +4 -2
  3. package/dist/cli/commands/assert.d.ts.map +1 -1
  4. package/dist/cli/commands/assert.js +7 -9
  5. package/dist/cli/commands/explain.d.ts +6 -1
  6. package/dist/cli/commands/explain.d.ts.map +1 -1
  7. package/dist/cli/commands/explain.js +9 -16
  8. package/dist/cli/commands/export.d.ts +35 -0
  9. package/dist/cli/commands/export.d.ts.map +1 -0
  10. package/dist/cli/commands/export.js +268 -0
  11. package/dist/cli/commands/generate.d.ts +10 -2
  12. package/dist/cli/commands/generate.d.ts.map +1 -1
  13. package/dist/cli/commands/generate.js +9 -7
  14. package/dist/cli/commands/reverseEnv.d.ts +8 -2
  15. package/dist/cli/commands/reverseEnv.d.ts.map +1 -1
  16. package/dist/cli/commands/reverseEnv.js +21 -8
  17. package/dist/cli/index.js +323 -99
  18. package/dist/cli/utils/exitCodes.d.ts +8 -0
  19. package/dist/cli/utils/exitCodes.d.ts.map +1 -0
  20. package/dist/cli/utils/exitCodes.js +8 -0
  21. package/dist/cli/utils/infer-schema.d.ts.map +1 -1
  22. package/dist/cli/utils/infer-schema.js +4 -3
  23. package/dist/cli/utils/load-schema.d.ts.map +1 -1
  24. package/dist/cli/utils/load-schema.js +20 -12
  25. package/dist/cli/utils/printer.d.ts.map +1 -1
  26. package/dist/cli/utils/printer.js +5 -4
  27. package/dist/cli/utils/resolve-schema.d.ts.map +1 -1
  28. package/dist/cli/utils/resolve-schema.js +7 -3
  29. package/dist/cli/utils/toFile.d.ts +2 -0
  30. package/dist/cli/utils/toFile.d.ts.map +1 -0
  31. package/dist/cli/utils/toFile.js +8 -0
  32. package/dist/core.d.ts +33 -33
  33. package/dist/core.js +10 -10
  34. package/dist/errors.d.ts +29 -2
  35. package/dist/errors.d.ts.map +1 -1
  36. package/dist/errors.js +33 -4
  37. package/dist/romaintaillandier1978-dotenv-never-lies-0.3.0.tgz +0 -0
  38. package/package.json +9 -9
package/README.md CHANGED
@@ -1,96 +1,90 @@
1
1
  # dotenv-never-lies
2
2
 
3
- > Parce que les variables d’environnement **mentent tout le temps**.
3
+ > Because environment variables lie all the time.
4
4
 
5
- **dotenv-never-lies** valide, type et documente tes variables d’environnement à partir d’un schéma TypeScript / Zod.
6
- Il échoue **vite**, **fort**, et **avant la prod**.
5
+ **dotenv-never-lies** validates, types, and documents your environment variables from a TypeScript / Zod schema.
6
+ It fails fast, loud, and before production.
7
7
 
8
- ---
9
-
10
- ## Pourquoi ?
8
+ ## Why?
11
9
 
12
- Parce que tout ça arrive **tout le temps** :
10
+ Because all of this happens all the time:
13
11
 
14
- - ❌ une variable d’env manquante**crash au runtime**
15
- - ❌ une URL mal formée **bug subtil en prod**
16
- - ❌ la CI n’a pas été mise à jour après une nouvelle variable → **déploiement rouge incompréhensible**
17
- - ❌ un `process.env.FOO!` optimiste **mensonge à toi-même**
12
+ - ❌ a missing env variableruntime crash
13
+ - ❌ a malformed URL subtle production bug
14
+ - ❌ CI was not updated after a new variable → confusing red deployment
15
+ - ❌ an optimistic `process.env.FOO!` → lying to yourself
18
16
 
19
- Et parce que `.env` est :
17
+ And because `.env` files are:
20
18
 
21
- - non typé
22
- - non documenté
23
- - partagé à la main
24
- - rarement à jour
19
+ - untyped
20
+ - undocumented
21
+ - shared manually
22
+ - rarely up to date
25
23
 
26
- 👉 **dotenv-never-lies** transforme cette configuration fragile en **contrat explicite**.
24
+ 👉 **dotenv-never-lies** turns this fragile configuration into an explicit contract.
27
25
 
28
26
  ---
29
27
 
30
- ## Ce que fait la lib
28
+ ## What the library does
31
29
 
32
- - ✅ valide les variables d’environnement au démarrage
33
- - ✅ fournit un typage TypeScript fiable
34
- - ✅ documente chaque variable
35
- - ✅ expose un CLI pour la CI et les humains
36
- - ✅ permet des transformations complexes (arrays, parsing, coercion…)
30
+ - ✅ validates environment variables at startup
31
+ - ✅ provides reliable TypeScript typings
32
+ - ✅ documents each variable
33
+ - ✅ exposes a CLI for CI and humans
34
+ - ✅ enables complex transformations (arrays, parsing, coercion…)
37
35
 
38
36
  ---
39
37
 
40
- ## Ce que dotenv-never-lies n’est pas
38
+ ## What dotenv-never-lies is not
41
39
 
42
- Ce package a un périmètre volontairement **limité**.
40
+ This package has a deliberately limited scope.
43
41
 
44
- - ❌ **Ce n’est pas un outil frontend**
45
- Il n’est pas destiné à être utilisé dans un navigateur.
46
- Pas de bundler, pas de `import.meta.env`, pas de variables exposées au client.
42
+ - ❌ **It is not a frontend tool**
43
+ It is not meant to be used in a browser.
44
+ No bundler, no `import.meta.env`, no variables exposed to the client.
47
45
 
48
- - ❌ **Ce n’est pas un gestionnaire de secrets**
49
- Il ne chiffre rien, ne stocke rien, ne remplace ni Vault, ni AWS Secrets Manager,
50
- ni les variables sécurisées de ton provider CI/CD.
46
+ - ❌ **It is not a secrets manager**
47
+ It does not encrypt anything, does not store anything, and does not replace Vault, AWS Secrets Manager,
48
+ nor your CI/CD provider’s secure variables.
51
49
 
52
- - ❌ **Ce n’est pas une solution cross-runtime**
53
- Support ciblé : **Node.js**.
54
- Deno, Bun, Cloudflare Workers, edge runtimes : hors scope (pour l’instant).
50
+ - ❌ **It is not a cross-runtime solution**
51
+ Targeted support: **Node.js**.
52
+ Deno, Bun, Cloudflare Workers, edge runtimes: out of scope (for now).
55
53
 
56
- - ❌ **Ce n’est pas un framework de configuration global**
57
- Il ne gère ni les fichiers YAML/JSON, ni les profiles dynamiques,
58
- ni les overrides magiques par environnement.
54
+ - ❌ **It is not a global configuration framework**
55
+ It does not manage YAML/JSON files, dynamic profiles,
56
+ nor magical per-environment overrides.
59
57
 
60
- - ❌ **Ce n’est pas permissif**
61
- S’il manque une variable ou qu’une valeur est invalide, ça plante.
62
- Cest le but.
58
+ - ❌ **It is not permissive**
59
+ If a variable is missing or a value is invalid, it crashes.
60
+ Thats the point.
63
61
 
64
- En résumé :
65
- **dotenv-never-lies** est fait pour des **APIs Node.js** et des **services backend**
66
- qui préfèrent **échouer proprement au démarrage** plutôt que **bugger silencieusement en prod**.
62
+ In short:
63
+ **dotenv-never-lies** is for **Node.js APIs** and **backend services**
64
+ that prefer to **fail cleanly at startup** rather than **silently break in production**.
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
- yarn add dotenv-never-lies
81
- # ou
82
- npm install dotenv-never-lies
71
+ npm install @romaintaillandier1978/dotenv-never-lies
72
+ # or
73
+ yarn add @romaintaillandier1978/dotenv-never-lies
83
74
  ```
84
75
 
85
- ## Expansion des variables (`dotenv-expand`)
76
+ ## Dependencies and compatibility
77
+
78
+ **[`zod`](https://www.npmjs.com/package/zod)** — dotenv-never-lies exposes Zod schemas in its public API.
86
79
 
87
- **dotenv-never-lies** gère automatiquement l’expansion des variables d’environnement,
88
- via [`dotenv-expand`](https://www.npmjs.com/package/dotenv-expand).
80
+ ⚠️ Important: Zod **v4.2.1** minimum is required.
81
+ Using Zod v3 will cause typing or inference errors.
89
82
 
90
- Cela permet de définir des variables composées à partir d’autres variables,
91
- sans duplication ni copier-coller fragile.
83
+ **[`dotenv`](https://www.npmjs.com/package/dotenv)** allows dotenv-never-lies to automatically handle parsing of env files.
92
84
 
93
- ### Exemple
85
+ **[`dotenv-expand`](https://www.npmjs.com/package/dotenv-expand)** allows dotenv-never-lies to automatically handle environment variable expansion. This lets you define variables composed from other variables without duplication or fragile copy-paste.
86
+
87
+ **Example**
94
88
 
95
89
  ```env
96
90
  FRONT_A=https://a.site.com
@@ -100,51 +94,55 @@ FRONT_C=https://c.site.com
100
94
  NODE_CORS_ORIGIN="${FRONT_A};${FRONT_B};${FRONT_C}"
101
95
  ```
102
96
 
103
- ## Définir un schéma
97
+ ## DNL schema
98
+
99
+ The DNL schema is your new source of truth.
100
+
101
+ (`dnl reverse-env` will help you scaffold the first skeleton)
102
+
103
+ ### schema location
104
104
 
105
- env.dnl.ts
105
+ Recommended: `env.dnl.ts`
106
+
107
+ Supported in this order for all CLI commands:
108
+
109
+ 1. `--schema path/to/my-dnl.ts`
110
+ 2. declared in `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. one of `env.dnl.ts`, `env.dnl.js`, `dnl.config.ts`, `dnl.config.js`
123
+
124
+ ### define a schema
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: {
113
- description: "Environnement d’exécution",
132
+ description: "Runtime environment",
114
133
  schema: z.enum(["test", "development", "staging", "production"]),
115
134
  },
116
135
 
117
136
  NODE_PORT: {
118
- description: "Port de l’API",
119
- schema: z.coerce.number(),
137
+ description: "API port",
138
+ schema: z.coerce.number().default(3000),
120
139
  },
121
140
 
122
- FRONT_A: {
123
- description: "Mon site A",
141
+ FRONT_URL: {
142
+ description: "My website",
124
143
  schema: z.url(),
125
144
  },
126
145
 
127
- FRONT_B: {
128
- description: "Mon site B",
129
- schema: z.url(),
130
- },
131
-
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,153 +151,285 @@ export default define({
153
151
  });
154
152
  ```
155
153
 
156
- ## Utilisation runtime
154
+ ## Secrets handling
155
+
156
+ Reminder: dotenv-never-lies is not a secrets manager.
157
+
158
+ ### declaration in the DNL schema
159
+
160
+ A variable is considered secret if and only if it is explicitly marked in the schema with `secret: true`.
161
+ (`secret: undefined` is equivalent to `secret: false`)
162
+ This rule is intentionally strict.
163
+
164
+ ```ts
165
+ JWT_SECRET: {
166
+ description: "JWT signing key",
167
+ schema: z.string(),
168
+ secret: true,
169
+ }
170
+ ```
171
+
172
+ ### Secrets and CLI commands
173
+
174
+ assert: validates secrets like any other variable
175
+
176
+ reverse-env: when generating the schema, with `--guess-secret` option, the command tries to automatically identify sensitive variables (e.g. SECRET, KEY, TOKEN, PASSWORD).
177
+ **This detection is heuristic and must always be reviewed and corrected manually.**
178
+
179
+ export: adapts behavior depending on the target format (env, docker, CI, Kubernetes…). See the table below for details by format.
180
+
181
+ ### During export
182
+
183
+ Variables marked `secret: true` in the schema are treated differently depending on the export format.
184
+
185
+ | Format | Secrets included by default | Maskable (`--hide-secret`) | Excludable (`--exclude-secret`) | Notes |
186
+ | ------------- | --------------------------- | -------------------------- | ------------------------------- | -------------------------- |
187
+ | env | yes | yes | yes | classic .env |
188
+ | docker-env | yes | yes | yes | For --env-file |
189
+ | docker-args | yes | yes | yes | For docker run -e |
190
+ | json | yes | yes | yes | Debug / tooling |
191
+ | ts | yes | yes | yes | Typed export |
192
+ | js | yes | yes | yes | Runtime export |
193
+ | github-env | yes | yes | yes | visible in logs |
194
+ | github-secret | secrets only | no | yes | Via gh secret set |
195
+ | gitlab-env | yes | yes | yes | GitLab CI variables |
196
+ | k8s-configmap | yes | yes | yes | warning if secret unmasked |
197
+ | k8s-secret | secrets only | yes | yes | Kubernetes Secret |
198
+
199
+ ## Variable lifecycle
200
+
201
+ dotenv-never-lies works with three distinct representations of environment variables:
202
+
203
+ 1. **Raw value**
204
+ The original value coming from the source (`.env` file or `process.env`).
205
+ Always a string (or undefined).
206
+
207
+ 2. **Runtime value (validated)**
208
+ The value after validation — and possible transformation — by the Zod schema.
209
+ This value may be strongly typed (number, boolean, array, object, etc.).
210
+
211
+ 3. **Exported value**
212
+ The value written by `dnl export`.
213
+ - For env-like formats (`env`, `docker-*`, `github-*`, `k8s-*`), this is the **raw value**, after validation.
214
+ - For `js`, `ts` and `json`, the runtime value can be exported using `--serialize-typed`.
215
+
216
+ This separation ensures that validation, runtime usage and configuration export remain explicit and predictable.
217
+
218
+ ## Runtime usage
157
219
 
158
220
  ```typescript
159
221
  import envDef from "./env.dnl";
160
222
 
161
223
  export const ENV = envDef.load();
162
224
 
163
- if(ENV.NODE_ENV === "test"){...}
164
-
225
+ // if (process.env.NODE_ENV && process.env.NODE_ENV === "test") {
226
+ if (ENV.NODE_ENV === "test") {
227
+ doAdditionalTest();
228
+ }
165
229
 
230
+ const server = http.createServer(app);
231
+ //server.listen(process.env.NODE_PORT||3000, () => {
232
+ server.listen(ENV.NODE_PORT, () => {
233
+ console.log(`Server started on ${ENV.NODE_PORT}`);
234
+ });
166
235
  ```
167
236
 
168
- Résultat :
237
+ Result:
169
238
 
170
- - ENV.NODE_ENV est un enum
171
- - ENV.NODE_PORT est un number
172
- - ENV.FRONT_A ENV.FRONT_B ENV.FRONT_C sont des URLs valides
173
- - ENV.NODE_CORS_ORIGIN est un string[] contenant des URLs valides
174
- - ENV.JWT_SECRET est une string
239
+ - `ENV.NODE_ENV` is an enum
240
+ - `ENV.NODE_PORT` is a number
241
+ - `FRONT_URL` is a valid URL
242
+ - `ENV.JWT_SECRET` is a string
175
243
 
176
- Si une variable est absente ou invalidele process s’arrête immédiatement. \
177
- C’est volontaire.
244
+ If a variable is missing or invalidthe process exits immediately.
245
+ This is intentional.
178
246
 
179
- ## Éviter `process.env` dans le code applicatif
247
+ ## Avoid `process.env` in application code
180
248
 
181
- Une fois le schéma chargé, l’accès aux variables d’environnement
182
- doit se faire exclusivement via l’objet `ENV`.
249
+ Once the schema is loaded, environment variables
250
+ must be accessed exclusively via the `ENV` object.
183
251
 
184
- Cela garantit :
252
+ This guarantees:
185
253
 
186
- - un typage strict
187
- - des valeurs validées
188
- - un point d’entrée unique pour la configuration
254
+ - strict typing
255
+ - validated values
256
+ - a single entry point for configuration
189
257
 
190
- Pour identifier les usages résiduels de `process.env` dans votre codebase, un simple outil de recherche suffit :
258
+ To identify residual `process.env` usages in your codebase, a simple search tool is enough:
191
259
 
192
260
  ```bash
193
261
  grep -R "process\.env" src
194
262
  ```
195
263
 
196
- Le choix de corriger (ou non) ces usages dépend du contexte et reste volontairement laissé au développeur.
264
+ Choosing to refactor (or not) those usages depends on context and is intentionally left to the developer.
197
265
 
198
266
  ## CLI
199
267
 
200
- Le CLI permet de valider, charger, générer et documenter les variables d’environnement à partir d’un schéma `dotenv-never-lies`.
268
+ The CLI lets you validate, load, generate, export, and document environment variables from a `dotenv-never-lies` schema.
269
+
270
+ It is designed to be used:
201
271
 
202
- Il est conçu pour être utilisé :
272
+ - locally (by humans)
273
+ - in CI (without surprises)
274
+ - before the application starts (not after)
203
275
 
204
- - en local (par des humains)
205
- - en CI (sans surprise)
206
- - avant que l’application ne démarre (et pas après)
276
+ ### Exit codes
207
277
 
208
- ### Valider un fichier `.env` (CI-friendly)
278
+ `dotenv-never-lies` uses explicit exit codes, designed for CI:
209
279
 
210
- Valide les variables sans les injecter dans `process.env`.
280
+ | Code | Meaning |
281
+ | ---: | ----------------------------- |
282
+ | 0 | Success |
283
+ | 1 | Usage error or internal error |
284
+ | 2 | DNL schema not found |
285
+ | 3 | Environment validation failed |
286
+ | 4 | Error during export |
287
+
288
+ ### assert: Validate a `.env` file (CI-friendly)
289
+
290
+ Validates variables without injecting them into `process.env`.
211
291
 
212
292
  ```bash
213
- dnl check --schema env.dnl.ts
293
+ dnl assert --source .env --schema env.dnl.ts
214
294
  ```
215
295
 
216
- échoue si :
296
+ Without `--source`, `dnl assert` validates `process.env`.
297
+ This is the recommended mode when variables are injected by the runtime or CI.
217
298
 
218
- - une variable est manquante
219
- - une valeur est invalide
220
- - le schéma n’est pas respecté
299
+ fails if:
221
300
 
222
- ### Charger les variables dans le process
301
+ - a variable is missing
302
+ - a value is invalid
303
+ - the schema is not respected
223
304
 
224
- Charge et valide les variables dans `process.env`.
305
+ ### generate: Generate a .env file from the schema
306
+
307
+ Generates a documented `.env` from the schema.
225
308
 
226
309
  ```bash
227
- dnl load --schema env.dnl.ts
310
+ dnl generate --schema env.dnl.ts --out .env
228
311
  ```
229
312
 
230
- Usage typique : scripts de démarrage, tooling local.
313
+ Useful for:
231
314
 
232
- ### Générer un fichier .env à partir du schéma
315
+ - bootstrapping a project
316
+ - sharing a template
317
+ - avoiding obsolete `.env.example` files
233
318
 
234
- Génère un .env documenté à partir du schéma.
319
+ ### reverse-env: Generate a schema from an existing .env
320
+
321
+ Creates an `env.dnl.ts` file from a `.env`.
235
322
 
236
323
  ```bash
237
- dnl generate --schema env.dnl.ts --out .env
324
+ dnl reverse-env --source .env
238
325
  ```
239
326
 
240
- Utile pour :
327
+ Useful for:
241
328
 
242
- - initialiser un projet
243
- - partager un template
244
- - éviter les .env.example obsolètes
329
+ - migrating an existing project
330
+ - documenting a legacy configuration afterwards
245
331
 
246
- ### Générer un schéma depuis un .env existant
332
+ ### explain: Display variables documentation
247
333
 
248
- Crée un fichier env.dnl.ts à partir d’un .env.
334
+ Displays the list of known variables and their description.
249
335
 
250
336
  ```bash
251
- dnl reverse-env --source .env
337
+ dnl explain
338
+ ```
339
+
340
+ Sample output:
341
+
342
+ ```bash
343
+ NODE_ENV: Runtime environment
344
+ NODE_PORT: API port
345
+ FRONT_URL: My website
346
+ JWT_SECRET: JWT Secret
252
347
  ```
253
348
 
254
- Utile pour :
349
+ ### export: Export variables to other formats
350
+
351
+ The `export` command transforms variables validated by the schema
352
+ into formats directly consumable by other tools (Docker, CI, Kubernetes, scripts…).
255
353
 
256
- - migrer un projet existant
257
- - documenter a posteriori une configuration legacy
354
+ The schema remains the source of truth.
355
+ Values are validated before export.
356
+
357
+ ```bash
358
+ dnl export <format>
359
+ ```
258
360
 
259
- ### Afficher la documentation des variables
361
+ By default, values are read from `process.env`.
362
+ A `.env` file can be provided via `--source`.
260
363
 
261
- Affiche la liste des variables connues et leur description.
364
+ Examples:
365
+ Export environment variables as JSON from a `.env` file
262
366
 
263
367
  ```bash
264
- dnl print
368
+ dnl export json --source .env
265
369
  ```
266
370
 
267
- Exemple de sortie :
371
+ Clean a `.env` file (remove comments and extraneous lines)
268
372
 
269
373
  ```bash
270
- FRONT_A: Mon site A
271
- FRONT_B: Mon site B
272
- FRONT_C: Mon site C
273
- NODE_CORS_ORIGIN: URLs frontend autorisées à appeler cette API
274
- JWT_SECRET: JWT Secret
374
+ dnl export env --source .env --out .env.clean
375
+ dnl export env --source .env --out .env --force
376
+ ```
377
+
378
+ Export variables as `docker-args`
275
379
 
380
+ ```bash
381
+ dnl export docker-args --source .env
276
382
  ```
277
383
 
278
- TODO : check CI in real life.
384
+ Result:
279
385
 
280
- ## Usages dans la vraie vie
386
+ ```bash
387
+ -e "NODE_ENV=production" -e "NODE_PORT=3000"
388
+ ```
389
+
390
+ Export for GitHub Actions (variables)
391
+
392
+ ```bash
393
+ dnl export github-env
394
+ ```
395
+
396
+ Result:
397
+
398
+ ```bash
399
+ printf '%s\n' "NODE_ENV=production" >> $GITHUB_ENV
400
+ printf '%s\n' "NODE_PORT=3000" >> $GITHUB_ENV
401
+ ```
402
+
403
+ There are a few more formats and options (see CLI docs `dnl export --help`).
404
+
405
+ ## Real-life usage
406
+
407
+ ### GitIgnore
408
+
409
+ dotenv-never-lies creates temporary files in your project directory.
410
+ Add `.dnl/` to your `.gitignore`.
281
411
 
282
412
  ### Git
283
413
 
284
- #### Git hooks recommandés
414
+ #### Recommended Git hooks
285
415
 
286
- Il est fortement conseillé d’utiliser **dotenv-never-lies** via des hooks Git :
416
+ Using **dotenv-never-lies** via Git hooks is strongly recommended:
287
417
 
288
- - **pre-commit** : empêche de committer si la configuration locale n’est pas conforme au schéma
289
- - **post-merge** : détecte immédiatement les changements de schéma impactant l’environnement local
418
+ - **pre-commit**: prevents committing if the local configuration is not compliant with the schema
419
+ - **post-merge**: immediately detects schema changes impacting the local environment
290
420
 
291
- L’objectif est simple :
292
- **si la configuration locale n’est pas conforme au schéma, le code ne doit pas être committé.**
421
+ The goal is simple:
422
+ **if the local configuration is not compliant with the schema, code must not be committed.**
293
423
 
294
- Le schéma est la source de vérité, pas les fichiers `.env`.
424
+ The schema is the source of truth, not `.env` files.
295
425
 
296
- Ces hooks permettent d’éviter les erreurs classiques :
426
+ These hooks help avoid classic mistakes:
297
427
 
298
- - variable manquante après un pull
299
- - format invalide détecté trop tard
300
- - “ça marche chez moi à un `.env` obsolète
428
+ - missing variable after a pull
429
+ - invalid format detected too late
430
+ - “works on my machinedue to an outdated `.env`
301
431
 
302
- #### Installation des hooks
432
+ #### Hooks installation
303
433
 
304
434
  ```bash
305
435
  git config core.hooksPath .githooks
@@ -317,3 +447,150 @@ EOF
317
447
 
318
448
  chmod +x .githooks/pre-commit .githooks/post-merge
319
449
  ```
450
+
451
+ ### GitLab CI
452
+
453
+ Environment variables validation step.
454
+
455
+ ```yaml
456
+ # .gitlab-ci.yml
457
+ check-env:
458
+ stage: test
459
+ image: node:20-alpine
460
+ script:
461
+ - corepack enable
462
+ - yarn install --frozen-lockfile
463
+ - yarn dnl assert --source $DOT_ENV_FILE
464
+ ```
465
+
466
+ ### GitHub Actions
467
+
468
+ ```yaml
469
+ # .github/workflows/check-env.yml
470
+ name: Check environment
471
+
472
+ on: [push, pull_request]
473
+
474
+ jobs:
475
+ check-env:
476
+ runs-on: ubuntu-latest
477
+ steps:
478
+ - uses: actions/checkout@v4
479
+
480
+ - uses: actions/setup-node@v4
481
+ with:
482
+ node-version: 20
483
+
484
+ - run: corepack enable
485
+ - run: yarn install --frozen-lockfile
486
+
487
+ # Example with a .env file provided by a secret
488
+ - run: yarn dnl assert --source .env
489
+ ```
490
+
491
+ The `.env` file can be generated from a GitHub secret or mounted dynamically.
492
+
493
+ ```yaml
494
+ - run: echo "$ENV_FILE_CONTENT" > .env
495
+ env:
496
+ ENV_FILE_CONTENT: ${{ secrets.ENV_FILE }}
497
+ ```
498
+
499
+ ### Which commands should I use?
500
+
501
+ | Situation | Command to use |
502
+ | --------------------------------------: | ------------------------------ |
503
+ | New project | generate |
504
+ | Existing project with a .env | reverse-env |
505
+ | Validate configuration in CI | assert |
506
+ | Validate config injected by the runtime | assert |
507
+ | Document variables | explain |
508
+ | Generate a clean .env | export env |
509
+ | Prepare a Docker build | export docker-\* |
510
+ | Inject variables in CI | export github-env / gitlab-env |
511
+ | Kubernetes (ConfigMap / Secret) | export k8s-\* |
512
+
513
+ Simple rule:
514
+
515
+ > The schema is always the source of truth.
516
+ > Commands only validate, document, or transform.
517
+
518
+ ## FAQ / Design choices
519
+
520
+ ### Why so strict?
521
+
522
+ Because configuration errors are bugs, not warnings.
523
+
524
+ If a variable is missing or invalid:
525
+
526
+ - the application must not start
527
+ - the error must be immediate and explicit
528
+
529
+ Tolerating an invalid config is just moving the bug to production.
530
+
531
+ ### Why Node.js only?
532
+
533
+ Because the target runtime is clear:
534
+
535
+ - APIs
536
+ - workers
537
+ - jobs
538
+ - CI
539
+
540
+ Edge runtimes (Deno, Bun, Cloudflare Workers…) have:
541
+
542
+ - different environment models
543
+ - different constraints
544
+ - different expectations
545
+
546
+ They are deliberately out of scope.
547
+
548
+ ### Why Zod?
549
+
550
+ Because Zod provides:
551
+
552
+ - reliable TypeScript typing
553
+ - consistent runtime validation
554
+ - expressive transformations
555
+
556
+ The schema is both:
557
+
558
+ - documentation
559
+ - contract
560
+ - validation
561
+ - typing source
562
+
563
+ No other tool covers these four points as cleanly today.
564
+
565
+ ### Why not use dotenv-safe / env-schema / others?
566
+
567
+ These tools:
568
+
569
+ - partially validate
570
+ - provide little or weak typing
571
+ - do not really document
572
+ - do not offer a coherent CLI
573
+
574
+ dotenv-never-lies assumes a stricter scope,
575
+ but provides a full chain:
576
+ schema → validation → typing → CI → export.
577
+
578
+ ### Why not manage secrets?
579
+
580
+ Because it is not the right level.
581
+
582
+ dotenv-never-lies:
583
+
584
+ - identifies secrets
585
+ - can exclude, mask, or export them
586
+
587
+ But it:
588
+
589
+ - encrypts nothing
590
+ - stores nothing
591
+
592
+ It integrates with existing tools; it does not compete with them.
593
+
594
+ # Conclusion:
595
+
596
+ > dotenv-never-lies does not aim to be flexible. It aims to be reliable, explicit, and predictable.