veylix-cli 1.3.0 → 1.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 +4 -0
- package/package.json +1 -1
- package/veylix.mjs +33 -1
package/README.md
CHANGED
|
@@ -96,6 +96,7 @@ veylix pull > .env # or write a .env file
|
|
|
96
96
|
| `veylix push [--file .env] [--replace] [--dry-run] [--yes]` | Upload a `.env` file (needs a read-write token). Shows the changes first and asks before deleting anything |
|
|
97
97
|
| `veylix set KEY=value [KEY2=value…]` | Create or update variables |
|
|
98
98
|
| `veylix unset KEY` | Delete a variable |
|
|
99
|
+
| `veylix diff <env> <other-env> [--all] [--fail-on-missing]` | Compare two environments: keys missing on either side (names only) |
|
|
99
100
|
| `veylix help` / `veylix --version` | Show help or the version |
|
|
100
101
|
|
|
101
102
|
Every command accepts `--project <name|id>` and `--env <name>` to override the folder link.
|
|
@@ -119,6 +120,9 @@ veylix pull --output .env.local
|
|
|
119
120
|
veylix push --file .env.production --env production --replace --dry-run # preview only
|
|
120
121
|
veylix push --file .env.production --env production --replace # asks before deleting
|
|
121
122
|
|
|
123
|
+
# Find keys missing from production (exit 1 in CI if any)
|
|
124
|
+
veylix diff development production --fail-on-missing
|
|
125
|
+
|
|
122
126
|
# Quick edits
|
|
123
127
|
veylix set STRIPE_KEY=sk_live_… FEATURE_FLAG=true
|
|
124
128
|
veylix unset LEGACY_API_URL
|
package/package.json
CHANGED
package/veylix.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { homedir } from "node:os";
|
|
|
8
8
|
import { dirname, join, relative } from "node:path";
|
|
9
9
|
import { createInterface } from "node:readline/promises";
|
|
10
10
|
|
|
11
|
-
const VERSION = "1.
|
|
11
|
+
const VERSION = "1.4.0";
|
|
12
12
|
const DEFAULT_URL = "https://veylix-omega.vercel.app";
|
|
13
13
|
const CONFIG_PATH = join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "veylix", "config.json");
|
|
14
14
|
// Per-folder project/environment choice (no secrets, safe to commit).
|
|
@@ -33,6 +33,8 @@ Usage:
|
|
|
33
33
|
the file and asks before deleting (--yes skips the question)
|
|
34
34
|
veylix set KEY=value [KEY2=value…]
|
|
35
35
|
veylix unset KEY
|
|
36
|
+
veylix diff <env> <other-env> [--all] [--fail-on-missing]
|
|
37
|
+
Compare two environments (key names only, never values)
|
|
36
38
|
|
|
37
39
|
Every command accepts --project <name> and --env <name> to override the link.
|
|
38
40
|
|
|
@@ -388,6 +390,36 @@ const commands = {
|
|
|
388
390
|
success(`${where(res)}: ${res.created} created, ${res.updated} updated${res.deleted ? `, ${res.deleted} deleted` : ""}`);
|
|
389
391
|
},
|
|
390
392
|
|
|
393
|
+
async diff(args, flags) {
|
|
394
|
+
const linked = target(flags).env;
|
|
395
|
+
const [envA, envB] = args.length >= 2 ? args : [linked, args[0]];
|
|
396
|
+
if (!envA || !envB) fail("Usage: veylix diff <env> <other-env> (or `veylix diff <other-env>` in a linked folder)");
|
|
397
|
+
const [a, b] = await Promise.all([fetchSecrets({ ...flags, env: envA }), fetchSecrets({ ...flags, env: envB })]);
|
|
398
|
+
const keys = [...new Set([...Object.keys(a.secrets), ...Object.keys(b.secrets)])].sort();
|
|
399
|
+
const missing = [];
|
|
400
|
+
let different = 0;
|
|
401
|
+
let same = 0;
|
|
402
|
+
const width = Math.max(0, ...keys.map((k) => k.length));
|
|
403
|
+
info(`${c(1, `${a.project} › ${a.environment}`)} ↔ ${c(1, b.environment)}`);
|
|
404
|
+
for (const key of keys) {
|
|
405
|
+
if (!(key in b.secrets)) {
|
|
406
|
+
missing.push(key);
|
|
407
|
+
info(` ${c(31, "-")} ${key.padEnd(width)} ${c(31, `missing in ${b.environment}`)}`);
|
|
408
|
+
} else if (!(key in a.secrets)) {
|
|
409
|
+
missing.push(key);
|
|
410
|
+
info(` ${c(31, "-")} ${key.padEnd(width)} ${c(31, `missing in ${a.environment}`)}`);
|
|
411
|
+
} else if (a.secrets[key] !== b.secrets[key]) {
|
|
412
|
+
different++;
|
|
413
|
+
if (flags.all) info(` ${c(33, "~")} ${key.padEnd(width)} ${c(2, "different")}`);
|
|
414
|
+
} else {
|
|
415
|
+
same++;
|
|
416
|
+
if (flags.all) info(` ${c(2, "=")} ${key.padEnd(width)} ${c(2, "same value")}`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
info(c(2, ` ${missing.length} missing · ${different} different · ${same} same${flags.all ? "" : " (--all lists every key)"}`));
|
|
420
|
+
if (missing.length && flags["fail-on-missing"]) process.exit(1);
|
|
421
|
+
},
|
|
422
|
+
|
|
391
423
|
async set(args, flags) {
|
|
392
424
|
const pairs = args.map((a) => a.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/s));
|
|
393
425
|
if (!pairs.length || pairs.some((p) => !p)) fail("Usage: veylix set KEY=value [KEY2=value…]");
|