@penvhq/cli 0.3.1 → 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 +376 -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 +375 -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}
|
|
642
|
+
`);
|
|
643
|
+
if (error.remedy !== void 0) {
|
|
644
|
+
process.stderr.write(` ${err.cyan("\u2192")} ${error.remedy}
|
|
515
645
|
`);
|
|
646
|
+
}
|
|
516
647
|
} else if (error instanceof Error) {
|
|
517
|
-
process.stderr.write(`${
|
|
648
|
+
process.stderr.write(`${err.red(CROSS)} ${error.message}
|
|
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)}
|
|
518
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;
|
|
@@ -782,7 +919,9 @@ function loadSchema(project, environment) {
|
|
|
782
919
|
async function loadSchemaExclusively(file, schemaPath, environment) {
|
|
783
920
|
const jiti = (0, import_core6.jitiFor)(file);
|
|
784
921
|
const previous = process.env.PENV_ENV;
|
|
922
|
+
const previousHarvest = process.env[import_core6.SCHEMA_HARVEST_ENV];
|
|
785
923
|
process.env.PENV_ENV = environment;
|
|
924
|
+
process.env[import_core6.SCHEMA_HARVEST_ENV] = "1";
|
|
786
925
|
let loaded;
|
|
787
926
|
try {
|
|
788
927
|
loaded = await jiti.import(file);
|
|
@@ -808,6 +947,11 @@ async function loadSchemaExclusively(file, schemaPath, environment) {
|
|
|
808
947
|
} else {
|
|
809
948
|
process.env.PENV_ENV = previous;
|
|
810
949
|
}
|
|
950
|
+
if (previousHarvest === void 0) {
|
|
951
|
+
delete process.env[import_core6.SCHEMA_HARVEST_ENV];
|
|
952
|
+
} else {
|
|
953
|
+
process.env[import_core6.SCHEMA_HARVEST_ENV] = previousHarvest;
|
|
954
|
+
}
|
|
811
955
|
}
|
|
812
956
|
const exported = typeof loaded === "object" && loaded !== null ? loaded[SCHEMA_EXPORT] : void 0;
|
|
813
957
|
if (!isZodType(exported)) {
|
|
@@ -907,18 +1051,26 @@ function renderValidate(result2) {
|
|
|
907
1051
|
]);
|
|
908
1052
|
}
|
|
909
1053
|
const rows = result2.issues.map((issue) => ({
|
|
910
|
-
glyph:
|
|
1054
|
+
glyph: CROSS,
|
|
911
1055
|
label: LABELS[issue.kind],
|
|
912
1056
|
subject: issue.subject,
|
|
913
1057
|
detail: issue.message
|
|
914
1058
|
}));
|
|
915
1059
|
const lines = formatRows(rows);
|
|
916
|
-
const remedies = [...new Set(result2.issues.map((issue) => issue.remedy))]
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
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));
|
|
920
1067
|
}
|
|
921
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
|
+
);
|
|
922
1074
|
return lines;
|
|
923
1075
|
}
|
|
924
1076
|
var validateCommand = (0, import_citty2.defineCommand)({
|
|
@@ -1673,22 +1825,51 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
1673
1825
|
}
|
|
1674
1826
|
];
|
|
1675
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
|
+
}
|
|
1676
1852
|
function renderDoctor(report) {
|
|
1677
1853
|
const rows = report.findings.map((finding) => ({
|
|
1678
|
-
glyph: finding.severity
|
|
1854
|
+
glyph: GLYPHS[finding.severity],
|
|
1679
1855
|
label: finding.label,
|
|
1680
1856
|
...finding.subject === void 0 ? {} : { subject: finding.subject },
|
|
1681
1857
|
...finding.detail === void 0 ? {} : { detail: finding.detail }
|
|
1682
1858
|
}));
|
|
1683
|
-
const lines =
|
|
1859
|
+
const lines = [heading("penv doctor", `environment ${report.environment}`), ""];
|
|
1860
|
+
lines.push(...formatRows(rows));
|
|
1684
1861
|
const remedies = [
|
|
1685
1862
|
...new Set(
|
|
1686
1863
|
report.findings.filter((finding) => finding.severity !== "pass").map((finding) => finding.remedy).filter((remedy) => remedy !== void 0)
|
|
1687
1864
|
)
|
|
1688
1865
|
];
|
|
1689
|
-
|
|
1690
|
-
lines.push(
|
|
1866
|
+
if (remedies.length > 0) {
|
|
1867
|
+
lines.push("");
|
|
1868
|
+
for (const remedy of remedies) {
|
|
1869
|
+
lines.push(tip(remedy));
|
|
1870
|
+
}
|
|
1691
1871
|
}
|
|
1872
|
+
lines.push("", summarize(report));
|
|
1692
1873
|
return lines;
|
|
1693
1874
|
}
|
|
1694
1875
|
var doctorCommand = (0, import_citty3.defineCommand)({
|
|
@@ -1979,9 +2160,52 @@ var decryptCommand = (0, import_citty5.defineCommand)({
|
|
|
1979
2160
|
});
|
|
1980
2161
|
|
|
1981
2162
|
// src/commands/fill.ts
|
|
1982
|
-
var import_promises = require("readline/promises");
|
|
1983
2163
|
var import_core10 = require("@penvhq/core");
|
|
1984
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
|
|
1985
2209
|
var BLOCKING = /* @__PURE__ */ new Set(["config", "collision", "reserved"]);
|
|
1986
2210
|
async function runFill(options) {
|
|
1987
2211
|
const validation = await runValidate({
|
|
@@ -2003,6 +2227,7 @@ ${detail}`,
|
|
|
2003
2227
|
}
|
|
2004
2228
|
const written = [];
|
|
2005
2229
|
const skipped2 = [];
|
|
2230
|
+
const kept = [];
|
|
2006
2231
|
const unreachable = [];
|
|
2007
2232
|
for (const drift of validation.drift.declared) {
|
|
2008
2233
|
if (drift.ref === void 0) {
|
|
@@ -2011,7 +2236,12 @@ ${detail}`,
|
|
|
2011
2236
|
}
|
|
2012
2237
|
const ref = drift.ref;
|
|
2013
2238
|
const key = [...ref.namespace, ref.name].join("/");
|
|
2014
|
-
const value = await options.ask({
|
|
2239
|
+
const value = await options.ask({
|
|
2240
|
+
parameter: key,
|
|
2241
|
+
environment,
|
|
2242
|
+
secret: false,
|
|
2243
|
+
optional: false
|
|
2244
|
+
});
|
|
2015
2245
|
if (value === void 0 || value === "") {
|
|
2016
2246
|
skipped2.push(drift.subject);
|
|
2017
2247
|
continue;
|
|
@@ -2023,17 +2253,45 @@ ${detail}`,
|
|
|
2023
2253
|
encrypted: result2.encrypted
|
|
2024
2254
|
});
|
|
2025
2255
|
}
|
|
2026
|
-
|
|
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 };
|
|
2027
2282
|
}
|
|
2028
2283
|
function summaryLine(result2) {
|
|
2029
2284
|
const filled = result2.written.length;
|
|
2030
|
-
if (filled === 0 && result2.skipped.length === 0 && result2.unreachable.length === 0) {
|
|
2031
|
-
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`;
|
|
2032
2287
|
}
|
|
2033
2288
|
const parts = [`${filled} written`];
|
|
2034
2289
|
if (result2.skipped.length > 0) {
|
|
2035
2290
|
parts.push(`${result2.skipped.length} skipped`);
|
|
2036
2291
|
}
|
|
2292
|
+
if (result2.kept.length > 0) {
|
|
2293
|
+
parts.push(`${result2.kept.length} left to the schema's defaults`);
|
|
2294
|
+
}
|
|
2037
2295
|
if (result2.unreachable.length > 0) {
|
|
2038
2296
|
parts.push(`${result2.unreachable.length} unreachable`);
|
|
2039
2297
|
}
|
|
@@ -2054,6 +2312,16 @@ function renderFill(result2) {
|
|
|
2054
2312
|
lines.push(summaryLine(result2));
|
|
2055
2313
|
return lines;
|
|
2056
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
|
+
}
|
|
2057
2325
|
var fillCommand = (0, import_citty6.defineCommand)({
|
|
2058
2326
|
meta: {
|
|
2059
2327
|
name: "fill",
|
|
@@ -2064,8 +2332,8 @@ var fillCommand = (0, import_citty6.defineCommand)({
|
|
|
2064
2332
|
},
|
|
2065
2333
|
run({ args }) {
|
|
2066
2334
|
return guard(async () => {
|
|
2067
|
-
const
|
|
2068
|
-
const ask = (
|
|
2335
|
+
const reader = lineReader();
|
|
2336
|
+
const ask = (prompt2) => reader.ask(prompt(prompt2.parameter, contextFor(prompt2)));
|
|
2069
2337
|
try {
|
|
2070
2338
|
write(
|
|
2071
2339
|
renderFill(
|
|
@@ -2077,7 +2345,7 @@ var fillCommand = (0, import_citty6.defineCommand)({
|
|
|
2077
2345
|
)
|
|
2078
2346
|
);
|
|
2079
2347
|
} finally {
|
|
2080
|
-
|
|
2348
|
+
reader.close();
|
|
2081
2349
|
}
|
|
2082
2350
|
});
|
|
2083
2351
|
}
|
|
@@ -2239,12 +2507,14 @@ async function runExplain(options) {
|
|
|
2239
2507
|
function renderExplain(explanation) {
|
|
2240
2508
|
const target = explanation.location === void 0 ? "nothing" : `${PENV_DIR}/${explanation.location}`;
|
|
2241
2509
|
const rows = explanation.candidates.map((candidate) => [
|
|
2242
|
-
candidate.location,
|
|
2243
|
-
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
|
+
)
|
|
2244
2514
|
]);
|
|
2245
2515
|
return [
|
|
2246
|
-
`${explanation.parameter} resolves to ${target} for environment ${explanation.environment}`,
|
|
2247
|
-
...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}`],
|
|
2248
2518
|
"",
|
|
2249
2519
|
...columns(rows).map((line) => ` ${line}`)
|
|
2250
2520
|
];
|
|
@@ -2382,7 +2652,7 @@ function hasImportsBlock(cwd) {
|
|
|
2382
2652
|
// src/commands/init.ts
|
|
2383
2653
|
var import_node_fs3 = require("fs");
|
|
2384
2654
|
var import_node_path6 = require("path");
|
|
2385
|
-
var
|
|
2655
|
+
var import_promises = require("readline/promises");
|
|
2386
2656
|
var import_core14 = require("@penvhq/core");
|
|
2387
2657
|
var import_citty9 = require("citty");
|
|
2388
2658
|
var CONFIG_FILE = "penv.config.ts";
|
|
@@ -2523,23 +2793,27 @@ function planInit(root, flags = {}) {
|
|
|
2523
2793
|
function renderPlan(plan2) {
|
|
2524
2794
|
const rows = [];
|
|
2525
2795
|
rows.push([
|
|
2526
|
-
"
|
|
2527
|
-
plan2.suggestedEnvironments.length === 0 ? "" : `[${plan2.suggestedEnvironments.join(", ")}]
|
|
2528
|
-
|
|
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
|
+
)
|
|
2529
2801
|
]);
|
|
2530
2802
|
rows.push([
|
|
2531
|
-
"
|
|
2803
|
+
` ${out.dim("schemaFile")}`,
|
|
2532
2804
|
plan2.decisions.schemaFile,
|
|
2533
|
-
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})`)
|
|
2534
2806
|
]);
|
|
2535
2807
|
for (const prefix of plan2.decisions.publicPrefixes) {
|
|
2536
|
-
rows.push(["
|
|
2808
|
+
rows.push([` ${out.dim("publicPrefix")}`, prefix, ""]);
|
|
2537
2809
|
}
|
|
2538
|
-
const headline =
|
|
2810
|
+
const headline = out.bold(
|
|
2811
|
+
plan2.detected === void 0 ? "No framework detected in package.json." : `Detected ${plan2.detected.name}.`
|
|
2812
|
+
);
|
|
2539
2813
|
return [headline, "", ...columns(rows), ""];
|
|
2540
2814
|
}
|
|
2541
2815
|
function environmentsHint(plan2) {
|
|
2542
|
-
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');
|
|
2543
2817
|
}
|
|
2544
2818
|
async function promptForDecisions(plan2, io) {
|
|
2545
2819
|
for (const line of renderPlan(plan2)) {
|
|
@@ -2554,7 +2828,7 @@ async function promptForDecisions(plan2, io) {
|
|
|
2554
2828
|
);
|
|
2555
2829
|
io.write("");
|
|
2556
2830
|
}
|
|
2557
|
-
const proceed = (await io.ask("Proceed?
|
|
2831
|
+
const proceed = (await io.ask(prompt("Proceed?", "Y/n"))).trim().toLowerCase();
|
|
2558
2832
|
if (proceed.length > 0 && proceed !== "y" && proceed !== "yes") {
|
|
2559
2833
|
return void 0;
|
|
2560
2834
|
}
|
|
@@ -2959,14 +3233,15 @@ function renderInit(result2) {
|
|
|
2959
3233
|
return [
|
|
2960
3234
|
...formatSteps(steps),
|
|
2961
3235
|
"",
|
|
2962
|
-
|
|
3236
|
+
`${out.green(CHECK)} ${out.bold("Done.")} Declare your parameters in ${result2.decisions.schemaFile}, then:`,
|
|
3237
|
+
tip(out.cyan("penv set <key>")),
|
|
2963
3238
|
...result2.decisions.environments.length === 0 ? [
|
|
2964
3239
|
`Then declare your environments in ${CONFIG_FILE}: penv leaves the whitelist empty rather than inventing one, and every command needs it.`
|
|
2965
3240
|
] : []
|
|
2966
3241
|
];
|
|
2967
3242
|
}
|
|
2968
3243
|
async function askOnTty(plan2) {
|
|
2969
|
-
const rl = (0,
|
|
3244
|
+
const rl = (0, import_promises.createInterface)({ input: process.stdin, output: process.stdout });
|
|
2970
3245
|
try {
|
|
2971
3246
|
return await promptForDecisions(plan2, {
|
|
2972
3247
|
ask: (question) => rl.question(question),
|
|
@@ -3429,20 +3704,28 @@ function runKeyCreate(options) {
|
|
|
3429
3704
|
function renderKeyCreate(result2) {
|
|
3430
3705
|
if (result2.source === "keychain") {
|
|
3431
3706
|
return [
|
|
3432
|
-
|
|
3707
|
+
`${out.green(CHECK)} A new key for environment ${result2.environment}, stored in your OS keychain as \`${result2.id}\`.`,
|
|
3433
3708
|
"",
|
|
3434
|
-
|
|
3435
|
-
|
|
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
|
+
)
|
|
3436
3715
|
];
|
|
3437
3716
|
}
|
|
3438
3717
|
return [
|
|
3439
|
-
|
|
3718
|
+
`${out.green(CHECK)} A new key for environment ${result2.environment}. penv did not store it.`,
|
|
3440
3719
|
"",
|
|
3441
|
-
` ${result2.variable}=${result2.key}`,
|
|
3720
|
+
` ${out.cyan(`${result2.variable}=${result2.key}`)}`,
|
|
3442
3721
|
"",
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
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.")
|
|
3446
3729
|
];
|
|
3447
3730
|
}
|
|
3448
3731
|
var keyCommand = (0, import_citty11.defineCommand)({
|
|
@@ -3510,11 +3793,31 @@ async function runList(options) {
|
|
|
3510
3793
|
}
|
|
3511
3794
|
return { environment, parameters };
|
|
3512
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
|
+
}
|
|
3513
3802
|
function renderList(result2) {
|
|
3514
3803
|
if (result2.parameters.length === 0) {
|
|
3515
|
-
return [
|
|
3804
|
+
return [
|
|
3805
|
+
`No parameters in ${PENV_DIR}/ for environment ${result2.environment}.`,
|
|
3806
|
+
tip(`penv set <key> --env ${result2.environment}`)
|
|
3807
|
+
];
|
|
3516
3808
|
}
|
|
3517
|
-
|
|
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
|
+
];
|
|
3518
3821
|
}
|
|
3519
3822
|
var listCommand = (0, import_citty12.defineCommand)({
|
|
3520
3823
|
meta: { name: "list", description: "List parameters" },
|
|
@@ -3654,8 +3957,9 @@ function renderMove(result2) {
|
|
|
3654
3957
|
const lines = formatRows(rows);
|
|
3655
3958
|
lines.push(
|
|
3656
3959
|
"",
|
|
3657
|
-
|
|
3658
|
-
|
|
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
|
+
)
|
|
3659
3963
|
);
|
|
3660
3964
|
return lines;
|
|
3661
3965
|
}
|
|
@@ -4184,9 +4488,9 @@ function renderDrift(drift, environment) {
|
|
|
4184
4488
|
if (rows.length === 0) {
|
|
4185
4489
|
return [];
|
|
4186
4490
|
}
|
|
4187
|
-
const lines = ["", `Schema and tree differ for ${environment}
|
|
4491
|
+
const lines = ["", out.bold(`Schema and tree differ for ${environment}:`), ...formatRows(rows)];
|
|
4188
4492
|
for (const remedy of new Set(drift.declared.map((item) => item.remedy))) {
|
|
4189
|
-
lines.push(
|
|
4493
|
+
lines.push(tip(remedy));
|
|
4190
4494
|
}
|
|
4191
4495
|
return lines;
|
|
4192
4496
|
}
|
|
@@ -4217,7 +4521,7 @@ var watchCommand = (0, import_citty17.defineCommand)({
|
|
|
4217
4521
|
process.exitCode = previous;
|
|
4218
4522
|
}
|
|
4219
4523
|
});
|
|
4220
|
-
write([
|
|
4524
|
+
write([`Watching .penv/ and penv.config.ts. ${out.dim("Ctrl-C to stop.")}`]);
|
|
4221
4525
|
await new Promise((resolve7) => {
|
|
4222
4526
|
process.once("SIGINT", () => {
|
|
4223
4527
|
handle.close();
|