@penvhq/cli 0.9.0 → 0.9.1
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 +51434 -925
- package/dist/bin.cjs.map +1 -1
- package/dist/index.cjs +134 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7231 -44
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/dist/bin.d.cts +0 -1
- package/dist/bin.d.ts +0 -1
- package/dist/bin.js +0 -8
- package/dist/bin.js.map +0 -1
- package/dist/chunk-ASO5CF5D.js +0 -7252
- package/dist/chunk-ASO5CF5D.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -65,7 +65,7 @@ __export(src_exports, {
|
|
|
65
65
|
runWatch: () => runWatch
|
|
66
66
|
});
|
|
67
67
|
module.exports = __toCommonJS(src_exports);
|
|
68
|
-
var
|
|
68
|
+
var import_core35 = require("@penvhq/core");
|
|
69
69
|
var import_citty22 = require("citty");
|
|
70
70
|
|
|
71
71
|
// src/commands/artifact.ts
|
|
@@ -471,9 +471,9 @@ function resolvePlugin(specifier, fromDir) {
|
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
473
|
function importPlugin(resolvedPath) {
|
|
474
|
-
const
|
|
475
|
-
if (
|
|
476
|
-
return
|
|
474
|
+
const cached = pluginModuleCache.get(resolvedPath);
|
|
475
|
+
if (cached !== void 0) {
|
|
476
|
+
return cached;
|
|
477
477
|
}
|
|
478
478
|
const loading = import((0, import_node_url.pathToFileURL)(resolvedPath).href);
|
|
479
479
|
pluginModuleCache.set(resolvedPath, loading);
|
|
@@ -6054,30 +6054,51 @@ var importCommand = (0, import_citty14.defineCommand)({
|
|
|
6054
6054
|
|
|
6055
6055
|
// src/commands/key.ts
|
|
6056
6056
|
var import_node_crypto = require("crypto");
|
|
6057
|
-
var
|
|
6057
|
+
var import_core28 = require("@penvhq/core");
|
|
6058
6058
|
var import_citty15 = require("citty");
|
|
6059
6059
|
|
|
6060
6060
|
// src/keychain.ts
|
|
6061
6061
|
var import_node_module3 = require("module");
|
|
6062
|
+
var import_core27 = require("@penvhq/core");
|
|
6062
6063
|
var import_meta2 = {};
|
|
6063
|
-
var
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
return
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6064
|
+
var KEYRING_MODULE = "@napi-rs/keyring";
|
|
6065
|
+
var nodeRequire = (id) => (0, import_node_module3.createRequire)(import_meta2.url)(id);
|
|
6066
|
+
function firstLine2(cause) {
|
|
6067
|
+
return (cause instanceof Error ? cause.message : String(cause)).split("\n")[0] ?? "";
|
|
6068
|
+
}
|
|
6069
|
+
function keyringMissing(cause) {
|
|
6070
|
+
return new import_core27.PenvError(
|
|
6071
|
+
"KEYCHAIN_BINDING_MISSING",
|
|
6072
|
+
`penv could not load ${KEYRING_MODULE}, the native binding it reads your OS keychain through`,
|
|
6073
|
+
`This penv engine was installed as a plain tarball, which carries no native modules. Install penv with \`npm install -g @penvhq/launcher\`, or declare \`source: "env"\` for this environment and export its key. Original error: ${firstLine2(cause)}`
|
|
6074
|
+
);
|
|
6075
|
+
}
|
|
6076
|
+
function createKeychain(load = nodeRequire) {
|
|
6077
|
+
let cached;
|
|
6078
|
+
const entryConstructor = () => {
|
|
6079
|
+
if (cached === void 0) {
|
|
6080
|
+
let module2;
|
|
6081
|
+
try {
|
|
6082
|
+
module2 = load(KEYRING_MODULE);
|
|
6083
|
+
} catch (cause) {
|
|
6084
|
+
throw keyringMissing(cause);
|
|
6085
|
+
}
|
|
6086
|
+
cached = module2.Entry;
|
|
6087
|
+
}
|
|
6088
|
+
return cached;
|
|
6089
|
+
};
|
|
6090
|
+
return {
|
|
6091
|
+
getPassword(service, account) {
|
|
6092
|
+
const Entry = entryConstructor();
|
|
6093
|
+
return new Entry(service, account).getPassword();
|
|
6094
|
+
},
|
|
6095
|
+
setPassword(service, account, password) {
|
|
6096
|
+
const Entry = entryConstructor();
|
|
6097
|
+
new Entry(service, account).setPassword(password);
|
|
6098
|
+
}
|
|
6099
|
+
};
|
|
6100
|
+
}
|
|
6101
|
+
var defaultKeychain = createKeychain();
|
|
6081
6102
|
|
|
6082
6103
|
// src/commands/key.ts
|
|
6083
6104
|
function envVarFor(id) {
|
|
@@ -6088,35 +6109,38 @@ function runKeyCreate(options) {
|
|
|
6088
6109
|
const environment = targetEnvironment(project, options.environment);
|
|
6089
6110
|
const declared = project.config.keys?.[environment];
|
|
6090
6111
|
if (declared === void 0) {
|
|
6091
|
-
throw new
|
|
6112
|
+
throw new import_core28.PenvError(
|
|
6092
6113
|
"KEY_SOURCE_UNDECLARED",
|
|
6093
6114
|
`Environment ${environment} declares no key source, so penv does not know what a key for it would be`,
|
|
6094
6115
|
`Add a \`keys\` entry to penv.config.ts \u2014 e.g. \`keys: { ${environment}: { source: "env", id: "${environment}" } }\` \u2014 then run this again.`
|
|
6095
6116
|
);
|
|
6096
6117
|
}
|
|
6097
|
-
const key = (0, import_node_crypto.randomBytes)(
|
|
6118
|
+
const key = (0, import_node_crypto.randomBytes)(import_core28.KEY_BYTES).toString("base64");
|
|
6098
6119
|
if (declared.source === "keychain") {
|
|
6099
6120
|
const keychain = options.keychain ?? defaultKeychain;
|
|
6100
6121
|
if (options.force !== true) {
|
|
6101
6122
|
let existing;
|
|
6102
6123
|
try {
|
|
6103
|
-
existing = keychain.getPassword(
|
|
6124
|
+
existing = keychain.getPassword(import_core28.KEYCHAIN_SERVICE, declared.id);
|
|
6104
6125
|
} catch (cause) {
|
|
6105
|
-
|
|
6126
|
+
if (cause instanceof import_core28.PenvError) {
|
|
6127
|
+
throw cause;
|
|
6128
|
+
}
|
|
6129
|
+
throw new import_core28.PenvError(
|
|
6106
6130
|
"KEYCHAIN_UNAVAILABLE",
|
|
6107
6131
|
`penv could not read your OS keychain to check for an existing key \`${declared.id}\``,
|
|
6108
6132
|
`Unlock your keychain and run this again. Original error: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
6109
6133
|
);
|
|
6110
6134
|
}
|
|
6111
6135
|
if (existing !== null) {
|
|
6112
|
-
throw new
|
|
6136
|
+
throw new import_core28.PenvError(
|
|
6113
6137
|
"KEY_EXISTS",
|
|
6114
6138
|
`Environment ${environment} already has a key \`${declared.id}\` in your OS keychain`,
|
|
6115
6139
|
"Replacing it would orphan every value already sealed under it \u2014 they could never be decrypted again. Re-run with `--force` only if you are certain nothing is sealed under the current key."
|
|
6116
6140
|
);
|
|
6117
6141
|
}
|
|
6118
6142
|
}
|
|
6119
|
-
keychain.setPassword(
|
|
6143
|
+
keychain.setPassword(import_core28.KEYCHAIN_SERVICE, declared.id, key);
|
|
6120
6144
|
return { source: "keychain", environment, id: declared.id };
|
|
6121
6145
|
}
|
|
6122
6146
|
return {
|
|
@@ -6184,7 +6208,7 @@ var keyCommand = (0, import_citty15.defineCommand)({
|
|
|
6184
6208
|
});
|
|
6185
6209
|
|
|
6186
6210
|
// src/commands/list.ts
|
|
6187
|
-
var
|
|
6211
|
+
var import_core29 = require("@penvhq/core");
|
|
6188
6212
|
var import_citty16 = require("citty");
|
|
6189
6213
|
function scopeLabel2(scope) {
|
|
6190
6214
|
switch (scope.kind) {
|
|
@@ -6197,7 +6221,7 @@ function scopeLabel2(scope) {
|
|
|
6197
6221
|
case "unscoped":
|
|
6198
6222
|
return "default";
|
|
6199
6223
|
default:
|
|
6200
|
-
return (0,
|
|
6224
|
+
return (0, import_core29.assertNever)(scope, "scope");
|
|
6201
6225
|
}
|
|
6202
6226
|
}
|
|
6203
6227
|
async function runList(options) {
|
|
@@ -6205,12 +6229,12 @@ async function runList(options) {
|
|
|
6205
6229
|
const environment = targetEnvironment(project, options.environment);
|
|
6206
6230
|
const keys = keySourceFor(project, environment);
|
|
6207
6231
|
const parameters = [];
|
|
6208
|
-
for (const resolution of await (0,
|
|
6232
|
+
for (const resolution of await (0, import_core29.resolveAll)(environment, project.provider, keys)) {
|
|
6209
6233
|
const winner = resolution.winner;
|
|
6210
6234
|
const scope = winner?.file.scope;
|
|
6211
6235
|
parameters.push({
|
|
6212
6236
|
parameter: resolution.parameter,
|
|
6213
|
-
variable: (0,
|
|
6237
|
+
variable: (0, import_core29.variableName)(resolution.ref, project.config),
|
|
6214
6238
|
scope: scope === void 0 ? "absent" : scopeLabel2(scope),
|
|
6215
6239
|
location: winner?.location,
|
|
6216
6240
|
encrypted: winner?.file.encrypted === true,
|
|
@@ -6228,7 +6252,7 @@ function paintScope(entry) {
|
|
|
6228
6252
|
function renderList(result2) {
|
|
6229
6253
|
if (result2.parameters.length === 0) {
|
|
6230
6254
|
return [
|
|
6231
|
-
`No parameters in ${
|
|
6255
|
+
`No parameters in ${import_core29.RECORDS_PATH}/ for environment ${result2.environment}.`,
|
|
6232
6256
|
tip(`penv set <key> --env ${result2.environment}`)
|
|
6233
6257
|
];
|
|
6234
6258
|
}
|
|
@@ -6266,34 +6290,34 @@ var listCommand = (0, import_citty16.defineCommand)({
|
|
|
6266
6290
|
var import_node_fs13 = require("fs");
|
|
6267
6291
|
var import_node_path14 = require("path");
|
|
6268
6292
|
var import_promises2 = require("readline/promises");
|
|
6269
|
-
var
|
|
6293
|
+
var import_core30 = require("@penvhq/core");
|
|
6270
6294
|
var import_citty17 = require("citty");
|
|
6271
|
-
var OLD_GITIGNORE = `${
|
|
6295
|
+
var OLD_GITIGNORE = `${import_core30.PENV_DIR}/.gitignore`;
|
|
6272
6296
|
function planMigrate(cwd) {
|
|
6273
|
-
const { config, file } = (0,
|
|
6297
|
+
const { config, file } = (0, import_core30.loadConfig)(cwd);
|
|
6274
6298
|
const root = (0, import_node_path14.dirname)(file);
|
|
6275
|
-
const entries = (0,
|
|
6276
|
-
const tree = (0,
|
|
6299
|
+
const entries = (0, import_core30.oldLayoutEntries)(root, config);
|
|
6300
|
+
const tree = (0, import_core30.recordsDir)(root);
|
|
6277
6301
|
const collisions = collidingEntries(entries, tree);
|
|
6278
6302
|
if (collisions.length > 0) {
|
|
6279
|
-
throw new
|
|
6303
|
+
throw new import_core30.PenvError(
|
|
6280
6304
|
"HALF_MIGRATED",
|
|
6281
|
-
`${describe(collisions)} in both \`${
|
|
6282
|
-
`Move what is left under \`${
|
|
6305
|
+
`${describe(collisions)} in both \`${import_core30.PENV_DIR}/\` and \`${import_core30.RECORDS_PATH}/\`, and penv cannot tell which copy is current`,
|
|
6306
|
+
`Move what is left under \`${import_core30.PENV_DIR}/\` into \`${import_core30.RECORDS_PATH}/\` yourself, keeping the copy you want, then run \`penv validate\`.`
|
|
6283
6307
|
);
|
|
6284
6308
|
}
|
|
6285
6309
|
const creates = [];
|
|
6286
6310
|
if (entries.length > 0 && !(0, import_node_fs13.existsSync)(tree)) {
|
|
6287
|
-
creates.push(`${
|
|
6311
|
+
creates.push(`${import_core30.RECORDS_PATH}/`);
|
|
6288
6312
|
}
|
|
6289
|
-
if (readIfPresent((0, import_node_path14.join)(root, ...
|
|
6290
|
-
creates.push(
|
|
6313
|
+
if (readIfPresent((0, import_node_path14.join)(root, ...import_core30.STATE_GITIGNORE_PATH.split("/"))) !== (0, import_core30.renderStateGitignore)(config)) {
|
|
6314
|
+
creates.push(import_core30.STATE_GITIGNORE_PATH);
|
|
6291
6315
|
}
|
|
6292
6316
|
return {
|
|
6293
6317
|
root,
|
|
6294
6318
|
moves: entries.map((entry) => ({
|
|
6295
|
-
from: `${
|
|
6296
|
-
to: `${
|
|
6319
|
+
from: `${import_core30.PENV_DIR}/${entry}`,
|
|
6320
|
+
to: `${import_core30.RECORDS_PATH}/${entry}`
|
|
6297
6321
|
})),
|
|
6298
6322
|
creates,
|
|
6299
6323
|
removes: (0, import_node_fs13.existsSync)((0, import_node_path14.join)(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
|
|
@@ -6322,17 +6346,17 @@ function applyMigrate(plan2) {
|
|
|
6322
6346
|
if (isNoop(plan2)) {
|
|
6323
6347
|
return { ...plan2, status: "current" };
|
|
6324
6348
|
}
|
|
6325
|
-
const { config } = (0,
|
|
6349
|
+
const { config } = (0, import_core30.loadConfig)(plan2.root);
|
|
6326
6350
|
if (plan2.moves.length > 0) {
|
|
6327
|
-
(0, import_node_fs13.mkdirSync)((0,
|
|
6351
|
+
(0, import_node_fs13.mkdirSync)((0, import_core30.recordsDir)(plan2.root), { recursive: true });
|
|
6328
6352
|
for (const move of plan2.moves) {
|
|
6329
6353
|
(0, import_node_fs13.renameSync)((0, import_node_path14.join)(plan2.root, ...move.from.split("/")), (0, import_node_path14.join)(plan2.root, ...move.to.split("/")));
|
|
6330
6354
|
}
|
|
6331
6355
|
}
|
|
6332
|
-
if (plan2.creates.includes(
|
|
6333
|
-
const ignore = (0, import_node_path14.join)(plan2.root, ...
|
|
6356
|
+
if (plan2.creates.includes(import_core30.STATE_GITIGNORE_PATH)) {
|
|
6357
|
+
const ignore = (0, import_node_path14.join)(plan2.root, ...import_core30.STATE_GITIGNORE_PATH.split("/"));
|
|
6334
6358
|
(0, import_node_fs13.mkdirSync)((0, import_node_path14.dirname)(ignore), { recursive: true });
|
|
6335
|
-
(0, import_node_fs13.writeFileSync)(ignore, (0,
|
|
6359
|
+
(0, import_node_fs13.writeFileSync)(ignore, (0, import_core30.renderStateGitignore)(config), "utf8");
|
|
6336
6360
|
}
|
|
6337
6361
|
for (const removed of plan2.removes) {
|
|
6338
6362
|
(0, import_node_fs13.rmSync)((0, import_node_path14.join)(plan2.root, ...removed.split("/")), { force: true });
|
|
@@ -6348,7 +6372,7 @@ function runMigrate(options) {
|
|
|
6348
6372
|
}
|
|
6349
6373
|
function renderMigrate(result2) {
|
|
6350
6374
|
if (result2.status === "current") {
|
|
6351
|
-
return [`${out.green(CHECK)} Already on ${
|
|
6375
|
+
return [`${out.green(CHECK)} Already on ${import_core30.RECORDS_PATH}/ \u2014 nothing to migrate.`];
|
|
6352
6376
|
}
|
|
6353
6377
|
const done = result2.status === "migrated";
|
|
6354
6378
|
const glyph = done ? CHECK : "\u2192";
|
|
@@ -6362,7 +6386,7 @@ function renderMigrate(result2) {
|
|
|
6362
6386
|
...result2.removes.map((removed) => ({
|
|
6363
6387
|
glyph,
|
|
6364
6388
|
text: `${done ? "Removed" : "Remove"} ${removed}`,
|
|
6365
|
-
note: `replaced by ${
|
|
6389
|
+
note: `replaced by ${import_core30.STATE_GITIGNORE_PATH}`
|
|
6366
6390
|
}))
|
|
6367
6391
|
];
|
|
6368
6392
|
return [
|
|
@@ -6389,7 +6413,7 @@ async function approveOnTty() {
|
|
|
6389
6413
|
var migrateCommand = (0, import_citty17.defineCommand)({
|
|
6390
6414
|
meta: {
|
|
6391
6415
|
name: "migrate",
|
|
6392
|
-
description: `Move a project written under an earlier layout to ${
|
|
6416
|
+
description: `Move a project written under an earlier layout to ${import_core30.RECORDS_PATH}/`
|
|
6393
6417
|
},
|
|
6394
6418
|
args: {
|
|
6395
6419
|
yes: { type: "boolean", description: "Apply the previewed move without asking" }
|
|
@@ -6412,7 +6436,7 @@ var migrateCommand = (0, import_citty17.defineCommand)({
|
|
|
6412
6436
|
});
|
|
6413
6437
|
|
|
6414
6438
|
// src/commands/mv.ts
|
|
6415
|
-
var
|
|
6439
|
+
var import_core31 = require("@penvhq/core");
|
|
6416
6440
|
var import_citty18 = require("citty");
|
|
6417
6441
|
function environmentOf2(file) {
|
|
6418
6442
|
const scope = file.scope;
|
|
@@ -6428,39 +6452,39 @@ async function planFile(project, source, target, parameter) {
|
|
|
6428
6452
|
}
|
|
6429
6453
|
const environment = environmentOf2(source);
|
|
6430
6454
|
if (environment === void 0) {
|
|
6431
|
-
throw new
|
|
6455
|
+
throw new import_core31.PenvError(
|
|
6432
6456
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
6433
|
-
`${(0,
|
|
6457
|
+
`${(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`,
|
|
6434
6458
|
"Keys are declared per environment in the `keys` block of penv.config.ts. Decrypt it with `penv decrypt`, move the parameter, then encrypt it again at its new address."
|
|
6435
6459
|
);
|
|
6436
6460
|
}
|
|
6437
6461
|
const keys = keySourceFor(project, environment);
|
|
6438
|
-
const opened = (0,
|
|
6462
|
+
const opened = (0, import_core31.openValue)(source, stored, keys);
|
|
6439
6463
|
if (opened.kind === "failed") {
|
|
6440
|
-
throw new
|
|
6464
|
+
throw new import_core31.PenvError(
|
|
6441
6465
|
"VALUE_UNDECRYPTABLE",
|
|
6442
|
-
`${(0,
|
|
6466
|
+
`${(0, import_core31.recordPath)((0, import_core31.formatValueFile)(source))} could not be decrypted, so penv cannot re-seal it at its new address: ${opened.failure.detail}`,
|
|
6443
6467
|
"A sealed value is bound to the file it lives in, so moving it means opening it and sealing it again. Make the key available and run this again. Nothing has been moved."
|
|
6444
6468
|
);
|
|
6445
6469
|
}
|
|
6446
6470
|
return {
|
|
6447
6471
|
source,
|
|
6448
6472
|
target,
|
|
6449
|
-
contents: (0,
|
|
6473
|
+
contents: (0, import_core31.sealValue)(target, opened.value, keys, parameter, environment),
|
|
6450
6474
|
resealed: true
|
|
6451
6475
|
};
|
|
6452
6476
|
}
|
|
6453
6477
|
function filesOf(all, ref) {
|
|
6454
|
-
const id = (0,
|
|
6455
|
-
return all.filter((file) => (0,
|
|
6478
|
+
const id = (0, import_core31.parameterId)(ref);
|
|
6479
|
+
return all.filter((file) => (0, import_core31.parameterId)(file) === id);
|
|
6456
6480
|
}
|
|
6457
6481
|
async function runMove(options) {
|
|
6458
6482
|
const project = openProject(options.cwd);
|
|
6459
6483
|
const from = refFromKey(options.from, project.config);
|
|
6460
6484
|
assertWritableKey(options.to);
|
|
6461
6485
|
const to = refFromKey(options.to, project.config);
|
|
6462
|
-
if ((0,
|
|
6463
|
-
throw new
|
|
6486
|
+
if ((0, import_core31.parameterId)(from) === (0, import_core31.parameterId)(to)) {
|
|
6487
|
+
throw new import_core31.PenvError(
|
|
6464
6488
|
"PARAMETER_UNCHANGED",
|
|
6465
6489
|
`\`${options.from}\` and \`${options.to}\` are the same parameter`,
|
|
6466
6490
|
"Name a different destination, e.g. `penv mv redis-password redis/password`."
|
|
@@ -6470,24 +6494,24 @@ async function runMove(options) {
|
|
|
6470
6494
|
const sources = filesOf(all, from);
|
|
6471
6495
|
const meta = await project.provider.readMeta(from);
|
|
6472
6496
|
if (sources.length === 0 && meta === void 0) {
|
|
6473
|
-
throw new
|
|
6497
|
+
throw new import_core31.PenvError(
|
|
6474
6498
|
"PARAMETER_ABSENT",
|
|
6475
|
-
`Parameter ${(0,
|
|
6499
|
+
`Parameter ${(0, import_core31.parameterId)(from)} has no value files and no meta, so there is nothing to move`,
|
|
6476
6500
|
`\`penv list\` shows every parameter penv holds.`
|
|
6477
6501
|
);
|
|
6478
6502
|
}
|
|
6479
6503
|
const occupied2 = filesOf(all, to);
|
|
6480
6504
|
if (occupied2.length > 0 || await project.provider.readMeta(to) !== void 0) {
|
|
6481
|
-
throw new
|
|
6505
|
+
throw new import_core31.PenvError(
|
|
6482
6506
|
"PARAMETER_EXISTS",
|
|
6483
|
-
`Parameter ${(0,
|
|
6484
|
-
`Remove or rename ${(0,
|
|
6507
|
+
`Parameter ${(0, import_core31.parameterId)(to)} already exists, and penv will not merge two parameters into one`,
|
|
6508
|
+
`Remove or rename ${(0, import_core31.parameterId)(to)} first. \`penv get ${options.to} --explain\` shows every file it holds.`
|
|
6485
6509
|
);
|
|
6486
6510
|
}
|
|
6487
6511
|
const planned = [];
|
|
6488
6512
|
for (const source of sources) {
|
|
6489
6513
|
const target = { ...source, namespace: to.namespace, name: to.name };
|
|
6490
|
-
const one = await planFile(project, source, target, (0,
|
|
6514
|
+
const one = await planFile(project, source, target, (0, import_core31.parameterId)(to));
|
|
6491
6515
|
if (one !== void 0) {
|
|
6492
6516
|
planned.push(one);
|
|
6493
6517
|
}
|
|
@@ -6505,18 +6529,18 @@ async function runMove(options) {
|
|
|
6505
6529
|
await project.provider.removeMeta(from);
|
|
6506
6530
|
}
|
|
6507
6531
|
return {
|
|
6508
|
-
from: (0,
|
|
6509
|
-
to: (0,
|
|
6532
|
+
from: (0, import_core31.parameterId)(from),
|
|
6533
|
+
to: (0, import_core31.parameterId)(to),
|
|
6510
6534
|
files: planned.map((file) => ({
|
|
6511
|
-
from: (0,
|
|
6512
|
-
to: (0,
|
|
6535
|
+
from: (0, import_core31.formatValueFile)(file.source),
|
|
6536
|
+
to: (0, import_core31.formatValueFile)(file.target),
|
|
6513
6537
|
resealed: file.resealed
|
|
6514
6538
|
})),
|
|
6515
6539
|
// The meta's path, not the parameter's dotted id: `redis.password` is what
|
|
6516
6540
|
// the schema calls it and `redis/password.json` is the file, and a report
|
|
6517
6541
|
// that printed the first while moving the second names no file on disk.
|
|
6518
|
-
meta: meta === void 0 ? void 0 : (0,
|
|
6519
|
-
schema: { was: (0,
|
|
6542
|
+
meta: meta === void 0 ? void 0 : (0, import_core31.formatMetaFile)({ ...to, format: "json" }),
|
|
6543
|
+
schema: { was: (0, import_core31.accessPath)(from).join("."), now: (0, import_core31.accessPath)(to).join(".") },
|
|
6520
6544
|
schemaFile: schemaShapeFileOf(project)
|
|
6521
6545
|
};
|
|
6522
6546
|
}
|
|
@@ -6524,11 +6548,11 @@ function renderMove(result2) {
|
|
|
6524
6548
|
const rows = result2.files.map((file) => ({
|
|
6525
6549
|
glyph: CHECK,
|
|
6526
6550
|
label: "Moved",
|
|
6527
|
-
subject: (0,
|
|
6551
|
+
subject: (0, import_core31.recordPath)(file.to),
|
|
6528
6552
|
...file.resealed ? { detail: "re-sealed for its new address" } : {}
|
|
6529
6553
|
}));
|
|
6530
6554
|
if (result2.meta !== void 0) {
|
|
6531
|
-
rows.push({ glyph: CHECK, label: "Moved", subject: (0,
|
|
6555
|
+
rows.push({ glyph: CHECK, label: "Moved", subject: (0, import_core31.recordPath)(result2.meta) });
|
|
6532
6556
|
}
|
|
6533
6557
|
const lines = formatRows(rows);
|
|
6534
6558
|
lines.push(
|
|
@@ -6561,7 +6585,7 @@ var mvCommand = (0, import_citty18.defineCommand)({
|
|
|
6561
6585
|
});
|
|
6562
6586
|
|
|
6563
6587
|
// src/commands/remove.ts
|
|
6564
|
-
var
|
|
6588
|
+
var import_core32 = require("@penvhq/core");
|
|
6565
6589
|
var import_citty19 = require("citty");
|
|
6566
6590
|
async function runRemove(options) {
|
|
6567
6591
|
const project = openProject(options.cwd);
|
|
@@ -6579,12 +6603,12 @@ async function runRemove(options) {
|
|
|
6579
6603
|
continue;
|
|
6580
6604
|
}
|
|
6581
6605
|
await project.provider.remove(file);
|
|
6582
|
-
removed.push((0,
|
|
6606
|
+
removed.push((0, import_core32.formatValueFile)(file));
|
|
6583
6607
|
}
|
|
6584
6608
|
return {
|
|
6585
6609
|
parameter: options.key,
|
|
6586
6610
|
removed,
|
|
6587
|
-
considered: files.map((file) => (0,
|
|
6611
|
+
considered: files.map((file) => (0, import_core32.formatValueFile)(file))
|
|
6588
6612
|
};
|
|
6589
6613
|
}
|
|
6590
6614
|
function renderRemove(result2) {
|
|
@@ -6594,7 +6618,7 @@ function renderRemove(result2) {
|
|
|
6594
6618
|
{
|
|
6595
6619
|
glyph: WARN,
|
|
6596
6620
|
label: "Nothing to remove",
|
|
6597
|
-
subject: (0,
|
|
6621
|
+
subject: (0, import_core32.recordPath)(first),
|
|
6598
6622
|
detail: "no value file at that scope"
|
|
6599
6623
|
}
|
|
6600
6624
|
]);
|
|
@@ -6602,7 +6626,7 @@ function renderRemove(result2) {
|
|
|
6602
6626
|
const rows = result2.removed.map((location) => ({
|
|
6603
6627
|
glyph: CHECK,
|
|
6604
6628
|
label: "Removed",
|
|
6605
|
-
subject: (0,
|
|
6629
|
+
subject: (0, import_core32.recordPath)(location)
|
|
6606
6630
|
}));
|
|
6607
6631
|
return formatRows(rows);
|
|
6608
6632
|
}
|
|
@@ -6633,7 +6657,7 @@ var removeCommand = (0, import_citty19.defineCommand)({
|
|
|
6633
6657
|
});
|
|
6634
6658
|
|
|
6635
6659
|
// src/commands/rotate.ts
|
|
6636
|
-
var
|
|
6660
|
+
var import_core33 = require("@penvhq/core");
|
|
6637
6661
|
var import_citty20 = require("citty");
|
|
6638
6662
|
function rotatingFile(ref, environment) {
|
|
6639
6663
|
return {
|
|
@@ -6659,7 +6683,7 @@ async function writeRotatedValue(project, provider, ref, environment, value) {
|
|
|
6659
6683
|
}
|
|
6660
6684
|
function requireNewValue(value, phase, key) {
|
|
6661
6685
|
if (value === void 0) {
|
|
6662
|
-
throw new
|
|
6686
|
+
throw new import_core33.PenvError(
|
|
6663
6687
|
"ROTATION_NO_VALUE",
|
|
6664
6688
|
`A ${phase} rotation of ${key} writes a new value, and none was given`,
|
|
6665
6689
|
"Pass the new value as the argument \u2014 `penv rotate <key> <value>` \u2014 or pipe it in on stdin."
|
|
@@ -6668,8 +6692,8 @@ function requireNewValue(value, phase, key) {
|
|
|
6668
6692
|
return value;
|
|
6669
6693
|
}
|
|
6670
6694
|
function requireRetaining(provider, environment) {
|
|
6671
|
-
if (!(0,
|
|
6672
|
-
throw new
|
|
6695
|
+
if (!(0, import_core33.retainsPrevious)(provider)) {
|
|
6696
|
+
throw new import_core33.PenvError(
|
|
6673
6697
|
"ROTATION_NOT_RETAINING",
|
|
6674
6698
|
`A dual-valid rotation needs the previous value to stay readable during the grace window, and the \`${provider.type}\` provider for environment ${environment} does not retain it`,
|
|
6675
6699
|
"Point this environment at a provider that keeps prior versions (its `readPrevious` is what penv reads during the window), or, if a momentary overlap is not required, declare the parameter `atomic-cutover` in its meta and flip it in one step."
|
|
@@ -6682,8 +6706,8 @@ async function runRotate(options) {
|
|
|
6682
6706
|
const environment = targetEnvironment(project, options.environment);
|
|
6683
6707
|
const ref = refFromKey(options.key, project.config);
|
|
6684
6708
|
const source = await sourceProviderFor(project, environment);
|
|
6685
|
-
if (!(0,
|
|
6686
|
-
throw new
|
|
6709
|
+
if (!(0, import_core33.holdsRecords)(source)) {
|
|
6710
|
+
throw new import_core33.PenvError(
|
|
6687
6711
|
"ROTATION_NOT_RECORDS",
|
|
6688
6712
|
`Environment ${environment} is backed by \`${source.type}\`, which holds a resolved projection penv cannot rotate in place`,
|
|
6689
6713
|
"Rotate the parameter in the environment that holds the records (the local tree, Vault, SSM), then `penv push` the result to this destination."
|
|
@@ -6692,9 +6716,9 @@ async function runRotate(options) {
|
|
|
6692
6716
|
const provider = source;
|
|
6693
6717
|
const nowIso = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
6694
6718
|
const before = await provider.readMeta(ref);
|
|
6695
|
-
const { mechanism } = (0,
|
|
6719
|
+
const { mechanism } = (0, import_core33.rotationOf)(before, environment);
|
|
6696
6720
|
if (mechanism === void 0) {
|
|
6697
|
-
throw new
|
|
6721
|
+
throw new import_core33.PenvError(
|
|
6698
6722
|
"ROTATION_NO_MECHANISM",
|
|
6699
6723
|
`Parameter ${options.key} declares no rotation mechanism for environment ${environment}, so penv does not know how to rotate it`,
|
|
6700
6724
|
'Set `rotationMechanism` in the parameter\'s meta to `"dual-valid"` (a grace-window overlap) or `"atomic-cutover"` (a single flip), then run `penv rotate` again.'
|
|
@@ -6704,7 +6728,7 @@ async function runRotate(options) {
|
|
|
6704
6728
|
const complete = options.complete === true;
|
|
6705
6729
|
if (mechanism === "atomic-cutover") {
|
|
6706
6730
|
if (begin || complete) {
|
|
6707
|
-
throw new
|
|
6731
|
+
throw new import_core33.PenvError(
|
|
6708
6732
|
"ROTATION_MECHANISM_MISMATCH",
|
|
6709
6733
|
`Parameter ${options.key} is atomic-cutover, which flips in one step, so \`--begin\`/\`--complete\` do not apply`,
|
|
6710
6734
|
"Run `penv rotate <key> <value>` with no phase flag to flip it. `--begin`/`--complete` bracket a dual-valid grace window, which atomic-cutover has none of."
|
|
@@ -6712,13 +6736,13 @@ async function runRotate(options) {
|
|
|
6712
6736
|
}
|
|
6713
6737
|
const value = requireNewValue(options.value, "cutover", options.key);
|
|
6714
6738
|
await writeRotatedValue(project, provider, ref, environment, value);
|
|
6715
|
-
const after2 = (0,
|
|
6739
|
+
const after2 = (0, import_core33.completeRotation)(before, environment, nowIso);
|
|
6716
6740
|
await provider.writeMeta(ref, after2);
|
|
6717
6741
|
return result(ref, environment, mechanism, "cutover", provider.type, true, after2);
|
|
6718
6742
|
}
|
|
6719
6743
|
const retaining = requireRetaining(provider, environment);
|
|
6720
6744
|
if (begin === complete) {
|
|
6721
|
-
throw new
|
|
6745
|
+
throw new import_core33.PenvError(
|
|
6722
6746
|
"ROTATION_PHASE_REQUIRED",
|
|
6723
6747
|
`A dual-valid rotation of ${options.key} needs exactly one of \`--begin\` or \`--complete\``,
|
|
6724
6748
|
"`--begin` writes the new value and opens the grace window; `--complete` closes it once every reader has moved to the new value. Run them in that order, one at a time."
|
|
@@ -6727,16 +6751,16 @@ async function runRotate(options) {
|
|
|
6727
6751
|
if (begin) {
|
|
6728
6752
|
const value = requireNewValue(options.value, "begin", options.key);
|
|
6729
6753
|
await writeRotatedValue(project, retaining, ref, environment, value);
|
|
6730
|
-
const after2 = (0,
|
|
6754
|
+
const after2 = (0, import_core33.beginRotation)(before, environment, nowIso);
|
|
6731
6755
|
await retaining.writeMeta(ref, after2);
|
|
6732
6756
|
return result(ref, environment, mechanism, "begin", retaining.type, true, after2);
|
|
6733
6757
|
}
|
|
6734
|
-
const after = (0,
|
|
6758
|
+
const after = (0, import_core33.completeRotation)(before, environment, nowIso);
|
|
6735
6759
|
await retaining.writeMeta(ref, after);
|
|
6736
6760
|
return result(ref, environment, mechanism, "complete", retaining.type, false, after);
|
|
6737
6761
|
}
|
|
6738
6762
|
function result(ref, environment, mechanism, phase, source, wroteValue, after) {
|
|
6739
|
-
const { state, rotatingSince, lastRotated } = (0,
|
|
6763
|
+
const { state, rotatingSince, lastRotated } = (0, import_core33.rotationOf)(after, environment);
|
|
6740
6764
|
return {
|
|
6741
6765
|
parameter: [...ref.namespace, ref.name].join("/"),
|
|
6742
6766
|
environment,
|
|
@@ -6823,7 +6847,7 @@ var rotateCommand = (0, import_citty20.defineCommand)({
|
|
|
6823
6847
|
// src/commands/watch.ts
|
|
6824
6848
|
var import_node_fs14 = require("fs");
|
|
6825
6849
|
var import_node_path15 = require("path");
|
|
6826
|
-
var
|
|
6850
|
+
var import_core34 = require("@penvhq/core");
|
|
6827
6851
|
var import_citty21 = require("citty");
|
|
6828
6852
|
var DEBOUNCE_MS2 = 100;
|
|
6829
6853
|
function runWatch(options) {
|
|
@@ -6956,9 +6980,9 @@ function runWatch(options) {
|
|
|
6956
6980
|
}
|
|
6957
6981
|
addWatcher(project.recordsDir, true);
|
|
6958
6982
|
addWatcher((0, import_node_path15.dirname)(project.configFile), false, configFile);
|
|
6959
|
-
addWatcher(project.root, false,
|
|
6960
|
-
if ((0,
|
|
6961
|
-
const schemaFile = (0, import_node_path15.resolve)(project.root, (0,
|
|
6983
|
+
addWatcher(project.root, false, import_core34.SCHEMA_SHAPE_FILE);
|
|
6984
|
+
if ((0, import_core34.schemaInsideTree)(project.config) === void 0) {
|
|
6985
|
+
const schemaFile = (0, import_node_path15.resolve)(project.root, (0, import_core34.schemaFileOf)(project.config));
|
|
6962
6986
|
addWatcher((0, import_node_path15.dirname)(schemaFile), false, (0, import_node_path15.basename)(schemaFile));
|
|
6963
6987
|
}
|
|
6964
6988
|
void validate();
|
|
@@ -7042,10 +7066,13 @@ var watchCommand = (0, import_citty21.defineCommand)({
|
|
|
7042
7066
|
|
|
7043
7067
|
// src/index.ts
|
|
7044
7068
|
var main = (0, import_citty22.defineCommand)({
|
|
7045
|
-
|
|
7069
|
+
// Lazy, so reading the engine's own version off disk is something `--version`
|
|
7070
|
+
// does rather than something importing this module does.
|
|
7071
|
+
meta: () => ({
|
|
7046
7072
|
name: "penv",
|
|
7047
|
-
description: "Configuration that shares a data model with your production secret manager"
|
|
7048
|
-
|
|
7073
|
+
description: "Configuration that shares a data model with your production secret manager",
|
|
7074
|
+
version: engineVersion()
|
|
7075
|
+
}),
|
|
7049
7076
|
subCommands: {
|
|
7050
7077
|
init: initCommand,
|
|
7051
7078
|
import: importCommand,
|
|
@@ -7072,7 +7099,7 @@ var main = (0, import_citty22.defineCommand)({
|
|
|
7072
7099
|
}
|
|
7073
7100
|
});
|
|
7074
7101
|
function runMain() {
|
|
7075
|
-
(0,
|
|
7102
|
+
(0, import_core35.setKeychain)(defaultKeychain);
|
|
7076
7103
|
return (0, import_citty22.runMain)(main);
|
|
7077
7104
|
}
|
|
7078
7105
|
// Annotate the CommonJS export names for ESM import in node:
|