@jondotsoy/envs 0.1.3 → 0.1.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/README.md +20 -0
- package/SECURITY.md +10 -0
- package/envs.js +26 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,6 +33,26 @@ Run the commands inside the repo or any of its worktrees. Inside a worktree, `.e
|
|
|
33
33
|
|
|
34
34
|
Worktrees are identified by their branch name (or their folder name if they are in detached HEAD). The main repo uses its branch name, for example `main`.
|
|
35
35
|
|
|
36
|
+
### Output
|
|
37
|
+
|
|
38
|
+
`envs pull` prints one green line with the pulled branches and the number of variables:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
↓ pulling main, feature/biz - 3 variables
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`envs push` prints one yellow line for each variable whose value changes, listing the branches where it changed. Variables that already have the same value are not shown:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
↻ LOG_LEVEL=info → main, feature/biz
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Colors are disabled when the [`NO_COLOR`](https://no-color.org) environment variable is set to a non-empty value:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
NO_COLOR=1 envs push
|
|
54
|
+
```
|
|
55
|
+
|
|
36
56
|
### `envs lint`
|
|
37
57
|
|
|
38
58
|
It never prints values, only the variable and its location (`envs.DB_PASSWORD.main`).
|
package/SECURITY.md
CHANGED
|
@@ -44,3 +44,13 @@ Additionally:
|
|
|
44
44
|
- `envs init` creates `.envs/.gitignore` with `*`, so the `.envs/` folder is not committed to git. Do not remove that file.
|
|
45
45
|
- `envs push` writes the values to the `.env` files of each worktree. Make sure `.env` is in your project's `.gitignore`.
|
|
46
46
|
- Do not share `values.yml` or paste it in issues or logs.
|
|
47
|
+
|
|
48
|
+
## Console output
|
|
49
|
+
|
|
50
|
+
`envs push` prints one line for each variable whose value changes, in the form `↻ NAME=VALUE → branch, ...`, and `envs pull` prints `↓ pulling branch, ... - N variables`. To keep secrets out of the terminal and CI logs:
|
|
51
|
+
|
|
52
|
+
- The value is replaced by `********` when the variable name contains a sensitive word: `SECRET`, `PASSWORD`, `PASSWD`, `PWD`, `TOKEN`, `KEY` (including `API_KEY` and `PRIVATE_KEY`), `CREDENTIAL`, `AUTH`, `SALT` or `SIGNING`. The match is case-insensitive and applies to whole `_`-separated words, so `KEY_FOO` and `AWS_BUCKET_SECRET` are masked but `MONKEY` is not.
|
|
53
|
+
- Masking is based on the name only. A secret stored in a variable with an innocuous name (for example `DATABASE_URL` with a password inside) is printed as is.
|
|
54
|
+
- `envs pull` never prints values, only branch names and the number of variables.
|
|
55
|
+
- `envs lint` messages never include the matched value.
|
|
56
|
+
- Set `NO_COLOR` to a non-empty value to print the output without colors.
|
package/envs.js
CHANGED
|
@@ -229,6 +229,12 @@ Commands:
|
|
|
229
229
|
edit Pull, open .envs/values.yml in VS Code (code -w), then push
|
|
230
230
|
help Show this help message
|
|
231
231
|
`;
|
|
232
|
+
var colorsDisabled = () => Boolean(process.env.NO_COLOR);
|
|
233
|
+
var MASK = "********";
|
|
234
|
+
var isMaskedKey = (key) => isSensitiveKey(key) || /(^|_)KEY(_|$)/i.test(key);
|
|
235
|
+
var paint = (code, text) => colorsDisabled() ? text : `\x1B[${code}m${text}\x1B[0m`;
|
|
236
|
+
var green = (text) => paint(32, text);
|
|
237
|
+
var yellow = (text) => paint(33, text);
|
|
232
238
|
var defaultContext = () => ({
|
|
233
239
|
cwd: process.cwd(),
|
|
234
240
|
log: (message) => console.log(message),
|
|
@@ -348,6 +354,7 @@ async function push(ctx) {
|
|
|
348
354
|
if (!read)
|
|
349
355
|
return 1;
|
|
350
356
|
const { values } = read;
|
|
357
|
+
const changes = new Map;
|
|
351
358
|
for (const { name, path } of await listWorktrees(ctx.cwd)) {
|
|
352
359
|
const entries = {};
|
|
353
360
|
for (const [key, value] of Object.entries(values.defaults ?? {})) {
|
|
@@ -361,8 +368,18 @@ async function push(ctx) {
|
|
|
361
368
|
continue;
|
|
362
369
|
const dotenv = Bun.file(join(path, ".env"));
|
|
363
370
|
const current = await dotenv.exists() ? await dotenv.text() : "";
|
|
371
|
+
const existing = parseDotenv(current);
|
|
372
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
373
|
+
if (existing[key] === value)
|
|
374
|
+
continue;
|
|
375
|
+
const shown = isMaskedKey(key) ? MASK : formatDotenvValue(value);
|
|
376
|
+
const line = `${key}=${shown}`;
|
|
377
|
+
changes.set(line, [...changes.get(line) ?? [], name]);
|
|
378
|
+
}
|
|
364
379
|
await Bun.write(dotenv, updateDotenv(current, entries));
|
|
365
|
-
|
|
380
|
+
}
|
|
381
|
+
for (const [line, names] of changes) {
|
|
382
|
+
ctx.log(yellow(`\u21BB ${line} \u2192 ${names.join(", ")}`));
|
|
366
383
|
}
|
|
367
384
|
return 0;
|
|
368
385
|
}
|
|
@@ -373,11 +390,16 @@ async function pull(ctx) {
|
|
|
373
390
|
const { path: valuesPath, values } = read;
|
|
374
391
|
const envs = values.envs ??= {};
|
|
375
392
|
const defaults = values.defaults ?? {};
|
|
393
|
+
const pulledNames = [];
|
|
394
|
+
const pulledKeys = new Set;
|
|
376
395
|
for (const { name, path } of await listWorktrees(ctx.cwd)) {
|
|
377
396
|
const dotenv = Bun.file(join(path, ".env"));
|
|
378
397
|
if (!await dotenv.exists())
|
|
379
398
|
continue;
|
|
380
399
|
const parsed = parseDotenv(await dotenv.text());
|
|
400
|
+
pulledNames.push(name);
|
|
401
|
+
for (const key of Object.keys(parsed))
|
|
402
|
+
pulledKeys.add(key);
|
|
381
403
|
for (const [key, byWorktree] of Object.entries(envs)) {
|
|
382
404
|
if (!(key in parsed))
|
|
383
405
|
delete byWorktree[name];
|
|
@@ -392,13 +414,15 @@ async function pull(ctx) {
|
|
|
392
414
|
(envs[key] ??= {})[name] = parsedValue;
|
|
393
415
|
}
|
|
394
416
|
}
|
|
395
|
-
ctx.log(`Pulled ${name}`);
|
|
396
417
|
}
|
|
397
418
|
for (const [key, byWorktree] of Object.entries(envs)) {
|
|
398
419
|
if (Object.keys(byWorktree).length === 0)
|
|
399
420
|
delete envs[key];
|
|
400
421
|
}
|
|
401
422
|
await Bun.write(valuesPath, Bun.YAML.stringify(values, null, 2));
|
|
423
|
+
if (pulledNames.length > 0) {
|
|
424
|
+
ctx.log(green(`\u2193 pulling ${pulledNames.join(", ")} - ${pulledKeys.size} variables`));
|
|
425
|
+
}
|
|
402
426
|
return 0;
|
|
403
427
|
}
|
|
404
428
|
async function isIgnored(dir, file) {
|