getmyenv 0.9.2 → 0.9.4
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/README.md +1 -1
- package/dist/bin.js +2 -1
- package/dist/commands/import.js +1 -1
- package/dist/commands/run.js +2 -2
- package/dist/lib/context.js +2 -2
- package/dist/lib/run-env.d.ts +1 -37
- package/dist/lib/run-env.js +3 -88
- package/dist/lib/vault.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Prompts and status lines go to stderr, so `export --stdout > .env` stays clean.
|
|
|
35
35
|
Commit `.getmyenv/project.json`. `token.json` and `claim.json` stay local and are gitignored.
|
|
36
36
|
|
|
37
37
|
<!-- cli:help -->
|
|
38
|
-
Output of `npx getmyenv --help` (0.9.
|
|
38
|
+
Output of `npx getmyenv --help` (0.9.4):
|
|
39
39
|
|
|
40
40
|
```text
|
|
41
41
|
Usage: getmyenv [options] [command]
|
package/dist/bin.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { formatCliError } from "@getmyenv/shared";
|
|
2
3
|
import { runCli } from "./cli.js";
|
|
3
4
|
import { maybeSuggestUpdate } from "./lib/update-check.js";
|
|
4
5
|
async function main() {
|
|
@@ -8,7 +9,7 @@ async function main() {
|
|
|
8
9
|
main().catch((err) => {
|
|
9
10
|
const message = err instanceof Error ? err.message : "Unexpected error";
|
|
10
11
|
// Never print secret values - sanitize to message only
|
|
11
|
-
console.error(
|
|
12
|
+
console.error(formatCliError(message));
|
|
12
13
|
// Not process.exit(): on Windows it can abort while sockets are still closing.
|
|
13
14
|
process.exitCode = process.exitCode ? Number(process.exitCode) : 1;
|
|
14
15
|
});
|
package/dist/commands/import.js
CHANGED
|
@@ -44,7 +44,7 @@ async function ensureTargetContext(linked, slug, opts) {
|
|
|
44
44
|
if (found)
|
|
45
45
|
return found;
|
|
46
46
|
if (linked.who.isGuest || linked.who.token.kind !== "folder") {
|
|
47
|
-
const why = linked.who.isGuest ?
|
|
47
|
+
const why = linked.who.isGuest ? FACTS.guestContexts : "Server tokens are read-only.";
|
|
48
48
|
console.error(pc.yellow(`Skipped ${slug}: no such context. ${why}`));
|
|
49
49
|
return null;
|
|
50
50
|
}
|
package/dist/commands/run.js
CHANGED
|
@@ -6,7 +6,7 @@ import { ensureTokenAndLink } from "../lib/bootstrap.js";
|
|
|
6
6
|
import { resolveContext } from "../lib/context.js";
|
|
7
7
|
import { resolveValues } from "../lib/resolve.js";
|
|
8
8
|
import { contextKey } from "../lib/vault.js";
|
|
9
|
-
import { assertVariableNames, explainLines, mergeEnv, signalExitCode, validationError, windowsCommandLine, } from "../lib/run-env.js";
|
|
9
|
+
import { assertVariableNames, explainLines, mergeEnv, runContextLabel, signalExitCode, validationError, windowsCommandLine, } from "../lib/run-env.js";
|
|
10
10
|
/** Status lines go to stderr so the child's stdout stays clean for pipes. */
|
|
11
11
|
const log = (line = "") => console.error(line);
|
|
12
12
|
/** Resolve, validate, merge, spawn. */
|
|
@@ -27,7 +27,7 @@ export async function runCommand(opts, childArgs) {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
const merged = mergeEnv(process.env, values, res.missing, opts.keepExisting === true);
|
|
30
|
-
const label =
|
|
30
|
+
const label = runContextLabel(res.context);
|
|
31
31
|
if (opts.explain) {
|
|
32
32
|
log(pc.bold(`${who.project.name} / ${label}`));
|
|
33
33
|
for (const line of explainLines(merged, res.context.slug))
|
package/dist/lib/context.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_CONTEXT_SLUG, slugify } from "@getmyenv/shared";
|
|
1
|
+
import { DEFAULT_CONTEXT_SLUG, FACTS, slugify } from "@getmyenv/shared";
|
|
2
2
|
/**
|
|
3
3
|
* Default context for this folder:
|
|
4
4
|
* server token's bound context, then project.json `context`, then `development`.
|
|
@@ -39,7 +39,7 @@ export function resolveContext(who, meta, explicit) {
|
|
|
39
39
|
}
|
|
40
40
|
const available = who.contexts.map((c) => c.slug).join(", ") || "none";
|
|
41
41
|
const next = who.isGuest
|
|
42
|
-
?
|
|
42
|
+
? `${FACTS.guestContexts} Claim it to add contexts: npx getmyenv claim`
|
|
43
43
|
: `Create one: npx getmyenv context create "${wanted}"`;
|
|
44
44
|
throw new Error(`No context "${wanted}". Available: ${available}\n${next}`);
|
|
45
45
|
}
|
package/dist/lib/run-env.d.ts
CHANGED
|
@@ -1,40 +1,6 @@
|
|
|
1
|
+
export { explainLines, mergeEnv, runContextLabel, validationError, type MissingKey } from "@getmyenv/shared";
|
|
1
2
|
/** Names from the server go into an environment or a file. Refuse any that are not variable names. */
|
|
2
3
|
export declare function assertVariableNames(names: Iterable<string>): void;
|
|
3
|
-
export type MissingKey = {
|
|
4
|
-
name: string;
|
|
5
|
-
reason: "missing" | "expired";
|
|
6
|
-
};
|
|
7
|
-
export type ParentEnv = Record<string, string | undefined>;
|
|
8
|
-
export type Origin = "context" | "context-overrides-parent" | "parent-kept" | "parent-fills-missing" | "missing";
|
|
9
|
-
export type ExplainEntry = {
|
|
10
|
-
name: string;
|
|
11
|
-
origin: Origin;
|
|
12
|
-
reason?: MissingKey["reason"];
|
|
13
|
-
};
|
|
14
|
-
export type MergeResult = {
|
|
15
|
-
env: ParentEnv;
|
|
16
|
-
/** Names set from the context. */
|
|
17
|
-
applied: string[];
|
|
18
|
-
/** Names the context replaced in the parent env. */
|
|
19
|
-
overridden: string[];
|
|
20
|
-
/** Names the parent env kept because of --keep-existing. */
|
|
21
|
-
kept: string[];
|
|
22
|
-
/** Keys missing in the context but present in the parent env. */
|
|
23
|
-
filledByParent: string[];
|
|
24
|
-
/** Keys missing in the context and absent from the parent env. */
|
|
25
|
-
unresolved: MissingKey[];
|
|
26
|
-
entries: ExplainEntry[];
|
|
27
|
-
};
|
|
28
|
-
/** The child never sees the CLI's own settings (token, API URL, config dir). */
|
|
29
|
-
export declare function withoutOwnEnv(parent: ParentEnv): ParentEnv;
|
|
30
|
-
/**
|
|
31
|
-
* Merge context values into the parent env.
|
|
32
|
-
* The context wins unless keepExisting is set. A key missing in the context is
|
|
33
|
-
* satisfied when the parent env already has it (for example from `op run`).
|
|
34
|
-
*/
|
|
35
|
-
export declare function mergeEnv(parent: ParentEnv, values: Record<string, string>, missing: MissingKey[], keepExisting: boolean): MergeResult;
|
|
36
|
-
/** Error text when keys are unresolved and --allow-missing is not set. Null when fine. */
|
|
37
|
-
export declare function validationError(result: Pick<MergeResult, "unresolved">, contextSlug: string, allowMissing: boolean): string | null;
|
|
38
4
|
/**
|
|
39
5
|
* One cmd.exe command line from argv. Windows needs the shell to find `npm.cmd`
|
|
40
6
|
* and friends, and Node does not quote args passed alongside `shell: true`.
|
|
@@ -44,5 +10,3 @@ export declare function validationError(result: Pick<MergeResult, "unresolved">,
|
|
|
44
10
|
export declare function windowsCommandLine(args: string[]): string;
|
|
45
11
|
/** Exit code for a child that ended by signal: 128 + signal number, like a shell. */
|
|
46
12
|
export declare function signalExitCode(signal: NodeJS.Signals, numbers: Record<string, number>): number;
|
|
47
|
-
/** `--explain` output. Names and origins only, never values. */
|
|
48
|
-
export declare function explainLines(result: MergeResult, contextSlug: string): string[];
|
package/dist/lib/run-env.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pure pieces of `getmyenv run
|
|
2
|
+
* Pure pieces of `getmyenv run`. Merge, validate and explain live in
|
|
3
|
+
* @getmyenv/shared so the homepage demo prints the same text.
|
|
3
4
|
*/
|
|
4
5
|
import { isValidVariableName } from "@getmyenv/shared";
|
|
6
|
+
export { explainLines, mergeEnv, runContextLabel, validationError } from "@getmyenv/shared";
|
|
5
7
|
/** Names from the server go into an environment or a file. Refuse any that are not variable names. */
|
|
6
8
|
export function assertVariableNames(names) {
|
|
7
9
|
for (const name of names) {
|
|
@@ -10,85 +12,6 @@ export function assertVariableNames(names) {
|
|
|
10
12
|
}
|
|
11
13
|
}
|
|
12
14
|
}
|
|
13
|
-
/** The child never sees the CLI's own settings (token, API URL, config dir). */
|
|
14
|
-
export function withoutOwnEnv(parent) {
|
|
15
|
-
const env = {};
|
|
16
|
-
for (const [k, v] of Object.entries(parent)) {
|
|
17
|
-
if (!k.toUpperCase().startsWith("GETMYENV_"))
|
|
18
|
-
env[k] = v;
|
|
19
|
-
}
|
|
20
|
-
return env;
|
|
21
|
-
}
|
|
22
|
-
function present(parent, name) {
|
|
23
|
-
const v = parent[name];
|
|
24
|
-
return v !== undefined && v !== "";
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Merge context values into the parent env.
|
|
28
|
-
* The context wins unless keepExisting is set. A key missing in the context is
|
|
29
|
-
* satisfied when the parent env already has it (for example from `op run`).
|
|
30
|
-
*/
|
|
31
|
-
export function mergeEnv(parent, values, missing, keepExisting) {
|
|
32
|
-
const env = withoutOwnEnv(parent);
|
|
33
|
-
const applied = [];
|
|
34
|
-
const overridden = [];
|
|
35
|
-
const kept = [];
|
|
36
|
-
const entries = [];
|
|
37
|
-
for (const name of Object.keys(values).sort()) {
|
|
38
|
-
if (present(parent, name)) {
|
|
39
|
-
if (keepExisting) {
|
|
40
|
-
kept.push(name);
|
|
41
|
-
entries.push({ name, origin: "parent-kept" });
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
if (parent[name] !== values[name]) {
|
|
45
|
-
overridden.push(name);
|
|
46
|
-
entries.push({ name, origin: "context-overrides-parent" });
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
entries.push({ name, origin: "context" });
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
entries.push({ name, origin: "context" });
|
|
54
|
-
}
|
|
55
|
-
env[name] = values[name];
|
|
56
|
-
applied.push(name);
|
|
57
|
-
}
|
|
58
|
-
const filledByParent = [];
|
|
59
|
-
const unresolved = [];
|
|
60
|
-
for (const m of [...missing].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
61
|
-
if (present(parent, m.name)) {
|
|
62
|
-
filledByParent.push(m.name);
|
|
63
|
-
entries.push({ name: m.name, origin: "parent-fills-missing", reason: m.reason });
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
unresolved.push(m);
|
|
67
|
-
entries.push({ name: m.name, origin: "missing", reason: m.reason });
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
71
|
-
return { env, applied, overridden, kept, filledByParent, unresolved, entries };
|
|
72
|
-
}
|
|
73
|
-
/** Error text when keys are unresolved and --allow-missing is not set. Null when fine. */
|
|
74
|
-
export function validationError(result, contextSlug, allowMissing) {
|
|
75
|
-
if (allowMissing || result.unresolved.length === 0)
|
|
76
|
-
return null;
|
|
77
|
-
const lines = result.unresolved.map((m) => ` ${m.name}${m.reason === "expired" ? " (expired)" : ""}`);
|
|
78
|
-
return [
|
|
79
|
-
`${result.unresolved.length} variable(s) have no value in ${contextSlug}:`,
|
|
80
|
-
...lines,
|
|
81
|
-
`Set them: npx getmyenv set NAME -c ${contextSlug}`,
|
|
82
|
-
"Or run anyway: --allow-missing",
|
|
83
|
-
].join("\n");
|
|
84
|
-
}
|
|
85
|
-
const ORIGIN_TEXT = {
|
|
86
|
-
context: (ctx) => `getmyenv (${ctx})`,
|
|
87
|
-
"context-overrides-parent": (ctx) => `getmyenv (${ctx}), replaces parent env`,
|
|
88
|
-
"parent-kept": () => "parent env (--keep-existing)",
|
|
89
|
-
"parent-fills-missing": (ctx, reason) => `parent env (${reason === "expired" ? "expired" : "missing"} in ${ctx})`,
|
|
90
|
-
missing: (ctx, reason) => `not set (${reason === "expired" ? "expired" : "missing"} in ${ctx})`,
|
|
91
|
-
};
|
|
92
15
|
/**
|
|
93
16
|
* One cmd.exe command line from argv. Windows needs the shell to find `npm.cmd`
|
|
94
17
|
* and friends, and Node does not quote args passed alongside `shell: true`.
|
|
@@ -112,11 +35,3 @@ export function signalExitCode(signal, numbers) {
|
|
|
112
35
|
const n = numbers[signal];
|
|
113
36
|
return typeof n === "number" ? 128 + n : 1;
|
|
114
37
|
}
|
|
115
|
-
/** `--explain` output. Names and origins only, never values. */
|
|
116
|
-
export function explainLines(result, contextSlug) {
|
|
117
|
-
const width = Math.max(4, ...result.entries.map((e) => e.name.length));
|
|
118
|
-
const lines = result.entries.map((e) => ` ${e.name.padEnd(width)} ${ORIGIN_TEXT[e.origin](contextSlug, e.reason)}`);
|
|
119
|
-
lines.push("");
|
|
120
|
-
lines.push(` ${result.applied.length} from getmyenv, ${result.overridden.length} replaced, ${result.kept.length} kept, ${result.filledByParent.length} from parent env, ${result.unresolved.length} not set`);
|
|
121
|
-
return lines;
|
|
122
|
-
}
|
package/dist/lib/vault.js
CHANGED
|
@@ -44,7 +44,7 @@ export async function contextKey(linked, target, opts = {}) {
|
|
|
44
44
|
const key = opened.get(target.id);
|
|
45
45
|
if (!key) {
|
|
46
46
|
throw new Error(member
|
|
47
|
-
? `
|
|
47
|
+
? `Waiting for the owner to send the key for ${target.slug}. It arrives the next time they unlock the vault in the dashboard.`
|
|
48
48
|
: `No key for ${target.slug} on this account. Open the project in the dashboard once.`);
|
|
49
49
|
}
|
|
50
50
|
for (const [id, k] of opened)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "getmyenv",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "CLI for getmyenv, an encrypted store for environment variables and secrets. We store ciphertext only.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@getmyenv/crypto": "0.4.1",
|
|
37
|
-
"@getmyenv/shared": "0.
|
|
37
|
+
"@getmyenv/shared": "0.8.0",
|
|
38
38
|
"@napi-rs/keyring": "^2.1.0",
|
|
39
39
|
"commander": "^15.0.0",
|
|
40
40
|
"dotenv": "^16.4.7",
|