@penvhq/cli 0.14.0 → 0.16.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 +249 -270
- package/dist/bin.cjs.map +1 -1
- package/dist/index.cjs +105 -131
- 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 +119 -134
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -602,22 +602,10 @@ var import_provider_filesystem2 = require("@penvhq/provider-filesystem");
|
|
|
602
602
|
// src/env-flags.ts
|
|
603
603
|
var import_core3 = require("@penvhq/core");
|
|
604
604
|
var BASE_RESERVED = ["help", "version", "h", "v", "_", "--"];
|
|
605
|
-
var COMMAND_FLAGS = [
|
|
606
|
-
...BASE_RESERVED,
|
|
607
|
-
"env",
|
|
608
|
-
"out",
|
|
609
|
-
"allow-decrypt",
|
|
610
|
-
"destination",
|
|
611
|
-
"dest",
|
|
612
|
-
"d",
|
|
613
|
-
"location",
|
|
614
|
-
"l",
|
|
615
|
-
"yes",
|
|
616
|
-
"y"
|
|
617
|
-
];
|
|
605
|
+
var COMMAND_FLAGS = [...BASE_RESERVED, "env", "out", "allow-decrypt", "yes", "y"];
|
|
618
606
|
function shadowedEnvironments(config) {
|
|
619
607
|
const flags = new Set(COMMAND_FLAGS);
|
|
620
|
-
return config.
|
|
608
|
+
return (0, import_core3.environmentNames)(config).filter((environment) => flags.has(environment));
|
|
621
609
|
}
|
|
622
610
|
function shorthandCandidates(args, declared) {
|
|
623
611
|
const reserved = /* @__PURE__ */ new Set([...BASE_RESERVED, ...declared]);
|
|
@@ -630,13 +618,14 @@ function environmentFromShorthand(config, candidates, explicit) {
|
|
|
630
618
|
if (candidates.length === 0) {
|
|
631
619
|
return void 0;
|
|
632
620
|
}
|
|
633
|
-
const
|
|
634
|
-
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));
|
|
635
624
|
if (strangers.length > 0) {
|
|
636
625
|
throw new import_core3.PenvError(
|
|
637
626
|
"UNKNOWN_FLAG",
|
|
638
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`,
|
|
639
|
-
`Declared environments work as bare flags: ${quoteList(
|
|
628
|
+
`Declared environments work as bare flags: ${quoteList(declared)}. Anything else needs \`--env <name>\`.`
|
|
640
629
|
);
|
|
641
630
|
}
|
|
642
631
|
if (hits.length > 1) {
|
|
@@ -727,14 +716,18 @@ async function loadPluginProvider(type, context) {
|
|
|
727
716
|
function assertProvidersRegistered(config, projectRoot, options) {
|
|
728
717
|
const local = localExtensions(projectRoot);
|
|
729
718
|
const ci = options?.ci ?? isCi(process.env.CI);
|
|
730
|
-
for (const
|
|
731
|
-
|
|
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)) {
|
|
732
725
|
continue;
|
|
733
726
|
}
|
|
734
|
-
if (ci && local.includes(provider
|
|
735
|
-
throw localExtensionInCi(provider
|
|
727
|
+
if (ci && local.includes(provider)) {
|
|
728
|
+
throw localExtensionInCi(provider, environment);
|
|
736
729
|
}
|
|
737
|
-
resolveExtension(provider
|
|
730
|
+
resolveExtension(provider, projectRoot, environment, local);
|
|
738
731
|
}
|
|
739
732
|
}
|
|
740
733
|
function resolveExtension(type, projectRoot, environment, local = localExtensions(projectRoot)) {
|
|
@@ -892,11 +885,11 @@ function schemaShapeFileOf(project) {
|
|
|
892
885
|
return selfContainedSchemaModule(project.root, schemaFile) ?? import_core5.SCHEMA_SHAPE_FILE;
|
|
893
886
|
}
|
|
894
887
|
async function sourceProviderFor(project, environment) {
|
|
895
|
-
const providerConfig = project.config
|
|
888
|
+
const providerConfig = (0, import_core5.environmentEntry)(project.config, environment);
|
|
896
889
|
if (providerConfig === void 0) {
|
|
897
890
|
return createProvider(LOCAL_TREE_TYPE, { root: project.root, config: project.config });
|
|
898
891
|
}
|
|
899
|
-
return createSourceProvider(providerConfig.
|
|
892
|
+
return createSourceProvider(providerConfig.provider, {
|
|
900
893
|
root: project.root,
|
|
901
894
|
config: project.config,
|
|
902
895
|
providerConfig,
|
|
@@ -908,7 +901,7 @@ function targetEnvironment(project, explicit, shorthand) {
|
|
|
908
901
|
return (0, import_core5.resolveEnvironment)(project.config, explicit ?? fromFlags);
|
|
909
902
|
}
|
|
910
903
|
var KEY_SEPARATOR = /[./\\]/;
|
|
911
|
-
var NO_ENVIRONMENTS = { environments:
|
|
904
|
+
var NO_ENVIRONMENTS = { environments: {} };
|
|
912
905
|
function refFromKey(key, config) {
|
|
913
906
|
const segments = key.split(KEY_SEPARATOR).filter((segment) => segment.length > 0);
|
|
914
907
|
const name = segments[segments.length - 1];
|
|
@@ -1232,8 +1225,9 @@ function discoverDotenvFiles(root) {
|
|
|
1232
1225
|
return files.sort((a, b) => order(a) < order(b) ? -1 : order(a) > order(b) ? 1 : 0);
|
|
1233
1226
|
}
|
|
1234
1227
|
function activeDotenvFiles(root, config) {
|
|
1228
|
+
const declared = (0, import_core7.environmentNames)(config);
|
|
1235
1229
|
return discoverDotenvFiles(root).filter(
|
|
1236
|
-
(file) => file.environment === void 0 ||
|
|
1230
|
+
(file) => file.environment === void 0 || declared.includes(file.environment)
|
|
1237
1231
|
);
|
|
1238
1232
|
}
|
|
1239
1233
|
function environmentsDeclaredBy(files) {
|
|
@@ -1974,11 +1968,12 @@ function packageManifest(specifier, root) {
|
|
|
1974
1968
|
}
|
|
1975
1969
|
function declaredCredentials(config, root) {
|
|
1976
1970
|
const declared = {};
|
|
1977
|
-
for (const
|
|
1978
|
-
|
|
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) {
|
|
1979
1974
|
continue;
|
|
1980
1975
|
}
|
|
1981
|
-
const penv = packageManifest(provider
|
|
1976
|
+
const penv = packageManifest(provider, root)?.penv;
|
|
1982
1977
|
const credentials = isPlainObject(penv) ? penv.credentials : void 0;
|
|
1983
1978
|
if (credentials === void 0) {
|
|
1984
1979
|
continue;
|
|
@@ -1986,11 +1981,11 @@ function declaredCredentials(config, root) {
|
|
|
1986
1981
|
if (!Array.isArray(credentials) || credentials.some((name) => typeof name !== "string" || !VARIABLE.test(name))) {
|
|
1987
1982
|
throw new import_core11.PenvError(
|
|
1988
1983
|
"PROVIDER_CREDENTIALS_INVALID",
|
|
1989
|
-
`\`${provider
|
|
1984
|
+
`\`${provider}\` declares \`penv.credentials\`, and it is not a list of variable names`,
|
|
1990
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.`
|
|
1991
1986
|
);
|
|
1992
1987
|
}
|
|
1993
|
-
declared[provider
|
|
1988
|
+
declared[provider] = credentials;
|
|
1994
1989
|
}
|
|
1995
1990
|
return declared;
|
|
1996
1991
|
}
|
|
@@ -2682,38 +2677,23 @@ function lineReader(input = process.stdin, output = process.stdout) {
|
|
|
2682
2677
|
// src/commands/push.ts
|
|
2683
2678
|
var LAST_PUSHED_KEY = "lastPushedAt";
|
|
2684
2679
|
async function destinationFor(project, environment, options) {
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
type: options.destination,
|
|
2688
|
-
...options.location === void 0 ? {} : { location: options.location }
|
|
2689
|
-
};
|
|
2690
|
-
const provider2 = options.provider ?? await createSourceProvider(options.destination, {
|
|
2691
|
-
root: project.root,
|
|
2692
|
-
config: project.config,
|
|
2693
|
-
providerConfig,
|
|
2694
|
-
environment
|
|
2695
|
-
});
|
|
2696
|
-
return { provider: provider2, location: options.location };
|
|
2697
|
-
}
|
|
2698
|
-
const declared = project.config.providers[environment];
|
|
2699
|
-
const location = declared?.location;
|
|
2700
|
-
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) {
|
|
2701
2682
|
throw new import_core15.PenvError(
|
|
2702
2683
|
"NO_DESTINATION",
|
|
2703
2684
|
`Environment ${environment}'s provider is the local records tree itself, so penv has nowhere to push`,
|
|
2704
|
-
`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.`
|
|
2705
2686
|
);
|
|
2706
2687
|
}
|
|
2707
2688
|
if (options.provider !== void 0) {
|
|
2708
|
-
return
|
|
2689
|
+
return options.provider;
|
|
2709
2690
|
}
|
|
2710
|
-
|
|
2691
|
+
return createSourceProvider(declared.provider, {
|
|
2711
2692
|
root: project.root,
|
|
2712
2693
|
config: project.config,
|
|
2713
2694
|
providerConfig: declared,
|
|
2714
2695
|
environment
|
|
2715
2696
|
});
|
|
2716
|
-
return { provider, location };
|
|
2717
2697
|
}
|
|
2718
2698
|
function plan(resolutions, config, environment, allowDecrypt) {
|
|
2719
2699
|
const outbound = [];
|
|
@@ -2797,7 +2777,7 @@ async function ensureTargetApproved(provider, environment, options) {
|
|
|
2797
2777
|
await provider.ensureTarget(environment);
|
|
2798
2778
|
return true;
|
|
2799
2779
|
}
|
|
2800
|
-
async function pushProjection(project, provider, environment,
|
|
2780
|
+
async function pushProjection(project, provider, environment, options) {
|
|
2801
2781
|
const tree = localTree(project);
|
|
2802
2782
|
const keys = keySourceFor(project, environment);
|
|
2803
2783
|
const resolutions = resolveAllSync(tree, environment, keys, true);
|
|
@@ -2828,7 +2808,6 @@ async function pushProjection(project, provider, environment, location, options)
|
|
|
2828
2808
|
environment,
|
|
2829
2809
|
destination: provider.type,
|
|
2830
2810
|
mode: "projection",
|
|
2831
|
-
location,
|
|
2832
2811
|
pushed: outbound.length,
|
|
2833
2812
|
meta: 0,
|
|
2834
2813
|
repositorySecrets,
|
|
@@ -2844,7 +2823,7 @@ function crossesToRecords(file, environment) {
|
|
|
2844
2823
|
}
|
|
2845
2824
|
return scope.kind === "environment" && scope.environment === environment;
|
|
2846
2825
|
}
|
|
2847
|
-
async function pushRecords(project, provider, environment
|
|
2826
|
+
async function pushRecords(project, provider, environment) {
|
|
2848
2827
|
const tree = localTree(project);
|
|
2849
2828
|
const files = tree.listSync().filter((file) => crossesToRecords(file, environment));
|
|
2850
2829
|
let pushed = 0;
|
|
@@ -2870,7 +2849,6 @@ async function pushRecords(project, provider, environment, location) {
|
|
|
2870
2849
|
environment,
|
|
2871
2850
|
destination: provider.type,
|
|
2872
2851
|
mode: "records",
|
|
2873
|
-
location,
|
|
2874
2852
|
pushed,
|
|
2875
2853
|
meta,
|
|
2876
2854
|
repositorySecrets: 0,
|
|
@@ -2882,11 +2860,11 @@ async function pushRecords(project, provider, environment, location) {
|
|
|
2882
2860
|
async function runPush(options) {
|
|
2883
2861
|
const project = openProject(options.cwd);
|
|
2884
2862
|
const environment = targetEnvironment(project, options.environment, options.envFlags);
|
|
2885
|
-
const
|
|
2863
|
+
const provider = await destinationFor(project, environment, options);
|
|
2886
2864
|
if ((0, import_core15.holdsProjection)(provider)) {
|
|
2887
|
-
return pushProjection(project, provider, environment,
|
|
2865
|
+
return pushProjection(project, provider, environment, options);
|
|
2888
2866
|
}
|
|
2889
|
-
return pushRecords(project, provider, environment
|
|
2867
|
+
return pushRecords(project, provider, environment);
|
|
2890
2868
|
}
|
|
2891
2869
|
function renderPush(result2) {
|
|
2892
2870
|
if (result2.pushed === 0 && result2.meta === 0) {
|
|
@@ -2898,7 +2876,7 @@ function renderPush(result2) {
|
|
|
2898
2876
|
}
|
|
2899
2877
|
]);
|
|
2900
2878
|
}
|
|
2901
|
-
const target = `${result2.destination} for environment ${result2.environment}
|
|
2879
|
+
const target = `${result2.destination} for environment ${result2.environment}`;
|
|
2902
2880
|
if (result2.mode === "records") {
|
|
2903
2881
|
return formatRows([
|
|
2904
2882
|
{
|
|
@@ -2947,6 +2925,19 @@ function renderPush(result2) {
|
|
|
2947
2925
|
}
|
|
2948
2926
|
return formatRows(rows);
|
|
2949
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
|
+
}
|
|
2950
2941
|
var pushCommand = (0, import_citty6.defineCommand)({
|
|
2951
2942
|
meta: {
|
|
2952
2943
|
name: "push",
|
|
@@ -2958,16 +2949,6 @@ var pushCommand = (0, import_citty6.defineCommand)({
|
|
|
2958
2949
|
type: "boolean",
|
|
2959
2950
|
description: "Decrypt sealed values locally and push them as plaintext for the destination to re-seal"
|
|
2960
2951
|
},
|
|
2961
|
-
destination: {
|
|
2962
|
-
type: "string",
|
|
2963
|
-
alias: ["dest", "d"],
|
|
2964
|
-
description: "Push once to this provider package instead of the declared one"
|
|
2965
|
-
},
|
|
2966
|
-
location: {
|
|
2967
|
-
type: "string",
|
|
2968
|
-
alias: "l",
|
|
2969
|
-
description: "The destination-side place, when --destination needs one"
|
|
2970
|
-
},
|
|
2971
2952
|
yes: {
|
|
2972
2953
|
type: "boolean",
|
|
2973
2954
|
alias: "y",
|
|
@@ -2976,25 +2957,13 @@ var pushCommand = (0, import_citty6.defineCommand)({
|
|
|
2976
2957
|
},
|
|
2977
2958
|
run({ args }) {
|
|
2978
2959
|
return guard(async () => {
|
|
2960
|
+
assertNoRedirect(args);
|
|
2979
2961
|
const result2 = await runPush({
|
|
2980
2962
|
cwd: process.cwd(),
|
|
2981
2963
|
...args.env === void 0 ? {} : { environment: args.env },
|
|
2982
2964
|
...args["allow-decrypt"] === void 0 ? {} : { allowDecrypt: args["allow-decrypt"] },
|
|
2983
|
-
...args.destination === void 0 ? {} : { destination: args.destination },
|
|
2984
|
-
...args.location === void 0 ? {} : { location: args.location },
|
|
2985
2965
|
...args.yes === void 0 ? {} : { yes: args.yes },
|
|
2986
|
-
envFlags: shorthandCandidates(args, [
|
|
2987
|
-
"env",
|
|
2988
|
-
"allow-decrypt",
|
|
2989
|
-
"allowDecrypt",
|
|
2990
|
-
"destination",
|
|
2991
|
-
"dest",
|
|
2992
|
-
"d",
|
|
2993
|
-
"location",
|
|
2994
|
-
"l",
|
|
2995
|
-
"yes",
|
|
2996
|
-
"y"
|
|
2997
|
-
])
|
|
2966
|
+
envFlags: shorthandCandidates(args, ["env", "allow-decrypt", "allowDecrypt", "yes", "y"])
|
|
2998
2967
|
});
|
|
2999
2968
|
write(renderPush(result2));
|
|
3000
2969
|
});
|
|
@@ -3093,7 +3062,7 @@ async function runDoctor(options) {
|
|
|
3093
3062
|
check: "provider",
|
|
3094
3063
|
severity: "pass",
|
|
3095
3064
|
label: "Provider",
|
|
3096
|
-
subject: project.config
|
|
3065
|
+
subject: (0, import_core16.environmentEntry)(project.config, environment)?.provider ?? project.provider.type
|
|
3097
3066
|
});
|
|
3098
3067
|
return {
|
|
3099
3068
|
environment,
|
|
@@ -3363,8 +3332,8 @@ async function projectionFindings(project, environment, override) {
|
|
|
3363
3332
|
if (override !== void 0) {
|
|
3364
3333
|
projection = override;
|
|
3365
3334
|
} else {
|
|
3366
|
-
const declared = project.config
|
|
3367
|
-
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) {
|
|
3368
3337
|
return [];
|
|
3369
3338
|
}
|
|
3370
3339
|
let source;
|
|
@@ -3376,7 +3345,7 @@ async function projectionFindings(project, environment, override) {
|
|
|
3376
3345
|
check: "projection-unreachable",
|
|
3377
3346
|
severity: "unknown",
|
|
3378
3347
|
label: "Destination",
|
|
3379
|
-
subject: `could not build the ${declared.
|
|
3348
|
+
subject: `could not build the ${declared.provider} provider for ${environment}`,
|
|
3380
3349
|
detail: errorDetail(error)
|
|
3381
3350
|
}
|
|
3382
3351
|
];
|
|
@@ -3649,8 +3618,8 @@ function refPathOf(ref) {
|
|
|
3649
3618
|
return [...ref.namespace, ref.name].join("/");
|
|
3650
3619
|
}
|
|
3651
3620
|
async function rotationSubjects(project, environment, local, override) {
|
|
3652
|
-
const providerConfig = project.config
|
|
3653
|
-
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)) {
|
|
3654
3623
|
return { kind: "read", subjects: local };
|
|
3655
3624
|
}
|
|
3656
3625
|
const source = override ?? await sourceProviderFor(project, environment);
|
|
@@ -3672,7 +3641,7 @@ async function rotationSubjects(project, environment, local, override) {
|
|
|
3672
3641
|
check: "rotation-overdue",
|
|
3673
3642
|
severity: "unknown",
|
|
3674
3643
|
label: "Rotation",
|
|
3675
|
-
subject: `could not reach the ${providerConfig?.
|
|
3644
|
+
subject: `could not reach the ${providerConfig?.provider ?? source.type} source of truth for ${environment}`,
|
|
3676
3645
|
detail: errorDetail(error)
|
|
3677
3646
|
}
|
|
3678
3647
|
};
|
|
@@ -3793,8 +3762,8 @@ async function readRelevant(provider, environment) {
|
|
|
3793
3762
|
return entries;
|
|
3794
3763
|
}
|
|
3795
3764
|
async function providerDriftFindings(project, environment, override) {
|
|
3796
|
-
const providerConfig = project.config
|
|
3797
|
-
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) {
|
|
3798
3767
|
return [
|
|
3799
3768
|
{
|
|
3800
3769
|
check: "provider-value-drift",
|
|
@@ -3819,7 +3788,7 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
3819
3788
|
check: "provider-value-drift",
|
|
3820
3789
|
severity: "unknown",
|
|
3821
3790
|
label: "Provider values",
|
|
3822
|
-
subject: `could not reach the ${providerConfig.
|
|
3791
|
+
subject: `could not reach the ${providerConfig.provider} provider for ${environment}`,
|
|
3823
3792
|
detail: errorDetail(error)
|
|
3824
3793
|
}
|
|
3825
3794
|
];
|
|
@@ -3838,7 +3807,7 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
3838
3807
|
severity: "unknown",
|
|
3839
3808
|
label: "Provider value unreadable",
|
|
3840
3809
|
subject: (0, import_core16.formatValueFile)(here.file),
|
|
3841
|
-
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`
|
|
3842
3811
|
});
|
|
3843
3812
|
continue;
|
|
3844
3813
|
}
|
|
@@ -3848,7 +3817,7 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
3848
3817
|
severity: "failure",
|
|
3849
3818
|
label: "Provider value drift",
|
|
3850
3819
|
subject: (0, import_core16.formatValueFile)(here.file),
|
|
3851
|
-
detail: `the local tree and the ${providerConfig.
|
|
3820
|
+
detail: `the local tree and the ${providerConfig.provider} source of truth hold different values`
|
|
3852
3821
|
});
|
|
3853
3822
|
}
|
|
3854
3823
|
continue;
|
|
@@ -3862,7 +3831,7 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
3862
3831
|
severity: "warning",
|
|
3863
3832
|
label: here !== void 0 ? "Only in the local tree" : "Only in the source",
|
|
3864
3833
|
subject: (0, import_core16.formatValueFile)(present.file),
|
|
3865
|
-
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`
|
|
3866
3835
|
});
|
|
3867
3836
|
}
|
|
3868
3837
|
if (findings.length > 0) {
|
|
@@ -3873,7 +3842,7 @@ async function providerDriftFindings(project, environment, override) {
|
|
|
3873
3842
|
check: "provider-value-drift",
|
|
3874
3843
|
severity: "pass",
|
|
3875
3844
|
label: "Provider values",
|
|
3876
|
-
subject: `every value matches the ${providerConfig.
|
|
3845
|
+
subject: `every value matches the ${providerConfig.provider} source of truth for ${environment}`
|
|
3877
3846
|
}
|
|
3878
3847
|
];
|
|
3879
3848
|
}
|
|
@@ -3975,7 +3944,7 @@ function targetScope(project, options, key) {
|
|
|
3975
3944
|
throw new import_core17.PenvError(
|
|
3976
3945
|
"ENVIRONMENT_FLAG_EMPTY",
|
|
3977
3946
|
`\`--env\` for parameter ${key} names no environment`,
|
|
3978
|
-
`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.`
|
|
3979
3948
|
);
|
|
3980
3949
|
}
|
|
3981
3950
|
return scopeFrom({ ...options, environment: targetEnvironment(project, environment) });
|
|
@@ -3988,7 +3957,7 @@ function sealFor(project, file, value, parameter, environment) {
|
|
|
3988
3957
|
throw new import_core17.PenvError(
|
|
3989
3958
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
3990
3959
|
`Parameter ${parameter} is a secret, and ${(0, import_core17.recordPath)((0, import_core17.formatValueFile)(file))} names no environment`,
|
|
3991
|
-
"
|
|
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."
|
|
3992
3961
|
);
|
|
3993
3962
|
}
|
|
3994
3963
|
return (0, import_core17.sealValue)(file, value, keySourceFor(project, environment), parameter, environment);
|
|
@@ -5074,8 +5043,14 @@ function environmentsFromFlag(flag) {
|
|
|
5074
5043
|
}
|
|
5075
5044
|
return [...new Set(names)];
|
|
5076
5045
|
}
|
|
5046
|
+
function environmentsRecord(names) {
|
|
5047
|
+
return Object.fromEntries(names.map((environment) => [environment, LOCAL_PROVIDER]));
|
|
5048
|
+
}
|
|
5077
5049
|
function configOf(decisions) {
|
|
5078
|
-
return {
|
|
5050
|
+
return {
|
|
5051
|
+
environments: environmentsRecord(decisions.environments),
|
|
5052
|
+
schemaFile: decisions.schemaFile
|
|
5053
|
+
};
|
|
5079
5054
|
}
|
|
5080
5055
|
function declaredIn2(root) {
|
|
5081
5056
|
const file = (0, import_node_path13.join)(root, CONFIG_FILE);
|
|
@@ -5107,7 +5082,7 @@ function planInit(root, flags = {}) {
|
|
|
5107
5082
|
if (schemaFile.length === 0) {
|
|
5108
5083
|
throw emptyFlag("schema");
|
|
5109
5084
|
}
|
|
5110
|
-
const error = (0, import_core25.validateSchemaFile)({ environments:
|
|
5085
|
+
const error = (0, import_core25.validateSchemaFile)({ environments: {}, schemaFile })[0];
|
|
5111
5086
|
if (error !== void 0) {
|
|
5112
5087
|
throw error;
|
|
5113
5088
|
}
|
|
@@ -5130,7 +5105,7 @@ function planInit(root, flags = {}) {
|
|
|
5130
5105
|
}
|
|
5131
5106
|
const suggestedEnvironments = suggestEnvironments(root);
|
|
5132
5107
|
const safeDefault = flags.yes === true && declared === void 0;
|
|
5133
|
-
const environments = flags.environments ?? declared
|
|
5108
|
+
const environments = flags.environments ?? (declared === void 0 ? void 0 : (0, import_core25.environmentNames)(declared)) ?? (safeDefault ? [DEVELOPMENT] : []);
|
|
5134
5109
|
const defaultEnvironment = declared?.defaultEnvironment ?? (safeDefault ? DEVELOPMENT : void 0);
|
|
5135
5110
|
if (safeDefault) {
|
|
5136
5111
|
notes.push(
|
|
@@ -5263,27 +5238,27 @@ export { schema };
|
|
|
5263
5238
|
|
|
5264
5239
|
` + loadComment + loadCall;
|
|
5265
5240
|
}
|
|
5241
|
+
function entryKey(name) {
|
|
5242
|
+
return /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name);
|
|
5243
|
+
}
|
|
5266
5244
|
function renderEnvironments(decisions) {
|
|
5267
|
-
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";
|
|
5268
5246
|
if (decisions.environments.length === 0) {
|
|
5269
5247
|
return `${shared} // It starts empty because which environments you deploy is not something penv
|
|
5270
5248
|
// can read off your codebase, and an environment you do not have is worse
|
|
5271
|
-
// than one you have not declared yet. Name yours
|
|
5272
|
-
// environments:
|
|
5273
|
-
//
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
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: {},
|
|
5277
5255
|
`;
|
|
5278
5256
|
}
|
|
5279
|
-
const
|
|
5280
|
-
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)},
|
|
5281
5258
|
`).join("");
|
|
5282
|
-
return `${shared}
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
providers: {
|
|
5286
|
-
${providers} },
|
|
5259
|
+
return `${shared} // A provider that needs no fields is just its package name.
|
|
5260
|
+
environments: {
|
|
5261
|
+
${entries} },
|
|
5287
5262
|
`;
|
|
5288
5263
|
}
|
|
5289
5264
|
function renderConfigModule(decisions) {
|
|
@@ -5802,13 +5777,13 @@ function planCutover(input) {
|
|
|
5802
5777
|
const declared = declaredIn2(root);
|
|
5803
5778
|
const named = environmentsDeclaredBy(selected);
|
|
5804
5779
|
const chosen = named.length > 0 ? named : [requireEnvironment(input)];
|
|
5805
|
-
const environments = declared === void 0 ? chosen : declared
|
|
5780
|
+
const environments = declared === void 0 ? chosen : (0, import_core25.environmentNames)(declared);
|
|
5806
5781
|
for (const environment of chosen) {
|
|
5807
5782
|
if (!environments.includes(environment)) {
|
|
5808
5783
|
throw new import_core25.PenvError(
|
|
5809
5784
|
"INIT_ENVIRONMENT_UNDECLARED",
|
|
5810
5785
|
`${CONFIG_FILE} declares ${describeList(environments)}, and adopting these files needs \`${environment}\` declared too`,
|
|
5811
|
-
`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.`
|
|
5812
5787
|
);
|
|
5813
5788
|
}
|
|
5814
5789
|
}
|
|
@@ -5875,10 +5850,7 @@ function distinct(refs) {
|
|
|
5875
5850
|
}
|
|
5876
5851
|
function configFor(decisions) {
|
|
5877
5852
|
return {
|
|
5878
|
-
environments: decisions.environments,
|
|
5879
|
-
providers: Object.fromEntries(
|
|
5880
|
-
decisions.environments.map((environment) => [environment, { type: LOCAL_PROVIDER }])
|
|
5881
|
-
),
|
|
5853
|
+
environments: environmentsRecord(decisions.environments),
|
|
5882
5854
|
schemaFile: decisions.schemaFile,
|
|
5883
5855
|
...decisions.defaultEnvironment === void 0 ? {} : { defaultEnvironment: decisions.defaultEnvironment },
|
|
5884
5856
|
...decisions.publicPrefixes.length === 0 ? {} : { publicPrefixes: [...decisions.publicPrefixes] }
|
|
@@ -6218,7 +6190,8 @@ function offeredEnvironment(root) {
|
|
|
6218
6190
|
if (declared === void 0) {
|
|
6219
6191
|
return DEVELOPMENT;
|
|
6220
6192
|
}
|
|
6221
|
-
|
|
6193
|
+
const names = (0, import_core25.environmentNames)(declared);
|
|
6194
|
+
return declared.defaultEnvironment ?? (names.includes(DEVELOPMENT) ? DEVELOPMENT : names[0] ?? DEVELOPMENT);
|
|
6222
6195
|
}
|
|
6223
6196
|
async function cutoverWithYes(root, base, adoption) {
|
|
6224
6197
|
const selected = selectionForYes(adoption);
|
|
@@ -6326,10 +6299,11 @@ var BACKUP_SUFFIX = ".backup";
|
|
|
6326
6299
|
var DOTENV_SEGMENT = "env";
|
|
6327
6300
|
var LOCAL2 = "local";
|
|
6328
6301
|
function assertDeclared(segment, config) {
|
|
6329
|
-
|
|
6302
|
+
const declared = (0, import_core26.environmentNames)(config);
|
|
6303
|
+
if (declared.includes(segment)) {
|
|
6330
6304
|
return segment;
|
|
6331
6305
|
}
|
|
6332
|
-
throw new import_core26.UnknownEnvironmentError(segment,
|
|
6306
|
+
throw new import_core26.UnknownEnvironmentError(segment, declared);
|
|
6333
6307
|
}
|
|
6334
6308
|
function scopeFromFilename(file, config) {
|
|
6335
6309
|
const name = (0, import_node_path14.basename)(file);
|
|
@@ -6399,7 +6373,7 @@ function explicitEnvironment(options, source, config) {
|
|
|
6399
6373
|
throw new import_core26.PenvError(
|
|
6400
6374
|
"IMPORT_ENV_FLAG_EMPTY",
|
|
6401
6375
|
`\`--env\` for the import of ${source} names no environment`,
|
|
6402
|
-
`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.`
|
|
6403
6377
|
);
|
|
6404
6378
|
}
|
|
6405
6379
|
function assertEnvironmentAgrees(derived, explicit, source) {
|
|
@@ -6426,7 +6400,7 @@ function environmentNamed(file, explicit) {
|
|
|
6426
6400
|
}
|
|
6427
6401
|
function decisionsOf(config, cwd) {
|
|
6428
6402
|
return {
|
|
6429
|
-
environments: config
|
|
6403
|
+
environments: (0, import_core26.environmentNames)(config),
|
|
6430
6404
|
// `import` re-scaffolds env.ts for an existing project; injection is an init
|
|
6431
6405
|
// choice, so it is not turned on here.
|
|
6432
6406
|
inject: false,
|
|
@@ -6482,7 +6456,7 @@ function importDotenv(options) {
|
|
|
6482
6456
|
backup,
|
|
6483
6457
|
scope,
|
|
6484
6458
|
environment,
|
|
6485
|
-
environments: config
|
|
6459
|
+
environments: (0, import_core26.environmentNames)(config),
|
|
6486
6460
|
variables: parsed.entries.length,
|
|
6487
6461
|
orphanComments: parsed.orphanComments,
|
|
6488
6462
|
steps
|
|
@@ -6630,12 +6604,12 @@ function envVarFor(id) {
|
|
|
6630
6604
|
function runKeyCreate(options) {
|
|
6631
6605
|
const project = openProject(options.cwd);
|
|
6632
6606
|
const environment = targetEnvironment(project, options.environment);
|
|
6633
|
-
const declared = project.config
|
|
6607
|
+
const declared = (0, import_core28.keyConfigFor)(project.config, environment);
|
|
6634
6608
|
if (declared === void 0) {
|
|
6635
6609
|
throw new import_core28.PenvError(
|
|
6636
6610
|
"KEY_SOURCE_UNDECLARED",
|
|
6637
6611
|
`Environment ${environment} declares no key source, so penv does not know what a key for it would be`,
|
|
6638
|
-
`
|
|
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.`
|
|
6639
6613
|
);
|
|
6640
6614
|
}
|
|
6641
6615
|
const key = (0, import_node_crypto.randomBytes)(import_core28.KEY_BYTES).toString("base64");
|
|
@@ -6978,7 +6952,7 @@ async function planFile(project, source, target, parameter) {
|
|
|
6978
6952
|
throw new import_core31.PenvError(
|
|
6979
6953
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
6980
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`,
|
|
6981
|
-
"
|
|
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."
|
|
6982
6956
|
);
|
|
6983
6957
|
}
|
|
6984
6958
|
const keys = keySourceFor(project, environment);
|