@penvhq/cli 0.10.0 → 0.12.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 +250 -55
- package/dist/bin.cjs.map +1 -1
- package/dist/chunk-NHTDIZZ2.js +504 -0
- package/dist/chunk-NHTDIZZ2.js.map +1 -0
- package/dist/index.cjs +239 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -47
- package/dist/index.d.ts +1 -47
- package/dist/index.js +228 -517
- package/dist/index.js.map +1 -1
- package/dist/install.cjs +537 -0
- package/dist/install.cjs.map +1 -0
- package/dist/install.d.cts +126 -0
- package/dist/install.d.ts +126 -0
- package/dist/install.js +27 -0
- package/dist/install.js.map +1 -0
- package/package.json +18 -7
package/dist/index.js
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectPackageManager,
|
|
3
|
+
engineVersion,
|
|
4
|
+
installWithPackageManager,
|
|
5
|
+
installedPackages,
|
|
6
|
+
noCommand,
|
|
7
|
+
planInstall,
|
|
8
|
+
renderInstallPlan,
|
|
9
|
+
startChild
|
|
10
|
+
} from "./chunk-NHTDIZZ2.js";
|
|
11
|
+
|
|
1
12
|
// src/index.ts
|
|
2
13
|
import { setKeychain } from "@penvhq/core";
|
|
3
14
|
import { runMain as cittyRunMain, defineCommand as defineCommand22 } from "citty";
|
|
4
15
|
|
|
5
16
|
// src/commands/artifact.ts
|
|
6
17
|
import { mkdirSync, writeFileSync } from "fs";
|
|
7
|
-
import { dirname as dirname3, isAbsolute
|
|
18
|
+
import { dirname as dirname3, isAbsolute, relative, resolve as resolve3 } from "path";
|
|
8
19
|
import {
|
|
9
20
|
ARTIFACT_FORMAT,
|
|
10
21
|
candidatesFor as candidatesFor2,
|
|
@@ -12,7 +23,7 @@ import {
|
|
|
12
23
|
formatValueFile as formatValueFile2,
|
|
13
24
|
isSecret as isSecret2,
|
|
14
25
|
keySourceIdentifier,
|
|
15
|
-
PenvError as
|
|
26
|
+
PenvError as PenvError6,
|
|
16
27
|
parameterId as parameterId3,
|
|
17
28
|
serializeArtifact,
|
|
18
29
|
variableName as variableName3
|
|
@@ -20,313 +31,9 @@ import {
|
|
|
20
31
|
import { assertDeliverableNames, declaredRefs as declaredRefs2 } from "@penvhq/runtime";
|
|
21
32
|
import { defineCommand as defineCommand4 } from "citty";
|
|
22
33
|
|
|
23
|
-
// src/install.ts
|
|
24
|
-
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
25
|
-
import { join as join2 } from "path";
|
|
26
|
-
import { PenvError as PenvError2 } from "@penvhq/core";
|
|
27
|
-
|
|
28
|
-
// src/child.ts
|
|
29
|
-
import { spawn } from "child_process";
|
|
30
|
-
import { existsSync, statSync } from "fs";
|
|
31
|
-
import { delimiter, isAbsolute, join, win32 } from "path";
|
|
32
|
-
import { PenvError } from "@penvhq/core";
|
|
33
|
-
var FORWARDED = ["SIGINT", "SIGTERM", "SIGHUP", "SIGBREAK"];
|
|
34
|
-
var startChild = (invocation) => {
|
|
35
|
-
const [executable, ...args] = invocation.command;
|
|
36
|
-
if (executable === void 0) {
|
|
37
|
-
throw noCommand();
|
|
38
|
-
}
|
|
39
|
-
const target = resolveTarget(executable, args, invocation.env);
|
|
40
|
-
const child = spawn(target.file, target.args, {
|
|
41
|
-
cwd: invocation.cwd,
|
|
42
|
-
env: invocation.env,
|
|
43
|
-
stdio: "inherit",
|
|
44
|
-
...target.verbatim ? { windowsVerbatimArguments: true } : {}
|
|
45
|
-
});
|
|
46
|
-
const forward = /* @__PURE__ */ new Map();
|
|
47
|
-
for (const signal of FORWARDED) {
|
|
48
|
-
const handler = () => {
|
|
49
|
-
child.kill(signal);
|
|
50
|
-
};
|
|
51
|
-
forward.set(signal, handler);
|
|
52
|
-
process.on(signal, handler);
|
|
53
|
-
}
|
|
54
|
-
const release = () => {
|
|
55
|
-
for (const [signal, handler] of forward) {
|
|
56
|
-
process.off(signal, handler);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
const ended = new Promise((resolve9, reject) => {
|
|
60
|
-
child.on("error", (cause) => {
|
|
61
|
-
release();
|
|
62
|
-
reject(cannotStart(executable, cause, invocation.purpose));
|
|
63
|
-
});
|
|
64
|
-
child.on("exit", (code, signal) => {
|
|
65
|
-
release();
|
|
66
|
-
resolve9({ exitCode: code ?? 1, signal });
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
return {
|
|
70
|
-
ended,
|
|
71
|
-
kill(signal) {
|
|
72
|
-
child.kill(signal);
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
};
|
|
76
|
-
function noCommand() {
|
|
77
|
-
return new PenvError(
|
|
78
|
-
"RUN_NO_COMMAND",
|
|
79
|
-
"`penv run` was given no command to start",
|
|
80
|
-
"Put the command after `--`, e.g. `penv run -- pnpm dev`."
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
function cannotStart(executable, cause, purpose) {
|
|
84
|
-
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
85
|
-
if (purpose !== void 0) {
|
|
86
|
-
return new PenvError(
|
|
87
|
-
"PENV_COMMAND_NOT_STARTED",
|
|
88
|
-
`penv could not start \`${executable}\` to ${purpose}: ${detail}`,
|
|
89
|
-
`Check that \`${executable}\` runs on its own \u2014 penv starts it the way your shell does, so it has to be on PATH. Nothing was changed.`
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
return new PenvError(
|
|
93
|
-
"RUN_COMMAND_NOT_STARTED",
|
|
94
|
-
`\`${executable}\` could not be started: ${detail}`,
|
|
95
|
-
`Check the command after \`--\` runs on its own \u2014 \`${executable}\` has to be on PATH, exactly as it is spelled here.`
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
function resolveTarget(executable, args, env) {
|
|
99
|
-
if (process.platform !== "win32") {
|
|
100
|
-
return { file: executable, args, verbatim: false };
|
|
101
|
-
}
|
|
102
|
-
const resolved = findExecutable(executable, env);
|
|
103
|
-
if (resolved === void 0 || !/\.(cmd|bat)$/i.test(resolved)) {
|
|
104
|
-
return { file: resolved ?? executable, args, verbatim: false };
|
|
105
|
-
}
|
|
106
|
-
return {
|
|
107
|
-
file: env.ComSpec ?? "cmd.exe",
|
|
108
|
-
args: ["/d", "/s", "/c", `"${cmdCommandLine(resolved, args)}"`],
|
|
109
|
-
verbatim: true
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
var SHIM = /(?:^|\\)node_modules\\\.bin\\[^\\]+\.cmd$/i;
|
|
113
|
-
function cmdCommandLine(resolved, args) {
|
|
114
|
-
const command = win32.normalize(resolved);
|
|
115
|
-
const shim = SHIM.test(command);
|
|
116
|
-
return [escapeCommand(command), ...args.map((argument) => escapeArgument(argument, shim))].join(
|
|
117
|
-
" "
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
function extensions(env, platform) {
|
|
121
|
-
if (platform !== "win32") {
|
|
122
|
-
return [""];
|
|
123
|
-
}
|
|
124
|
-
const declared = env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD";
|
|
125
|
-
return [...declared.split(";").filter((extension) => extension.length > 0), ""];
|
|
126
|
-
}
|
|
127
|
-
function findExecutable(executable, env, platform = process.platform) {
|
|
128
|
-
const candidates = extensions(env, platform);
|
|
129
|
-
const isFile = (path2) => existsSync(path2) && statSync(path2).isFile();
|
|
130
|
-
if (executable.includes("/") || executable.includes("\\") || isAbsolute(executable)) {
|
|
131
|
-
return candidates.map((extension) => executable + extension).find(isFile);
|
|
132
|
-
}
|
|
133
|
-
const path = env.PATH ?? env.Path ?? "";
|
|
134
|
-
for (const directory of path.split(delimiter).filter((entry) => entry.length > 0)) {
|
|
135
|
-
const hit = candidates.map((extension) => join(directory, executable + extension)).find(isFile);
|
|
136
|
-
if (hit !== void 0) {
|
|
137
|
-
return hit;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return void 0;
|
|
141
|
-
}
|
|
142
|
-
var CMD_METACHARACTERS = /([()\][%!^"`<>&|;, *?])/g;
|
|
143
|
-
function escapeCommand(command) {
|
|
144
|
-
return command.replace(CMD_METACHARACTERS, "^$1");
|
|
145
|
-
}
|
|
146
|
-
function escapeArgument(argument, doubleEscape) {
|
|
147
|
-
const quoted = `"${argument.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\*)$/, "$1$1")}"`;
|
|
148
|
-
const escaped = quoted.replace(CMD_METACHARACTERS, "^$1");
|
|
149
|
-
return doubleEscape ? escaped.replace(CMD_METACHARACTERS, "^$1") : escaped;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// src/install.ts
|
|
153
|
-
var RUNTIME_PACKAGE = "@penvhq/penv";
|
|
154
|
-
var SCHEMA_PACKAGE = "zod";
|
|
155
|
-
var LOCKFILES = [
|
|
156
|
-
["pnpm", "pnpm-lock.yaml"],
|
|
157
|
-
["yarn", "yarn.lock"],
|
|
158
|
-
["bun", "bun.lock"],
|
|
159
|
-
["bun", "bun.lockb"],
|
|
160
|
-
["npm", "package-lock.json"]
|
|
161
|
-
];
|
|
162
|
-
var ADD = {
|
|
163
|
-
pnpm: ["pnpm", "add", "--save-exact"],
|
|
164
|
-
npm: ["npm", "install", "--save-exact"],
|
|
165
|
-
yarn: ["yarn", "add", "--exact"],
|
|
166
|
-
bun: ["bun", "add", "--exact"]
|
|
167
|
-
};
|
|
168
|
-
function engineVersion() {
|
|
169
|
-
const version = ownManifest()?.version;
|
|
170
|
-
if (typeof version === "string" && version.length > 0) {
|
|
171
|
-
return version;
|
|
172
|
-
}
|
|
173
|
-
throw new PenvError2(
|
|
174
|
-
"ENGINE_VERSION_UNREADABLE",
|
|
175
|
-
"penv could not read its own version, so it cannot say which `@penvhq/penv` this project needs",
|
|
176
|
-
`Reinstall penv, then run \`penv init\` again.`
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
function schemaPackageVersion() {
|
|
180
|
-
const peers = ownManifest()?.peerDependencies;
|
|
181
|
-
const declared = peers !== null && typeof peers === "object" && !Array.isArray(peers) ? peers[SCHEMA_PACKAGE] : void 0;
|
|
182
|
-
const floor = typeof declared === "string" ? declared.replace(/^[\^~>=\s]+/, "").trim() : "";
|
|
183
|
-
if (floor.length > 0) {
|
|
184
|
-
return floor;
|
|
185
|
-
}
|
|
186
|
-
throw new PenvError2(
|
|
187
|
-
"ENGINE_PEER_UNREADABLE",
|
|
188
|
-
`penv could not read its own \`${SCHEMA_PACKAGE}\` peer range, so it cannot say which ${SCHEMA_PACKAGE} this project needs`,
|
|
189
|
-
`Reinstall penv, then run \`penv init\` again.`
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
function ownManifest() {
|
|
193
|
-
try {
|
|
194
|
-
const parsed = JSON.parse(
|
|
195
|
-
readFileSync(new URL("../package.json", import.meta.url), "utf8")
|
|
196
|
-
);
|
|
197
|
-
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
198
|
-
} catch {
|
|
199
|
-
return void 0;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
function detectPackageManager(root) {
|
|
203
|
-
for (const [manager, lockfile] of LOCKFILES) {
|
|
204
|
-
if (existsSync2(join2(root, lockfile))) {
|
|
205
|
-
return manager;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return declaredManager(root) ?? "npm";
|
|
209
|
-
}
|
|
210
|
-
function declaredManager(root) {
|
|
211
|
-
const declared = manifestOf(root)?.packageManager;
|
|
212
|
-
if (typeof declared !== "string") {
|
|
213
|
-
return void 0;
|
|
214
|
-
}
|
|
215
|
-
const name = declared.split("@")[0];
|
|
216
|
-
return name === "pnpm" || name === "npm" || name === "yarn" || name === "bun" ? name : void 0;
|
|
217
|
-
}
|
|
218
|
-
function manifestOf(root) {
|
|
219
|
-
const file = join2(root, "package.json");
|
|
220
|
-
if (!existsSync2(file)) {
|
|
221
|
-
return void 0;
|
|
222
|
-
}
|
|
223
|
-
try {
|
|
224
|
-
const manifest = JSON.parse(readFileSync(file, "utf8"));
|
|
225
|
-
return manifest !== null && typeof manifest === "object" && !Array.isArray(manifest) ? manifest : void 0;
|
|
226
|
-
} catch {
|
|
227
|
-
return void 0;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
function declaredVersion(root, name) {
|
|
231
|
-
const manifest = manifestOf(root);
|
|
232
|
-
for (const field of ["dependencies", "devDependencies"]) {
|
|
233
|
-
const block = manifest?.[field];
|
|
234
|
-
if (block !== null && typeof block === "object" && !Array.isArray(block)) {
|
|
235
|
-
const version = block[name];
|
|
236
|
-
if (typeof version === "string") {
|
|
237
|
-
return version;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return void 0;
|
|
242
|
-
}
|
|
243
|
-
function planInstall(root, version = engineVersion()) {
|
|
244
|
-
const manager = detectPackageManager(root);
|
|
245
|
-
const lockfile = LOCKFILES.find(
|
|
246
|
-
([name, file]) => name === manager && existsSync2(join2(root, file))
|
|
247
|
-
)?.[1];
|
|
248
|
-
const runtimeDeclared = declaredVersion(root, RUNTIME_PACKAGE);
|
|
249
|
-
const zodDeclared = declaredVersion(root, SCHEMA_PACKAGE);
|
|
250
|
-
const packages = [
|
|
251
|
-
{
|
|
252
|
-
name: RUNTIME_PACKAGE,
|
|
253
|
-
version,
|
|
254
|
-
...runtimeDeclared === void 0 ? {} : { declared: runtimeDeclared },
|
|
255
|
-
satisfied: runtimeDeclared === version
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
name: SCHEMA_PACKAGE,
|
|
259
|
-
version: schemaPackageVersion(),
|
|
260
|
-
...zodDeclared === void 0 ? {} : { declared: zodDeclared },
|
|
261
|
-
// Any declared zod counts: which zod a project uses is the project's
|
|
262
|
-
// decision, and penv is here to make sure there is one, not to move it.
|
|
263
|
-
satisfied: zodDeclared !== void 0
|
|
264
|
-
}
|
|
265
|
-
];
|
|
266
|
-
const pending = packages.filter((entry) => !entry.satisfied);
|
|
267
|
-
const specs = (pending.length === 0 ? packages : pending).map(
|
|
268
|
-
(entry) => `${entry.name}@${entry.version}`
|
|
269
|
-
);
|
|
270
|
-
return {
|
|
271
|
-
root,
|
|
272
|
-
manager,
|
|
273
|
-
packages,
|
|
274
|
-
command: [...ADD[manager], ...specs],
|
|
275
|
-
...lockfile === void 0 ? {} : { lockfile },
|
|
276
|
-
satisfied: pending.length === 0
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
function describe(entry) {
|
|
280
|
-
return `${entry.name} ${entry.version}`;
|
|
281
|
-
}
|
|
282
|
-
function renderInstallPlan(plan2) {
|
|
283
|
-
if (plan2.satisfied) {
|
|
284
|
-
return [
|
|
285
|
-
`package.json already has ${plan2.packages.map(describe).join(" and ")} \u2014 nothing to install.`
|
|
286
|
-
];
|
|
287
|
-
}
|
|
288
|
-
const pending = plan2.packages.filter((entry) => !entry.satisfied);
|
|
289
|
-
const added = pending.filter((entry) => entry.declared === void 0);
|
|
290
|
-
const replaced = pending.filter((entry) => entry.declared !== void 0);
|
|
291
|
-
return [
|
|
292
|
-
"package.json",
|
|
293
|
-
...added.length === 0 ? [] : [
|
|
294
|
-
' + "dependencies": {',
|
|
295
|
-
...added.map((entry) => ` + "${entry.name}": "${entry.version}"`),
|
|
296
|
-
" + }"
|
|
297
|
-
],
|
|
298
|
-
...replaced.flatMap((entry) => [
|
|
299
|
-
` - "${entry.name}": "${entry.declared}"`,
|
|
300
|
-
` + "${entry.name}": "${entry.version}"`
|
|
301
|
-
]),
|
|
302
|
-
...plan2.lockfile === void 0 ? [] : [plan2.lockfile, ...pending.map((entry) => ` + ${entry.name}@${entry.version}`)],
|
|
303
|
-
"",
|
|
304
|
-
`Run with: ${plan2.command.join(" ")}`
|
|
305
|
-
];
|
|
306
|
-
}
|
|
307
|
-
var installWithPackageManager = async (plan2) => {
|
|
308
|
-
const child = startChild({
|
|
309
|
-
command: plan2.command,
|
|
310
|
-
env: process.env,
|
|
311
|
-
cwd: plan2.root,
|
|
312
|
-
purpose: `install ${plan2.packages.map(describe).join(" and ")}`
|
|
313
|
-
});
|
|
314
|
-
const ended = await child.ended;
|
|
315
|
-
if (ended.exitCode !== 0 || ended.signal !== null) {
|
|
316
|
-
throw installFailed(plan2);
|
|
317
|
-
}
|
|
318
|
-
};
|
|
319
|
-
function installFailed(plan2) {
|
|
320
|
-
return new PenvError2(
|
|
321
|
-
"INIT_INSTALL_FAILED",
|
|
322
|
-
`${plan2.command.join(" ")} did not finish, so penv migrated nothing`,
|
|
323
|
-
`Run \`${plan2.command.join(" ")}\` yourself, then start this command again. Your dotenv files are exactly where they were.`
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
34
|
// src/project.ts
|
|
328
|
-
import { existsSync
|
|
329
|
-
import { dirname, join as
|
|
35
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
36
|
+
import { dirname, join as join2 } from "path";
|
|
330
37
|
import {
|
|
331
38
|
assertMigrated,
|
|
332
39
|
candidatesFor,
|
|
@@ -335,7 +42,7 @@ import {
|
|
|
335
42
|
isReservedToken,
|
|
336
43
|
loadConfig,
|
|
337
44
|
openValue,
|
|
338
|
-
PenvError as
|
|
45
|
+
PenvError as PenvError3,
|
|
339
46
|
parameterId,
|
|
340
47
|
ReservedTokenError,
|
|
341
48
|
recordsDir as recordsDir2,
|
|
@@ -348,7 +55,7 @@ import {
|
|
|
348
55
|
import { FilesystemProvider } from "@penvhq/provider-filesystem";
|
|
349
56
|
|
|
350
57
|
// src/env-flags.ts
|
|
351
|
-
import { PenvError
|
|
58
|
+
import { PenvError } from "@penvhq/core";
|
|
352
59
|
var BASE_RESERVED = ["help", "version", "h", "v", "_", "--"];
|
|
353
60
|
var COMMAND_FLAGS = [
|
|
354
61
|
...BASE_RESERVED,
|
|
@@ -381,14 +88,14 @@ function environmentFromShorthand(config, candidates, explicit) {
|
|
|
381
88
|
const hits = candidates.filter((candidate) => config.environments.includes(candidate));
|
|
382
89
|
const strangers = candidates.filter((candidate) => !config.environments.includes(candidate));
|
|
383
90
|
if (strangers.length > 0) {
|
|
384
|
-
throw new
|
|
91
|
+
throw new PenvError(
|
|
385
92
|
"UNKNOWN_FLAG",
|
|
386
93
|
`${quoteList(strangers)} ${strangers.length === 1 ? "is not a flag" : "are not flags"} this command takes, and ${strangers.length === 1 ? "names" : "name"} no declared environment`,
|
|
387
94
|
`Declared environments work as bare flags: ${quoteList(config.environments)}. Anything else needs \`--env <name>\`.`
|
|
388
95
|
);
|
|
389
96
|
}
|
|
390
97
|
if (hits.length > 1) {
|
|
391
|
-
throw new
|
|
98
|
+
throw new PenvError(
|
|
392
99
|
"ENVIRONMENT_FLAG_AMBIGUOUS",
|
|
393
100
|
`${quoteList(hits)} name two environments at once, and a command acts on exactly one`,
|
|
394
101
|
"Pass a single environment flag, or the canonical `--env <name>`."
|
|
@@ -396,7 +103,7 @@ function environmentFromShorthand(config, candidates, explicit) {
|
|
|
396
103
|
}
|
|
397
104
|
const hit = hits[0];
|
|
398
105
|
if (hit !== void 0 && explicit !== void 0 && explicit !== hit) {
|
|
399
|
-
throw new
|
|
106
|
+
throw new PenvError(
|
|
400
107
|
"ENVIRONMENT_FLAG_AMBIGUOUS",
|
|
401
108
|
`\`--env ${explicit}\` and \`--${hit}\` name two environments at once`,
|
|
402
109
|
"Drop one of them \u2014 `--env` is the canonical spelling."
|
|
@@ -406,9 +113,9 @@ function environmentFromShorthand(config, candidates, explicit) {
|
|
|
406
113
|
}
|
|
407
114
|
|
|
408
115
|
// src/registry.ts
|
|
409
|
-
import { readFileSync
|
|
116
|
+
import { readFileSync } from "fs";
|
|
410
117
|
import { createRequire } from "module";
|
|
411
|
-
import { join
|
|
118
|
+
import { join, resolve } from "path";
|
|
412
119
|
import { pathToFileURL } from "url";
|
|
413
120
|
import {
|
|
414
121
|
holdsProjection,
|
|
@@ -416,7 +123,7 @@ import {
|
|
|
416
123
|
localExtensionsFile,
|
|
417
124
|
MANIFEST_PATH,
|
|
418
125
|
PENV_DIR,
|
|
419
|
-
PenvError as
|
|
126
|
+
PenvError as PenvError2,
|
|
420
127
|
packageDir,
|
|
421
128
|
packageEntry,
|
|
422
129
|
parseLocalExtensions,
|
|
@@ -475,7 +182,7 @@ async function loadPluginProvider(type, context) {
|
|
|
475
182
|
}
|
|
476
183
|
const factory = mod[PLUGIN_FACTORY_EXPORT];
|
|
477
184
|
if (typeof factory !== "function") {
|
|
478
|
-
throw new
|
|
185
|
+
throw new PenvError2(
|
|
479
186
|
"PROVIDER_PLUGIN_INVALID",
|
|
480
187
|
`\`${type}\` does not export \`${PLUGIN_FACTORY_EXPORT}\``,
|
|
481
188
|
`A penv provider package must export \`${PLUGIN_FACTORY_EXPORT}(context) => Provider\`.`
|
|
@@ -522,7 +229,7 @@ function resolveExtension(type, projectRoot, environment, local = localExtension
|
|
|
522
229
|
function pinnedVersion(type, projectRoot) {
|
|
523
230
|
let manifest;
|
|
524
231
|
try {
|
|
525
|
-
manifest = parseManifest(
|
|
232
|
+
manifest = parseManifest(readFileSync(join(projectRoot, ...MANIFEST_PATH.split("/")), "utf8"));
|
|
526
233
|
} catch {
|
|
527
234
|
return void 0;
|
|
528
235
|
}
|
|
@@ -535,7 +242,7 @@ function storedExtension(type, version) {
|
|
|
535
242
|
function localExtensions(projectRoot) {
|
|
536
243
|
let text;
|
|
537
244
|
try {
|
|
538
|
-
text =
|
|
245
|
+
text = readFileSync(localExtensionsFile(projectRoot), "utf8");
|
|
539
246
|
} catch {
|
|
540
247
|
return [];
|
|
541
248
|
}
|
|
@@ -545,7 +252,7 @@ function isCi(value) {
|
|
|
545
252
|
return value !== void 0 && value !== "" && value !== "0" && value.toLowerCase() !== "false";
|
|
546
253
|
}
|
|
547
254
|
function localExtensionInCi(type, environment) {
|
|
548
|
-
return new
|
|
255
|
+
return new PenvError2(
|
|
549
256
|
"LOCAL_EXTENSION_IN_CI",
|
|
550
257
|
`The provider \`${type}\` for environment ${environment} is a local extension, and this is CI`,
|
|
551
258
|
`${LOCAL_EXTENSIONS_PATH} records it as a package this project develops, so nothing pins the bytes CI would run. Publish it and run \`penv add ${type}\` to pin a release.`
|
|
@@ -574,7 +281,7 @@ function assertSatisfiesContract(provider, specifier) {
|
|
|
574
281
|
const contract = projection ? "the @penvhq/core ProjectionProvider contract its declared capabilities select" : "the @penvhq/core Provider contract that the filesystem provider defines";
|
|
575
282
|
for (const method of methods) {
|
|
576
283
|
if (typeof provider[method] !== "function") {
|
|
577
|
-
throw new
|
|
284
|
+
throw new PenvError2(
|
|
578
285
|
"PROVIDER_PLUGIN_INVALID",
|
|
579
286
|
`The provider from \`${specifier}\` is missing \`${method}()\``,
|
|
580
287
|
`It must satisfy ${contract}.`
|
|
@@ -587,21 +294,21 @@ function where(environment) {
|
|
|
587
294
|
}
|
|
588
295
|
function unknownProvider(type, environment) {
|
|
589
296
|
const preinstalled = [...REGISTRY.keys()].map((name) => `\`${name}\``).join(", ");
|
|
590
|
-
return new
|
|
297
|
+
return new PenvError2(
|
|
591
298
|
"UNKNOWN_PROVIDER",
|
|
592
299
|
`The provider \`${type}\`${where(environment)} in penv.config.ts is nowhere penv looks for it`,
|
|
593
300
|
`Run \`penv add ${type}\` to pin a release, or \`penv add --local ${type}\` if this repository is the one that builds it. penv looks in ${LOCAL_EXTENSIONS_PATH}, this project's node_modules, and then $PENV_HOME at the version ${MANIFEST_PATH} pins. The CLI ships ${preinstalled} pre-installed.`
|
|
594
301
|
);
|
|
595
302
|
}
|
|
596
303
|
function extensionNotInstalled(type, version, environment) {
|
|
597
|
-
return new
|
|
304
|
+
return new PenvError2(
|
|
598
305
|
"EXTENSION_NOT_INSTALLED",
|
|
599
306
|
`${MANIFEST_PATH} pins \`${type}\` ${version}${where(environment)}, and it is not installed in ${penvHome(process.env)}`,
|
|
600
307
|
"Run `penv install` \u2014 it downloads and verifies every version the manifest pins, extensions included."
|
|
601
308
|
);
|
|
602
309
|
}
|
|
603
310
|
function localExtensionUnresolved(type, projectRoot, environment) {
|
|
604
|
-
return new
|
|
311
|
+
return new PenvError2(
|
|
605
312
|
"LOCAL_EXTENSION_UNRESOLVED",
|
|
606
313
|
`${LOCAL_EXTENSIONS_PATH} records \`${type}\`${where(environment)} as a package this project develops, and it does not resolve from ${projectRoot}`,
|
|
607
314
|
`Add it as a dependency of the root (a workspace link is one) and run \`pnpm install\`, or drop the name from ${LOCAL_EXTENSIONS_PATH} if this project no longer builds it.`
|
|
@@ -609,7 +316,7 @@ function localExtensionUnresolved(type, projectRoot, environment) {
|
|
|
609
316
|
}
|
|
610
317
|
function providerLoadFailed(type, path, cause) {
|
|
611
318
|
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
612
|
-
return new
|
|
319
|
+
return new PenvError2(
|
|
613
320
|
"PROVIDER_PLUGIN_LOAD",
|
|
614
321
|
`The provider package \`${type}\` failed to load from ${path}: ${detail}`,
|
|
615
322
|
"penv imports that file exactly as it stands, with no transform, so it has to be built JavaScript with its dependencies installed."
|
|
@@ -632,7 +339,7 @@ function openProject(cwd) {
|
|
|
632
339
|
}
|
|
633
340
|
function localTree(project) {
|
|
634
341
|
if (!(project.provider instanceof FilesystemProvider)) {
|
|
635
|
-
throw new
|
|
342
|
+
throw new PenvError3(
|
|
636
343
|
"PROVIDER_NOT_LOCAL",
|
|
637
344
|
`This command reads the local .penv tree synchronously, which the \`${project.provider.type}\` provider is not`,
|
|
638
345
|
"Run this against a filesystem-backed project, or use a command that speaks the async provider contract."
|
|
@@ -641,11 +348,11 @@ function localTree(project) {
|
|
|
641
348
|
return project.provider;
|
|
642
349
|
}
|
|
643
350
|
function selfContainedSchemaModule(root, schemaFile) {
|
|
644
|
-
const file =
|
|
645
|
-
if (!
|
|
351
|
+
const file = join2(root, ...schemaFile.split("/"));
|
|
352
|
+
if (!existsSync(file)) {
|
|
646
353
|
return void 0;
|
|
647
354
|
}
|
|
648
|
-
const source =
|
|
355
|
+
const source = readFileSync2(file, "utf8");
|
|
649
356
|
return source.includes("PenvSchemaShape") || source.includes("z.object") ? schemaFile : void 0;
|
|
650
357
|
}
|
|
651
358
|
function schemaShapeFileOf(project) {
|
|
@@ -674,7 +381,7 @@ function refFromKey(key, config) {
|
|
|
674
381
|
const segments = key.split(KEY_SEPARATOR).filter((segment) => segment.length > 0);
|
|
675
382
|
const name = segments[segments.length - 1];
|
|
676
383
|
if (name === void 0) {
|
|
677
|
-
throw new
|
|
384
|
+
throw new PenvError3(
|
|
678
385
|
"PARAMETER_KEY",
|
|
679
386
|
`\`${key}\` names no parameter`,
|
|
680
387
|
"A key is `<namespace>/<name>` or `<namespace>.<name>`, e.g. `redis/password`."
|
|
@@ -693,13 +400,13 @@ function assertWritableKey(key) {
|
|
|
693
400
|
const ref = refFromAccessPath(segments);
|
|
694
401
|
if (ref !== void 0) {
|
|
695
402
|
const suggestion = [...ref.namespace, ref.name].join("/");
|
|
696
|
-
throw new
|
|
403
|
+
throw new PenvError3(
|
|
697
404
|
"PARAMETER_KEY_CASING",
|
|
698
405
|
`\`${key}\` is not a canonical parameter name`,
|
|
699
406
|
`Parameter files are lower-case and hyphenated. Did you mean \`${suggestion}\`? That is the file that backs the \`${key}\` key in your schema.`
|
|
700
407
|
);
|
|
701
408
|
}
|
|
702
|
-
throw new
|
|
409
|
+
throw new PenvError3(
|
|
703
410
|
"PARAMETER_KEY_UNREACHABLE",
|
|
704
411
|
`No value file can be named that reaches \`${key}\``,
|
|
705
412
|
"Parameter files are lower-case and hyphenated, and this key maps to no such file \u2014 a run of capitals like `apiURL` cannot be reached (use `api-url`, which the schema reads as `apiUrl`). Run `penv validate` or `penv fill` to see the names penv expects."
|
|
@@ -747,7 +454,7 @@ function refsFrom(files) {
|
|
|
747
454
|
}
|
|
748
455
|
|
|
749
456
|
// src/ui.ts
|
|
750
|
-
import {
|
|
457
|
+
import { isPenvErrorLike } from "@penvhq/core";
|
|
751
458
|
|
|
752
459
|
// src/style.ts
|
|
753
460
|
function supportsColor(stream) {
|
|
@@ -880,7 +587,7 @@ function writeError(lines) {
|
|
|
880
587
|
}
|
|
881
588
|
}
|
|
882
589
|
function reportError(error) {
|
|
883
|
-
if (error
|
|
590
|
+
if (isPenvErrorLike(error)) {
|
|
884
591
|
process.stderr.write(`${err.red(CROSS)} ${error.summary}
|
|
885
592
|
`);
|
|
886
593
|
if (error.remedy !== void 0) {
|
|
@@ -911,9 +618,9 @@ async function guard(run) {
|
|
|
911
618
|
}
|
|
912
619
|
|
|
913
620
|
// src/commands/run.ts
|
|
914
|
-
import { existsSync as
|
|
621
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, watch } from "fs";
|
|
915
622
|
import { createRequire as createRequire2 } from "module";
|
|
916
|
-
import { basename, dirname as dirname2, join as
|
|
623
|
+
import { basename, dirname as dirname2, join as join3, resolve as resolve2 } from "path";
|
|
917
624
|
import {
|
|
918
625
|
ARTIFACT_BUILD_COMMAND,
|
|
919
626
|
assertArtifactFor,
|
|
@@ -923,7 +630,7 @@ import {
|
|
|
923
630
|
MissingMaterializationError,
|
|
924
631
|
openSealed,
|
|
925
632
|
own,
|
|
926
|
-
PenvError as
|
|
633
|
+
PenvError as PenvError5,
|
|
927
634
|
parseArtifact,
|
|
928
635
|
RECORDS_PATH,
|
|
929
636
|
UndecryptableValueError,
|
|
@@ -1172,7 +879,7 @@ import {
|
|
|
1172
879
|
checkNameCollisions,
|
|
1173
880
|
jitiFor,
|
|
1174
881
|
NameCollisionError,
|
|
1175
|
-
PenvError as
|
|
882
|
+
PenvError as PenvError4,
|
|
1176
883
|
ReservedTokenError as ReservedTokenError2,
|
|
1177
884
|
resolveAll,
|
|
1178
885
|
SCHEMA_HARVEST_ENV,
|
|
@@ -1521,7 +1228,7 @@ async function checkEnvironment(project, environment) {
|
|
|
1521
1228
|
try {
|
|
1522
1229
|
files = await project.provider.list();
|
|
1523
1230
|
} catch (error) {
|
|
1524
|
-
if (!(error instanceof
|
|
1231
|
+
if (!(error instanceof PenvError4)) {
|
|
1525
1232
|
throw error;
|
|
1526
1233
|
}
|
|
1527
1234
|
return {
|
|
@@ -1673,7 +1380,7 @@ function assertNotNested(host, inner) {
|
|
|
1673
1380
|
if (outer === void 0) {
|
|
1674
1381
|
return;
|
|
1675
1382
|
}
|
|
1676
|
-
throw new
|
|
1383
|
+
throw new PenvError5(
|
|
1677
1384
|
"RUN_NESTED",
|
|
1678
1385
|
`\`${inner}\` is starting inside \`${outer}\`, and two penv environments cannot own one process`,
|
|
1679
1386
|
`Drop one of the two wrappers \u2014 the inner one is in a package.json script \u2014 then run \`${outer}\` again.`
|
|
@@ -1683,7 +1390,7 @@ function assertSource(source, inner) {
|
|
|
1683
1390
|
if (source === "project" || source === "snapshot") {
|
|
1684
1391
|
return source;
|
|
1685
1392
|
}
|
|
1686
|
-
throw new
|
|
1393
|
+
throw new PenvError5(
|
|
1687
1394
|
"RUN_SOURCE_UNKNOWN",
|
|
1688
1395
|
`\`--source ${source}\` names no source penv reads`,
|
|
1689
1396
|
`A run reads ${SOURCES.map((name) => `\`${name}\``).join(" or ")}, and \`project\` is the default \u2014 so \`${inner}\` reads the local tree.`
|
|
@@ -1704,7 +1411,7 @@ async function assertNoPublicSecret(project, environment, refs, retry) {
|
|
|
1704
1411
|
}
|
|
1705
1412
|
const parameter = [...ref.namespace, ref.name].join("/");
|
|
1706
1413
|
const prefix = prefixes.find((candidate) => variable.startsWith(candidate));
|
|
1707
|
-
throw new
|
|
1414
|
+
throw new PenvError5(
|
|
1708
1415
|
"RUN_PUBLIC_SECRET",
|
|
1709
1416
|
`The secret ${parameter} maps to ${variable}, which the \`${prefix}\` prefix publishes to the browser`,
|
|
1710
1417
|
`Rename the parameter, or drop \`secret\` from its meta if it is not one \u2014 then \`${retry}\`.`
|
|
@@ -1713,7 +1420,7 @@ async function assertNoPublicSecret(project, environment, refs, retry) {
|
|
|
1713
1420
|
}
|
|
1714
1421
|
function invalidConfiguration(result2, inner) {
|
|
1715
1422
|
const lines = result2.issues.map((issue) => ` ${issue.subject}: ${issue.message}`).join("\n");
|
|
1716
|
-
return new
|
|
1423
|
+
return new PenvError5(
|
|
1717
1424
|
"RUN_INVALID_CONFIGURATION",
|
|
1718
1425
|
`Configuration for environment ${result2.environment} is not valid, so nothing was started:
|
|
1719
1426
|
${lines}`,
|
|
@@ -1735,7 +1442,7 @@ function assertNoActiveDotenv(project) {
|
|
|
1735
1442
|
if (first === void 0) {
|
|
1736
1443
|
return;
|
|
1737
1444
|
}
|
|
1738
|
-
throw new
|
|
1445
|
+
throw new PenvError5(
|
|
1739
1446
|
"RUN_DOTENV_ACTIVE",
|
|
1740
1447
|
`${first.name} is active configuration again, and your framework would read it beside penv's records`,
|
|
1741
1448
|
`Adopt it with \`penv init\`, or delete ${first.name} \u2014 its values belong in ${RECORDS_PATH}/.`
|
|
@@ -1753,10 +1460,10 @@ function packageManifest(specifier, root) {
|
|
|
1753
1460
|
return void 0;
|
|
1754
1461
|
}
|
|
1755
1462
|
for (; ; ) {
|
|
1756
|
-
const file =
|
|
1757
|
-
if (
|
|
1463
|
+
const file = join3(directory, "package.json");
|
|
1464
|
+
if (existsSync2(file)) {
|
|
1758
1465
|
try {
|
|
1759
|
-
const parsed = JSON.parse(
|
|
1466
|
+
const parsed = JSON.parse(readFileSync3(file, "utf8"));
|
|
1760
1467
|
if (isPlainObject(parsed) && parsed.name === specifier) {
|
|
1761
1468
|
return parsed;
|
|
1762
1469
|
}
|
|
@@ -1783,7 +1490,7 @@ function declaredCredentials(config, root) {
|
|
|
1783
1490
|
continue;
|
|
1784
1491
|
}
|
|
1785
1492
|
if (!Array.isArray(credentials) || credentials.some((name) => typeof name !== "string" || !VARIABLE.test(name))) {
|
|
1786
|
-
throw new
|
|
1493
|
+
throw new PenvError5(
|
|
1787
1494
|
"PROVIDER_CREDENTIALS_INVALID",
|
|
1788
1495
|
`\`${provider.type}\` declares \`penv.credentials\`, and it is not a list of variable names`,
|
|
1789
1496
|
`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.`
|
|
@@ -1819,7 +1526,7 @@ async function prepare(project, environment, host, inner) {
|
|
|
1819
1526
|
function snapshotPath(host) {
|
|
1820
1527
|
const path = host[SNAPSHOT_VARIABLE]?.trim();
|
|
1821
1528
|
if (path === void 0 || path.length === 0) {
|
|
1822
|
-
throw new
|
|
1529
|
+
throw new PenvError5(
|
|
1823
1530
|
"RUN_SNAPSHOT_UNSET",
|
|
1824
1531
|
`\`--source snapshot\` reads the sealed artifact ${SNAPSHOT_VARIABLE} names, and ${SNAPSHOT_VARIABLE} is not set`,
|
|
1825
1532
|
`Build one with \`${ARTIFACT_BUILD_COMMAND}\` and point ${SNAPSHOT_VARIABLE} at it.`
|
|
@@ -1829,9 +1536,9 @@ function snapshotPath(host) {
|
|
|
1829
1536
|
}
|
|
1830
1537
|
function readSnapshot(path) {
|
|
1831
1538
|
try {
|
|
1832
|
-
return
|
|
1539
|
+
return readFileSync3(path, "utf8");
|
|
1833
1540
|
} catch {
|
|
1834
|
-
throw new
|
|
1541
|
+
throw new PenvError5(
|
|
1835
1542
|
"RUN_SNAPSHOT_MISSING",
|
|
1836
1543
|
`${SNAPSHOT_VARIABLE} names ${path}, and penv cannot read a sealed artifact there`,
|
|
1837
1544
|
`Point ${SNAPSHOT_VARIABLE} at the artifact your release mounted \u2014 \`${ARTIFACT_BUILD_COMMAND}\` writes one.`
|
|
@@ -1908,7 +1615,7 @@ function watchProject(project, onChange) {
|
|
|
1908
1615
|
timer = setTimeout(onChange, DEBOUNCE_MS);
|
|
1909
1616
|
};
|
|
1910
1617
|
const add = (target, recursive, only) => {
|
|
1911
|
-
if (!
|
|
1618
|
+
if (!existsSync2(target)) {
|
|
1912
1619
|
return;
|
|
1913
1620
|
}
|
|
1914
1621
|
try {
|
|
@@ -1947,7 +1654,7 @@ async function runRun(options) {
|
|
|
1947
1654
|
}
|
|
1948
1655
|
if (source === "snapshot") {
|
|
1949
1656
|
if (options.watch === true) {
|
|
1950
|
-
throw new
|
|
1657
|
+
throw new PenvError5(
|
|
1951
1658
|
"RUN_SNAPSHOT_WATCH",
|
|
1952
1659
|
"`--watch` re-syncs the project tree, and a run from a sealed artifact has no tree to watch",
|
|
1953
1660
|
"Drop `--watch` \u2014 an artifact is built once and read unchanged. Watch the project instead: `penv run --watch -- <command>`."
|
|
@@ -2091,7 +1798,7 @@ function buildCommand(environment, out2) {
|
|
|
2091
1798
|
function targetOf(options) {
|
|
2092
1799
|
const environment = options.environment?.trim();
|
|
2093
1800
|
if (environment === void 0 || environment.length === 0) {
|
|
2094
|
-
throw new
|
|
1801
|
+
throw new PenvError6(
|
|
2095
1802
|
"ARTIFACT_ENV_REQUIRED",
|
|
2096
1803
|
"`penv artifact build` names the environment it builds for, and `--env` was not given",
|
|
2097
1804
|
`Name it: \`${buildCommand(void 0, options.out)}\`. An artifact carries one environment, and penv will not pick which.`
|
|
@@ -2102,13 +1809,13 @@ function targetOf(options) {
|
|
|
2102
1809
|
function outputOf(options, environment) {
|
|
2103
1810
|
const out2 = options.out?.trim();
|
|
2104
1811
|
if (out2 === void 0 || out2.length === 0) {
|
|
2105
|
-
throw new
|
|
1812
|
+
throw new PenvError6(
|
|
2106
1813
|
"ARTIFACT_OUT_REQUIRED",
|
|
2107
1814
|
"`penv artifact build` writes where it is told, and `--out` was not given",
|
|
2108
1815
|
`Name the path: \`${buildCommand(environment, void 0)}\`. The artifact belongs outside the repository, so there is no default worth having.`
|
|
2109
1816
|
);
|
|
2110
1817
|
}
|
|
2111
|
-
return
|
|
1818
|
+
return isAbsolute(out2) ? out2 : resolve3(options.cwd, out2);
|
|
2112
1819
|
}
|
|
2113
1820
|
async function winnerOf(project, ref, environment) {
|
|
2114
1821
|
for (const file of candidatesFor2(ref, environment, true)) {
|
|
@@ -2120,7 +1827,7 @@ async function winnerOf(project, ref, environment) {
|
|
|
2120
1827
|
return void 0;
|
|
2121
1828
|
}
|
|
2122
1829
|
function plaintextSecret(parameter, location, environment) {
|
|
2123
|
-
return new
|
|
1830
|
+
return new PenvError6(
|
|
2124
1831
|
"ARTIFACT_PLAINTEXT_SECRET",
|
|
2125
1832
|
`${parameter} is a secret for environment ${environment}, and its value comes from ${location}, which is not sealed`,
|
|
2126
1833
|
`Seal it with \`penv encrypt ${parameter} --env ${environment}\` \u2014 an artifact carries ciphertext or nothing.`
|
|
@@ -2134,7 +1841,7 @@ async function runArtifactBuild(options) {
|
|
|
2134
1841
|
const { schema, issues } = await loadSchema(project, environment);
|
|
2135
1842
|
if (schema === void 0) {
|
|
2136
1843
|
const lines = issues.map((issue) => ` ${issue.subject}: ${issue.message}`).join("\n");
|
|
2137
|
-
throw new
|
|
1844
|
+
throw new PenvError6(
|
|
2138
1845
|
"ARTIFACT_NO_SCHEMA",
|
|
2139
1846
|
`The schema did not load, so penv cannot tell what this artifact should deliver:
|
|
2140
1847
|
${lines}`,
|
|
@@ -2186,7 +1893,7 @@ ${lines}`,
|
|
|
2186
1893
|
values: present,
|
|
2187
1894
|
sealed,
|
|
2188
1895
|
absent: refs.length - present,
|
|
2189
|
-
insideRepo: inside !== "" && !inside.startsWith("..") && !
|
|
1896
|
+
insideRepo: inside !== "" && !inside.startsWith("..") && !isAbsolute(inside)
|
|
2190
1897
|
};
|
|
2191
1898
|
}
|
|
2192
1899
|
function displayPath(cwd, file) {
|
|
@@ -2247,25 +1954,25 @@ import { defineCommand as defineCommand5 } from "citty";
|
|
|
2247
1954
|
|
|
2248
1955
|
// src/cutover.ts
|
|
2249
1956
|
import {
|
|
2250
|
-
existsSync as
|
|
1957
|
+
existsSync as existsSync3,
|
|
2251
1958
|
mkdirSync as mkdirSync2,
|
|
2252
1959
|
readdirSync as readdirSync2,
|
|
2253
|
-
readFileSync as
|
|
1960
|
+
readFileSync as readFileSync4,
|
|
2254
1961
|
renameSync,
|
|
2255
1962
|
rmSync,
|
|
2256
1963
|
writeFileSync as writeFileSync2
|
|
2257
1964
|
} from "fs";
|
|
2258
|
-
import { dirname as dirname4, join as
|
|
1965
|
+
import { dirname as dirname4, join as join4, resolve as resolve4 } from "path";
|
|
2259
1966
|
import {
|
|
2260
1967
|
CUTOVER_PATH,
|
|
2261
1968
|
findConfigFile,
|
|
2262
|
-
PenvError as
|
|
1969
|
+
PenvError as PenvError7,
|
|
2263
1970
|
ROLLBACK_DOTENV_PATH,
|
|
2264
1971
|
ROLLBACK_PATH
|
|
2265
1972
|
} from "@penvhq/core";
|
|
2266
1973
|
var CUTOVER_FORMAT = 1;
|
|
2267
1974
|
function fileFor(root, relativePosix) {
|
|
2268
|
-
return
|
|
1975
|
+
return join4(root, ...relativePosix.split("/"));
|
|
2269
1976
|
}
|
|
2270
1977
|
function bundleDir(root) {
|
|
2271
1978
|
return fileFor(root, ROLLBACK_DOTENV_PATH);
|
|
@@ -2289,12 +1996,12 @@ function bundleUnresolved(root) {
|
|
|
2289
1996
|
}
|
|
2290
1997
|
function readCutover(root) {
|
|
2291
1998
|
const file = cutoverFile(root);
|
|
2292
|
-
if (!
|
|
1999
|
+
if (!existsSync3(file)) {
|
|
2293
2000
|
return void 0;
|
|
2294
2001
|
}
|
|
2295
2002
|
let parsed;
|
|
2296
2003
|
try {
|
|
2297
|
-
parsed = JSON.parse(
|
|
2004
|
+
parsed = JSON.parse(readFileSync4(file, "utf8"));
|
|
2298
2005
|
} catch {
|
|
2299
2006
|
throw unreadable("it is not JSON");
|
|
2300
2007
|
}
|
|
@@ -2320,7 +2027,7 @@ function readCutover(root) {
|
|
|
2320
2027
|
};
|
|
2321
2028
|
}
|
|
2322
2029
|
function unreadable(what) {
|
|
2323
|
-
return new
|
|
2030
|
+
return new PenvError7(
|
|
2324
2031
|
"CUTOVER_UNREADABLE",
|
|
2325
2032
|
`${CUTOVER_PATH} records the last dotenv cutover, and ${what}`,
|
|
2326
2033
|
`Run \`penv cleanup\` to drop that record and the rollback bundle it names.`
|
|
@@ -2352,7 +2059,7 @@ function bundleDotenvFiles(root, files, environments, now = /* @__PURE__ */ new
|
|
|
2352
2059
|
const bundle = bundleDir(root);
|
|
2353
2060
|
mkdirSync2(bundle, { recursive: true });
|
|
2354
2061
|
for (const name of files) {
|
|
2355
|
-
renameSync(
|
|
2062
|
+
renameSync(join4(root, name), join4(bundle, name));
|
|
2356
2063
|
}
|
|
2357
2064
|
return cutover;
|
|
2358
2065
|
}
|
|
@@ -2361,7 +2068,7 @@ function runUndo(options) {
|
|
|
2361
2068
|
const cutover = readCutover(root);
|
|
2362
2069
|
const bundled = bundledFiles(root);
|
|
2363
2070
|
if (cutover === void 0 && bundled.length === 0) {
|
|
2364
|
-
throw new
|
|
2071
|
+
throw new PenvError7(
|
|
2365
2072
|
"INIT_UNDO_NOTHING",
|
|
2366
2073
|
"There is no dotenv cutover to undo in this project",
|
|
2367
2074
|
"Run `penv init` to adopt your dotenv files; undo puts them back afterwards."
|
|
@@ -2371,11 +2078,11 @@ function runUndo(options) {
|
|
|
2371
2078
|
const names = [...recorded, ...bundled.filter((name) => !recorded.includes(name))];
|
|
2372
2079
|
const bundle = bundleDir(root);
|
|
2373
2080
|
const held = new Set(bundled);
|
|
2374
|
-
const occupied2 = names.filter((name) => held.has(name) &&
|
|
2081
|
+
const occupied2 = names.filter((name) => held.has(name) && existsSync3(join4(root, name)));
|
|
2375
2082
|
if (occupied2.length > 0) {
|
|
2376
2083
|
const listed = occupied2.join(", ");
|
|
2377
2084
|
const many = occupied2.length > 1;
|
|
2378
|
-
throw new
|
|
2085
|
+
throw new PenvError7(
|
|
2379
2086
|
"INIT_UNDO_OCCUPIED",
|
|
2380
2087
|
`${listed} ${many ? "exist" : "exists"} again, and restoring what penv moved aside would write over ${many ? "them" : "it"}`,
|
|
2381
2088
|
`Move ${listed} out of the way, or run \`penv cleanup\` to keep ${many ? "them" : "it"} and drop the bundle. Nothing was restored.`
|
|
@@ -2386,9 +2093,9 @@ function runUndo(options) {
|
|
|
2386
2093
|
const missing = [];
|
|
2387
2094
|
for (const name of names) {
|
|
2388
2095
|
if (held.has(name)) {
|
|
2389
|
-
renameSync(
|
|
2096
|
+
renameSync(join4(bundle, name), join4(root, name));
|
|
2390
2097
|
restored.push(name);
|
|
2391
|
-
} else if (
|
|
2098
|
+
} else if (existsSync3(join4(root, name))) {
|
|
2392
2099
|
alreadyBack.push(name);
|
|
2393
2100
|
} else {
|
|
2394
2101
|
missing.push(name);
|
|
@@ -2400,7 +2107,7 @@ function runUndo(options) {
|
|
|
2400
2107
|
function runCleanup(options) {
|
|
2401
2108
|
const root = cutoverRoot(options.cwd);
|
|
2402
2109
|
const held = bundledFiles(root);
|
|
2403
|
-
const cleaned = held.length > 0 ||
|
|
2110
|
+
const cleaned = held.length > 0 || existsSync3(cutoverFile(root)) || existsSync3(fileFor(root, ROLLBACK_PATH));
|
|
2404
2111
|
removeBundle(root);
|
|
2405
2112
|
return { root, removed: held, cleaned };
|
|
2406
2113
|
}
|
|
@@ -2441,8 +2148,8 @@ var cleanupCommand = defineCommand5({
|
|
|
2441
2148
|
});
|
|
2442
2149
|
|
|
2443
2150
|
// src/commands/doctor.ts
|
|
2444
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
2445
|
-
import { join as
|
|
2151
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync } from "fs";
|
|
2152
|
+
import { join as join5, relative as relative2 } from "path";
|
|
2446
2153
|
import {
|
|
2447
2154
|
ARTIFACT_BUILD_COMMAND as ARTIFACT_BUILD_COMMAND2,
|
|
2448
2155
|
accessPath as accessPath3,
|
|
@@ -2468,7 +2175,7 @@ import { defineCommand as defineCommand7 } from "citty";
|
|
|
2468
2175
|
import {
|
|
2469
2176
|
checkNameCollisions as checkNameCollisions2,
|
|
2470
2177
|
holdsProjection as holdsProjection3,
|
|
2471
|
-
PenvError as
|
|
2178
|
+
PenvError as PenvError8,
|
|
2472
2179
|
recordPath,
|
|
2473
2180
|
requireValue,
|
|
2474
2181
|
variableName as variableName4
|
|
@@ -2536,7 +2243,7 @@ async function destinationFor(project, environment, options) {
|
|
|
2536
2243
|
const declared = project.config.providers[environment];
|
|
2537
2244
|
const location = declared?.location;
|
|
2538
2245
|
if (declared === void 0 || declared.type === LOCAL_TREE_TYPE) {
|
|
2539
|
-
throw new
|
|
2246
|
+
throw new PenvError8(
|
|
2540
2247
|
"NO_DESTINATION",
|
|
2541
2248
|
`Environment ${environment}'s provider is the local records tree itself, so penv has nowhere to push`,
|
|
2542
2249
|
`Declare a provider for it in penv.config.ts \u2014 e.g. \`${environment}: { type: "@penvhq/provider-github", location: "owner/repo" }\` \u2014 or push somewhere once with \`penv push --env ${environment} --destination <package> --location <place>\`.`
|
|
@@ -2563,7 +2270,7 @@ function plan(resolutions, config, environment, allowDecrypt) {
|
|
|
2563
2270
|
let encrypted = false;
|
|
2564
2271
|
if (winner.file.encrypted) {
|
|
2565
2272
|
if (!allowDecrypt) {
|
|
2566
|
-
throw new
|
|
2273
|
+
throw new PenvError8(
|
|
2567
2274
|
"ENCRYPTED_VALUE_REFUSED",
|
|
2568
2275
|
`Parameter ${resolution.parameter} for environment ${environment} resolves to the encrypted value file ${recordPath(winner.location)}, and a push sends plaintext for the destination to re-seal`,
|
|
2569
2276
|
"Re-run with `--allow-decrypt` to decrypt it locally and push it, or push an environment whose values are plaintext. penv's encryption stops at the projection; the destination seals it under its own key."
|
|
@@ -2616,7 +2323,7 @@ async function ensureTargetApproved(provider, environment, options) {
|
|
|
2616
2323
|
return false;
|
|
2617
2324
|
}
|
|
2618
2325
|
if (provider.ensureTarget === void 0) {
|
|
2619
|
-
throw new
|
|
2326
|
+
throw new PenvError8(
|
|
2620
2327
|
"MISSING_TARGET",
|
|
2621
2328
|
`The destination has no environment \`${environment}\` to receive this push, and this provider cannot create one`,
|
|
2622
2329
|
`Create the environment on the destination side, then run \`penv push --env ${environment}\` again.`
|
|
@@ -2626,7 +2333,7 @@ async function ensureTargetApproved(provider, environment, options) {
|
|
|
2626
2333
|
`The destination has no environment \`${environment}\`. Create it?`
|
|
2627
2334
|
);
|
|
2628
2335
|
if (!approved) {
|
|
2629
|
-
throw new
|
|
2336
|
+
throw new PenvError8(
|
|
2630
2337
|
"MISSING_TARGET",
|
|
2631
2338
|
`The destination has no environment \`${environment}\` to receive this push`,
|
|
2632
2339
|
`Re-run with \`--yes\` to create it, answer \`y\` at the prompt, or create it on the destination side yourself.`
|
|
@@ -3396,7 +3103,7 @@ function scannableFiles(directory, out2) {
|
|
|
3396
3103
|
return;
|
|
3397
3104
|
}
|
|
3398
3105
|
for (const entry of entries) {
|
|
3399
|
-
const path =
|
|
3106
|
+
const path = join5(directory, entry.name);
|
|
3400
3107
|
if (entry.isDirectory()) {
|
|
3401
3108
|
if (!UNSCANNED_DIRS.has(entry.name)) {
|
|
3402
3109
|
scannableFiles(path, out2);
|
|
@@ -3438,10 +3145,10 @@ function artifactFindings(project) {
|
|
|
3438
3145
|
for (const file of files) {
|
|
3439
3146
|
let text;
|
|
3440
3147
|
try {
|
|
3441
|
-
if (
|
|
3148
|
+
if (statSync(file).size > ARTIFACT_SCAN_LIMIT) {
|
|
3442
3149
|
continue;
|
|
3443
3150
|
}
|
|
3444
|
-
text =
|
|
3151
|
+
text = readFileSync5(file, "utf8");
|
|
3445
3152
|
} catch {
|
|
3446
3153
|
continue;
|
|
3447
3154
|
}
|
|
@@ -3790,7 +3497,7 @@ import {
|
|
|
3790
3497
|
formatValueFile as formatValueFile5,
|
|
3791
3498
|
isSecret as isSecret5,
|
|
3792
3499
|
openValue as openValue3,
|
|
3793
|
-
PenvError as
|
|
3500
|
+
PenvError as PenvError10,
|
|
3794
3501
|
parameterId as parameterId5,
|
|
3795
3502
|
recordPath as recordPath3,
|
|
3796
3503
|
sealValue as sealValue2
|
|
@@ -3801,7 +3508,7 @@ import { defineCommand as defineCommand9 } from "citty";
|
|
|
3801
3508
|
import {
|
|
3802
3509
|
formatValueFile as formatValueFile4,
|
|
3803
3510
|
isSecret as isSecret4,
|
|
3804
|
-
PenvError as
|
|
3511
|
+
PenvError as PenvError9,
|
|
3805
3512
|
parameterId as parameterId4,
|
|
3806
3513
|
recordPath as recordPath2,
|
|
3807
3514
|
sealValue
|
|
@@ -3825,7 +3532,7 @@ function targetScope(project, options, key) {
|
|
|
3825
3532
|
return scopeFrom(options);
|
|
3826
3533
|
}
|
|
3827
3534
|
if (environment.trim().length === 0) {
|
|
3828
|
-
throw new
|
|
3535
|
+
throw new PenvError9(
|
|
3829
3536
|
"ENVIRONMENT_FLAG_EMPTY",
|
|
3830
3537
|
`\`--env\` for parameter ${key} names no environment`,
|
|
3831
3538
|
`Pass a declared environment \u2014 ${project.config.environments.map((e) => `\`${e}\``).join(", ")} \u2014 e.g. \`--env production\`, or drop \`--env\` to write the scope that has no environment.`
|
|
@@ -3838,7 +3545,7 @@ function policyEnvironment(project, options) {
|
|
|
3838
3545
|
}
|
|
3839
3546
|
function sealFor(project, file, value, parameter, environment) {
|
|
3840
3547
|
if (environment === void 0) {
|
|
3841
|
-
throw new
|
|
3548
|
+
throw new PenvError9(
|
|
3842
3549
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
3843
3550
|
`Parameter ${parameter} is a secret, and ${recordPath2(formatValueFile4(file))} names no environment`,
|
|
3844
3551
|
"Keys are declared per environment in the `keys` block of 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."
|
|
@@ -3940,7 +3647,7 @@ function twins(project, key, options) {
|
|
|
3940
3647
|
}
|
|
3941
3648
|
function environmentFor(project, options, verb) {
|
|
3942
3649
|
if (options.environment === void 0) {
|
|
3943
|
-
throw new
|
|
3650
|
+
throw new PenvError10(
|
|
3944
3651
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
3945
3652
|
`\`penv ${verb}\` names no environment, and keys are declared per environment`,
|
|
3946
3653
|
"Pass `--env <environment>`. penv cannot tell which environment's key applies to a file that names none, and will not pick one for you."
|
|
@@ -3959,7 +3666,7 @@ async function runEncrypt(options) {
|
|
|
3959
3666
|
const value = await readOne(project, plain);
|
|
3960
3667
|
if (value === void 0) {
|
|
3961
3668
|
const already = await readOne(project, sealed);
|
|
3962
|
-
throw new
|
|
3669
|
+
throw new PenvError10(
|
|
3963
3670
|
"PARAMETER_ABSENT",
|
|
3964
3671
|
already === void 0 ? `Parameter ${parameter} has no value file at ${recordPath3(formatValueFile5(plain))}` : `Parameter ${parameter} is already encrypted at ${recordPath3(formatValueFile5(sealed))}`,
|
|
3965
3672
|
already === void 0 ? `Write it first with \`penv set ${options.key} --env ${environment}\`, which seals it automatically when the parameter's meta declares it a secret.` : "Nothing to do."
|
|
@@ -3980,7 +3687,7 @@ async function runDecrypt(options) {
|
|
|
3980
3687
|
const [plain, sealed] = twins(project, options.key, options);
|
|
3981
3688
|
const parameter = parameterId5(plain);
|
|
3982
3689
|
if (isSecret5(await project.provider.readMeta(plain), environment)) {
|
|
3983
|
-
throw new
|
|
3690
|
+
throw new PenvError10(
|
|
3984
3691
|
"SECRET_DECRYPT_REFUSED",
|
|
3985
3692
|
`Parameter ${parameter} is declared a secret for environment ${environment}, so penv will not write it in plaintext`,
|
|
3986
3693
|
"A secret with a plaintext value file is a `penv doctor` failure. Drop `secret` from the parameter's meta if it is not one, or run `penv generate --allow-decrypt` if you need the plaintext value in a `.env` artifact."
|
|
@@ -3988,7 +3695,7 @@ async function runDecrypt(options) {
|
|
|
3988
3695
|
}
|
|
3989
3696
|
const stored = await readOne(project, sealed);
|
|
3990
3697
|
if (stored === void 0) {
|
|
3991
|
-
throw new
|
|
3698
|
+
throw new PenvError10(
|
|
3992
3699
|
"PARAMETER_ABSENT",
|
|
3993
3700
|
`Parameter ${parameter} has no encrypted value file at ${recordPath3(formatValueFile5(sealed))}`,
|
|
3994
3701
|
`Nothing to decrypt. \`penv get ${options.key} --env ${environment} --explain\` shows every file penv looked at.`
|
|
@@ -4011,7 +3718,7 @@ async function runDecrypt(options) {
|
|
|
4011
3718
|
removed: formatValueFile5(sealed)
|
|
4012
3719
|
};
|
|
4013
3720
|
}
|
|
4014
|
-
var UndecryptableAt = class extends
|
|
3721
|
+
var UndecryptableAt = class extends PenvError10 {
|
|
4015
3722
|
constructor(parameter, environment, location, detail) {
|
|
4016
3723
|
super(
|
|
4017
3724
|
"VALUE_UNDECRYPTABLE",
|
|
@@ -4066,7 +3773,7 @@ var decryptCommand = defineCommand9({
|
|
|
4066
3773
|
});
|
|
4067
3774
|
|
|
4068
3775
|
// src/commands/fill.ts
|
|
4069
|
-
import { PenvError as
|
|
3776
|
+
import { PenvError as PenvError11, recordPath as recordPath4, SCHEMA_SHAPE_FILE as SCHEMA_SHAPE_FILE2 } from "@penvhq/core";
|
|
4070
3777
|
import { defineCommand as defineCommand10 } from "citty";
|
|
4071
3778
|
var BLOCKING = /* @__PURE__ */ new Set(["config", "collision", "reserved"]);
|
|
4072
3779
|
async function runFill(options) {
|
|
@@ -4081,7 +3788,7 @@ async function runFill(options) {
|
|
|
4081
3788
|
const detail = blockers.map(
|
|
4082
3789
|
(issue) => ` - ${issue.message}${issue.remedy === void 0 ? "" : ` (${issue.remedy})`}`
|
|
4083
3790
|
).join("\n");
|
|
4084
|
-
throw new
|
|
3791
|
+
throw new PenvError11(
|
|
4085
3792
|
"FILL_BLOCKED",
|
|
4086
3793
|
`penv fill cannot run: environment ${environment} has ${blockers.length} unresolved configuration ${blockers.length === 1 ? "issue" : "issues"}:
|
|
4087
3794
|
${detail}`,
|
|
@@ -4217,11 +3924,11 @@ var fillCommand = defineCommand10({
|
|
|
4217
3924
|
|
|
4218
3925
|
// src/commands/generate.ts
|
|
4219
3926
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
4220
|
-
import { isAbsolute as
|
|
3927
|
+
import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve5 } from "path";
|
|
4221
3928
|
import {
|
|
4222
3929
|
checkNameCollisions as checkNameCollisions3,
|
|
4223
3930
|
effectiveMeta as effectiveMeta2,
|
|
4224
|
-
PenvError as
|
|
3931
|
+
PenvError as PenvError12,
|
|
4225
3932
|
recordPath as recordPath5,
|
|
4226
3933
|
requireValue as requireValue2,
|
|
4227
3934
|
serializeDotenv,
|
|
@@ -4246,7 +3953,7 @@ function entriesFor(project, environment, allowDecrypt) {
|
|
|
4246
3953
|
const winner = resolution.winner;
|
|
4247
3954
|
if (winner?.file.encrypted === true) {
|
|
4248
3955
|
if (!allowDecrypt) {
|
|
4249
|
-
throw new
|
|
3956
|
+
throw new PenvError12(
|
|
4250
3957
|
"ENCRYPTED_VALUE_REFUSED",
|
|
4251
3958
|
`Parameter ${resolution.parameter} for environment ${environment} resolves to the encrypted value file ${recordPath5(winner.location)}, and \`penv generate\` writes plaintext`,
|
|
4252
3959
|
`Re-run with \`--allow-decrypt\` to write the decrypted value into the artifact, or generate for an environment whose values are plaintext. The artifact is gitignored; a committed plaintext secret is a \`penv doctor\` failure.`
|
|
@@ -4277,7 +3984,7 @@ function runGenerate(options) {
|
|
|
4277
3984
|
const project = openProject(options.cwd);
|
|
4278
3985
|
const environment = targetEnvironment(project, options.environment, options.envFlags);
|
|
4279
3986
|
const { entries, decrypted } = entriesFor(project, environment, options.allowDecrypt === true);
|
|
4280
|
-
const file = options.out === void 0 ? resolve5(project.root, DEFAULT_OUTPUT) :
|
|
3987
|
+
const file = options.out === void 0 ? resolve5(project.root, DEFAULT_OUTPUT) : isAbsolute2(options.out) ? options.out : resolve5(options.cwd, options.out);
|
|
4281
3988
|
writeFileSync3(file, serializeDotenv(entries), "utf8");
|
|
4282
3989
|
return { file, environment, entries: entries.length, decrypted };
|
|
4283
3990
|
}
|
|
@@ -4330,7 +4037,7 @@ var generateCommand = defineCommand11({
|
|
|
4330
4037
|
});
|
|
4331
4038
|
|
|
4332
4039
|
// src/commands/get.ts
|
|
4333
|
-
import { PenvError as
|
|
4040
|
+
import { PenvError as PenvError13, recordPath as recordPath6, requireValue as requireValue3, resolveParameter } from "@penvhq/core";
|
|
4334
4041
|
import { defineCommand as defineCommand12 } from "citty";
|
|
4335
4042
|
async function runGet(options) {
|
|
4336
4043
|
const project = openProject(options.cwd);
|
|
@@ -4340,7 +4047,7 @@ async function runGet(options) {
|
|
|
4340
4047
|
const resolution = await resolveParameter(ref, environment, project.provider, keys);
|
|
4341
4048
|
const value = requireValue3(resolution, environment);
|
|
4342
4049
|
if (value === void 0) {
|
|
4343
|
-
throw new
|
|
4050
|
+
throw new PenvError13(
|
|
4344
4051
|
"PARAMETER_ABSENT",
|
|
4345
4052
|
`Parameter ${resolution.parameter} resolves to no value for environment ${environment}`,
|
|
4346
4053
|
`Set it with \`penv set ${options.key} --env ${environment}\`, or run \`penv get ${options.key} --env ${environment} --explain\` to see every file penv looked at.`
|
|
@@ -4416,15 +4123,15 @@ var getCommand = defineCommand12({
|
|
|
4416
4123
|
});
|
|
4417
4124
|
|
|
4418
4125
|
// src/commands/import.ts
|
|
4419
|
-
import { copyFileSync, existsSync as
|
|
4420
|
-
import { basename as basename2, isAbsolute as
|
|
4126
|
+
import { copyFileSync, existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
|
|
4127
|
+
import { basename as basename2, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve7 } from "path";
|
|
4421
4128
|
import {
|
|
4422
4129
|
assertNever as assertNever2,
|
|
4423
4130
|
FilenameGrammarError,
|
|
4424
4131
|
findConfigFile as findConfigFile2,
|
|
4425
4132
|
loadConfigFrom as loadConfigFrom2,
|
|
4426
4133
|
lookupEnvironment,
|
|
4427
|
-
PenvError as
|
|
4134
|
+
PenvError as PenvError16,
|
|
4428
4135
|
parseDotenv as parseDotenv2,
|
|
4429
4136
|
schemaFileOf as schemaFileOf4,
|
|
4430
4137
|
UnknownEnvironmentError
|
|
@@ -4435,7 +4142,7 @@ import { defineCommand as defineCommand14 } from "citty";
|
|
|
4435
4142
|
import {
|
|
4436
4143
|
checkNameCollisions as checkNameCollisions4,
|
|
4437
4144
|
isReservedToken as isReservedToken3,
|
|
4438
|
-
PenvError as
|
|
4145
|
+
PenvError as PenvError14,
|
|
4439
4146
|
ReservedTokenError as ReservedTokenError3,
|
|
4440
4147
|
refFromVariable as refFromVariable2,
|
|
4441
4148
|
roundTripsCleanly,
|
|
@@ -4445,7 +4152,7 @@ function assertImportable(ref, variable) {
|
|
|
4445
4152
|
if (!ref.name.includes(".")) {
|
|
4446
4153
|
return;
|
|
4447
4154
|
}
|
|
4448
|
-
throw new
|
|
4155
|
+
throw new PenvError14(
|
|
4449
4156
|
"IMPORT_UNPARSEABLE_NAME",
|
|
4450
4157
|
`The variable ${variable} becomes the parameter \`${ref.name}\`, whose \`.\` would be read as a scope`,
|
|
4451
4158
|
`Filenames are split on \`.\`. Rename ${variable} in the source file, then import it again.`
|
|
@@ -4464,7 +4171,7 @@ function assertRoundTrips(ref, variable, config) {
|
|
|
4464
4171
|
return;
|
|
4465
4172
|
}
|
|
4466
4173
|
const generated = variableName7(ref, config);
|
|
4467
|
-
throw new
|
|
4174
|
+
throw new PenvError14(
|
|
4468
4175
|
"IMPORT_LOSSY_NAME",
|
|
4469
4176
|
`The variable ${variable} becomes the parameter \`${ref.name}\`, which regenerates as ${generated}`,
|
|
4470
4177
|
`\`penv generate\` would write ${generated}, so anything reading \`process.env["${variable}"]\` would read \`undefined\`. Declare the name you want in the \`override\` block of penv.config.ts \u2014 \`override: { "${ref.name}": "${variable}" }\` \u2014 then import it again. Nothing was imported.`
|
|
@@ -4505,8 +4212,8 @@ function writeEntries(tree, entries, refs, scope) {
|
|
|
4505
4212
|
}
|
|
4506
4213
|
|
|
4507
4214
|
// src/detect.ts
|
|
4508
|
-
import { existsSync as
|
|
4509
|
-
import { join as
|
|
4215
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
|
|
4216
|
+
import { join as join6 } from "path";
|
|
4510
4217
|
import { DEFAULT_SCHEMA_FILE } from "@penvhq/core";
|
|
4511
4218
|
var SIGNATURES = [
|
|
4512
4219
|
{ name: "Next.js", packages: ["next"], publicPrefixes: ["NEXT_PUBLIC_"] },
|
|
@@ -4523,7 +4230,7 @@ var SIGNATURES = [
|
|
|
4523
4230
|
function exportsSchema(file) {
|
|
4524
4231
|
let source;
|
|
4525
4232
|
try {
|
|
4526
|
-
source =
|
|
4233
|
+
source = readFileSync6(file, "utf8");
|
|
4527
4234
|
} catch {
|
|
4528
4235
|
return false;
|
|
4529
4236
|
}
|
|
@@ -4534,8 +4241,8 @@ function exportsSchema(file) {
|
|
|
4534
4241
|
);
|
|
4535
4242
|
}
|
|
4536
4243
|
function occupied(cwd, relative5) {
|
|
4537
|
-
const file =
|
|
4538
|
-
return
|
|
4244
|
+
const file = join6(cwd, ...relative5.split("/"));
|
|
4245
|
+
return existsSync4(file) && !exportsSchema(file);
|
|
4539
4246
|
}
|
|
4540
4247
|
function schemaFileFor(cwd) {
|
|
4541
4248
|
const dir = srcPrefix(cwd);
|
|
@@ -4550,10 +4257,10 @@ function schemaFileFor(cwd) {
|
|
|
4550
4257
|
return { file: DEFAULT_SCHEMA_FILE, displaced: preferred };
|
|
4551
4258
|
}
|
|
4552
4259
|
function srcPrefix(cwd) {
|
|
4553
|
-
return
|
|
4260
|
+
return existsSync4(join6(cwd, "src")) ? "src/" : "";
|
|
4554
4261
|
}
|
|
4555
4262
|
function dependenciesOf(cwd) {
|
|
4556
|
-
const manifest =
|
|
4263
|
+
const manifest = manifestOf(cwd);
|
|
4557
4264
|
if (manifest === void 0) {
|
|
4558
4265
|
return void 0;
|
|
4559
4266
|
}
|
|
@@ -4568,14 +4275,14 @@ function dependenciesOf(cwd) {
|
|
|
4568
4275
|
}
|
|
4569
4276
|
return names;
|
|
4570
4277
|
}
|
|
4571
|
-
function
|
|
4572
|
-
const file =
|
|
4573
|
-
if (!
|
|
4278
|
+
function manifestOf(cwd) {
|
|
4279
|
+
const file = join6(cwd, "package.json");
|
|
4280
|
+
if (!existsSync4(file)) {
|
|
4574
4281
|
return void 0;
|
|
4575
4282
|
}
|
|
4576
4283
|
let manifest;
|
|
4577
4284
|
try {
|
|
4578
|
-
manifest = JSON.parse(
|
|
4285
|
+
manifest = JSON.parse(readFileSync6(file, "utf8"));
|
|
4579
4286
|
} catch {
|
|
4580
4287
|
return void 0;
|
|
4581
4288
|
}
|
|
@@ -4591,7 +4298,7 @@ function detectFramework(cwd) {
|
|
|
4591
4298
|
return detectedFrom(cwd, signature.name, signature.publicPrefixes);
|
|
4592
4299
|
}
|
|
4593
4300
|
}
|
|
4594
|
-
if (
|
|
4301
|
+
if (existsSync4(join6(cwd, "bunfig.toml")) || dependencies.has("bun-types")) {
|
|
4595
4302
|
return detectedFrom(cwd, "Bun", []);
|
|
4596
4303
|
}
|
|
4597
4304
|
return void 0;
|
|
@@ -4611,7 +4318,7 @@ function detectAlias(cwd) {
|
|
|
4611
4318
|
return hasImportsBlock(cwd) ? IMPORTS_ALIAS : DEFAULT_ALIAS;
|
|
4612
4319
|
}
|
|
4613
4320
|
function hasImportsBlock(cwd) {
|
|
4614
|
-
const manifest =
|
|
4321
|
+
const manifest = manifestOf(cwd);
|
|
4615
4322
|
const imports = manifest?.imports;
|
|
4616
4323
|
return imports !== null && typeof imports === "object" && !Array.isArray(imports);
|
|
4617
4324
|
}
|
|
@@ -4670,8 +4377,8 @@ function draftFieldsAcross(sources, environments) {
|
|
|
4670
4377
|
}
|
|
4671
4378
|
|
|
4672
4379
|
// src/commands/init.ts
|
|
4673
|
-
import { existsSync as
|
|
4674
|
-
import { dirname as dirname6, join as
|
|
4380
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
4381
|
+
import { dirname as dirname6, join as join8, resolve as resolve6 } from "path";
|
|
4675
4382
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
4676
4383
|
import {
|
|
4677
4384
|
CUTOVER_PATH as CUTOVER_PATH3,
|
|
@@ -4679,7 +4386,7 @@ import {
|
|
|
4679
4386
|
isLegalEnvironmentName as isLegalEnvironmentName2,
|
|
4680
4387
|
loadConfigFrom,
|
|
4681
4388
|
PENV_DIR as PENV_DIR2,
|
|
4682
|
-
PenvError as
|
|
4389
|
+
PenvError as PenvError15,
|
|
4683
4390
|
parameterId as parameterId6,
|
|
4684
4391
|
parseDotenv,
|
|
4685
4392
|
RECORDS_PATH as RECORDS_PATH3,
|
|
@@ -4696,32 +4403,32 @@ import { defineCommand as defineCommand13 } from "citty";
|
|
|
4696
4403
|
|
|
4697
4404
|
// src/scaffold-undo.ts
|
|
4698
4405
|
import {
|
|
4699
|
-
existsSync as
|
|
4406
|
+
existsSync as existsSync5,
|
|
4700
4407
|
mkdirSync as mkdirSync3,
|
|
4701
4408
|
readdirSync as readdirSync4,
|
|
4702
|
-
readFileSync as
|
|
4409
|
+
readFileSync as readFileSync7,
|
|
4703
4410
|
rmdirSync,
|
|
4704
|
-
statSync as
|
|
4411
|
+
statSync as statSync2,
|
|
4705
4412
|
unlinkSync,
|
|
4706
4413
|
writeFileSync as writeFileSync4
|
|
4707
4414
|
} from "fs";
|
|
4708
|
-
import { dirname as dirname5, join as
|
|
4415
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
4709
4416
|
function record(path, files, dirs) {
|
|
4710
4417
|
let stats;
|
|
4711
4418
|
try {
|
|
4712
|
-
stats =
|
|
4419
|
+
stats = statSync2(path);
|
|
4713
4420
|
} catch {
|
|
4714
4421
|
return;
|
|
4715
4422
|
}
|
|
4716
4423
|
if (stats.isDirectory()) {
|
|
4717
4424
|
dirs.add(path);
|
|
4718
4425
|
for (const entry of readdirSync4(path)) {
|
|
4719
|
-
record(
|
|
4426
|
+
record(join7(path, entry), files, dirs);
|
|
4720
4427
|
}
|
|
4721
4428
|
return;
|
|
4722
4429
|
}
|
|
4723
4430
|
if (stats.isFile()) {
|
|
4724
|
-
files.set(path,
|
|
4431
|
+
files.set(path, readFileSync7(path));
|
|
4725
4432
|
}
|
|
4726
4433
|
}
|
|
4727
4434
|
function captureScaffold(root, paths) {
|
|
@@ -4729,7 +4436,7 @@ function captureScaffold(root, paths) {
|
|
|
4729
4436
|
const dirs = /* @__PURE__ */ new Set();
|
|
4730
4437
|
for (const path of paths) {
|
|
4731
4438
|
for (let dir = dirname5(path); dir.startsWith(root) && dir !== root; dir = dirname5(dir)) {
|
|
4732
|
-
if (
|
|
4439
|
+
if (existsSync5(dir)) {
|
|
4733
4440
|
dirs.add(dir);
|
|
4734
4441
|
}
|
|
4735
4442
|
}
|
|
@@ -4752,7 +4459,7 @@ function restoreScaffold(undo) {
|
|
|
4752
4459
|
function removeAdded(path, undo) {
|
|
4753
4460
|
let stats;
|
|
4754
4461
|
try {
|
|
4755
|
-
stats =
|
|
4462
|
+
stats = statSync2(path);
|
|
4756
4463
|
} catch {
|
|
4757
4464
|
return;
|
|
4758
4465
|
}
|
|
@@ -4766,7 +4473,7 @@ function removeAdded(path, undo) {
|
|
|
4766
4473
|
return;
|
|
4767
4474
|
}
|
|
4768
4475
|
for (const entry of readdirSync4(path)) {
|
|
4769
|
-
removeAdded(
|
|
4476
|
+
removeAdded(join7(path, entry), undo);
|
|
4770
4477
|
}
|
|
4771
4478
|
if (!undo.dirs.has(path) && readdirSync4(path).length === 0) {
|
|
4772
4479
|
rmdirSync(path);
|
|
@@ -4960,7 +4667,7 @@ function suggestEnvironments(root) {
|
|
|
4960
4667
|
return [...found].sort();
|
|
4961
4668
|
}
|
|
4962
4669
|
function emptyFlag(flag) {
|
|
4963
|
-
return new
|
|
4670
|
+
return new PenvError15(
|
|
4964
4671
|
"INIT_FLAG_EMPTY",
|
|
4965
4672
|
`\`--${flag}\` was given without a value`,
|
|
4966
4673
|
flag === "schema" ? `Name the module that exports the schema, e.g. \`--schema src/env.ts\`, or drop the flag to use ${DEFAULT_SCHEMA_FILE2}.` : "Name the environment, e.g. `--env production`, or drop the flag to leave the whitelist empty and declare it in penv.config.ts."
|
|
@@ -4984,8 +4691,8 @@ function configOf(decisions) {
|
|
|
4984
4691
|
return { environments: decisions.environments, providers: {}, schemaFile: decisions.schemaFile };
|
|
4985
4692
|
}
|
|
4986
4693
|
function declaredIn(root) {
|
|
4987
|
-
const file =
|
|
4988
|
-
if (!
|
|
4694
|
+
const file = join8(root, CONFIG_FILE);
|
|
4695
|
+
if (!existsSync6(file)) {
|
|
4989
4696
|
return void 0;
|
|
4990
4697
|
}
|
|
4991
4698
|
return loadConfigFrom(file);
|
|
@@ -5023,7 +4730,7 @@ function planInit(root, flags = {}) {
|
|
|
5023
4730
|
throw emptyFlag("alias");
|
|
5024
4731
|
}
|
|
5025
4732
|
if (!ALIAS_NAME.test(alias)) {
|
|
5026
|
-
throw new
|
|
4733
|
+
throw new PenvError15(
|
|
5027
4734
|
"INIT_ALIAS_INVALID",
|
|
5028
4735
|
`\`${alias}\` is not an alias penv can write`,
|
|
5029
4736
|
`An alias is \`@name\` \u2014 a tsconfig \`paths\` entry a bundler resolves \u2014 or \`#name\`, a package.json \`imports\` entry Node resolves itself. Those are the two things a module specifier can be that is not a package.`
|
|
@@ -5357,7 +5064,7 @@ ${objectIndent}${source.slice(close)}`;
|
|
|
5357
5064
|
${entryIndent}${member},${source.slice(open + 1)}`;
|
|
5358
5065
|
}
|
|
5359
5066
|
function shapeError(what, target, alias) {
|
|
5360
|
-
return new
|
|
5067
|
+
return new PenvError15(
|
|
5361
5068
|
"TSCONFIG_SHAPE",
|
|
5362
5069
|
`penv cannot add the \`${alias}\` path alias to tsconfig.json: ${what}`,
|
|
5363
5070
|
`Add it by hand: \`{ "compilerOptions": { "paths": { "${alias}": ["${target}"] } } }\`.`
|
|
@@ -5428,15 +5135,15 @@ function renderTsconfig(target, alias) {
|
|
|
5428
5135
|
}
|
|
5429
5136
|
function ensurePenvDir(root) {
|
|
5430
5137
|
const dir = resolve6(root, PENV_DIR2);
|
|
5431
|
-
if (
|
|
5138
|
+
if (existsSync6(dir)) {
|
|
5432
5139
|
return { target: "penv-dir", action: "kept", text: `Found ${PENV_DIR2}/` };
|
|
5433
5140
|
}
|
|
5434
5141
|
mkdirSync4(dir, { recursive: true });
|
|
5435
5142
|
return { target: "penv-dir", action: "created", text: `Created ${PENV_DIR2}/` };
|
|
5436
5143
|
}
|
|
5437
5144
|
function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS) {
|
|
5438
|
-
const file =
|
|
5439
|
-
if (
|
|
5145
|
+
const file = join8(root, SCHEMA_SHAPE_FILE3);
|
|
5146
|
+
if (existsSync6(file)) {
|
|
5440
5147
|
return {
|
|
5441
5148
|
target: "schema",
|
|
5442
5149
|
action: "kept",
|
|
@@ -5462,8 +5169,8 @@ function writeSchemaShapeFile(root, fields, draft, decisions = DEFAULT_DECISIONS
|
|
|
5462
5169
|
};
|
|
5463
5170
|
}
|
|
5464
5171
|
function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
|
|
5465
|
-
const file =
|
|
5466
|
-
if (
|
|
5172
|
+
const file = join8(root, ...decisions.schemaFile.split("/"));
|
|
5173
|
+
if (existsSync6(file)) {
|
|
5467
5174
|
return {
|
|
5468
5175
|
target: "env",
|
|
5469
5176
|
action: "kept",
|
|
@@ -5481,8 +5188,8 @@ function writeEnvFile(root, decisions = DEFAULT_DECISIONS) {
|
|
|
5481
5188
|
};
|
|
5482
5189
|
}
|
|
5483
5190
|
function writeConfigFile(root, decisions = DEFAULT_DECISIONS) {
|
|
5484
|
-
const file =
|
|
5485
|
-
if (
|
|
5191
|
+
const file = join8(root, CONFIG_FILE);
|
|
5192
|
+
if (existsSync6(file)) {
|
|
5486
5193
|
return { target: "config", action: "kept", text: `Kept ${CONFIG_FILE}` };
|
|
5487
5194
|
}
|
|
5488
5195
|
writeFileSync5(file, renderConfigModule(decisions), "utf8");
|
|
@@ -5491,9 +5198,9 @@ function writeConfigFile(root, decisions = DEFAULT_DECISIONS) {
|
|
|
5491
5198
|
function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
|
|
5492
5199
|
const alias = decisions.alias;
|
|
5493
5200
|
const imports = alias.startsWith(IMPORTS_PREFIX);
|
|
5494
|
-
const file =
|
|
5201
|
+
const file = join8(root, imports ? PACKAGE_FILE : TSCONFIG_FILE);
|
|
5495
5202
|
const where2 = imports ? PACKAGE_FILE : TSCONFIG_FILE;
|
|
5496
|
-
if (!
|
|
5203
|
+
if (!existsSync6(file)) {
|
|
5497
5204
|
if (imports) {
|
|
5498
5205
|
return {
|
|
5499
5206
|
target: "tsconfig",
|
|
@@ -5509,7 +5216,7 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
|
|
|
5509
5216
|
text: `Created ${TSCONFIG_FILE} with the ${alias} path alias`
|
|
5510
5217
|
};
|
|
5511
5218
|
}
|
|
5512
|
-
const source =
|
|
5219
|
+
const source = readFileSync8(file, "utf8");
|
|
5513
5220
|
const edit = imports ? insertImportsAlias(source, decisions.schemaFile, alias) : insertEnvAlias(source, decisions.schemaFile, alias);
|
|
5514
5221
|
if (edit.conflict !== void 0) {
|
|
5515
5222
|
return {
|
|
@@ -5534,9 +5241,9 @@ function writeTsconfigAlias(root, decisions = DEFAULT_DECISIONS) {
|
|
|
5534
5241
|
};
|
|
5535
5242
|
}
|
|
5536
5243
|
function writeGitignore(root, decisions = DEFAULT_DECISIONS) {
|
|
5537
|
-
const file =
|
|
5244
|
+
const file = join8(root, ...STATE_GITIGNORE_PATH.split("/"));
|
|
5538
5245
|
const wanted = renderStateGitignore(configOf(decisions));
|
|
5539
|
-
const existing =
|
|
5246
|
+
const existing = existsSync6(file) ? readFileSync8(file, "utf8") : void 0;
|
|
5540
5247
|
if (existing === wanted) {
|
|
5541
5248
|
return { target: "gitignore", action: "kept", text: `Kept ${STATE_GITIGNORE_PATH}` };
|
|
5542
5249
|
}
|
|
@@ -5557,12 +5264,12 @@ function outdatedRuntimeWarning(root) {
|
|
|
5557
5264
|
return `Injection needs @penvhq/penv ${INJECT_MIN_VERSION}+ \u2014 this project has ${version}, whose \`load\` ignores \`{ inject: true }\`. Upgrade, or process.env stays empty.`;
|
|
5558
5265
|
}
|
|
5559
5266
|
function installedPenvVersion(root) {
|
|
5560
|
-
const file =
|
|
5561
|
-
if (!
|
|
5267
|
+
const file = join8(root, "node_modules", "@penvhq", "penv", "package.json");
|
|
5268
|
+
if (!existsSync6(file)) {
|
|
5562
5269
|
return void 0;
|
|
5563
5270
|
}
|
|
5564
5271
|
try {
|
|
5565
|
-
const version = JSON.parse(
|
|
5272
|
+
const version = JSON.parse(readFileSync8(file, "utf8")).version;
|
|
5566
5273
|
return typeof version === "string" ? version : void 0;
|
|
5567
5274
|
} catch {
|
|
5568
5275
|
return void 0;
|
|
@@ -5602,11 +5309,11 @@ function writeSeam(root, decisions = DEFAULT_DECISIONS, framework = detectFramew
|
|
|
5602
5309
|
};
|
|
5603
5310
|
}
|
|
5604
5311
|
const alsoNote = writeAlso(root, seam.also);
|
|
5605
|
-
const file =
|
|
5312
|
+
const file = join8(root, ...seam.file.split("/"));
|
|
5606
5313
|
const baseNotes = [...seam.notes, ...alsoNote === void 0 ? [] : [alsoNote]];
|
|
5607
5314
|
const notes = baseNotes.length === 0 ? "" : `
|
|
5608
5315
|
${baseNotes.map((n) => ` ${n}`).join("\n")}`;
|
|
5609
|
-
if (
|
|
5316
|
+
if (existsSync6(file)) {
|
|
5610
5317
|
return {
|
|
5611
5318
|
target: "seam",
|
|
5612
5319
|
action: "info",
|
|
@@ -5627,8 +5334,8 @@ function writeAlso(root, also) {
|
|
|
5627
5334
|
if (also === void 0) {
|
|
5628
5335
|
return void 0;
|
|
5629
5336
|
}
|
|
5630
|
-
const file =
|
|
5631
|
-
if (
|
|
5337
|
+
const file = join8(root, ...also.file.split("/"));
|
|
5338
|
+
if (existsSync6(file)) {
|
|
5632
5339
|
return also.ifPresent;
|
|
5633
5340
|
}
|
|
5634
5341
|
mkdirSync4(dirname6(file), { recursive: true });
|
|
@@ -5658,7 +5365,7 @@ function scaffold(root, fields, draft, decisions = DEFAULT_DECISIONS, framework
|
|
|
5658
5365
|
return seam === void 0 ? steps : [...steps, seam];
|
|
5659
5366
|
}
|
|
5660
5367
|
function scaffoldPaths(root, decisions = DEFAULT_DECISIONS, framework = detectFramework(root)?.name) {
|
|
5661
|
-
const fileAt = (relative5) =>
|
|
5368
|
+
const fileAt = (relative5) => join8(root, ...relative5.split("/"));
|
|
5662
5369
|
const seam = decisions.inject ? seamFor(framework, {
|
|
5663
5370
|
alias: decisions.alias,
|
|
5664
5371
|
srcDir: srcPrefix(root),
|
|
@@ -5699,7 +5406,7 @@ function planCutover(input) {
|
|
|
5699
5406
|
assertBundleResolved(root);
|
|
5700
5407
|
const selected = [...input.selected];
|
|
5701
5408
|
if (selected.length === 0) {
|
|
5702
|
-
throw new
|
|
5409
|
+
throw new PenvError15(
|
|
5703
5410
|
"INIT_NOTHING_SELECTED",
|
|
5704
5411
|
"No dotenv file was selected, so there is nothing to migrate",
|
|
5705
5412
|
"Run `penv init` again and choose the files penv should adopt."
|
|
@@ -5711,7 +5418,7 @@ function planCutover(input) {
|
|
|
5711
5418
|
const environments = declared === void 0 ? chosen : declared.environments;
|
|
5712
5419
|
for (const environment of chosen) {
|
|
5713
5420
|
if (!environments.includes(environment)) {
|
|
5714
|
-
throw new
|
|
5421
|
+
throw new PenvError15(
|
|
5715
5422
|
"INIT_ENVIRONMENT_UNDECLARED",
|
|
5716
5423
|
`${CONFIG_FILE} declares ${describeList(environments)}, and adopting these files needs \`${environment}\` declared too`,
|
|
5717
5424
|
`Add \`${environment}\` to \`environments\` in ${CONFIG_FILE}, with a provider for it, then run \`penv init\` again. Nothing was changed.`
|
|
@@ -5736,7 +5443,7 @@ function planCutover(input) {
|
|
|
5736
5443
|
const diagnostics = [];
|
|
5737
5444
|
let variables = 0;
|
|
5738
5445
|
for (const file of selected) {
|
|
5739
|
-
const parsed = parseDotenv(
|
|
5446
|
+
const parsed = parseDotenv(readFileSync8(join8(root, file.name), "utf8"));
|
|
5740
5447
|
const fileRefs = refsForEntries(parsed.entries, file.name, config);
|
|
5741
5448
|
adopted.push({ file, entries: parsed.entries, refs: fileRefs, scope: scopeOf(file) });
|
|
5742
5449
|
refs.push(...fileRefs);
|
|
@@ -5798,7 +5505,7 @@ function requireEnvironment(input) {
|
|
|
5798
5505
|
if (environment.length > 0) {
|
|
5799
5506
|
return environment;
|
|
5800
5507
|
}
|
|
5801
|
-
throw new
|
|
5508
|
+
throw new PenvError15(
|
|
5802
5509
|
"INIT_ENVIRONMENT_UNNAMED",
|
|
5803
5510
|
"The selected files name no environment, and penv does not invent one",
|
|
5804
5511
|
`Run \`penv init --env ${DEVELOPMENT}\` to say which environment these values are for.`
|
|
@@ -5810,7 +5517,7 @@ function assertCascadeComplete(root, selected, environments) {
|
|
|
5810
5517
|
for (const environment of environments) {
|
|
5811
5518
|
for (const name of cascadeFor(environment)) {
|
|
5812
5519
|
if (present.has(name) && !taken.has(name)) {
|
|
5813
|
-
throw new
|
|
5520
|
+
throw new PenvError15(
|
|
5814
5521
|
"INIT_CUTOVER_INCOMPLETE",
|
|
5815
5522
|
`${name} is part of ${environment}'s cascade and was not selected, so your framework would keep reading it beside penv`,
|
|
5816
5523
|
"Run `penv init` again and take every file penv listed for that environment. Nothing was changed."
|
|
@@ -5823,7 +5530,7 @@ function assertBundleResolved(root) {
|
|
|
5823
5530
|
if (!bundleUnresolved(root)) {
|
|
5824
5531
|
return;
|
|
5825
5532
|
}
|
|
5826
|
-
throw new
|
|
5533
|
+
throw new PenvError15(
|
|
5827
5534
|
"INIT_BUNDLE_UNRESOLVED",
|
|
5828
5535
|
`The dotenv files from the last cutover are still in ${ROLLBACK_DOTENV_PATH3}/, and penv will not migrate a second time over them`,
|
|
5829
5536
|
"Run `penv init undo` to put them back, or `penv cleanup` to drop them once you are happy with the migration."
|
|
@@ -5835,7 +5542,7 @@ function selectionForYes(plan2) {
|
|
|
5835
5542
|
(file) => file.environment !== void 0 && file.environment !== DEVELOPMENT
|
|
5836
5543
|
);
|
|
5837
5544
|
if (shared !== void 0 && other !== void 0) {
|
|
5838
|
-
throw new
|
|
5545
|
+
throw new PenvError15(
|
|
5839
5546
|
"INIT_YES_SHARED_FALLBACK",
|
|
5840
5547
|
`${other.name} falls back to the shared ${shared.name} this cutover would move, so \`--yes\` will not decide what happens to ${other.environment ?? ""}`,
|
|
5841
5548
|
"Run `penv init` without `--yes` and choose every file the cutover takes. Nothing was changed."
|
|
@@ -5881,7 +5588,7 @@ function issueLines(result2) {
|
|
|
5881
5588
|
}
|
|
5882
5589
|
var SCAFFOLD_ROLLED_BACK = "penv put the project back as it found it \u2014 your dotenv files and everything else are exactly where they were.";
|
|
5883
5590
|
function invalidAfterImport(result2) {
|
|
5884
|
-
return new
|
|
5591
|
+
return new PenvError15(
|
|
5885
5592
|
"INIT_CUTOVER_INVALID",
|
|
5886
5593
|
`The imported values do not satisfy the draft schema for ${result2.environment}:
|
|
5887
5594
|
${issueLines(result2)}`,
|
|
@@ -5889,7 +5596,7 @@ ${issueLines(result2)}`,
|
|
|
5889
5596
|
);
|
|
5890
5597
|
}
|
|
5891
5598
|
function draftNotLoaded(result2) {
|
|
5892
|
-
return new
|
|
5599
|
+
return new PenvError15(
|
|
5893
5600
|
"INIT_DRAFT_NOT_LOADED",
|
|
5894
5601
|
`penv could not load the schema it drafted for ${result2.environment}, so nothing was checked against it:
|
|
5895
5602
|
${issueLines(result2)}`,
|
|
@@ -5956,7 +5663,11 @@ function renderCutover(result2) {
|
|
|
5956
5663
|
const glyph = step.action === "conflicted" ? WARN : step.action === "info" ? "\u2192" : CHECK;
|
|
5957
5664
|
return step.note === void 0 ? { glyph, text: step.text } : { glyph, text: step.text, note: step.note };
|
|
5958
5665
|
}),
|
|
5959
|
-
...plan2.install
|
|
5666
|
+
...installedPackages(plan2.install).map((entry) => ({
|
|
5667
|
+
glyph: CHECK,
|
|
5668
|
+
text: `Installed ${entry.name}`,
|
|
5669
|
+
note: entry.version
|
|
5670
|
+
})),
|
|
5960
5671
|
{
|
|
5961
5672
|
glyph: CHECK,
|
|
5962
5673
|
text: `Imported ${plan2.fields.length} parameters`,
|
|
@@ -5980,12 +5691,12 @@ function dailyCommand(root) {
|
|
|
5980
5691
|
return `penv run -- ${detectPackageManager(root)} ${devScript(root)}`;
|
|
5981
5692
|
}
|
|
5982
5693
|
function devScript(root) {
|
|
5983
|
-
const file =
|
|
5984
|
-
if (!
|
|
5694
|
+
const file = join8(root, PACKAGE_FILE);
|
|
5695
|
+
if (!existsSync6(file)) {
|
|
5985
5696
|
return "dev";
|
|
5986
5697
|
}
|
|
5987
5698
|
try {
|
|
5988
|
-
const scripts = JSON.parse(
|
|
5699
|
+
const scripts = JSON.parse(readFileSync8(file, "utf8")).scripts;
|
|
5989
5700
|
if (scripts !== null && typeof scripts === "object" && !Array.isArray(scripts)) {
|
|
5990
5701
|
const named = Object.keys(scripts);
|
|
5991
5702
|
return ["dev", "start"].find((script) => named.includes(script)) ?? named[0] ?? "dev";
|
|
@@ -6029,7 +5740,7 @@ async function promptForSelection(plan2, io) {
|
|
|
6029
5740
|
for (const name of names) {
|
|
6030
5741
|
const file = plan2.found.find((candidate) => candidate.name === name);
|
|
6031
5742
|
if (file === void 0) {
|
|
6032
|
-
throw new
|
|
5743
|
+
throw new PenvError15(
|
|
6033
5744
|
"INIT_SELECTION_UNKNOWN",
|
|
6034
5745
|
`\`${name}\` is not one of the dotenv files penv found`,
|
|
6035
5746
|
"Run `penv init` again and name the files exactly as they are listed, e.g. `.env.production`."
|
|
@@ -6192,7 +5903,7 @@ var initCommand = defineCommand13({
|
|
|
6192
5903
|
});
|
|
6193
5904
|
function runUndoAction(root, action) {
|
|
6194
5905
|
if (action !== "undo") {
|
|
6195
|
-
throw new
|
|
5906
|
+
throw new PenvError15(
|
|
6196
5907
|
"INIT_UNKNOWN_ACTION",
|
|
6197
5908
|
`\`penv init ${action}\` is not something init does`,
|
|
6198
5909
|
"Run `penv init undo` to put back the dotenv files of the last cutover."
|
|
@@ -6298,7 +6009,7 @@ function explicitEnvironment(options, source, config) {
|
|
|
6298
6009
|
if (value.length > 0) {
|
|
6299
6010
|
return value;
|
|
6300
6011
|
}
|
|
6301
|
-
throw new
|
|
6012
|
+
throw new PenvError16(
|
|
6302
6013
|
"IMPORT_ENV_FLAG_EMPTY",
|
|
6303
6014
|
`\`--env\` for the import of ${source} names no environment`,
|
|
6304
6015
|
`Pass a declared environment \u2014 ${config.environments.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.`
|
|
@@ -6308,7 +6019,7 @@ function assertEnvironmentAgrees(derived, explicit, source) {
|
|
|
6308
6019
|
if (derived === void 0 || explicit === void 0 || derived === explicit) {
|
|
6309
6020
|
return;
|
|
6310
6021
|
}
|
|
6311
|
-
throw new
|
|
6022
|
+
throw new PenvError16(
|
|
6312
6023
|
"IMPORT_ENV_CONFLICT",
|
|
6313
6024
|
`The file ${source} is scoped to environment ${derived}, but \`--env ${explicit}\` names ${explicit}`,
|
|
6314
6025
|
`Drop \`--env\` to import ${source} as ${derived}, pass \`--env ${derived}\` to say the same thing twice, or point \`penv import\` at the file that holds ${explicit}'s values. Nothing was imported.`
|
|
@@ -6353,15 +6064,15 @@ function configInEffect(cwd, environment) {
|
|
|
6353
6064
|
}
|
|
6354
6065
|
function importDotenv(options) {
|
|
6355
6066
|
const cwd = resolve7(options.cwd);
|
|
6356
|
-
const file =
|
|
6357
|
-
if (!
|
|
6358
|
-
throw new
|
|
6067
|
+
const file = isAbsolute3(options.file) ? options.file : resolve7(cwd, options.file);
|
|
6068
|
+
if (!existsSync7(file)) {
|
|
6069
|
+
throw new PenvError16(
|
|
6359
6070
|
"IMPORT_FILE_MISSING",
|
|
6360
6071
|
`There is no file at ${file} to import`,
|
|
6361
6072
|
"Point `penv import` at an existing dotenv file, e.g. `penv import .env`."
|
|
6362
6073
|
);
|
|
6363
6074
|
}
|
|
6364
|
-
const parsed = parseDotenv2(
|
|
6075
|
+
const parsed = parseDotenv2(readFileSync9(file, "utf8"));
|
|
6365
6076
|
const { config, decisions } = configInEffect(cwd, environmentNamed(file, options.environment));
|
|
6366
6077
|
const source = displayPath3(cwd, file);
|
|
6367
6078
|
const named = scopeFromFilename(file, config);
|
|
@@ -6479,19 +6190,19 @@ var importCommand = defineCommand14({
|
|
|
6479
6190
|
|
|
6480
6191
|
// src/commands/key.ts
|
|
6481
6192
|
import { randomBytes } from "crypto";
|
|
6482
|
-
import { KEY_BYTES, KEYCHAIN_SERVICE, PenvError as
|
|
6193
|
+
import { KEY_BYTES, KEYCHAIN_SERVICE, PenvError as PenvError18 } from "@penvhq/core";
|
|
6483
6194
|
import { defineCommand as defineCommand15 } from "citty";
|
|
6484
6195
|
|
|
6485
6196
|
// src/keychain.ts
|
|
6486
6197
|
import { createRequire as createRequire3 } from "module";
|
|
6487
|
-
import { PenvError as
|
|
6198
|
+
import { PenvError as PenvError17 } from "@penvhq/core";
|
|
6488
6199
|
var KEYRING_MODULE = "@napi-rs/keyring";
|
|
6489
6200
|
var nodeRequire = (id) => createRequire3(import.meta.url)(id);
|
|
6490
6201
|
function firstLine2(cause) {
|
|
6491
6202
|
return (cause instanceof Error ? cause.message : String(cause)).split("\n")[0] ?? "";
|
|
6492
6203
|
}
|
|
6493
6204
|
function keyringMissing(cause) {
|
|
6494
|
-
return new
|
|
6205
|
+
return new PenvError17(
|
|
6495
6206
|
"KEYCHAIN_BINDING_MISSING",
|
|
6496
6207
|
`penv could not load ${KEYRING_MODULE}, the native binding it reads your OS keychain through`,
|
|
6497
6208
|
`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)}`
|
|
@@ -6533,7 +6244,7 @@ function runKeyCreate(options) {
|
|
|
6533
6244
|
const environment = targetEnvironment(project, options.environment);
|
|
6534
6245
|
const declared = project.config.keys?.[environment];
|
|
6535
6246
|
if (declared === void 0) {
|
|
6536
|
-
throw new
|
|
6247
|
+
throw new PenvError18(
|
|
6537
6248
|
"KEY_SOURCE_UNDECLARED",
|
|
6538
6249
|
`Environment ${environment} declares no key source, so penv does not know what a key for it would be`,
|
|
6539
6250
|
`Add a \`keys\` entry to penv.config.ts \u2014 e.g. \`keys: { ${environment}: { source: "env", id: "${environment}" } }\` \u2014 then run this again.`
|
|
@@ -6547,17 +6258,17 @@ function runKeyCreate(options) {
|
|
|
6547
6258
|
try {
|
|
6548
6259
|
existing = keychain.getPassword(KEYCHAIN_SERVICE, declared.id);
|
|
6549
6260
|
} catch (cause) {
|
|
6550
|
-
if (cause instanceof
|
|
6261
|
+
if (cause instanceof PenvError18) {
|
|
6551
6262
|
throw cause;
|
|
6552
6263
|
}
|
|
6553
|
-
throw new
|
|
6264
|
+
throw new PenvError18(
|
|
6554
6265
|
"KEYCHAIN_UNAVAILABLE",
|
|
6555
6266
|
`penv could not read your OS keychain to check for an existing key \`${declared.id}\``,
|
|
6556
6267
|
`Unlock your keychain and run this again. Original error: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
6557
6268
|
);
|
|
6558
6269
|
}
|
|
6559
6270
|
if (existing !== null) {
|
|
6560
|
-
throw new
|
|
6271
|
+
throw new PenvError18(
|
|
6561
6272
|
"KEY_EXISTS",
|
|
6562
6273
|
`Environment ${environment} already has a key \`${declared.id}\` in your OS keychain`,
|
|
6563
6274
|
"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."
|
|
@@ -6712,21 +6423,21 @@ var listCommand = defineCommand16({
|
|
|
6712
6423
|
|
|
6713
6424
|
// src/commands/migrate.ts
|
|
6714
6425
|
import {
|
|
6715
|
-
existsSync as
|
|
6426
|
+
existsSync as existsSync8,
|
|
6716
6427
|
mkdirSync as mkdirSync5,
|
|
6717
6428
|
readdirSync as readdirSync6,
|
|
6718
|
-
readFileSync as
|
|
6429
|
+
readFileSync as readFileSync10,
|
|
6719
6430
|
renameSync as renameSync2,
|
|
6720
6431
|
rmSync as rmSync2,
|
|
6721
6432
|
writeFileSync as writeFileSync6
|
|
6722
6433
|
} from "fs";
|
|
6723
|
-
import { dirname as dirname7, join as
|
|
6434
|
+
import { dirname as dirname7, join as join9 } from "path";
|
|
6724
6435
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
6725
6436
|
import {
|
|
6726
6437
|
loadConfig as loadConfig2,
|
|
6727
6438
|
oldLayoutEntries,
|
|
6728
6439
|
PENV_DIR as PENV_DIR3,
|
|
6729
|
-
PenvError as
|
|
6440
|
+
PenvError as PenvError19,
|
|
6730
6441
|
RECORDS_PATH as RECORDS_PATH5,
|
|
6731
6442
|
recordsDir as recordsDir3,
|
|
6732
6443
|
renderStateGitignore as renderStateGitignore2,
|
|
@@ -6741,17 +6452,17 @@ function planMigrate(cwd) {
|
|
|
6741
6452
|
const tree = recordsDir3(root);
|
|
6742
6453
|
const collisions = collidingEntries(entries, tree);
|
|
6743
6454
|
if (collisions.length > 0) {
|
|
6744
|
-
throw new
|
|
6455
|
+
throw new PenvError19(
|
|
6745
6456
|
"HALF_MIGRATED",
|
|
6746
|
-
`${
|
|
6457
|
+
`${describe(collisions)} in both \`${PENV_DIR3}/\` and \`${RECORDS_PATH5}/\`, and penv cannot tell which copy is current`,
|
|
6747
6458
|
`Move what is left under \`${PENV_DIR3}/\` into \`${RECORDS_PATH5}/\` yourself, keeping the copy you want, then run \`penv validate\`.`
|
|
6748
6459
|
);
|
|
6749
6460
|
}
|
|
6750
6461
|
const creates = [];
|
|
6751
|
-
if (entries.length > 0 && !
|
|
6462
|
+
if (entries.length > 0 && !existsSync8(tree)) {
|
|
6752
6463
|
creates.push(`${RECORDS_PATH5}/`);
|
|
6753
6464
|
}
|
|
6754
|
-
if (readIfPresent(
|
|
6465
|
+
if (readIfPresent(join9(root, ...STATE_GITIGNORE_PATH2.split("/"))) !== renderStateGitignore2(config)) {
|
|
6755
6466
|
creates.push(STATE_GITIGNORE_PATH2);
|
|
6756
6467
|
}
|
|
6757
6468
|
return {
|
|
@@ -6761,11 +6472,11 @@ function planMigrate(cwd) {
|
|
|
6761
6472
|
to: `${RECORDS_PATH5}/${entry}`
|
|
6762
6473
|
})),
|
|
6763
6474
|
creates,
|
|
6764
|
-
removes:
|
|
6475
|
+
removes: existsSync8(join9(root, ...OLD_GITIGNORE.split("/"))) ? [OLD_GITIGNORE] : []
|
|
6765
6476
|
};
|
|
6766
6477
|
}
|
|
6767
6478
|
function readIfPresent(file) {
|
|
6768
|
-
return
|
|
6479
|
+
return existsSync8(file) ? readFileSync10(file, "utf8") : void 0;
|
|
6769
6480
|
}
|
|
6770
6481
|
function collidingEntries(entries, tree) {
|
|
6771
6482
|
let held;
|
|
@@ -6777,7 +6488,7 @@ function collidingEntries(entries, tree) {
|
|
|
6777
6488
|
const taken = new Set(held.map((name) => name.toLowerCase()));
|
|
6778
6489
|
return entries.filter((entry) => taken.has(entry.toLowerCase())).sort();
|
|
6779
6490
|
}
|
|
6780
|
-
function
|
|
6491
|
+
function describe(names) {
|
|
6781
6492
|
return names.length === 1 ? `\`${names[0]}\` is` : `${names.map((name) => `\`${name}\``).join(", ")} are`;
|
|
6782
6493
|
}
|
|
6783
6494
|
function isNoop(plan2) {
|
|
@@ -6791,16 +6502,16 @@ function applyMigrate(plan2) {
|
|
|
6791
6502
|
if (plan2.moves.length > 0) {
|
|
6792
6503
|
mkdirSync5(recordsDir3(plan2.root), { recursive: true });
|
|
6793
6504
|
for (const move of plan2.moves) {
|
|
6794
|
-
renameSync2(
|
|
6505
|
+
renameSync2(join9(plan2.root, ...move.from.split("/")), join9(plan2.root, ...move.to.split("/")));
|
|
6795
6506
|
}
|
|
6796
6507
|
}
|
|
6797
6508
|
if (plan2.creates.includes(STATE_GITIGNORE_PATH2)) {
|
|
6798
|
-
const ignore =
|
|
6509
|
+
const ignore = join9(plan2.root, ...STATE_GITIGNORE_PATH2.split("/"));
|
|
6799
6510
|
mkdirSync5(dirname7(ignore), { recursive: true });
|
|
6800
6511
|
writeFileSync6(ignore, renderStateGitignore2(config), "utf8");
|
|
6801
6512
|
}
|
|
6802
6513
|
for (const removed of plan2.removes) {
|
|
6803
|
-
rmSync2(
|
|
6514
|
+
rmSync2(join9(plan2.root, ...removed.split("/")), { force: true });
|
|
6804
6515
|
}
|
|
6805
6516
|
return { ...plan2, status: "migrated" };
|
|
6806
6517
|
}
|
|
@@ -6882,7 +6593,7 @@ import {
|
|
|
6882
6593
|
formatMetaFile,
|
|
6883
6594
|
formatValueFile as formatValueFile6,
|
|
6884
6595
|
openValue as openValue4,
|
|
6885
|
-
PenvError as
|
|
6596
|
+
PenvError as PenvError20,
|
|
6886
6597
|
parameterId as parameterId7,
|
|
6887
6598
|
recordPath as recordPath7,
|
|
6888
6599
|
sealValue as sealValue3
|
|
@@ -6902,7 +6613,7 @@ async function planFile(project, source, target, parameter) {
|
|
|
6902
6613
|
}
|
|
6903
6614
|
const environment = environmentOf2(source);
|
|
6904
6615
|
if (environment === void 0) {
|
|
6905
|
-
throw new
|
|
6616
|
+
throw new PenvError20(
|
|
6906
6617
|
"SECRET_SCOPE_AMBIGUOUS",
|
|
6907
6618
|
`${recordPath7(formatValueFile6(source))} is encrypted at a scope that names no environment, so penv cannot tell which key would re-seal it`,
|
|
6908
6619
|
"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."
|
|
@@ -6911,7 +6622,7 @@ async function planFile(project, source, target, parameter) {
|
|
|
6911
6622
|
const keys = keySourceFor(project, environment);
|
|
6912
6623
|
const opened = openValue4(source, stored, keys);
|
|
6913
6624
|
if (opened.kind === "failed") {
|
|
6914
|
-
throw new
|
|
6625
|
+
throw new PenvError20(
|
|
6915
6626
|
"VALUE_UNDECRYPTABLE",
|
|
6916
6627
|
`${recordPath7(formatValueFile6(source))} could not be decrypted, so penv cannot re-seal it at its new address: ${opened.failure.detail}`,
|
|
6917
6628
|
"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."
|
|
@@ -6934,7 +6645,7 @@ async function runMove(options) {
|
|
|
6934
6645
|
assertWritableKey(options.to);
|
|
6935
6646
|
const to = refFromKey(options.to, project.config);
|
|
6936
6647
|
if (parameterId7(from) === parameterId7(to)) {
|
|
6937
|
-
throw new
|
|
6648
|
+
throw new PenvError20(
|
|
6938
6649
|
"PARAMETER_UNCHANGED",
|
|
6939
6650
|
`\`${options.from}\` and \`${options.to}\` are the same parameter`,
|
|
6940
6651
|
"Name a different destination, e.g. `penv mv redis-password redis/password`."
|
|
@@ -6944,7 +6655,7 @@ async function runMove(options) {
|
|
|
6944
6655
|
const sources = filesOf(all, from);
|
|
6945
6656
|
const meta = await project.provider.readMeta(from);
|
|
6946
6657
|
if (sources.length === 0 && meta === void 0) {
|
|
6947
|
-
throw new
|
|
6658
|
+
throw new PenvError20(
|
|
6948
6659
|
"PARAMETER_ABSENT",
|
|
6949
6660
|
`Parameter ${parameterId7(from)} has no value files and no meta, so there is nothing to move`,
|
|
6950
6661
|
`\`penv list\` shows every parameter penv holds.`
|
|
@@ -6952,7 +6663,7 @@ async function runMove(options) {
|
|
|
6952
6663
|
}
|
|
6953
6664
|
const occupied2 = filesOf(all, to);
|
|
6954
6665
|
if (occupied2.length > 0 || await project.provider.readMeta(to) !== void 0) {
|
|
6955
|
-
throw new
|
|
6666
|
+
throw new PenvError20(
|
|
6956
6667
|
"PARAMETER_EXISTS",
|
|
6957
6668
|
`Parameter ${parameterId7(to)} already exists, and penv will not merge two parameters into one`,
|
|
6958
6669
|
`Remove or rename ${parameterId7(to)} first. \`penv get ${options.to} --explain\` shows every file it holds.`
|
|
@@ -7111,7 +6822,7 @@ import {
|
|
|
7111
6822
|
beginRotation,
|
|
7112
6823
|
completeRotation,
|
|
7113
6824
|
holdsRecords as holdsRecords2,
|
|
7114
|
-
PenvError as
|
|
6825
|
+
PenvError as PenvError21,
|
|
7115
6826
|
retainsPrevious,
|
|
7116
6827
|
rotationOf as rotationOf2
|
|
7117
6828
|
} from "@penvhq/core";
|
|
@@ -7140,7 +6851,7 @@ async function writeRotatedValue(project, provider, ref, environment, value) {
|
|
|
7140
6851
|
}
|
|
7141
6852
|
function requireNewValue(value, phase, key) {
|
|
7142
6853
|
if (value === void 0) {
|
|
7143
|
-
throw new
|
|
6854
|
+
throw new PenvError21(
|
|
7144
6855
|
"ROTATION_NO_VALUE",
|
|
7145
6856
|
`A ${phase} rotation of ${key} writes a new value, and none was given`,
|
|
7146
6857
|
"Pass the new value as the argument \u2014 `penv rotate <key> <value>` \u2014 or pipe it in on stdin."
|
|
@@ -7150,7 +6861,7 @@ function requireNewValue(value, phase, key) {
|
|
|
7150
6861
|
}
|
|
7151
6862
|
function requireRetaining(provider, environment) {
|
|
7152
6863
|
if (!retainsPrevious(provider)) {
|
|
7153
|
-
throw new
|
|
6864
|
+
throw new PenvError21(
|
|
7154
6865
|
"ROTATION_NOT_RETAINING",
|
|
7155
6866
|
`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`,
|
|
7156
6867
|
"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."
|
|
@@ -7164,7 +6875,7 @@ async function runRotate(options) {
|
|
|
7164
6875
|
const ref = refFromKey(options.key, project.config);
|
|
7165
6876
|
const source = await sourceProviderFor(project, environment);
|
|
7166
6877
|
if (!holdsRecords2(source)) {
|
|
7167
|
-
throw new
|
|
6878
|
+
throw new PenvError21(
|
|
7168
6879
|
"ROTATION_NOT_RECORDS",
|
|
7169
6880
|
`Environment ${environment} is backed by \`${source.type}\`, which holds a resolved projection penv cannot rotate in place`,
|
|
7170
6881
|
"Rotate the parameter in the environment that holds the records (the local tree, Vault, SSM), then `penv push` the result to this destination."
|
|
@@ -7175,7 +6886,7 @@ async function runRotate(options) {
|
|
|
7175
6886
|
const before = await provider.readMeta(ref);
|
|
7176
6887
|
const { mechanism } = rotationOf2(before, environment);
|
|
7177
6888
|
if (mechanism === void 0) {
|
|
7178
|
-
throw new
|
|
6889
|
+
throw new PenvError21(
|
|
7179
6890
|
"ROTATION_NO_MECHANISM",
|
|
7180
6891
|
`Parameter ${options.key} declares no rotation mechanism for environment ${environment}, so penv does not know how to rotate it`,
|
|
7181
6892
|
'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.'
|
|
@@ -7185,7 +6896,7 @@ async function runRotate(options) {
|
|
|
7185
6896
|
const complete = options.complete === true;
|
|
7186
6897
|
if (mechanism === "atomic-cutover") {
|
|
7187
6898
|
if (begin || complete) {
|
|
7188
|
-
throw new
|
|
6899
|
+
throw new PenvError21(
|
|
7189
6900
|
"ROTATION_MECHANISM_MISMATCH",
|
|
7190
6901
|
`Parameter ${options.key} is atomic-cutover, which flips in one step, so \`--begin\`/\`--complete\` do not apply`,
|
|
7191
6902
|
"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."
|
|
@@ -7199,7 +6910,7 @@ async function runRotate(options) {
|
|
|
7199
6910
|
}
|
|
7200
6911
|
const retaining = requireRetaining(provider, environment);
|
|
7201
6912
|
if (begin === complete) {
|
|
7202
|
-
throw new
|
|
6913
|
+
throw new PenvError21(
|
|
7203
6914
|
"ROTATION_PHASE_REQUIRED",
|
|
7204
6915
|
`A dual-valid rotation of ${options.key} needs exactly one of \`--begin\` or \`--complete\``,
|
|
7205
6916
|
"`--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."
|
|
@@ -7302,7 +7013,7 @@ var rotateCommand = defineCommand20({
|
|
|
7302
7013
|
});
|
|
7303
7014
|
|
|
7304
7015
|
// src/commands/watch.ts
|
|
7305
|
-
import { existsSync as
|
|
7016
|
+
import { existsSync as existsSync9, watch as watch2 } from "fs";
|
|
7306
7017
|
import { basename as basename3, dirname as dirname8, resolve as resolve8 } from "path";
|
|
7307
7018
|
import { SCHEMA_SHAPE_FILE as SCHEMA_SHAPE_FILE4, schemaFileOf as schemaFileOf5, schemaInsideTree } from "@penvhq/core";
|
|
7308
7019
|
import { defineCommand as defineCommand21 } from "citty";
|
|
@@ -7370,14 +7081,14 @@ function runWatch(options) {
|
|
|
7370
7081
|
if (closed || recovery === void 0) {
|
|
7371
7082
|
return;
|
|
7372
7083
|
}
|
|
7373
|
-
if (!
|
|
7084
|
+
if (!existsSync9(parent)) {
|
|
7374
7085
|
stop2(recovery);
|
|
7375
7086
|
return;
|
|
7376
7087
|
}
|
|
7377
7088
|
if (filename !== null && basename3(filename) !== name) {
|
|
7378
7089
|
return;
|
|
7379
7090
|
}
|
|
7380
|
-
if (!
|
|
7091
|
+
if (!existsSync9(target)) {
|
|
7381
7092
|
return;
|
|
7382
7093
|
}
|
|
7383
7094
|
stop2(recovery);
|
|
@@ -7401,7 +7112,7 @@ function runWatch(options) {
|
|
|
7401
7112
|
if (closed) {
|
|
7402
7113
|
return;
|
|
7403
7114
|
}
|
|
7404
|
-
if (!
|
|
7115
|
+
if (!existsSync9(target)) {
|
|
7405
7116
|
if (watcher !== void 0) {
|
|
7406
7117
|
stop2(watcher);
|
|
7407
7118
|
}
|