@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.cjs
CHANGED
|
@@ -396,29 +396,61 @@ function permitsAbsence(node) {
|
|
|
396
396
|
}
|
|
397
397
|
return void 0;
|
|
398
398
|
}
|
|
399
|
-
function leaves(node, path, inherited,
|
|
399
|
+
function leaves(node, path, inherited, out2) {
|
|
400
400
|
const own = permitsAbsence(node);
|
|
401
401
|
const absencePermitted = inherited === true || own === true ? true : combine(inherited, own);
|
|
402
402
|
const shape = shapeOf(unwrap(node));
|
|
403
403
|
if (shape === void 0) {
|
|
404
404
|
if (path.length > 0) {
|
|
405
|
-
|
|
405
|
+
out2.push({ path, absencePermitted, node });
|
|
406
406
|
}
|
|
407
407
|
return;
|
|
408
408
|
}
|
|
409
409
|
for (const key of Object.keys(shape)) {
|
|
410
|
-
leaves(shape[key], [...path, key], absencePermitted,
|
|
410
|
+
leaves(shape[key], [...path, key], absencePermitted, out2);
|
|
411
411
|
}
|
|
412
412
|
}
|
|
413
413
|
function combine(left, right) {
|
|
414
414
|
return left === void 0 || right === void 0 ? void 0 : false;
|
|
415
415
|
}
|
|
416
416
|
function declaredLeaves(schema) {
|
|
417
|
-
const
|
|
418
|
-
leaves(schema, [], false,
|
|
419
|
-
return
|
|
417
|
+
const out2 = [];
|
|
418
|
+
leaves(schema, [], false, out2);
|
|
419
|
+
return out2;
|
|
420
420
|
}
|
|
421
|
-
|
|
421
|
+
function defaultValueOf(node) {
|
|
422
|
+
let current = node;
|
|
423
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
424
|
+
const def = defOf(current);
|
|
425
|
+
if (def === void 0) {
|
|
426
|
+
return void 0;
|
|
427
|
+
}
|
|
428
|
+
if (typeOf(current) === "default" || typeOf(current) === "prefault") {
|
|
429
|
+
let value = def.defaultValue;
|
|
430
|
+
if (typeof value === "function") {
|
|
431
|
+
try {
|
|
432
|
+
value = value();
|
|
433
|
+
} catch {
|
|
434
|
+
return void 0;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (typeof value === "string") {
|
|
438
|
+
return JSON.stringify(value);
|
|
439
|
+
}
|
|
440
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
441
|
+
return String(value);
|
|
442
|
+
}
|
|
443
|
+
return void 0;
|
|
444
|
+
}
|
|
445
|
+
const inner = def.innerType;
|
|
446
|
+
if (inner === void 0) {
|
|
447
|
+
return void 0;
|
|
448
|
+
}
|
|
449
|
+
current = inner;
|
|
450
|
+
}
|
|
451
|
+
return void 0;
|
|
452
|
+
}
|
|
453
|
+
var EMPTY_DRIFT = { declared: [], undeclared: [], optional: [] };
|
|
422
454
|
function hasValue(resolution) {
|
|
423
455
|
return resolution.winner !== void 0;
|
|
424
456
|
}
|
|
@@ -426,23 +458,39 @@ function computeDrift(input) {
|
|
|
426
458
|
const { schema, resolutions, config, environment } = input;
|
|
427
459
|
const valued = new Set(resolutions.filter(hasValue).map((resolution) => resolution.parameter));
|
|
428
460
|
const declared = [];
|
|
461
|
+
const optional = [];
|
|
429
462
|
for (const leaf of declaredLeaves(schema)) {
|
|
430
|
-
if (leaf.absencePermitted
|
|
463
|
+
if (leaf.absencePermitted === void 0) {
|
|
431
464
|
continue;
|
|
432
465
|
}
|
|
433
466
|
const path = leaf.path.join(".");
|
|
434
467
|
const ref = (0, import_core3.refFromAccessPath)(leaf.path);
|
|
468
|
+
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.`;
|
|
435
469
|
if (ref === void 0 || (0, import_core3.isReservedToken)(ref.name, config)) {
|
|
436
|
-
|
|
437
|
-
subject: path,
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
470
|
+
if (leaf.absencePermitted) {
|
|
471
|
+
optional.push({ subject: path, remedy: renameRemedy });
|
|
472
|
+
} else {
|
|
473
|
+
declared.push({
|
|
474
|
+
subject: path,
|
|
475
|
+
remedy: renameRemedy,
|
|
476
|
+
detail: "declared, no filename reaches it"
|
|
477
|
+
});
|
|
478
|
+
}
|
|
441
479
|
continue;
|
|
442
480
|
}
|
|
443
481
|
if (valued.has((0, import_core3.parameterId)(ref))) {
|
|
444
482
|
continue;
|
|
445
483
|
}
|
|
484
|
+
if (leaf.absencePermitted) {
|
|
485
|
+
const defaultValue = defaultValueOf(leaf.node);
|
|
486
|
+
optional.push({
|
|
487
|
+
subject: (0, import_core3.parameterId)(ref),
|
|
488
|
+
ref,
|
|
489
|
+
...defaultValue === void 0 ? {} : { defaultValue },
|
|
490
|
+
remedy: `penv set ${[...ref.namespace, ref.name].join("/")} --env ${environment}`
|
|
491
|
+
});
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
446
494
|
declared.push({
|
|
447
495
|
subject: (0, import_core3.parameterId)(ref),
|
|
448
496
|
ref,
|
|
@@ -460,28 +508,91 @@ function computeDrift(input) {
|
|
|
460
508
|
variable: (0, import_core3.variableName)(resolution.ref, config)
|
|
461
509
|
});
|
|
462
510
|
}
|
|
463
|
-
return { declared, undeclared };
|
|
511
|
+
return { declared, undeclared, optional };
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// src/style.ts
|
|
515
|
+
function supportsColor(stream) {
|
|
516
|
+
const env = process.env;
|
|
517
|
+
if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") {
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
if (env.FORCE_COLOR !== void 0 && env.FORCE_COLOR !== "" && env.FORCE_COLOR !== "0") {
|
|
521
|
+
return true;
|
|
522
|
+
}
|
|
523
|
+
if (env.TERM === "dumb") {
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
return stream.isTTY === true;
|
|
527
|
+
}
|
|
528
|
+
var ESC = `${String.fromCharCode(27)}[`;
|
|
529
|
+
var identity = (text) => text;
|
|
530
|
+
function sgr(open, close, on) {
|
|
531
|
+
if (!on) {
|
|
532
|
+
return identity;
|
|
533
|
+
}
|
|
534
|
+
const opener = `${ESC}${open}m`;
|
|
535
|
+
const closer = `${ESC}${close}m`;
|
|
536
|
+
return (text) => `${opener}${text}${closer}`;
|
|
537
|
+
}
|
|
538
|
+
function paletteFor(stream) {
|
|
539
|
+
const on = supportsColor(stream);
|
|
540
|
+
return {
|
|
541
|
+
enabled: on,
|
|
542
|
+
bold: sgr(1, 22, on),
|
|
543
|
+
dim: sgr(2, 22, on),
|
|
544
|
+
red: sgr(31, 39, on),
|
|
545
|
+
green: sgr(32, 39, on),
|
|
546
|
+
yellow: sgr(33, 39, on),
|
|
547
|
+
cyan: sgr(36, 39, on),
|
|
548
|
+
magenta: sgr(35, 39, on)
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
var out = paletteFor(process.stdout);
|
|
552
|
+
var err = paletteFor(process.stderr);
|
|
553
|
+
var STYLE_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
554
|
+
function visibleWidth(text) {
|
|
555
|
+
return text.replace(STYLE_PATTERN, "").length;
|
|
556
|
+
}
|
|
557
|
+
function padVisible(text, width) {
|
|
558
|
+
const missing = width - visibleWidth(text);
|
|
559
|
+
return missing > 0 ? text + " ".repeat(missing) : text;
|
|
464
560
|
}
|
|
465
561
|
|
|
466
562
|
// src/ui.ts
|
|
467
563
|
var import_core4 = require("@penvhq/core");
|
|
468
564
|
var CHECK = "\u2713";
|
|
469
565
|
var WARN = "\u26A0";
|
|
566
|
+
var CROSS = "\u2717";
|
|
470
567
|
var UNKNOWN = "?";
|
|
568
|
+
function paintGlyph(glyph) {
|
|
569
|
+
switch (glyph) {
|
|
570
|
+
case CHECK:
|
|
571
|
+
return out.green(glyph);
|
|
572
|
+
case WARN:
|
|
573
|
+
return out.yellow(glyph);
|
|
574
|
+
case CROSS:
|
|
575
|
+
return out.red(glyph);
|
|
576
|
+
case UNKNOWN:
|
|
577
|
+
return out.dim(glyph);
|
|
578
|
+
default:
|
|
579
|
+
return glyph;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
471
582
|
var NOTE_COLUMN = 29;
|
|
472
583
|
function widest(values) {
|
|
473
|
-
return values.reduce((max, value) => Math.max(max, value
|
|
584
|
+
return values.reduce((max, value) => Math.max(max, visibleWidth(value)), 0);
|
|
474
585
|
}
|
|
475
586
|
function formatRows(rows) {
|
|
476
587
|
const labelWidth = widest(rows.map((row) => row.label)) + 2;
|
|
477
588
|
const detailed = rows.filter((row) => row.detail !== void 0);
|
|
478
589
|
const subjectWidth = widest(detailed.map((row) => row.subject ?? "")) + 1;
|
|
479
590
|
return rows.map((row) => {
|
|
480
|
-
const head = `${row.glyph} ${row.label
|
|
591
|
+
const head = `${paintGlyph(row.glyph)} ${padVisible(row.label, labelWidth)}`;
|
|
481
592
|
if (row.detail === void 0) {
|
|
482
593
|
return `${head}${row.subject ?? ""}`.trimEnd();
|
|
483
594
|
}
|
|
484
|
-
return `${head}${(row.subject ?? ""
|
|
595
|
+
return `${head}${padVisible(row.subject ?? "", subjectWidth)}${out.dim(row.detail)}`.trimEnd();
|
|
485
596
|
});
|
|
486
597
|
}
|
|
487
598
|
function columns(rows, gap = 2) {
|
|
@@ -491,18 +602,31 @@ function columns(rows, gap = 2) {
|
|
|
491
602
|
widths.push(widest(rows.map((row) => row[column] ?? "")) + gap);
|
|
492
603
|
}
|
|
493
604
|
return rows.map(
|
|
494
|
-
(row) => row.map(
|
|
605
|
+
(row) => row.map(
|
|
606
|
+
(cell, index) => index === row.length - 1 ? cell : padVisible(cell, widths[index] ?? 0)
|
|
607
|
+
).join("").trimEnd()
|
|
495
608
|
);
|
|
496
609
|
}
|
|
497
610
|
function formatSteps(steps) {
|
|
498
611
|
return steps.map((step) => {
|
|
499
612
|
if (step.note === void 0) {
|
|
500
|
-
return `${step.glyph} ${step.text}`;
|
|
613
|
+
return `${paintGlyph(step.glyph)} ${step.text}`;
|
|
501
614
|
}
|
|
502
|
-
const text = step.text
|
|
503
|
-
return `${step.glyph} ${text}${step.note}`;
|
|
615
|
+
const text = visibleWidth(step.text) >= NOTE_COLUMN ? `${step.text} ` : padVisible(step.text, NOTE_COLUMN);
|
|
616
|
+
return `${paintGlyph(step.glyph)} ${text}${out.dim(step.note)}`;
|
|
504
617
|
});
|
|
505
618
|
}
|
|
619
|
+
function heading(command, context) {
|
|
620
|
+
return context === void 0 ? out.bold(command) : `${out.bold(command)} ${out.dim(`\xB7 ${context}`)}`;
|
|
621
|
+
}
|
|
622
|
+
function tip(text) {
|
|
623
|
+
return ` ${out.cyan("\u2192")} ${text}`;
|
|
624
|
+
}
|
|
625
|
+
function prompt(subject, context) {
|
|
626
|
+
const head = `${out.cyan("?")} ${out.bold(subject)}`;
|
|
627
|
+
const tail = out.dim("\u203A ");
|
|
628
|
+
return context === void 0 ? `${head} ${tail}` : `${head} ${out.dim(`\xB7 ${context}`)} ${tail}`;
|
|
629
|
+
}
|
|
506
630
|
function write(lines) {
|
|
507
631
|
for (const line of lines) {
|
|
508
632
|
process.stdout.write(`${line}
|
|
@@ -511,13 +635,26 @@ function write(lines) {
|
|
|
511
635
|
}
|
|
512
636
|
function reportError(error) {
|
|
513
637
|
if (error instanceof import_core4.PenvError) {
|
|
514
|
-
|
|
638
|
+
const suffix = error.remedy === void 0 ? void 0 : `
|
|
639
|
+
${error.remedy}`;
|
|
640
|
+
const message = suffix !== void 0 && error.message.endsWith(suffix) ? error.message.slice(0, -suffix.length) : error.message;
|
|
641
|
+
process.stderr.write(`${err.red(CROSS)} ${message}
|
|
515
642
|
`);
|
|
643
|
+
if (error.remedy !== void 0) {
|
|
644
|
+
process.stderr.write(` ${err.cyan("\u2192")} ${error.remedy}
|
|
645
|
+
`);
|
|
646
|
+
}
|
|
516
647
|
} else if (error instanceof Error) {
|
|
517
|
-
process.stderr.write(`${
|
|
648
|
+
process.stderr.write(`${err.red(CROSS)} ${error.message}
|
|
518
649
|
`);
|
|
650
|
+
const stack = error.stack;
|
|
651
|
+
const frames = stack?.startsWith(`${error.name}: ${error.message}`) ? stack.slice(`${error.name}: ${error.message}`.length).replace(/^\n/, "") : stack;
|
|
652
|
+
if (frames !== void 0 && frames !== "") {
|
|
653
|
+
process.stderr.write(`${err.dim(frames)}
|
|
654
|
+
`);
|
|
655
|
+
}
|
|
519
656
|
} else {
|
|
520
|
-
process.stderr.write(`${String(error)}
|
|
657
|
+
process.stderr.write(`${err.red(CROSS)} ${String(error)}
|
|
521
658
|
`);
|
|
522
659
|
}
|
|
523
660
|
process.exitCode = 1;
|
|
@@ -914,18 +1051,26 @@ function renderValidate(result2) {
|
|
|
914
1051
|
]);
|
|
915
1052
|
}
|
|
916
1053
|
const rows = result2.issues.map((issue) => ({
|
|
917
|
-
glyph:
|
|
1054
|
+
glyph: CROSS,
|
|
918
1055
|
label: LABELS[issue.kind],
|
|
919
1056
|
subject: issue.subject,
|
|
920
1057
|
detail: issue.message
|
|
921
1058
|
}));
|
|
922
1059
|
const lines = formatRows(rows);
|
|
923
|
-
const remedies = [...new Set(result2.issues.map((issue) => issue.remedy))]
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1060
|
+
const remedies = [...new Set(result2.issues.map((issue) => issue.remedy))].filter(
|
|
1061
|
+
(remedy) => remedy !== void 0
|
|
1062
|
+
);
|
|
1063
|
+
if (remedies.length > 0) {
|
|
1064
|
+
lines.push("");
|
|
1065
|
+
for (const remedy of remedies) {
|
|
1066
|
+
lines.push(tip(remedy));
|
|
927
1067
|
}
|
|
928
1068
|
}
|
|
1069
|
+
const count = result2.issues.length;
|
|
1070
|
+
lines.push(
|
|
1071
|
+
"",
|
|
1072
|
+
`${out.red(CROSS)} ${count} ${count === 1 ? "issue" : "issues"} for environment ${result2.environment}`
|
|
1073
|
+
);
|
|
929
1074
|
return lines;
|
|
930
1075
|
}
|
|
931
1076
|
var validateCommand = (0, import_citty2.defineCommand)({
|
|
@@ -1680,22 +1825,51 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
1680
1825
|
}
|
|
1681
1826
|
];
|
|
1682
1827
|
}
|
|
1828
|
+
var GLYPHS = {
|
|
1829
|
+
pass: CHECK,
|
|
1830
|
+
warning: WARN,
|
|
1831
|
+
failure: CROSS,
|
|
1832
|
+
unknown: UNKNOWN
|
|
1833
|
+
};
|
|
1834
|
+
function summarize(report) {
|
|
1835
|
+
const counts = { pass: 0, warning: 0, failure: 0, unknown: 0 };
|
|
1836
|
+
for (const finding of report.findings) {
|
|
1837
|
+
counts[finding.severity] += 1;
|
|
1838
|
+
}
|
|
1839
|
+
const parts = [out.green(`${counts.pass} passed`)];
|
|
1840
|
+
if (counts.warning > 0) {
|
|
1841
|
+
parts.push(out.yellow(`${counts.warning} ${counts.warning === 1 ? "warning" : "warnings"}`));
|
|
1842
|
+
}
|
|
1843
|
+
if (counts.failure > 0) {
|
|
1844
|
+
parts.push(out.red(`${counts.failure} ${counts.failure === 1 ? "failure" : "failures"}`));
|
|
1845
|
+
}
|
|
1846
|
+
if (counts.unknown > 0) {
|
|
1847
|
+
parts.push(out.dim(`${counts.unknown} could not be checked`));
|
|
1848
|
+
}
|
|
1849
|
+
const verdict = report.ok ? out.green(CHECK) : out.red(CROSS);
|
|
1850
|
+
return `${verdict} ${report.findings.length} checks: ${parts.join(out.dim(" \xB7 "))}`;
|
|
1851
|
+
}
|
|
1683
1852
|
function renderDoctor(report) {
|
|
1684
1853
|
const rows = report.findings.map((finding) => ({
|
|
1685
|
-
glyph: finding.severity
|
|
1854
|
+
glyph: GLYPHS[finding.severity],
|
|
1686
1855
|
label: finding.label,
|
|
1687
1856
|
...finding.subject === void 0 ? {} : { subject: finding.subject },
|
|
1688
1857
|
...finding.detail === void 0 ? {} : { detail: finding.detail }
|
|
1689
1858
|
}));
|
|
1690
|
-
const lines =
|
|
1859
|
+
const lines = [heading("penv doctor", `environment ${report.environment}`), ""];
|
|
1860
|
+
lines.push(...formatRows(rows));
|
|
1691
1861
|
const remedies = [
|
|
1692
1862
|
...new Set(
|
|
1693
1863
|
report.findings.filter((finding) => finding.severity !== "pass").map((finding) => finding.remedy).filter((remedy) => remedy !== void 0)
|
|
1694
1864
|
)
|
|
1695
1865
|
];
|
|
1696
|
-
|
|
1697
|
-
lines.push(
|
|
1866
|
+
if (remedies.length > 0) {
|
|
1867
|
+
lines.push("");
|
|
1868
|
+
for (const remedy of remedies) {
|
|
1869
|
+
lines.push(tip(remedy));
|
|
1870
|
+
}
|
|
1698
1871
|
}
|
|
1872
|
+
lines.push("", summarize(report));
|
|
1699
1873
|
return lines;
|
|
1700
1874
|
}
|
|
1701
1875
|
var doctorCommand = (0, import_citty3.defineCommand)({
|
|
@@ -1986,9 +2160,52 @@ var decryptCommand = (0, import_citty5.defineCommand)({
|
|
|
1986
2160
|
});
|
|
1987
2161
|
|
|
1988
2162
|
// src/commands/fill.ts
|
|
1989
|
-
var import_promises = require("readline/promises");
|
|
1990
2163
|
var import_core10 = require("@penvhq/core");
|
|
1991
2164
|
var import_citty6 = require("citty");
|
|
2165
|
+
|
|
2166
|
+
// src/input.ts
|
|
2167
|
+
var import_node_readline = require("readline");
|
|
2168
|
+
function lineReader(input = process.stdin, output = process.stdout) {
|
|
2169
|
+
const rl = (0, import_node_readline.createInterface)({ input, output });
|
|
2170
|
+
const buffered = [];
|
|
2171
|
+
const waiting = [];
|
|
2172
|
+
let closed = false;
|
|
2173
|
+
rl.on("line", (line) => {
|
|
2174
|
+
const next = waiting.shift();
|
|
2175
|
+
if (next === void 0) {
|
|
2176
|
+
buffered.push(line);
|
|
2177
|
+
} else {
|
|
2178
|
+
next(line);
|
|
2179
|
+
}
|
|
2180
|
+
});
|
|
2181
|
+
rl.on("close", () => {
|
|
2182
|
+
closed = true;
|
|
2183
|
+
for (const next of waiting.splice(0)) {
|
|
2184
|
+
next(void 0);
|
|
2185
|
+
}
|
|
2186
|
+
});
|
|
2187
|
+
return {
|
|
2188
|
+
ask(question) {
|
|
2189
|
+
const early = buffered.shift();
|
|
2190
|
+
if (early !== void 0) {
|
|
2191
|
+
return Promise.resolve(early);
|
|
2192
|
+
}
|
|
2193
|
+
if (closed) {
|
|
2194
|
+
return Promise.resolve(void 0);
|
|
2195
|
+
}
|
|
2196
|
+
rl.setPrompt(question);
|
|
2197
|
+
rl.prompt();
|
|
2198
|
+
return new Promise((resolve7) => {
|
|
2199
|
+
waiting.push(resolve7);
|
|
2200
|
+
});
|
|
2201
|
+
},
|
|
2202
|
+
close() {
|
|
2203
|
+
rl.close();
|
|
2204
|
+
}
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
// src/commands/fill.ts
|
|
1992
2209
|
var BLOCKING = /* @__PURE__ */ new Set(["config", "collision", "reserved"]);
|
|
1993
2210
|
async function runFill(options) {
|
|
1994
2211
|
const validation = await runValidate({
|
|
@@ -2010,6 +2227,7 @@ ${detail}`,
|
|
|
2010
2227
|
}
|
|
2011
2228
|
const written = [];
|
|
2012
2229
|
const skipped2 = [];
|
|
2230
|
+
const kept = [];
|
|
2013
2231
|
const unreachable = [];
|
|
2014
2232
|
for (const drift of validation.drift.declared) {
|
|
2015
2233
|
if (drift.ref === void 0) {
|
|
@@ -2018,7 +2236,12 @@ ${detail}`,
|
|
|
2018
2236
|
}
|
|
2019
2237
|
const ref = drift.ref;
|
|
2020
2238
|
const key = [...ref.namespace, ref.name].join("/");
|
|
2021
|
-
const value = await options.ask({
|
|
2239
|
+
const value = await options.ask({
|
|
2240
|
+
parameter: key,
|
|
2241
|
+
environment,
|
|
2242
|
+
secret: false,
|
|
2243
|
+
optional: false
|
|
2244
|
+
});
|
|
2022
2245
|
if (value === void 0 || value === "") {
|
|
2023
2246
|
skipped2.push(drift.subject);
|
|
2024
2247
|
continue;
|
|
@@ -2030,17 +2253,45 @@ ${detail}`,
|
|
|
2030
2253
|
encrypted: result2.encrypted
|
|
2031
2254
|
});
|
|
2032
2255
|
}
|
|
2033
|
-
|
|
2256
|
+
for (const item of validation.drift.optional) {
|
|
2257
|
+
if (item.ref === void 0) {
|
|
2258
|
+
unreachable.push({ subject: item.subject, remedy: item.remedy });
|
|
2259
|
+
continue;
|
|
2260
|
+
}
|
|
2261
|
+
const ref = item.ref;
|
|
2262
|
+
const key = [...ref.namespace, ref.name].join("/");
|
|
2263
|
+
const value = await options.ask({
|
|
2264
|
+
parameter: key,
|
|
2265
|
+
environment,
|
|
2266
|
+
secret: false,
|
|
2267
|
+
optional: true,
|
|
2268
|
+
...item.defaultValue === void 0 ? {} : { defaultValue: item.defaultValue }
|
|
2269
|
+
});
|
|
2270
|
+
if (value === void 0 || value === "") {
|
|
2271
|
+
kept.push(item.subject);
|
|
2272
|
+
continue;
|
|
2273
|
+
}
|
|
2274
|
+
const result2 = await runSet({ cwd: options.cwd, key, value, environment });
|
|
2275
|
+
written.push({
|
|
2276
|
+
parameter: item.subject,
|
|
2277
|
+
location: result2.location,
|
|
2278
|
+
encrypted: result2.encrypted
|
|
2279
|
+
});
|
|
2280
|
+
}
|
|
2281
|
+
return { environment, written, skipped: skipped2, kept, unreachable };
|
|
2034
2282
|
}
|
|
2035
2283
|
function summaryLine(result2) {
|
|
2036
2284
|
const filled = result2.written.length;
|
|
2037
|
-
if (filled === 0 && result2.skipped.length === 0 && result2.unreachable.length === 0) {
|
|
2038
|
-
return
|
|
2285
|
+
if (filled === 0 && result2.skipped.length === 0 && result2.kept.length === 0 && result2.unreachable.length === 0) {
|
|
2286
|
+
return `${out.green(CHECK)} Nothing to fill for environment ${result2.environment}: every declared parameter has a value`;
|
|
2039
2287
|
}
|
|
2040
2288
|
const parts = [`${filled} written`];
|
|
2041
2289
|
if (result2.skipped.length > 0) {
|
|
2042
2290
|
parts.push(`${result2.skipped.length} skipped`);
|
|
2043
2291
|
}
|
|
2292
|
+
if (result2.kept.length > 0) {
|
|
2293
|
+
parts.push(`${result2.kept.length} left to the schema's defaults`);
|
|
2294
|
+
}
|
|
2044
2295
|
if (result2.unreachable.length > 0) {
|
|
2045
2296
|
parts.push(`${result2.unreachable.length} unreachable`);
|
|
2046
2297
|
}
|
|
@@ -2061,6 +2312,16 @@ function renderFill(result2) {
|
|
|
2061
2312
|
lines.push(summaryLine(result2));
|
|
2062
2313
|
return lines;
|
|
2063
2314
|
}
|
|
2315
|
+
function contextFor(prompt2) {
|
|
2316
|
+
if (!prompt2.optional) {
|
|
2317
|
+
return `${prompt2.environment}, Enter to skip`;
|
|
2318
|
+
}
|
|
2319
|
+
if (prompt2.defaultValue === void 0) {
|
|
2320
|
+
return `${prompt2.environment} \xB7 optional, Enter leaves it unset`;
|
|
2321
|
+
}
|
|
2322
|
+
const shown = prompt2.defaultValue.length > 24 ? `${prompt2.defaultValue.slice(0, 23)}\u2026` : prompt2.defaultValue;
|
|
2323
|
+
return `${prompt2.environment} \xB7 optional, Enter keeps ${shown}`;
|
|
2324
|
+
}
|
|
2064
2325
|
var fillCommand = (0, import_citty6.defineCommand)({
|
|
2065
2326
|
meta: {
|
|
2066
2327
|
name: "fill",
|
|
@@ -2071,8 +2332,8 @@ var fillCommand = (0, import_citty6.defineCommand)({
|
|
|
2071
2332
|
},
|
|
2072
2333
|
run({ args }) {
|
|
2073
2334
|
return guard(async () => {
|
|
2074
|
-
const
|
|
2075
|
-
const ask = (
|
|
2335
|
+
const reader = lineReader();
|
|
2336
|
+
const ask = (prompt2) => reader.ask(prompt(prompt2.parameter, contextFor(prompt2)));
|
|
2076
2337
|
try {
|
|
2077
2338
|
write(
|
|
2078
2339
|
renderFill(
|
|
@@ -2084,7 +2345,7 @@ var fillCommand = (0, import_citty6.defineCommand)({
|
|
|
2084
2345
|
)
|
|
2085
2346
|
);
|
|
2086
2347
|
} finally {
|
|
2087
|
-
|
|
2348
|
+
reader.close();
|
|
2088
2349
|
}
|
|
2089
2350
|
});
|
|
2090
2351
|
}
|
|
@@ -2246,12 +2507,14 @@ async function runExplain(options) {
|
|
|
2246
2507
|
function renderExplain(explanation) {
|
|
2247
2508
|
const target = explanation.location === void 0 ? "nothing" : `${PENV_DIR}/${explanation.location}`;
|
|
2248
2509
|
const rows = explanation.candidates.map((candidate) => [
|
|
2249
|
-
candidate.location,
|
|
2250
|
-
candidate.wins ? "present, wins" :
|
|
2510
|
+
candidate.wins ? candidate.location : out.dim(candidate.location),
|
|
2511
|
+
candidate.wins ? out.green("present, wins") : out.dim(
|
|
2512
|
+
candidate.present ? candidate.skipped ?? "present" : candidate.skipped ?? "absent"
|
|
2513
|
+
)
|
|
2251
2514
|
]);
|
|
2252
2515
|
return [
|
|
2253
|
-
`${explanation.parameter} resolves to ${target} for environment ${explanation.environment}`,
|
|
2254
|
-
...explanation.undecryptable === void 0 ? [] : [` penv cannot decrypt it: ${explanation.undecryptable}`],
|
|
2516
|
+
`${out.bold(explanation.parameter)} resolves to ${target} for environment ${explanation.environment}`,
|
|
2517
|
+
...explanation.undecryptable === void 0 ? [] : [` ${out.red(CROSS)} penv cannot decrypt it: ${explanation.undecryptable}`],
|
|
2255
2518
|
"",
|
|
2256
2519
|
...columns(rows).map((line) => ` ${line}`)
|
|
2257
2520
|
];
|
|
@@ -2389,7 +2652,7 @@ function hasImportsBlock(cwd) {
|
|
|
2389
2652
|
// src/commands/init.ts
|
|
2390
2653
|
var import_node_fs3 = require("fs");
|
|
2391
2654
|
var import_node_path6 = require("path");
|
|
2392
|
-
var
|
|
2655
|
+
var import_promises = require("readline/promises");
|
|
2393
2656
|
var import_core14 = require("@penvhq/core");
|
|
2394
2657
|
var import_citty9 = require("citty");
|
|
2395
2658
|
var CONFIG_FILE = "penv.config.ts";
|
|
@@ -2530,23 +2793,27 @@ function planInit(root, flags = {}) {
|
|
|
2530
2793
|
function renderPlan(plan2) {
|
|
2531
2794
|
const rows = [];
|
|
2532
2795
|
rows.push([
|
|
2533
|
-
"
|
|
2534
|
-
plan2.suggestedEnvironments.length === 0 ? "" : `[${plan2.suggestedEnvironments.join(", ")}]
|
|
2535
|
-
|
|
2796
|
+
` ${out.dim("environments")}`,
|
|
2797
|
+
plan2.suggestedEnvironments.length === 0 ? "" : out.cyan(`[${plan2.suggestedEnvironments.join(", ")}]`),
|
|
2798
|
+
out.dim(
|
|
2799
|
+
plan2.suggestedEnvironments.length === 0 ? "\u2190 name them, or Enter to leave the whitelist empty" : "\u2190 from your .env files; edit, or Enter to accept"
|
|
2800
|
+
)
|
|
2536
2801
|
]);
|
|
2537
2802
|
rows.push([
|
|
2538
|
-
"
|
|
2803
|
+
` ${out.dim("schemaFile")}`,
|
|
2539
2804
|
plan2.decisions.schemaFile,
|
|
2540
|
-
plan2.decisions.schemaFile === import_core14.DEFAULT_SCHEMA_FILE ? "" : `(default: ${import_core14.DEFAULT_SCHEMA_FILE})`
|
|
2805
|
+
plan2.decisions.schemaFile === import_core14.DEFAULT_SCHEMA_FILE ? "" : out.dim(`(default: ${import_core14.DEFAULT_SCHEMA_FILE})`)
|
|
2541
2806
|
]);
|
|
2542
2807
|
for (const prefix of plan2.decisions.publicPrefixes) {
|
|
2543
|
-
rows.push(["
|
|
2808
|
+
rows.push([` ${out.dim("publicPrefix")}`, prefix, ""]);
|
|
2544
2809
|
}
|
|
2545
|
-
const headline =
|
|
2810
|
+
const headline = out.bold(
|
|
2811
|
+
plan2.detected === void 0 ? "No framework detected in package.json." : `Detected ${plan2.detected.name}.`
|
|
2812
|
+
);
|
|
2546
2813
|
return [headline, "", ...columns(rows), ""];
|
|
2547
2814
|
}
|
|
2548
2815
|
function environmentsHint(plan2) {
|
|
2549
|
-
return plan2.suggestedEnvironments.length === 0 ? "environments
|
|
2816
|
+
return plan2.suggestedEnvironments.length === 0 ? prompt("environments", "comma-separated, Enter for none") : prompt("environments", 'Enter to accept, "none" for an empty whitelist');
|
|
2550
2817
|
}
|
|
2551
2818
|
async function promptForDecisions(plan2, io) {
|
|
2552
2819
|
for (const line of renderPlan(plan2)) {
|
|
@@ -2561,7 +2828,7 @@ async function promptForDecisions(plan2, io) {
|
|
|
2561
2828
|
);
|
|
2562
2829
|
io.write("");
|
|
2563
2830
|
}
|
|
2564
|
-
const proceed = (await io.ask("Proceed?
|
|
2831
|
+
const proceed = (await io.ask(prompt("Proceed?", "Y/n"))).trim().toLowerCase();
|
|
2565
2832
|
if (proceed.length > 0 && proceed !== "y" && proceed !== "yes") {
|
|
2566
2833
|
return void 0;
|
|
2567
2834
|
}
|
|
@@ -2966,14 +3233,15 @@ function renderInit(result2) {
|
|
|
2966
3233
|
return [
|
|
2967
3234
|
...formatSteps(steps),
|
|
2968
3235
|
"",
|
|
2969
|
-
|
|
3236
|
+
`${out.green(CHECK)} ${out.bold("Done.")} Declare your parameters in ${result2.decisions.schemaFile}, then:`,
|
|
3237
|
+
tip(out.cyan("penv set <key>")),
|
|
2970
3238
|
...result2.decisions.environments.length === 0 ? [
|
|
2971
3239
|
`Then declare your environments in ${CONFIG_FILE}: penv leaves the whitelist empty rather than inventing one, and every command needs it.`
|
|
2972
3240
|
] : []
|
|
2973
3241
|
];
|
|
2974
3242
|
}
|
|
2975
3243
|
async function askOnTty(plan2) {
|
|
2976
|
-
const rl = (0,
|
|
3244
|
+
const rl = (0, import_promises.createInterface)({ input: process.stdin, output: process.stdout });
|
|
2977
3245
|
try {
|
|
2978
3246
|
return await promptForDecisions(plan2, {
|
|
2979
3247
|
ask: (question) => rl.question(question),
|
|
@@ -3436,20 +3704,28 @@ function runKeyCreate(options) {
|
|
|
3436
3704
|
function renderKeyCreate(result2) {
|
|
3437
3705
|
if (result2.source === "keychain") {
|
|
3438
3706
|
return [
|
|
3439
|
-
|
|
3707
|
+
`${out.green(CHECK)} A new key for environment ${result2.environment}, stored in your OS keychain as \`${result2.id}\`.`,
|
|
3440
3708
|
"",
|
|
3441
|
-
|
|
3442
|
-
|
|
3709
|
+
out.dim(
|
|
3710
|
+
"penv kept no copy. Anything sealed under it is unreadable without your keychain, and running"
|
|
3711
|
+
),
|
|
3712
|
+
out.dim(
|
|
3713
|
+
"`penv key create` again would replace it \u2014 so it lives in exactly one place, on this machine."
|
|
3714
|
+
)
|
|
3443
3715
|
];
|
|
3444
3716
|
}
|
|
3445
3717
|
return [
|
|
3446
|
-
|
|
3718
|
+
`${out.green(CHECK)} A new key for environment ${result2.environment}. penv did not store it.`,
|
|
3447
3719
|
"",
|
|
3448
|
-
` ${result2.variable}=${result2.key}`,
|
|
3720
|
+
` ${out.cyan(`${result2.variable}=${result2.key}`)}`,
|
|
3449
3721
|
"",
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3722
|
+
out.dim(
|
|
3723
|
+
"Export it where penv runs, and put it wherever this environment's secrets already live \u2014"
|
|
3724
|
+
),
|
|
3725
|
+
out.dim(
|
|
3726
|
+
"a KMS, your CI's secret store, a password manager. Anything sealed under it is unreadable"
|
|
3727
|
+
),
|
|
3728
|
+
out.dim("without it, and penv keeps no copy to fall back on.")
|
|
3453
3729
|
];
|
|
3454
3730
|
}
|
|
3455
3731
|
var keyCommand = (0, import_citty11.defineCommand)({
|
|
@@ -3517,11 +3793,31 @@ async function runList(options) {
|
|
|
3517
3793
|
}
|
|
3518
3794
|
return { environment, parameters };
|
|
3519
3795
|
}
|
|
3796
|
+
function paintScope(entry) {
|
|
3797
|
+
if (entry.scope === "absent" || entry.viaUnscopedFallback) {
|
|
3798
|
+
return out.yellow(entry.scope);
|
|
3799
|
+
}
|
|
3800
|
+
return out.green(entry.scope);
|
|
3801
|
+
}
|
|
3520
3802
|
function renderList(result2) {
|
|
3521
3803
|
if (result2.parameters.length === 0) {
|
|
3522
|
-
return [
|
|
3804
|
+
return [
|
|
3805
|
+
`No parameters in ${PENV_DIR}/ for environment ${result2.environment}.`,
|
|
3806
|
+
tip(`penv set <key> --env ${result2.environment}`)
|
|
3807
|
+
];
|
|
3523
3808
|
}
|
|
3524
|
-
|
|
3809
|
+
const header = ["parameter", "scope", "variable", ""].map((cell) => out.dim(cell));
|
|
3810
|
+
const rows = result2.parameters.map((entry) => [
|
|
3811
|
+
entry.parameter,
|
|
3812
|
+
paintScope(entry),
|
|
3813
|
+
entry.variable,
|
|
3814
|
+
entry.encrypted ? out.dim("encrypted") : ""
|
|
3815
|
+
]);
|
|
3816
|
+
return [
|
|
3817
|
+
heading("penv list", `environment ${result2.environment}`),
|
|
3818
|
+
"",
|
|
3819
|
+
...columns([header, ...rows])
|
|
3820
|
+
];
|
|
3525
3821
|
}
|
|
3526
3822
|
var listCommand = (0, import_citty12.defineCommand)({
|
|
3527
3823
|
meta: { name: "list", description: "List parameters" },
|
|
@@ -3661,8 +3957,9 @@ function renderMove(result2) {
|
|
|
3661
3957
|
const lines = formatRows(rows);
|
|
3662
3958
|
lines.push(
|
|
3663
3959
|
"",
|
|
3664
|
-
|
|
3665
|
-
|
|
3960
|
+
tip(
|
|
3961
|
+
`.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.`
|
|
3962
|
+
)
|
|
3666
3963
|
);
|
|
3667
3964
|
return lines;
|
|
3668
3965
|
}
|
|
@@ -4191,9 +4488,9 @@ function renderDrift(drift, environment) {
|
|
|
4191
4488
|
if (rows.length === 0) {
|
|
4192
4489
|
return [];
|
|
4193
4490
|
}
|
|
4194
|
-
const lines = ["", `Schema and tree differ for ${environment}
|
|
4491
|
+
const lines = ["", out.bold(`Schema and tree differ for ${environment}:`), ...formatRows(rows)];
|
|
4195
4492
|
for (const remedy of new Set(drift.declared.map((item) => item.remedy))) {
|
|
4196
|
-
lines.push(
|
|
4493
|
+
lines.push(tip(remedy));
|
|
4197
4494
|
}
|
|
4198
4495
|
return lines;
|
|
4199
4496
|
}
|
|
@@ -4224,7 +4521,7 @@ var watchCommand = (0, import_citty17.defineCommand)({
|
|
|
4224
4521
|
process.exitCode = previous;
|
|
4225
4522
|
}
|
|
4226
4523
|
});
|
|
4227
|
-
write([
|
|
4524
|
+
write([`Watching .penv/ and penv.config.ts. ${out.dim("Ctrl-C to stop.")}`]);
|
|
4228
4525
|
await new Promise((resolve7) => {
|
|
4229
4526
|
process.once("SIGINT", () => {
|
|
4230
4527
|
handle.close();
|