@penvhq/cli 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/dist/index.cjs +369 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -1
- package/dist/index.d.ts +41 -1
- package/dist/index.js +367 -70
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -376,29 +376,61 @@ function permitsAbsence(node) {
|
|
|
376
376
|
}
|
|
377
377
|
return void 0;
|
|
378
378
|
}
|
|
379
|
-
function leaves(node, path, inherited,
|
|
379
|
+
function leaves(node, path, inherited, out2) {
|
|
380
380
|
const own = permitsAbsence(node);
|
|
381
381
|
const absencePermitted = inherited === true || own === true ? true : combine(inherited, own);
|
|
382
382
|
const shape = shapeOf(unwrap(node));
|
|
383
383
|
if (shape === void 0) {
|
|
384
384
|
if (path.length > 0) {
|
|
385
|
-
|
|
385
|
+
out2.push({ path, absencePermitted, node });
|
|
386
386
|
}
|
|
387
387
|
return;
|
|
388
388
|
}
|
|
389
389
|
for (const key of Object.keys(shape)) {
|
|
390
|
-
leaves(shape[key], [...path, key], absencePermitted,
|
|
390
|
+
leaves(shape[key], [...path, key], absencePermitted, out2);
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
393
|
function combine(left, right) {
|
|
394
394
|
return left === void 0 || right === void 0 ? void 0 : false;
|
|
395
395
|
}
|
|
396
396
|
function declaredLeaves(schema) {
|
|
397
|
-
const
|
|
398
|
-
leaves(schema, [], false,
|
|
399
|
-
return
|
|
397
|
+
const out2 = [];
|
|
398
|
+
leaves(schema, [], false, out2);
|
|
399
|
+
return out2;
|
|
400
400
|
}
|
|
401
|
-
|
|
401
|
+
function defaultValueOf(node) {
|
|
402
|
+
let current = node;
|
|
403
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
404
|
+
const def = defOf(current);
|
|
405
|
+
if (def === void 0) {
|
|
406
|
+
return void 0;
|
|
407
|
+
}
|
|
408
|
+
if (typeOf(current) === "default" || typeOf(current) === "prefault") {
|
|
409
|
+
let value = def.defaultValue;
|
|
410
|
+
if (typeof value === "function") {
|
|
411
|
+
try {
|
|
412
|
+
value = value();
|
|
413
|
+
} catch {
|
|
414
|
+
return void 0;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (typeof value === "string") {
|
|
418
|
+
return JSON.stringify(value);
|
|
419
|
+
}
|
|
420
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
421
|
+
return String(value);
|
|
422
|
+
}
|
|
423
|
+
return void 0;
|
|
424
|
+
}
|
|
425
|
+
const inner = def.innerType;
|
|
426
|
+
if (inner === void 0) {
|
|
427
|
+
return void 0;
|
|
428
|
+
}
|
|
429
|
+
current = inner;
|
|
430
|
+
}
|
|
431
|
+
return void 0;
|
|
432
|
+
}
|
|
433
|
+
var EMPTY_DRIFT = { declared: [], undeclared: [], optional: [] };
|
|
402
434
|
function hasValue(resolution) {
|
|
403
435
|
return resolution.winner !== void 0;
|
|
404
436
|
}
|
|
@@ -406,23 +438,39 @@ function computeDrift(input) {
|
|
|
406
438
|
const { schema, resolutions, config, environment } = input;
|
|
407
439
|
const valued = new Set(resolutions.filter(hasValue).map((resolution) => resolution.parameter));
|
|
408
440
|
const declared = [];
|
|
441
|
+
const optional = [];
|
|
409
442
|
for (const leaf of declaredLeaves(schema)) {
|
|
410
|
-
if (leaf.absencePermitted
|
|
443
|
+
if (leaf.absencePermitted === void 0) {
|
|
411
444
|
continue;
|
|
412
445
|
}
|
|
413
446
|
const path = leaf.path.join(".");
|
|
414
447
|
const ref = refFromAccessPath2(leaf.path);
|
|
448
|
+
const renameRemedy = `Rename the \`${path}\` key in .penv/env.ts \u2014 a parameter name is lower-case, hyphenated, and never a reserved token, so no value file reaches this key.`;
|
|
415
449
|
if (ref === void 0 || isReservedToken2(ref.name, config)) {
|
|
416
|
-
|
|
417
|
-
subject: path,
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
450
|
+
if (leaf.absencePermitted) {
|
|
451
|
+
optional.push({ subject: path, remedy: renameRemedy });
|
|
452
|
+
} else {
|
|
453
|
+
declared.push({
|
|
454
|
+
subject: path,
|
|
455
|
+
remedy: renameRemedy,
|
|
456
|
+
detail: "declared, no filename reaches it"
|
|
457
|
+
});
|
|
458
|
+
}
|
|
421
459
|
continue;
|
|
422
460
|
}
|
|
423
461
|
if (valued.has(parameterId2(ref))) {
|
|
424
462
|
continue;
|
|
425
463
|
}
|
|
464
|
+
if (leaf.absencePermitted) {
|
|
465
|
+
const defaultValue = defaultValueOf(leaf.node);
|
|
466
|
+
optional.push({
|
|
467
|
+
subject: parameterId2(ref),
|
|
468
|
+
ref,
|
|
469
|
+
...defaultValue === void 0 ? {} : { defaultValue },
|
|
470
|
+
remedy: `penv set ${[...ref.namespace, ref.name].join("/")} --env ${environment}`
|
|
471
|
+
});
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
426
474
|
declared.push({
|
|
427
475
|
subject: parameterId2(ref),
|
|
428
476
|
ref,
|
|
@@ -440,28 +488,91 @@ function computeDrift(input) {
|
|
|
440
488
|
variable: variableName(resolution.ref, config)
|
|
441
489
|
});
|
|
442
490
|
}
|
|
443
|
-
return { declared, undeclared };
|
|
491
|
+
return { declared, undeclared, optional };
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// src/style.ts
|
|
495
|
+
function supportsColor(stream) {
|
|
496
|
+
const env = process.env;
|
|
497
|
+
if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
if (env.FORCE_COLOR !== void 0 && env.FORCE_COLOR !== "" && env.FORCE_COLOR !== "0") {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
if (env.TERM === "dumb") {
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
return stream.isTTY === true;
|
|
507
|
+
}
|
|
508
|
+
var ESC = `${String.fromCharCode(27)}[`;
|
|
509
|
+
var identity = (text) => text;
|
|
510
|
+
function sgr(open, close, on) {
|
|
511
|
+
if (!on) {
|
|
512
|
+
return identity;
|
|
513
|
+
}
|
|
514
|
+
const opener = `${ESC}${open}m`;
|
|
515
|
+
const closer = `${ESC}${close}m`;
|
|
516
|
+
return (text) => `${opener}${text}${closer}`;
|
|
517
|
+
}
|
|
518
|
+
function paletteFor(stream) {
|
|
519
|
+
const on = supportsColor(stream);
|
|
520
|
+
return {
|
|
521
|
+
enabled: on,
|
|
522
|
+
bold: sgr(1, 22, on),
|
|
523
|
+
dim: sgr(2, 22, on),
|
|
524
|
+
red: sgr(31, 39, on),
|
|
525
|
+
green: sgr(32, 39, on),
|
|
526
|
+
yellow: sgr(33, 39, on),
|
|
527
|
+
cyan: sgr(36, 39, on),
|
|
528
|
+
magenta: sgr(35, 39, on)
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
var out = paletteFor(process.stdout);
|
|
532
|
+
var err = paletteFor(process.stderr);
|
|
533
|
+
var STYLE_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
534
|
+
function visibleWidth(text) {
|
|
535
|
+
return text.replace(STYLE_PATTERN, "").length;
|
|
536
|
+
}
|
|
537
|
+
function padVisible(text, width) {
|
|
538
|
+
const missing = width - visibleWidth(text);
|
|
539
|
+
return missing > 0 ? text + " ".repeat(missing) : text;
|
|
444
540
|
}
|
|
445
541
|
|
|
446
542
|
// src/ui.ts
|
|
447
543
|
import { PenvError as PenvError3 } from "@penvhq/core";
|
|
448
544
|
var CHECK = "\u2713";
|
|
449
545
|
var WARN = "\u26A0";
|
|
546
|
+
var CROSS = "\u2717";
|
|
450
547
|
var UNKNOWN = "?";
|
|
548
|
+
function paintGlyph(glyph) {
|
|
549
|
+
switch (glyph) {
|
|
550
|
+
case CHECK:
|
|
551
|
+
return out.green(glyph);
|
|
552
|
+
case WARN:
|
|
553
|
+
return out.yellow(glyph);
|
|
554
|
+
case CROSS:
|
|
555
|
+
return out.red(glyph);
|
|
556
|
+
case UNKNOWN:
|
|
557
|
+
return out.dim(glyph);
|
|
558
|
+
default:
|
|
559
|
+
return glyph;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
451
562
|
var NOTE_COLUMN = 29;
|
|
452
563
|
function widest(values) {
|
|
453
|
-
return values.reduce((max, value) => Math.max(max, value
|
|
564
|
+
return values.reduce((max, value) => Math.max(max, visibleWidth(value)), 0);
|
|
454
565
|
}
|
|
455
566
|
function formatRows(rows) {
|
|
456
567
|
const labelWidth = widest(rows.map((row) => row.label)) + 2;
|
|
457
568
|
const detailed = rows.filter((row) => row.detail !== void 0);
|
|
458
569
|
const subjectWidth = widest(detailed.map((row) => row.subject ?? "")) + 1;
|
|
459
570
|
return rows.map((row) => {
|
|
460
|
-
const head = `${row.glyph} ${row.label
|
|
571
|
+
const head = `${paintGlyph(row.glyph)} ${padVisible(row.label, labelWidth)}`;
|
|
461
572
|
if (row.detail === void 0) {
|
|
462
573
|
return `${head}${row.subject ?? ""}`.trimEnd();
|
|
463
574
|
}
|
|
464
|
-
return `${head}${(row.subject ?? ""
|
|
575
|
+
return `${head}${padVisible(row.subject ?? "", subjectWidth)}${out.dim(row.detail)}`.trimEnd();
|
|
465
576
|
});
|
|
466
577
|
}
|
|
467
578
|
function columns(rows, gap = 2) {
|
|
@@ -471,18 +582,31 @@ function columns(rows, gap = 2) {
|
|
|
471
582
|
widths.push(widest(rows.map((row) => row[column] ?? "")) + gap);
|
|
472
583
|
}
|
|
473
584
|
return rows.map(
|
|
474
|
-
(row) => row.map(
|
|
585
|
+
(row) => row.map(
|
|
586
|
+
(cell, index) => index === row.length - 1 ? cell : padVisible(cell, widths[index] ?? 0)
|
|
587
|
+
).join("").trimEnd()
|
|
475
588
|
);
|
|
476
589
|
}
|
|
477
590
|
function formatSteps(steps) {
|
|
478
591
|
return steps.map((step) => {
|
|
479
592
|
if (step.note === void 0) {
|
|
480
|
-
return `${step.glyph} ${step.text}`;
|
|
593
|
+
return `${paintGlyph(step.glyph)} ${step.text}`;
|
|
481
594
|
}
|
|
482
|
-
const text = step.text
|
|
483
|
-
return `${step.glyph} ${text}${step.note}`;
|
|
595
|
+
const text = visibleWidth(step.text) >= NOTE_COLUMN ? `${step.text} ` : padVisible(step.text, NOTE_COLUMN);
|
|
596
|
+
return `${paintGlyph(step.glyph)} ${text}${out.dim(step.note)}`;
|
|
484
597
|
});
|
|
485
598
|
}
|
|
599
|
+
function heading(command, context) {
|
|
600
|
+
return context === void 0 ? out.bold(command) : `${out.bold(command)} ${out.dim(`\xB7 ${context}`)}`;
|
|
601
|
+
}
|
|
602
|
+
function tip(text) {
|
|
603
|
+
return ` ${out.cyan("\u2192")} ${text}`;
|
|
604
|
+
}
|
|
605
|
+
function prompt(subject, context) {
|
|
606
|
+
const head = `${out.cyan("?")} ${out.bold(subject)}`;
|
|
607
|
+
const tail = out.dim("\u203A ");
|
|
608
|
+
return context === void 0 ? `${head} ${tail}` : `${head} ${out.dim(`\xB7 ${context}`)} ${tail}`;
|
|
609
|
+
}
|
|
486
610
|
function write(lines) {
|
|
487
611
|
for (const line of lines) {
|
|
488
612
|
process.stdout.write(`${line}
|
|
@@ -491,13 +615,26 @@ function write(lines) {
|
|
|
491
615
|
}
|
|
492
616
|
function reportError(error) {
|
|
493
617
|
if (error instanceof PenvError3) {
|
|
494
|
-
|
|
618
|
+
const suffix = error.remedy === void 0 ? void 0 : `
|
|
619
|
+
${error.remedy}`;
|
|
620
|
+
const message = suffix !== void 0 && error.message.endsWith(suffix) ? error.message.slice(0, -suffix.length) : error.message;
|
|
621
|
+
process.stderr.write(`${err.red(CROSS)} ${message}
|
|
622
|
+
`);
|
|
623
|
+
if (error.remedy !== void 0) {
|
|
624
|
+
process.stderr.write(` ${err.cyan("\u2192")} ${error.remedy}
|
|
495
625
|
`);
|
|
626
|
+
}
|
|
496
627
|
} else if (error instanceof Error) {
|
|
497
|
-
process.stderr.write(`${
|
|
628
|
+
process.stderr.write(`${err.red(CROSS)} ${error.message}
|
|
629
|
+
`);
|
|
630
|
+
const stack = error.stack;
|
|
631
|
+
const frames = stack?.startsWith(`${error.name}: ${error.message}`) ? stack.slice(`${error.name}: ${error.message}`.length).replace(/^\n/, "") : stack;
|
|
632
|
+
if (frames !== void 0 && frames !== "") {
|
|
633
|
+
process.stderr.write(`${err.dim(frames)}
|
|
498
634
|
`);
|
|
635
|
+
}
|
|
499
636
|
} else {
|
|
500
|
-
process.stderr.write(`${String(error)}
|
|
637
|
+
process.stderr.write(`${err.red(CROSS)} ${String(error)}
|
|
501
638
|
`);
|
|
502
639
|
}
|
|
503
640
|
process.exitCode = 1;
|
|
@@ -905,18 +1042,26 @@ function renderValidate(result2) {
|
|
|
905
1042
|
]);
|
|
906
1043
|
}
|
|
907
1044
|
const rows = result2.issues.map((issue) => ({
|
|
908
|
-
glyph:
|
|
1045
|
+
glyph: CROSS,
|
|
909
1046
|
label: LABELS[issue.kind],
|
|
910
1047
|
subject: issue.subject,
|
|
911
1048
|
detail: issue.message
|
|
912
1049
|
}));
|
|
913
1050
|
const lines = formatRows(rows);
|
|
914
|
-
const remedies = [...new Set(result2.issues.map((issue) => issue.remedy))]
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1051
|
+
const remedies = [...new Set(result2.issues.map((issue) => issue.remedy))].filter(
|
|
1052
|
+
(remedy) => remedy !== void 0
|
|
1053
|
+
);
|
|
1054
|
+
if (remedies.length > 0) {
|
|
1055
|
+
lines.push("");
|
|
1056
|
+
for (const remedy of remedies) {
|
|
1057
|
+
lines.push(tip(remedy));
|
|
918
1058
|
}
|
|
919
1059
|
}
|
|
1060
|
+
const count = result2.issues.length;
|
|
1061
|
+
lines.push(
|
|
1062
|
+
"",
|
|
1063
|
+
`${out.red(CROSS)} ${count} ${count === 1 ? "issue" : "issues"} for environment ${result2.environment}`
|
|
1064
|
+
);
|
|
920
1065
|
return lines;
|
|
921
1066
|
}
|
|
922
1067
|
var validateCommand = defineCommand2({
|
|
@@ -1671,22 +1816,51 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
1671
1816
|
}
|
|
1672
1817
|
];
|
|
1673
1818
|
}
|
|
1819
|
+
var GLYPHS = {
|
|
1820
|
+
pass: CHECK,
|
|
1821
|
+
warning: WARN,
|
|
1822
|
+
failure: CROSS,
|
|
1823
|
+
unknown: UNKNOWN
|
|
1824
|
+
};
|
|
1825
|
+
function summarize(report) {
|
|
1826
|
+
const counts = { pass: 0, warning: 0, failure: 0, unknown: 0 };
|
|
1827
|
+
for (const finding of report.findings) {
|
|
1828
|
+
counts[finding.severity] += 1;
|
|
1829
|
+
}
|
|
1830
|
+
const parts = [out.green(`${counts.pass} passed`)];
|
|
1831
|
+
if (counts.warning > 0) {
|
|
1832
|
+
parts.push(out.yellow(`${counts.warning} ${counts.warning === 1 ? "warning" : "warnings"}`));
|
|
1833
|
+
}
|
|
1834
|
+
if (counts.failure > 0) {
|
|
1835
|
+
parts.push(out.red(`${counts.failure} ${counts.failure === 1 ? "failure" : "failures"}`));
|
|
1836
|
+
}
|
|
1837
|
+
if (counts.unknown > 0) {
|
|
1838
|
+
parts.push(out.dim(`${counts.unknown} could not be checked`));
|
|
1839
|
+
}
|
|
1840
|
+
const verdict = report.ok ? out.green(CHECK) : out.red(CROSS);
|
|
1841
|
+
return `${verdict} ${report.findings.length} checks: ${parts.join(out.dim(" \xB7 "))}`;
|
|
1842
|
+
}
|
|
1674
1843
|
function renderDoctor(report) {
|
|
1675
1844
|
const rows = report.findings.map((finding) => ({
|
|
1676
|
-
glyph: finding.severity
|
|
1845
|
+
glyph: GLYPHS[finding.severity],
|
|
1677
1846
|
label: finding.label,
|
|
1678
1847
|
...finding.subject === void 0 ? {} : { subject: finding.subject },
|
|
1679
1848
|
...finding.detail === void 0 ? {} : { detail: finding.detail }
|
|
1680
1849
|
}));
|
|
1681
|
-
const lines =
|
|
1850
|
+
const lines = [heading("penv doctor", `environment ${report.environment}`), ""];
|
|
1851
|
+
lines.push(...formatRows(rows));
|
|
1682
1852
|
const remedies = [
|
|
1683
1853
|
...new Set(
|
|
1684
1854
|
report.findings.filter((finding) => finding.severity !== "pass").map((finding) => finding.remedy).filter((remedy) => remedy !== void 0)
|
|
1685
1855
|
)
|
|
1686
1856
|
];
|
|
1687
|
-
|
|
1688
|
-
lines.push(
|
|
1857
|
+
if (remedies.length > 0) {
|
|
1858
|
+
lines.push("");
|
|
1859
|
+
for (const remedy of remedies) {
|
|
1860
|
+
lines.push(tip(remedy));
|
|
1861
|
+
}
|
|
1689
1862
|
}
|
|
1863
|
+
lines.push("", summarize(report));
|
|
1690
1864
|
return lines;
|
|
1691
1865
|
}
|
|
1692
1866
|
var doctorCommand = defineCommand3({
|
|
@@ -1984,9 +2158,52 @@ var decryptCommand = defineCommand5({
|
|
|
1984
2158
|
});
|
|
1985
2159
|
|
|
1986
2160
|
// src/commands/fill.ts
|
|
1987
|
-
import { createInterface } from "readline/promises";
|
|
1988
2161
|
import { PenvError as PenvError8 } from "@penvhq/core";
|
|
1989
2162
|
import { defineCommand as defineCommand6 } from "citty";
|
|
2163
|
+
|
|
2164
|
+
// src/input.ts
|
|
2165
|
+
import { createInterface } from "readline";
|
|
2166
|
+
function lineReader(input = process.stdin, output = process.stdout) {
|
|
2167
|
+
const rl = createInterface({ input, output });
|
|
2168
|
+
const buffered = [];
|
|
2169
|
+
const waiting = [];
|
|
2170
|
+
let closed = false;
|
|
2171
|
+
rl.on("line", (line) => {
|
|
2172
|
+
const next = waiting.shift();
|
|
2173
|
+
if (next === void 0) {
|
|
2174
|
+
buffered.push(line);
|
|
2175
|
+
} else {
|
|
2176
|
+
next(line);
|
|
2177
|
+
}
|
|
2178
|
+
});
|
|
2179
|
+
rl.on("close", () => {
|
|
2180
|
+
closed = true;
|
|
2181
|
+
for (const next of waiting.splice(0)) {
|
|
2182
|
+
next(void 0);
|
|
2183
|
+
}
|
|
2184
|
+
});
|
|
2185
|
+
return {
|
|
2186
|
+
ask(question) {
|
|
2187
|
+
const early = buffered.shift();
|
|
2188
|
+
if (early !== void 0) {
|
|
2189
|
+
return Promise.resolve(early);
|
|
2190
|
+
}
|
|
2191
|
+
if (closed) {
|
|
2192
|
+
return Promise.resolve(void 0);
|
|
2193
|
+
}
|
|
2194
|
+
rl.setPrompt(question);
|
|
2195
|
+
rl.prompt();
|
|
2196
|
+
return new Promise((resolve7) => {
|
|
2197
|
+
waiting.push(resolve7);
|
|
2198
|
+
});
|
|
2199
|
+
},
|
|
2200
|
+
close() {
|
|
2201
|
+
rl.close();
|
|
2202
|
+
}
|
|
2203
|
+
};
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
// src/commands/fill.ts
|
|
1990
2207
|
var BLOCKING = /* @__PURE__ */ new Set(["config", "collision", "reserved"]);
|
|
1991
2208
|
async function runFill(options) {
|
|
1992
2209
|
const validation = await runValidate({
|
|
@@ -2008,6 +2225,7 @@ ${detail}`,
|
|
|
2008
2225
|
}
|
|
2009
2226
|
const written = [];
|
|
2010
2227
|
const skipped2 = [];
|
|
2228
|
+
const kept = [];
|
|
2011
2229
|
const unreachable = [];
|
|
2012
2230
|
for (const drift of validation.drift.declared) {
|
|
2013
2231
|
if (drift.ref === void 0) {
|
|
@@ -2016,7 +2234,12 @@ ${detail}`,
|
|
|
2016
2234
|
}
|
|
2017
2235
|
const ref = drift.ref;
|
|
2018
2236
|
const key = [...ref.namespace, ref.name].join("/");
|
|
2019
|
-
const value = await options.ask({
|
|
2237
|
+
const value = await options.ask({
|
|
2238
|
+
parameter: key,
|
|
2239
|
+
environment,
|
|
2240
|
+
secret: false,
|
|
2241
|
+
optional: false
|
|
2242
|
+
});
|
|
2020
2243
|
if (value === void 0 || value === "") {
|
|
2021
2244
|
skipped2.push(drift.subject);
|
|
2022
2245
|
continue;
|
|
@@ -2028,17 +2251,45 @@ ${detail}`,
|
|
|
2028
2251
|
encrypted: result2.encrypted
|
|
2029
2252
|
});
|
|
2030
2253
|
}
|
|
2031
|
-
|
|
2254
|
+
for (const item of validation.drift.optional) {
|
|
2255
|
+
if (item.ref === void 0) {
|
|
2256
|
+
unreachable.push({ subject: item.subject, remedy: item.remedy });
|
|
2257
|
+
continue;
|
|
2258
|
+
}
|
|
2259
|
+
const ref = item.ref;
|
|
2260
|
+
const key = [...ref.namespace, ref.name].join("/");
|
|
2261
|
+
const value = await options.ask({
|
|
2262
|
+
parameter: key,
|
|
2263
|
+
environment,
|
|
2264
|
+
secret: false,
|
|
2265
|
+
optional: true,
|
|
2266
|
+
...item.defaultValue === void 0 ? {} : { defaultValue: item.defaultValue }
|
|
2267
|
+
});
|
|
2268
|
+
if (value === void 0 || value === "") {
|
|
2269
|
+
kept.push(item.subject);
|
|
2270
|
+
continue;
|
|
2271
|
+
}
|
|
2272
|
+
const result2 = await runSet({ cwd: options.cwd, key, value, environment });
|
|
2273
|
+
written.push({
|
|
2274
|
+
parameter: item.subject,
|
|
2275
|
+
location: result2.location,
|
|
2276
|
+
encrypted: result2.encrypted
|
|
2277
|
+
});
|
|
2278
|
+
}
|
|
2279
|
+
return { environment, written, skipped: skipped2, kept, unreachable };
|
|
2032
2280
|
}
|
|
2033
2281
|
function summaryLine(result2) {
|
|
2034
2282
|
const filled = result2.written.length;
|
|
2035
|
-
if (filled === 0 && result2.skipped.length === 0 && result2.unreachable.length === 0) {
|
|
2036
|
-
return
|
|
2283
|
+
if (filled === 0 && result2.skipped.length === 0 && result2.kept.length === 0 && result2.unreachable.length === 0) {
|
|
2284
|
+
return `${out.green(CHECK)} Nothing to fill for environment ${result2.environment}: every declared parameter has a value`;
|
|
2037
2285
|
}
|
|
2038
2286
|
const parts = [`${filled} written`];
|
|
2039
2287
|
if (result2.skipped.length > 0) {
|
|
2040
2288
|
parts.push(`${result2.skipped.length} skipped`);
|
|
2041
2289
|
}
|
|
2290
|
+
if (result2.kept.length > 0) {
|
|
2291
|
+
parts.push(`${result2.kept.length} left to the schema's defaults`);
|
|
2292
|
+
}
|
|
2042
2293
|
if (result2.unreachable.length > 0) {
|
|
2043
2294
|
parts.push(`${result2.unreachable.length} unreachable`);
|
|
2044
2295
|
}
|
|
@@ -2059,6 +2310,16 @@ function renderFill(result2) {
|
|
|
2059
2310
|
lines.push(summaryLine(result2));
|
|
2060
2311
|
return lines;
|
|
2061
2312
|
}
|
|
2313
|
+
function contextFor(prompt2) {
|
|
2314
|
+
if (!prompt2.optional) {
|
|
2315
|
+
return `${prompt2.environment}, Enter to skip`;
|
|
2316
|
+
}
|
|
2317
|
+
if (prompt2.defaultValue === void 0) {
|
|
2318
|
+
return `${prompt2.environment} \xB7 optional, Enter leaves it unset`;
|
|
2319
|
+
}
|
|
2320
|
+
const shown = prompt2.defaultValue.length > 24 ? `${prompt2.defaultValue.slice(0, 23)}\u2026` : prompt2.defaultValue;
|
|
2321
|
+
return `${prompt2.environment} \xB7 optional, Enter keeps ${shown}`;
|
|
2322
|
+
}
|
|
2062
2323
|
var fillCommand = defineCommand6({
|
|
2063
2324
|
meta: {
|
|
2064
2325
|
name: "fill",
|
|
@@ -2069,8 +2330,8 @@ var fillCommand = defineCommand6({
|
|
|
2069
2330
|
},
|
|
2070
2331
|
run({ args }) {
|
|
2071
2332
|
return guard(async () => {
|
|
2072
|
-
const
|
|
2073
|
-
const ask = (
|
|
2333
|
+
const reader = lineReader();
|
|
2334
|
+
const ask = (prompt2) => reader.ask(prompt(prompt2.parameter, contextFor(prompt2)));
|
|
2074
2335
|
try {
|
|
2075
2336
|
write(
|
|
2076
2337
|
renderFill(
|
|
@@ -2082,7 +2343,7 @@ var fillCommand = defineCommand6({
|
|
|
2082
2343
|
)
|
|
2083
2344
|
);
|
|
2084
2345
|
} finally {
|
|
2085
|
-
|
|
2346
|
+
reader.close();
|
|
2086
2347
|
}
|
|
2087
2348
|
});
|
|
2088
2349
|
}
|
|
@@ -2251,12 +2512,14 @@ async function runExplain(options) {
|
|
|
2251
2512
|
function renderExplain(explanation) {
|
|
2252
2513
|
const target = explanation.location === void 0 ? "nothing" : `${PENV_DIR}/${explanation.location}`;
|
|
2253
2514
|
const rows = explanation.candidates.map((candidate) => [
|
|
2254
|
-
candidate.location,
|
|
2255
|
-
candidate.wins ? "present, wins" :
|
|
2515
|
+
candidate.wins ? candidate.location : out.dim(candidate.location),
|
|
2516
|
+
candidate.wins ? out.green("present, wins") : out.dim(
|
|
2517
|
+
candidate.present ? candidate.skipped ?? "present" : candidate.skipped ?? "absent"
|
|
2518
|
+
)
|
|
2256
2519
|
]);
|
|
2257
2520
|
return [
|
|
2258
|
-
`${explanation.parameter} resolves to ${target} for environment ${explanation.environment}`,
|
|
2259
|
-
...explanation.undecryptable === void 0 ? [] : [` penv cannot decrypt it: ${explanation.undecryptable}`],
|
|
2521
|
+
`${out.bold(explanation.parameter)} resolves to ${target} for environment ${explanation.environment}`,
|
|
2522
|
+
...explanation.undecryptable === void 0 ? [] : [` ${out.red(CROSS)} penv cannot decrypt it: ${explanation.undecryptable}`],
|
|
2260
2523
|
"",
|
|
2261
2524
|
...columns(rows).map((line) => ` ${line}`)
|
|
2262
2525
|
];
|
|
@@ -2561,23 +2824,27 @@ function planInit(root, flags = {}) {
|
|
|
2561
2824
|
function renderPlan(plan2) {
|
|
2562
2825
|
const rows = [];
|
|
2563
2826
|
rows.push([
|
|
2564
|
-
"
|
|
2565
|
-
plan2.suggestedEnvironments.length === 0 ? "" : `[${plan2.suggestedEnvironments.join(", ")}]
|
|
2566
|
-
|
|
2827
|
+
` ${out.dim("environments")}`,
|
|
2828
|
+
plan2.suggestedEnvironments.length === 0 ? "" : out.cyan(`[${plan2.suggestedEnvironments.join(", ")}]`),
|
|
2829
|
+
out.dim(
|
|
2830
|
+
plan2.suggestedEnvironments.length === 0 ? "\u2190 name them, or Enter to leave the whitelist empty" : "\u2190 from your .env files; edit, or Enter to accept"
|
|
2831
|
+
)
|
|
2567
2832
|
]);
|
|
2568
2833
|
rows.push([
|
|
2569
|
-
"
|
|
2834
|
+
` ${out.dim("schemaFile")}`,
|
|
2570
2835
|
plan2.decisions.schemaFile,
|
|
2571
|
-
plan2.decisions.schemaFile === DEFAULT_SCHEMA_FILE2 ? "" : `(default: ${DEFAULT_SCHEMA_FILE2})`
|
|
2836
|
+
plan2.decisions.schemaFile === DEFAULT_SCHEMA_FILE2 ? "" : out.dim(`(default: ${DEFAULT_SCHEMA_FILE2})`)
|
|
2572
2837
|
]);
|
|
2573
2838
|
for (const prefix of plan2.decisions.publicPrefixes) {
|
|
2574
|
-
rows.push(["
|
|
2839
|
+
rows.push([` ${out.dim("publicPrefix")}`, prefix, ""]);
|
|
2575
2840
|
}
|
|
2576
|
-
const headline =
|
|
2841
|
+
const headline = out.bold(
|
|
2842
|
+
plan2.detected === void 0 ? "No framework detected in package.json." : `Detected ${plan2.detected.name}.`
|
|
2843
|
+
);
|
|
2577
2844
|
return [headline, "", ...columns(rows), ""];
|
|
2578
2845
|
}
|
|
2579
2846
|
function environmentsHint(plan2) {
|
|
2580
|
-
return plan2.suggestedEnvironments.length === 0 ? "environments
|
|
2847
|
+
return plan2.suggestedEnvironments.length === 0 ? prompt("environments", "comma-separated, Enter for none") : prompt("environments", 'Enter to accept, "none" for an empty whitelist');
|
|
2581
2848
|
}
|
|
2582
2849
|
async function promptForDecisions(plan2, io) {
|
|
2583
2850
|
for (const line of renderPlan(plan2)) {
|
|
@@ -2592,7 +2859,7 @@ async function promptForDecisions(plan2, io) {
|
|
|
2592
2859
|
);
|
|
2593
2860
|
io.write("");
|
|
2594
2861
|
}
|
|
2595
|
-
const proceed = (await io.ask("Proceed?
|
|
2862
|
+
const proceed = (await io.ask(prompt("Proceed?", "Y/n"))).trim().toLowerCase();
|
|
2596
2863
|
if (proceed.length > 0 && proceed !== "y" && proceed !== "yes") {
|
|
2597
2864
|
return void 0;
|
|
2598
2865
|
}
|
|
@@ -2997,7 +3264,8 @@ function renderInit(result2) {
|
|
|
2997
3264
|
return [
|
|
2998
3265
|
...formatSteps(steps),
|
|
2999
3266
|
"",
|
|
3000
|
-
|
|
3267
|
+
`${out.green(CHECK)} ${out.bold("Done.")} Declare your parameters in ${result2.decisions.schemaFile}, then:`,
|
|
3268
|
+
tip(out.cyan("penv set <key>")),
|
|
3001
3269
|
...result2.decisions.environments.length === 0 ? [
|
|
3002
3270
|
`Then declare your environments in ${CONFIG_FILE}: penv leaves the whitelist empty rather than inventing one, and every command needs it.`
|
|
3003
3271
|
] : []
|
|
@@ -3466,20 +3734,28 @@ function runKeyCreate(options) {
|
|
|
3466
3734
|
function renderKeyCreate(result2) {
|
|
3467
3735
|
if (result2.source === "keychain") {
|
|
3468
3736
|
return [
|
|
3469
|
-
|
|
3737
|
+
`${out.green(CHECK)} A new key for environment ${result2.environment}, stored in your OS keychain as \`${result2.id}\`.`,
|
|
3470
3738
|
"",
|
|
3471
|
-
|
|
3472
|
-
|
|
3739
|
+
out.dim(
|
|
3740
|
+
"penv kept no copy. Anything sealed under it is unreadable without your keychain, and running"
|
|
3741
|
+
),
|
|
3742
|
+
out.dim(
|
|
3743
|
+
"`penv key create` again would replace it \u2014 so it lives in exactly one place, on this machine."
|
|
3744
|
+
)
|
|
3473
3745
|
];
|
|
3474
3746
|
}
|
|
3475
3747
|
return [
|
|
3476
|
-
|
|
3748
|
+
`${out.green(CHECK)} A new key for environment ${result2.environment}. penv did not store it.`,
|
|
3477
3749
|
"",
|
|
3478
|
-
` ${result2.variable}=${result2.key}`,
|
|
3750
|
+
` ${out.cyan(`${result2.variable}=${result2.key}`)}`,
|
|
3479
3751
|
"",
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3752
|
+
out.dim(
|
|
3753
|
+
"Export it where penv runs, and put it wherever this environment's secrets already live \u2014"
|
|
3754
|
+
),
|
|
3755
|
+
out.dim(
|
|
3756
|
+
"a KMS, your CI's secret store, a password manager. Anything sealed under it is unreadable"
|
|
3757
|
+
),
|
|
3758
|
+
out.dim("without it, and penv keeps no copy to fall back on.")
|
|
3483
3759
|
];
|
|
3484
3760
|
}
|
|
3485
3761
|
var keyCommand = defineCommand11({
|
|
@@ -3547,11 +3823,31 @@ async function runList(options) {
|
|
|
3547
3823
|
}
|
|
3548
3824
|
return { environment, parameters };
|
|
3549
3825
|
}
|
|
3826
|
+
function paintScope(entry) {
|
|
3827
|
+
if (entry.scope === "absent" || entry.viaUnscopedFallback) {
|
|
3828
|
+
return out.yellow(entry.scope);
|
|
3829
|
+
}
|
|
3830
|
+
return out.green(entry.scope);
|
|
3831
|
+
}
|
|
3550
3832
|
function renderList(result2) {
|
|
3551
3833
|
if (result2.parameters.length === 0) {
|
|
3552
|
-
return [
|
|
3834
|
+
return [
|
|
3835
|
+
`No parameters in ${PENV_DIR}/ for environment ${result2.environment}.`,
|
|
3836
|
+
tip(`penv set <key> --env ${result2.environment}`)
|
|
3837
|
+
];
|
|
3553
3838
|
}
|
|
3554
|
-
|
|
3839
|
+
const header = ["parameter", "scope", "variable", ""].map((cell) => out.dim(cell));
|
|
3840
|
+
const rows = result2.parameters.map((entry) => [
|
|
3841
|
+
entry.parameter,
|
|
3842
|
+
paintScope(entry),
|
|
3843
|
+
entry.variable,
|
|
3844
|
+
entry.encrypted ? out.dim("encrypted") : ""
|
|
3845
|
+
]);
|
|
3846
|
+
return [
|
|
3847
|
+
heading("penv list", `environment ${result2.environment}`),
|
|
3848
|
+
"",
|
|
3849
|
+
...columns([header, ...rows])
|
|
3850
|
+
];
|
|
3555
3851
|
}
|
|
3556
3852
|
var listCommand = defineCommand12({
|
|
3557
3853
|
meta: { name: "list", description: "List parameters" },
|
|
@@ -3699,8 +3995,9 @@ function renderMove(result2) {
|
|
|
3699
3995
|
const lines = formatRows(rows);
|
|
3700
3996
|
lines.push(
|
|
3701
3997
|
"",
|
|
3702
|
-
|
|
3703
|
-
|
|
3998
|
+
tip(
|
|
3999
|
+
`.penv/env.ts still declares \`${result2.schema.was}\` \u2014 rename it to \`${result2.schema.now}\`, or \`penv validate\` will report the value as unused and the declaration as unset.`
|
|
4000
|
+
)
|
|
3704
4001
|
);
|
|
3705
4002
|
return lines;
|
|
3706
4003
|
}
|
|
@@ -4235,9 +4532,9 @@ function renderDrift(drift, environment) {
|
|
|
4235
4532
|
if (rows.length === 0) {
|
|
4236
4533
|
return [];
|
|
4237
4534
|
}
|
|
4238
|
-
const lines = ["", `Schema and tree differ for ${environment}
|
|
4535
|
+
const lines = ["", out.bold(`Schema and tree differ for ${environment}:`), ...formatRows(rows)];
|
|
4239
4536
|
for (const remedy of new Set(drift.declared.map((item) => item.remedy))) {
|
|
4240
|
-
lines.push(
|
|
4537
|
+
lines.push(tip(remedy));
|
|
4241
4538
|
}
|
|
4242
4539
|
return lines;
|
|
4243
4540
|
}
|
|
@@ -4268,7 +4565,7 @@ var watchCommand = defineCommand17({
|
|
|
4268
4565
|
process.exitCode = previous;
|
|
4269
4566
|
}
|
|
4270
4567
|
});
|
|
4271
|
-
write([
|
|
4568
|
+
write([`Watching .penv/ and penv.config.ts. ${out.dim("Ctrl-C to stop.")}`]);
|
|
4272
4569
|
await new Promise((resolve7) => {
|
|
4273
4570
|
process.once("SIGINT", () => {
|
|
4274
4571
|
handle.close();
|