@penvhq/cli 0.13.0 → 0.15.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/bin.cjs +281 -292
- package/dist/bin.cjs.map +1 -1
- package/dist/{chunk-OBIHFXCK.js → chunk-4RR6UFVZ.js} +48 -23
- package/dist/chunk-4RR6UFVZ.js.map +1 -0
- package/dist/index.cjs +137 -153
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -7
- package/dist/index.d.ts +1 -7
- package/dist/index.js +120 -135
- package/dist/index.js.map +1 -1
- package/dist/install.cjs +48 -22
- package/dist/install.cjs.map +1 -1
- package/dist/install.d.cts +29 -4
- package/dist/install.d.ts +29 -4
- package/dist/install.js +3 -1
- package/package.json +7 -7
- package/dist/chunk-OBIHFXCK.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -311,6 +311,10 @@ function declaredIn(dir, name) {
|
|
|
311
311
|
}
|
|
312
312
|
return void 0;
|
|
313
313
|
}
|
|
314
|
+
function blockPresentIn(dir, dev) {
|
|
315
|
+
const block = manifestOf(dir)?.[dev ? "devDependencies" : "dependencies"];
|
|
316
|
+
return block !== null && typeof block === "object" && !Array.isArray(block);
|
|
317
|
+
}
|
|
314
318
|
function isPnpmWorkspaceRoot(root) {
|
|
315
319
|
return (0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, PNPM_WORKSPACE)) && (0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, "package.json"));
|
|
316
320
|
}
|
|
@@ -435,6 +439,7 @@ function stepFor(manager, manifest, packages, options) {
|
|
|
435
439
|
packages,
|
|
436
440
|
command: addCommand(manager, options, specs),
|
|
437
441
|
dev: options.dev,
|
|
442
|
+
blockPresent: blockPresentIn(options.dir, options.dev),
|
|
438
443
|
satisfied: pending.length === 0
|
|
439
444
|
};
|
|
440
445
|
}
|
|
@@ -475,31 +480,35 @@ function planInstall(root, version = engineVersion()) {
|
|
|
475
480
|
const steps = [
|
|
476
481
|
stepFor(manager, "package.json", packages, {
|
|
477
482
|
workspaceRoot,
|
|
483
|
+
dir: root,
|
|
478
484
|
dev: runtime?.dev === true && pending.every((entry) => entry.name === RUNTIME_PACKAGE) && pending.length > 0
|
|
479
485
|
}),
|
|
480
|
-
stepFor(manager, "package.json", [typesPackage], { workspaceRoot, dev: true })
|
|
486
|
+
stepFor(manager, "package.json", [typesPackage], { workspaceRoot, dir: root, dev: true })
|
|
481
487
|
];
|
|
482
|
-
for (const
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
488
|
+
for (const name of [RUNTIME_PACKAGE, TYPES_PACKAGE]) {
|
|
489
|
+
for (const dir of workspaceMembers(root, name)) {
|
|
490
|
+
const declared = declaredIn(dir, name);
|
|
491
|
+
steps.push(
|
|
492
|
+
stepFor(
|
|
493
|
+
manager,
|
|
494
|
+
manifestPathOf(root, dir),
|
|
495
|
+
[
|
|
496
|
+
{
|
|
497
|
+
name,
|
|
498
|
+
version,
|
|
499
|
+
declared: declared.version,
|
|
500
|
+
satisfied: declared.version === version
|
|
501
|
+
}
|
|
502
|
+
],
|
|
489
503
|
{
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
504
|
+
filter: `./${(0, import_node_path2.relative)(root, dir).split(import_node_path2.sep).join("/")}`,
|
|
505
|
+
workspaceRoot: false,
|
|
506
|
+
dir,
|
|
507
|
+
dev: declared.dev
|
|
494
508
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
workspaceRoot: false,
|
|
499
|
-
dev: declared.dev
|
|
500
|
-
}
|
|
501
|
-
)
|
|
502
|
-
);
|
|
509
|
+
)
|
|
510
|
+
);
|
|
511
|
+
}
|
|
503
512
|
}
|
|
504
513
|
return {
|
|
505
514
|
root,
|
|
@@ -528,12 +537,13 @@ function renderStep(step) {
|
|
|
528
537
|
const added = pending.filter((entry) => entry.declared === void 0);
|
|
529
538
|
const replaced = pending.filter((entry) => entry.declared !== void 0);
|
|
530
539
|
const block = step.dev ? "devDependencies" : "dependencies";
|
|
540
|
+
const edge = step.blockPresent ? " " : " +";
|
|
531
541
|
return [
|
|
532
542
|
step.manifest,
|
|
533
543
|
...added.length === 0 ? [] : [
|
|
534
|
-
|
|
544
|
+
`${edge} "${block}": {`,
|
|
535
545
|
...added.map((entry) => ` + "${entry.name}": "${entry.version}"`),
|
|
536
|
-
|
|
546
|
+
`${edge} }`
|
|
537
547
|
],
|
|
538
548
|
...replaced.flatMap((entry) => [
|
|
539
549
|
` - "${entry.name}": "${entry.declared}"`,
|
|
@@ -592,22 +602,10 @@ var import_provider_filesystem2 = require("@penvhq/provider-filesystem");
|
|
|
592
602
|
// src/env-flags.ts
|
|
593
603
|
var import_core3 = require("@penvhq/core");
|
|
594
604
|
var BASE_RESERVED = ["help", "version", "h", "v", "_", "--"];
|
|
595
|
-
var COMMAND_FLAGS = [
|
|
596
|
-
...BASE_RESERVED,
|
|
597
|
-
"env",
|
|
598
|
-
"out",
|
|
599
|
-
"allow-decrypt",
|
|
600
|
-
"destination",
|
|
601
|
-
"dest",
|
|
602
|
-
"d",
|
|
603
|
-
"location",
|
|
604
|
-
"l",
|
|
605
|
-
"yes",
|
|
606
|
-
"y"
|
|
607
|
-
];
|
|
605
|
+
var COMMAND_FLAGS = [...BASE_RESERVED, "env", "out", "allow-decrypt", "yes", "y"];
|
|
608
606
|
function shadowedEnvironments(config) {
|
|
609
607
|
const flags = new Set(COMMAND_FLAGS);
|
|
610
|
-
return config.
|
|
608
|
+
return (0, import_core3.environmentNames)(config).filter((environment) => flags.has(environment));
|
|
611
609
|
}
|
|
612
610
|
function shorthandCandidates(args, declared) {
|
|
613
611
|
const reserved = /* @__PURE__ */ new Set([...BASE_RESERVED, ...declared]);
|
|
@@ -620,13 +618,14 @@ function environmentFromShorthand(config, candidates, explicit) {
|
|
|
620
618
|
if (candidates.length === 0) {
|
|
621
619
|
return void 0;
|
|
622
620
|
}
|
|
623
|
-
const
|
|
624
|
-
const
|
|
621
|
+
const declared = (0, import_core3.environmentNames)(config);
|
|
622
|
+
const hits = candidates.filter((candidate) => declared.includes(candidate));
|
|
623
|
+
const strangers = candidates.filter((candidate) => !declared.includes(candidate));
|
|
625
624
|
if (strangers.length > 0) {
|
|
626
625
|
throw new import_core3.PenvError(
|
|
627
626
|
"UNKNOWN_FLAG",
|
|
628
627
|
`${quoteList(strangers)} ${strangers.length === 1 ? "is not a flag" : "are not flags"} this command takes, and ${strangers.length === 1 ? "names" : "name"} no declared environment`,
|
|
629
|
-
`Declared environments work as bare flags: ${quoteList(
|
|
628
|
+
`Declared environments work as bare flags: ${quoteList(declared)}. Anything else needs \`--env <name>\`.`
|
|
630
629
|
);
|
|
631
630
|
}
|
|
632
631
|
if (hits.length > 1) {
|
|
@@ -717,14 +716,18 @@ async function loadPluginProvider(type, context) {
|
|
|
717
716
|
function assertProvidersRegistered(config, projectRoot, options) {
|
|
718
717
|
const local = localExtensions(projectRoot);
|
|
719
718
|
const ci = options?.ci ?? isCi(process.env.CI);
|
|
720
|
-
for (const
|
|
721
|
-
|
|
719
|
+
for (const environment of (0, import_core4.environmentNames)(config)) {
|
|
720
|
+
const provider = (0, import_core4.environmentEntry)(config, environment)?.provider;
|
|
721
|
+
if (typeof provider !== "string" || provider.trim().length === 0) {
|
|
722
|
+
throw (0, import_core4.providerMissing)(environment);
|
|
723
|
+
}
|
|
724
|
+
if (isProviderRegistered(provider)) {
|
|
722
725
|
continue;
|
|
723
726
|
}
|
|
724
|
-
if (ci && local.includes(provider
|
|
725
|
-
throw localExtensionInCi(provider
|
|
727
|
+
if (ci && local.includes(provider)) {
|
|
728
|
+
throw localExtensionInCi(provider, environment);
|
|
726
729
|
}
|
|
727
|
-
resolveExtension(provider
|
|
730
|
+
resolveExtension(provider, projectRoot, environment, local);
|
|
728
731
|
}
|
|
729
732
|
}
|
|
730
733
|
function resolveExtension(type, projectRoot, environment, local = localExtensions(projectRoot)) {
|
|
@@ -882,11 +885,11 @@ function schemaShapeFileOf(project) {
|
|
|
882
885
|
return selfContainedSchemaModule(project.root, schemaFile) ?? import_core5.SCHEMA_SHAPE_FILE;
|
|
883
886
|
}
|
|
884
887
|
async function sourceProviderFor(project, environment) {
|
|
885
|
-
const providerConfig = project.config
|
|
888
|
+
const providerConfig = (0, import_core5.environmentEntry)(project.config, environment);
|
|
886
889
|
if (providerConfig === void 0) {
|
|
887
890
|
return createProvider(LOCAL_TREE_TYPE, { root: project.root, config: project.config });
|
|
888
891
|
}
|
|
889
|
-
return createSourceProvider(providerConfig.
|
|
892
|
+
return createSourceProvider(providerConfig.provider, {
|
|
890
893
|
root: project.root,
|
|
891
894
|
config: project.config,
|
|
892
895
|
providerConfig,
|
|
@@ -898,7 +901,7 @@ function targetEnvironment(project, explicit, shorthand) {
|
|
|
898
901
|
return (0, import_core5.resolveEnvironment)(project.config, explicit ?? fromFlags);
|
|
899
902
|
}
|
|
900
903
|
var KEY_SEPARATOR = /[./\\]/;
|
|
901
|
-
var NO_ENVIRONMENTS = { environments:
|
|
904
|
+
var NO_ENVIRONMENTS = { environments: {} };
|
|
902
905
|
function refFromKey(key, config) {
|
|
903
906
|
const segments = key.split(KEY_SEPARATOR).filter((segment) => segment.length > 0);
|
|
904
907
|
const name = segments[segments.length - 1];
|
|
@@ -1222,8 +1225,9 @@ function discoverDotenvFiles(root) {
|
|
|
1222
1225
|
return files.sort((a, b) => order(a) < order(b) ? -1 : order(a) > order(b) ? 1 : 0);
|
|
1223
1226
|
}
|
|
1224
1227
|
function activeDotenvFiles(root, config) {
|
|
1228
|
+
const declared = (0, import_core7.environmentNames)(config);
|
|
1225
1229
|
return discoverDotenvFiles(root).filter(
|
|
1226
|
-
(file) => file.environment === void 0 ||
|
|
1230
|
+
(file) => file.environment === void 0 || declared.includes(file.environment)
|
|
1227
1231
|
);
|
|
1228
1232
|
}
|
|
1229
1233
|
function environmentsDeclaredBy(files) {
|
|
@@ -1964,11 +1968,12 @@ function packageManifest(specifier, root) {
|
|
|
1964
1968
|
}
|
|
1965
1969
|
function declaredCredentials(config, root) {
|
|
1966
1970
|
const declared = {};
|
|
1967
|
-
for (const
|
|
1968
|
-
|
|
1971
|
+
for (const environment of (0, import_core11.environmentNames)(config)) {
|
|
1972
|
+
const provider = (0, import_core11.environmentEntry)(config, environment)?.provider;
|
|
1973
|
+
if (provider === void 0 || (0, import_core11.own)(declared, provider) !== void 0) {
|
|
1969
1974
|
continue;
|
|
1970
1975
|
}
|
|
1971
|
-
const penv = packageManifest(provider
|
|
1976
|
+
const penv = packageManifest(provider, root)?.penv;
|
|
1972
1977
|
const credentials = isPlainObject(penv) ? penv.credentials : void 0;
|
|
1973
1978
|
if (credentials === void 0) {
|
|
1974
1979
|
continue;
|
|
@@ -1976,11 +1981,11 @@ function declaredCredentials(config, root) {
|
|
|
1976
1981
|
if (!Array.isArray(credentials) || credentials.some((name) => typeof name !== "string" || !VARIABLE.test(name))) {
|
|
1977
1982
|
throw new import_core11.PenvError(
|
|
1978
1983
|
"PROVIDER_CREDENTIALS_INVALID",
|
|
1979
|
-
`\`${provider
|
|
1984
|
+
`\`${provider}\` declares \`penv.credentials\`, and it is not a list of variable names`,
|
|
1980
1985
|
`Its package.json should read \`"penv": { "credentials": ["ACME_TOKEN"] }\` \u2014 penv strips exactly what an extension declares, so it will not guess at a declaration it cannot read.`
|
|
1981
1986
|
);
|
|
1982
1987
|
}
|
|
1983
|
-
declared[provider
|
|
1988
|
+
declared[provider] = credentials;
|
|
1984
1989
|
}
|
|
1985
1990
|
return declared;
|
|
1986
1991
|
}
|
|
@@ -2672,38 +2677,23 @@ function lineReader(input = process.stdin, output = process.stdout) {
|
|
|
2672
2677
|
// src/commands/push.ts
|
|
2673
2678
|
var LAST_PUSHED_KEY = "lastPushedAt";
|
|
2674
2679
|
async function destinationFor(project, environment, options) {
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
type: options.destination,
|
|
2678
|
-
...options.location === void 0 ? {} : { location: options.location }
|
|
2679
|
-
};
|
|
2680
|
-
const provider2 = options.provider ?? await createSourceProvider(options.destination, {
|
|
2681
|
-
root: project.root,
|
|
2682
|
-
config: project.config,
|
|
2683
|
-
providerConfig,
|
|
2684
|
-
environment
|
|
2685
|
-
});
|
|
2686
|
-
return { provider: provider2, location: options.location };
|
|
2687
|
-
}
|
|
2688
|
-
const declared = project.config.providers[environment];
|
|
2689
|
-
const location = declared?.location;
|
|
2690
|
-
if (declared === void 0 || declared.type === LOCAL_TREE_TYPE) {
|
|
2680
|
+
const declared = (0, import_core15.environmentEntry)(project.config, environment);
|
|
2681
|
+
if (declared === void 0 || declared.provider === LOCAL_TREE_TYPE) {
|
|
2691
2682
|
throw new import_core15.PenvError(
|
|
2692
2683
|
"NO_DESTINATION",
|
|
2693
2684
|
`Environment ${environment}'s provider is the local records tree itself, so penv has nowhere to push`,
|
|
2694
|
-
`Declare
|
|
2685
|
+
`Declare the backend that holds it in penv.config.ts \u2014 e.g. \`${environment}: { provider: "@penvhq/provider-github", repository: "owner/repo" }\` \u2014 and run this again. A push only ever goes where the config says this environment lives.`
|
|
2695
2686
|
);
|
|
2696
2687
|
}
|
|
2697
2688
|
if (options.provider !== void 0) {
|
|
2698
|
-
return
|
|
2689
|
+
return options.provider;
|
|
2699
2690
|
}
|
|
2700
|
-
|
|
2691
|
+
return createSourceProvider(declared.provider, {
|
|
2701
2692
|
root: project.root,
|
|
2702
2693
|
config: project.config,
|
|
2703
2694
|
providerConfig: declared,
|
|
2704
2695
|
environment
|
|
2705
2696
|
});
|
|
2706
|
-
return { provider, location };
|
|
2707
2697
|
}
|
|
2708
2698
|
function plan(resolutions, config, environment, allowDecrypt) {
|
|
2709
2699
|
const outbound = [];
|
|
@@ -2787,7 +2777,7 @@ async function ensureTargetApproved(provider, environment, options) {
|
|
|
2787
2777
|
await provider.ensureTarget(environment);
|
|
2788
2778
|
return true;
|
|
2789
2779
|
}
|
|
2790
|
-
async function pushProjection(project, provider, environment,
|
|
2780
|
+
async function pushProjection(project, provider, environment, options) {
|
|
2791
2781
|
const tree = localTree(project);
|
|
2792
2782
|
const keys = keySourceFor(project, environment);
|
|
2793
2783
|
const resolutions = resolveAllSync(tree, environment, keys, true);
|
|
@@ -2818,7 +2808,6 @@ async function pushProjection(project, provider, environment, location, options)
|
|
|
2818
2808
|
environment,
|
|
2819
2809
|
destination: provider.type,
|
|
2820
2810
|
mode: "projection",
|
|
2821
|
-
location,
|
|
2822
2811
|
pushed: outbound.length,
|
|
2823
2812
|
meta: 0,
|
|
2824
2813
|
repositorySecrets,
|
|
@@ -2834,7 +2823,7 @@ function crossesToRecords(file, environment) {
|
|
|
2834
2823
|
}
|
|
2835
2824
|
return scope.kind === "environment" && scope.environment === environment;
|
|
2836
2825
|
}
|
|
2837
|
-
async function pushRecords(project, provider, environment
|
|
2826
|
+
async function pushRecords(project, provider, environment) {
|
|
2838
2827
|
const tree = localTree(project);
|
|
2839
2828
|
const files = tree.listSync().filter((file) => crossesToRecords(file, environment));
|
|
2840
2829
|
let pushed = 0;
|
|
@@ -2860,7 +2849,6 @@ async function pushRecords(project, provider, environment, location) {
|
|
|
2860
2849
|
environment,
|
|
2861
2850
|
destination: provider.type,
|
|
2862
2851
|
mode: "records",
|
|
2863
|
-
location,
|
|
2864
2852
|
pushed,
|
|
2865
2853
|
meta,
|
|
2866
2854
|
repositorySecrets: 0,
|
|
@@ -2872,11 +2860,11 @@ async function pushRecords(project, provider, environment, location) {
|
|
|
2872
2860
|
async function runPush(options) {
|
|
2873
2861
|
const project = openProject(options.cwd);
|
|
2874
2862
|
const environment = targetEnvironment(project, options.environment, options.envFlags);
|
|
2875
|
-
const
|
|
2863
|
+
const provider = await destinationFor(project, environment, options);
|
|
2876
2864
|
if ((0, import_core15.holdsProjection)(provider)) {
|
|
2877
|
-
return pushProjection(project, provider, environment,
|
|
2865
|
+
return pushProjection(project, provider, environment, options);
|
|
2878
2866
|
}
|
|
2879
|
-
return pushRecords(project, provider, environment
|
|
2867
|
+
return pushRecords(project, provider, environment);
|
|
2880
2868
|
}
|
|
2881
2869
|
function renderPush(result2) {
|
|
2882
2870
|
if (result2.pushed === 0 && result2.meta === 0) {
|
|
@@ -2888,7 +2876,7 @@ function renderPush(result2) {
|
|
|
2888
2876
|
}
|
|
2889
2877
|
]);
|
|
2890
2878
|
}
|
|
2891
|
-
const target = `${result2.destination} for environment ${result2.environment}
|
|
2879
|
+
const target = `${result2.destination} for environment ${result2.environment}`;
|
|
2892
2880
|
if (result2.mode === "records") {
|
|
2893
2881
|
return formatRows([
|
|
2894
2882
|
{
|
|
@@ -2937,6 +2925,19 @@ function renderPush(result2) {
|
|
|
2937
2925
|
}
|
|
2938
2926
|
return formatRows(rows);
|
|
2939
2927
|
}
|
|
2928
|
+
var REDIRECT_FLAGS = ["destination", "dest", "d", "location", "l"];
|
|
2929
|
+
function assertNoRedirect(args) {
|
|
2930
|
+
const given = REDIRECT_FLAGS.filter((flag) => args[flag] !== void 0);
|
|
2931
|
+
const named = given[0];
|
|
2932
|
+
if (named === void 0) {
|
|
2933
|
+
return;
|
|
2934
|
+
}
|
|
2935
|
+
throw new import_core15.PenvError(
|
|
2936
|
+
"REMOVED_FLAG",
|
|
2937
|
+
`\`--${named}\` is not a flag \`penv push\` takes`,
|
|
2938
|
+
"A push only ever goes where the config says this environment lives. Declare the destination in `environments.<env>` \u2014 the provider package, addressed in that provider's own words \u2014 and push with `--env <name>`."
|
|
2939
|
+
);
|
|
2940
|
+
}
|
|
2940
2941
|
var pushCommand = (0, import_citty6.defineCommand)({
|
|
2941
2942
|
meta: {
|
|
2942
2943
|
name: "push",
|
|
@@ -2948,16 +2949,6 @@ var pushCommand = (0, import_citty6.defineCommand)({
|
|
|
2948
2949
|
type: "boolean",
|
|
2949
2950
|
description: "Decrypt sealed values locally and push them as plaintext for the destination to re-seal"
|
|
2950
2951
|
},
|
|
2951
|
-
destination: {
|
|
2952
|
-
type: "string",
|
|
2953
|
-
alias: ["dest", "d"],
|
|
2954
|
-
description: "Push once to this provider package instead of the declared one"
|
|
2955
|
-
},
|
|
2956
|
-
location: {
|
|
2957
|
-
type: "string",
|
|
2958
|
-
alias: "l",
|
|
2959
|
-
description: "The destination-side place, when --destination needs one"
|
|
2960
|
-
},
|
|
2961
2952
|
yes: {
|
|
2962
2953
|
type: "boolean",
|
|
2963
2954
|
alias: "y",
|
|
@@ -2966,25 +2957,13 @@ var pushCommand = (0, import_citty6.defineCommand)({
|
|
|
2966
2957
|
},
|
|
2967
2958
|
run({ args }) {
|
|
2968
2959
|
return guard(async () => {
|
|
2960
|
+
assertNoRedirect(args);
|
|
2969
2961
|
const result2 = await runPush({
|
|
2970
2962
|
cwd: process.cwd(),
|
|
2971
2963
|
...args.env === void 0 ? {} : { environment: args.env },
|
|
2972
2964
|
...args["allow-decrypt"] === void 0 ? {} : { allowDecrypt: args["allow-decrypt"] },
|
|
2973
|
-
...args.destination === void 0 ? {} : { destination: args.destination },
|
|
2974
|
-
...args.location === void 0 ? {} : { location: args.location },
|
|
2975
2965
|
...args.yes === void 0 ? {} : { yes: args.yes },
|
|
2976
|
-
envFlags: shorthandCandidates(args, [
|
|
2977
|
-
"env",
|
|
2978
|
-
"allow-decrypt",
|
|
2979
|
-
"allowDecrypt",
|
|
2980
|
-
"destination",
|
|
2981
|
-
"dest",
|
|
2982
|
-
"d",
|
|
2983
|
-
"location",
|
|
2984
|
-
"l",
|
|
2985
|
-
"yes",
|
|
2986
|
-
"y"
|
|
2987
|
-
])
|
|
2966
|
+
envFlags: shorthandCandidates(args, ["env", "allow-decrypt", "allowDecrypt", "yes", "y"])
|
|
2988
2967
|
});
|
|
2989
2968
|
write(renderPush(result2));
|
|
2990
2969
|
});
|
|
@@ -3083,7 +3062,7 @@ async function runDoctor(options) {
|
|
|
3083
3062
|
check: "provider",
|
|
3084
3063
|
severity: "pass",
|
|
3085
3064
|
label: "Provider",
|
|
3086
|
-
subject: project.config
|
|
3065
|
+
subject: (0, import_core16.environmentEntry)(project.config, environment)?.provider ?? project.provider.type
|
|
3087
3066
|
});
|
|
3088
3067
|
return {
|
|
3089
3068
|
environment,
|
|
@@ -3353,8 +3332,8 @@ async function projectionFindings(project, environment, override) {
|
|
|
3353
3332
|
if (override !== void 0) {
|
|
3354
3333
|
projection = override;
|
|
3355
3334
|
} else {
|
|
3356
|
-
const declared = project.config
|
|
3357
|
-
if (declared === void 0 || declared.
|
|
3335
|
+
const declared = (0, import_core16.environmentEntry)(project.config, environment);
|
|
3336
|
+
if (declared === void 0 || declared.provider === LOCAL_TREE_TYPE) {
|
|
3358
3337
|
return [];
|
|
3359
3338
|
}
|
|
3360
3339
|
let source;
|
|
@@ -3366,7 +3345,7 @@ async function projectionFindings(project, environment, override) {
|
|
|
3366
3345
|
check: "projection-unreachable",
|
|
3367
3346
|
severity: "unknown",
|
|
3368
3347
|
label: "Destination",
|
|
3369
|
-
subject: `could not build the ${declared.
|
|
3348
|
+
subject: `could not build the ${declared.provider} provider for ${environment}`,
|
|
3370
3349
|
detail: errorDetail(error)
|
|
3371
3350
|
}
|
|
3372
3351
|
];
|
|
@@ -3639,8 +3618,8 @@ function refPathOf(ref) {
|
|
|
3639
3618
|
return [...ref.namespace, ref.name].join("/");
|
|
3640
3619
|
}
|
|
3641
3620
|
async function rotationSubjects(project, environment, local, override) {
|
|
3642
|
-
const providerConfig = project.config
|
|
3643
|
-
if (override === void 0 && (providerConfig === void 0 || providerConfig.
|
|
3621
|
+
const providerConfig = (0, import_core16.environmentEntry)(project.config, environment);
|
|
3622
|
+
if (override === void 0 && (providerConfig === void 0 || providerConfig.provider === LOCAL_TREE_TYPE)) {
|
|
3644
3623
|
return { kind: "read", subjects: local };
|
|
3645
3624
|
}
|
|
3646
3625
|
const source = override ?? await sourceProviderFor(project, environment);
|
|
@@ -3662,7 +3641,7 @@ async function rotationSubjects(project, environment, local, override) {
|
|
|
3662
3641
|
check: "rotation-overdue",
|
|
3663
3642
|
severity: "unknown",
|
|
3664
3643
|
label: "Rotation",
|
|
3665
|
-
subject: `could not reach the ${providerConfig?.
|
|
3644
|
+
subject: `could not reach the ${providerConfig?.provider ?? source.type} source of truth for ${environment}`,
|
|
3666
3645
|
detail: errorDetail(error)
|
|
3667
3646
|
}
|
|
3668
3647
|
};
|
|
@@ -3783,8 +3762,8 @@ async function readRelevant(provider, environment) {
|
|
|
3783
3762
|
return entries;
|
|
3784
3763
|
}
|
|
3785
3764
|
async function providerDriftFindings(project, environment, override) {
|
|
3786
|
-
const providerConfig = project.config
|
|
3787
|
-
if (providerConfig === void 0 || providerConfig.
|
|
3765
|
+
const providerConfig = (0, import_core16.environmentEntry)(project.config, environment);
|
|
3766
|
+
if (providerConfig === void 0 || providerConfig.provider === LOCAL_TREE_TYPE) {
|
|
3788
3767
|
return [
|
|
3789
3768
|
{
|
|
3790
3769
|
check: "provider-value-drift",
|
|
@@ -3809,7 +3788,7 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
3809
3788
|
check: "provider-value-drift",
|
|
3810
3789
|
severity: "unknown",
|
|
3811
3790
|
label: "Provider values",
|
|
3812
|
-
subject: `could not reach the ${providerConfig.
|
|
3791
|
+
subject: `could not reach the ${providerConfig.provider} provider for ${environment}`,
|
|
3813
3792
|
detail: errorDetail(error)
|
|
3814
3793
|
}
|
|
3815
3794
|
];
|
|
@@ -3828,7 +3807,7 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
3828
3807
|
severity: "unknown",
|
|
3829
3808
|
label: "Provider value unreadable",
|
|
3830
3809
|
subject: (0, import_core16.formatValueFile)(here.file),
|
|
3831
|
-
detail: `the local value is sealed and did not open, so it cannot be compared against the ${providerConfig.
|
|
3810
|
+
detail: `the local value is sealed and did not open, so it cannot be compared against the ${providerConfig.provider} source of truth`
|
|
3832
3811
|
});
|
|
3833
3812
|
continue;
|
|
3834
3813
|
}
|
|
@@ -3838,7 +3817,7 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
3838
3817
|
severity: "failure",
|
|
3839
3818
|
label: "Provider value drift",
|
|
3840
3819
|
subject: (0, import_core16.formatValueFile)(here.file),
|
|
3841
|
-
detail: `the local tree and the ${providerConfig.
|
|
3820
|
+
detail: `the local tree and the ${providerConfig.provider} source of truth hold different values`
|
|
3842
3821
|
});
|
|
3843
3822
|
}
|
|
3844
3823
|
continue;
|
|
@@ -3852,7 +3831,7 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
3852
3831
|
severity: "warning",
|
|
3853
3832
|
label: here !== void 0 ? "Only in the local tree" : "Only in the source",
|
|
3854
3833
|
subject: (0, import_core16.formatValueFile)(present.file),
|
|
3855
|
-
detail: here !== void 0 ? `present locally, absent from the ${providerConfig.
|
|
3834
|
+
detail: here !== void 0 ? `present locally, absent from the ${providerConfig.provider} source of truth` : `present in the ${providerConfig.provider} source of truth, absent from the local tree`
|
|
3856
3835
|
});
|
|
3857
3836
|
}
|
|
3858
3837
|
if (findings.length > 0) {
|
|
@@ -3863,7 +3842,7 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
3863
3842
|
check: "provider-value-drift",
|
|
3864
3843
|
severity: "pass",
|
|
3865
3844
|
label: "Provider values",
|
|
3866
|
-
subject: `every value matches the ${providerConfig.
|
|
3845
|
+
subject: `every value matches the ${providerConfig.provider} source of truth for ${environment}`
|
|
3867
3846
|
}
|
|
3868
3847
|
];
|
|
3869
3848
|
}
|
|
@@ -3965,7 +3944,7 @@ function targetScope(project, options, key) {
|
|
|
3965
3944
|
throw new import_core17.PenvError(
|
|
3966
3945
|
"ENVIRONMENT_FLAG_EMPTY",
|
|
3967
3946
|
`\`--env\` for parameter ${key} names no environment`,
|
|
3968
|
-
`Pass a declared environment \u2014 ${project.config.
|
|
3947
|
+
`Pass a declared environment \u2014 ${(0, import_core17.environmentNames)(project.config).map((e) => `\`${e}\``).join(", ")} \u2014 e.g. \`--env production\`, or drop \`--env\` to write the scope that has no environment.`
|
|
3969
3948
|
);
|
|
3970
3949
|
}
|
|
3971
3950
|
return scopeFrom({ ...options, environment: targetEnvironment(project, environment) });
|
|
@@ -3978,7 +3957,7 @@ function sealFor(project, file, value, parameter, environment) {
|
|
|
3978
3957
|
throw new import_core17.PenvError(
|
|
3979
3958
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
3980
3959
|
`Parameter ${parameter} is a secret, and ${(0, import_core17.recordPath)((0, import_core17.formatValueFile)(file))} names no environment`,
|
|
3981
|
-
"
|
|
3960
|
+
"A key is declared by its environment's `keySource` in penv.config.ts, so penv cannot tell which key should seal a file that every environment reads. Write it at an environment scope \u2014 add `--env <environment>` \u2014 or drop `secret` from the parameter's meta."
|
|
3982
3961
|
);
|
|
3983
3962
|
}
|
|
3984
3963
|
return (0, import_core17.sealValue)(file, value, keySourceFor(project, environment), parameter, environment);
|
|
@@ -5064,8 +5043,14 @@ function environmentsFromFlag(flag) {
|
|
|
5064
5043
|
}
|
|
5065
5044
|
return [...new Set(names)];
|
|
5066
5045
|
}
|
|
5046
|
+
function environmentsRecord(names) {
|
|
5047
|
+
return Object.fromEntries(names.map((environment) => [environment, LOCAL_PROVIDER]));
|
|
5048
|
+
}
|
|
5067
5049
|
function configOf(decisions) {
|
|
5068
|
-
return {
|
|
5050
|
+
return {
|
|
5051
|
+
environments: environmentsRecord(decisions.environments),
|
|
5052
|
+
schemaFile: decisions.schemaFile
|
|
5053
|
+
};
|
|
5069
5054
|
}
|
|
5070
5055
|
function declaredIn2(root) {
|
|
5071
5056
|
const file = (0, import_node_path13.join)(root, CONFIG_FILE);
|
|
@@ -5097,7 +5082,7 @@ function planInit(root, flags = {}) {
|
|
|
5097
5082
|
if (schemaFile.length === 0) {
|
|
5098
5083
|
throw emptyFlag("schema");
|
|
5099
5084
|
}
|
|
5100
|
-
const error = (0, import_core25.validateSchemaFile)({ environments:
|
|
5085
|
+
const error = (0, import_core25.validateSchemaFile)({ environments: {}, schemaFile })[0];
|
|
5101
5086
|
if (error !== void 0) {
|
|
5102
5087
|
throw error;
|
|
5103
5088
|
}
|
|
@@ -5120,7 +5105,7 @@ function planInit(root, flags = {}) {
|
|
|
5120
5105
|
}
|
|
5121
5106
|
const suggestedEnvironments = suggestEnvironments(root);
|
|
5122
5107
|
const safeDefault = flags.yes === true && declared === void 0;
|
|
5123
|
-
const environments = flags.environments ?? declared
|
|
5108
|
+
const environments = flags.environments ?? (declared === void 0 ? void 0 : (0, import_core25.environmentNames)(declared)) ?? (safeDefault ? [DEVELOPMENT] : []);
|
|
5124
5109
|
const defaultEnvironment = declared?.defaultEnvironment ?? (safeDefault ? DEVELOPMENT : void 0);
|
|
5125
5110
|
if (safeDefault) {
|
|
5126
5111
|
notes.push(
|
|
@@ -5253,27 +5238,27 @@ export { schema };
|
|
|
5253
5238
|
|
|
5254
5239
|
` + loadComment + loadCall;
|
|
5255
5240
|
}
|
|
5241
|
+
function entryKey(name) {
|
|
5242
|
+
return /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name);
|
|
5243
|
+
}
|
|
5256
5244
|
function renderEnvironments(decisions) {
|
|
5257
|
-
const shared = " // Environments are a whitelist
|
|
5245
|
+
const shared = " // Environments are a whitelist, and each entry is that environment's whole\n // declaration: which provider holds it, in that provider's own words, and\n // which key seals it. A segment is an environment only if it is named here.\n";
|
|
5258
5246
|
if (decisions.environments.length === 0) {
|
|
5259
5247
|
return `${shared} // It starts empty because which environments you deploy is not something penv
|
|
5260
5248
|
// can read off your codebase, and an environment you do not have is worse
|
|
5261
|
-
// than one you have not declared yet. Name yours
|
|
5262
|
-
// environments:
|
|
5263
|
-
//
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5249
|
+
// than one you have not declared yet. Name yours:
|
|
5250
|
+
// environments: {
|
|
5251
|
+
// development: "@penvhq/provider-filesystem",
|
|
5252
|
+
// production: { provider: "@penvhq/provider-vault", path: "penv", keySource: "env" },
|
|
5253
|
+
// },
|
|
5254
|
+
environments: {},
|
|
5267
5255
|
`;
|
|
5268
5256
|
}
|
|
5269
|
-
const
|
|
5270
|
-
const providers = decisions.environments.map((name) => ` ${JSON.stringify(name)}: { type: "@penvhq/provider-filesystem" },
|
|
5257
|
+
const entries = decisions.environments.map((name) => ` ${entryKey(name)}: ${JSON.stringify(LOCAL_PROVIDER)},
|
|
5271
5258
|
`).join("");
|
|
5272
|
-
return `${shared}
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
providers: {
|
|
5276
|
-
${providers} },
|
|
5259
|
+
return `${shared} // A provider that needs no fields is just its package name.
|
|
5260
|
+
environments: {
|
|
5261
|
+
${entries} },
|
|
5277
5262
|
`;
|
|
5278
5263
|
}
|
|
5279
5264
|
function renderConfigModule(decisions) {
|
|
@@ -5792,13 +5777,13 @@ function planCutover(input) {
|
|
|
5792
5777
|
const declared = declaredIn2(root);
|
|
5793
5778
|
const named = environmentsDeclaredBy(selected);
|
|
5794
5779
|
const chosen = named.length > 0 ? named : [requireEnvironment(input)];
|
|
5795
|
-
const environments = declared === void 0 ? chosen : declared
|
|
5780
|
+
const environments = declared === void 0 ? chosen : (0, import_core25.environmentNames)(declared);
|
|
5796
5781
|
for (const environment of chosen) {
|
|
5797
5782
|
if (!environments.includes(environment)) {
|
|
5798
5783
|
throw new import_core25.PenvError(
|
|
5799
5784
|
"INIT_ENVIRONMENT_UNDECLARED",
|
|
5800
5785
|
`${CONFIG_FILE} declares ${describeList(environments)}, and adopting these files needs \`${environment}\` declared too`,
|
|
5801
|
-
`Add \`${environment}\` to \`environments\` in ${CONFIG_FILE},
|
|
5786
|
+
`Add \`${environment}: "${LOCAL_PROVIDER}"\` to \`environments\` in ${CONFIG_FILE}, or name the backend that holds it, then run \`penv init\` again. Nothing was changed.`
|
|
5802
5787
|
);
|
|
5803
5788
|
}
|
|
5804
5789
|
}
|
|
@@ -5865,10 +5850,7 @@ function distinct(refs) {
|
|
|
5865
5850
|
}
|
|
5866
5851
|
function configFor(decisions) {
|
|
5867
5852
|
return {
|
|
5868
|
-
environments: decisions.environments,
|
|
5869
|
-
providers: Object.fromEntries(
|
|
5870
|
-
decisions.environments.map((environment) => [environment, { type: LOCAL_PROVIDER }])
|
|
5871
|
-
),
|
|
5853
|
+
environments: environmentsRecord(decisions.environments),
|
|
5872
5854
|
schemaFile: decisions.schemaFile,
|
|
5873
5855
|
...decisions.defaultEnvironment === void 0 ? {} : { defaultEnvironment: decisions.defaultEnvironment },
|
|
5874
5856
|
...decisions.publicPrefixes.length === 0 ? {} : { publicPrefixes: [...decisions.publicPrefixes] }
|
|
@@ -6208,7 +6190,8 @@ function offeredEnvironment(root) {
|
|
|
6208
6190
|
if (declared === void 0) {
|
|
6209
6191
|
return DEVELOPMENT;
|
|
6210
6192
|
}
|
|
6211
|
-
|
|
6193
|
+
const names = (0, import_core25.environmentNames)(declared);
|
|
6194
|
+
return declared.defaultEnvironment ?? (names.includes(DEVELOPMENT) ? DEVELOPMENT : names[0] ?? DEVELOPMENT);
|
|
6212
6195
|
}
|
|
6213
6196
|
async function cutoverWithYes(root, base, adoption) {
|
|
6214
6197
|
const selected = selectionForYes(adoption);
|
|
@@ -6316,10 +6299,11 @@ var BACKUP_SUFFIX = ".backup";
|
|
|
6316
6299
|
var DOTENV_SEGMENT = "env";
|
|
6317
6300
|
var LOCAL2 = "local";
|
|
6318
6301
|
function assertDeclared(segment, config) {
|
|
6319
|
-
|
|
6302
|
+
const declared = (0, import_core26.environmentNames)(config);
|
|
6303
|
+
if (declared.includes(segment)) {
|
|
6320
6304
|
return segment;
|
|
6321
6305
|
}
|
|
6322
|
-
throw new import_core26.UnknownEnvironmentError(segment,
|
|
6306
|
+
throw new import_core26.UnknownEnvironmentError(segment, declared);
|
|
6323
6307
|
}
|
|
6324
6308
|
function scopeFromFilename(file, config) {
|
|
6325
6309
|
const name = (0, import_node_path14.basename)(file);
|
|
@@ -6389,7 +6373,7 @@ function explicitEnvironment(options, source, config) {
|
|
|
6389
6373
|
throw new import_core26.PenvError(
|
|
6390
6374
|
"IMPORT_ENV_FLAG_EMPTY",
|
|
6391
6375
|
`\`--env\` for the import of ${source} names no environment`,
|
|
6392
|
-
`Pass a declared environment \u2014 ${config.
|
|
6376
|
+
`Pass a declared environment \u2014 ${(0, import_core26.environmentNames)(config).map((e) => `\`${e}\``).join(", ")} \u2014 e.g. \`--env production\`, or drop \`--env\` to import ${source} as the scope that has no environment. Nothing was imported.`
|
|
6393
6377
|
);
|
|
6394
6378
|
}
|
|
6395
6379
|
function assertEnvironmentAgrees(derived, explicit, source) {
|
|
@@ -6416,7 +6400,7 @@ function environmentNamed(file, explicit) {
|
|
|
6416
6400
|
}
|
|
6417
6401
|
function decisionsOf(config, cwd) {
|
|
6418
6402
|
return {
|
|
6419
|
-
environments: config
|
|
6403
|
+
environments: (0, import_core26.environmentNames)(config),
|
|
6420
6404
|
// `import` re-scaffolds env.ts for an existing project; injection is an init
|
|
6421
6405
|
// choice, so it is not turned on here.
|
|
6422
6406
|
inject: false,
|
|
@@ -6472,7 +6456,7 @@ function importDotenv(options) {
|
|
|
6472
6456
|
backup,
|
|
6473
6457
|
scope,
|
|
6474
6458
|
environment,
|
|
6475
|
-
environments: config
|
|
6459
|
+
environments: (0, import_core26.environmentNames)(config),
|
|
6476
6460
|
variables: parsed.entries.length,
|
|
6477
6461
|
orphanComments: parsed.orphanComments,
|
|
6478
6462
|
steps
|
|
@@ -6620,12 +6604,12 @@ function envVarFor(id) {
|
|
|
6620
6604
|
function runKeyCreate(options) {
|
|
6621
6605
|
const project = openProject(options.cwd);
|
|
6622
6606
|
const environment = targetEnvironment(project, options.environment);
|
|
6623
|
-
const declared = project.config
|
|
6607
|
+
const declared = (0, import_core28.keyConfigFor)(project.config, environment);
|
|
6624
6608
|
if (declared === void 0) {
|
|
6625
6609
|
throw new import_core28.PenvError(
|
|
6626
6610
|
"KEY_SOURCE_UNDECLARED",
|
|
6627
6611
|
`Environment ${environment} declares no key source, so penv does not know what a key for it would be`,
|
|
6628
|
-
`
|
|
6612
|
+
`Give its entry in penv.config.ts a \`keySource\` \u2014 e.g. \`${environment}: { provider: "@penvhq/provider-filesystem", keySource: "env" }\` \u2014 then run this again. The key is named after the environment unless the object form names another.`
|
|
6629
6613
|
);
|
|
6630
6614
|
}
|
|
6631
6615
|
const key = (0, import_node_crypto.randomBytes)(import_core28.KEY_BYTES).toString("base64");
|
|
@@ -6968,7 +6952,7 @@ async function planFile(project, source, target, parameter) {
|
|
|
6968
6952
|
throw new import_core31.PenvError(
|
|
6969
6953
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
6970
6954
|
`${(0, import_core31.recordPath)((0, import_core31.formatValueFile)(source))} is encrypted at a scope that names no environment, so penv cannot tell which key would re-seal it`,
|
|
6971
|
-
"
|
|
6955
|
+
"A key is declared by its environment's `keySource` in penv.config.ts. Decrypt it with `penv decrypt`, move the parameter, then encrypt it again at its new address."
|
|
6972
6956
|
);
|
|
6973
6957
|
}
|
|
6974
6958
|
const keys = keySourceFor(project, environment);
|