@dotenc/cli 0.4.3 → 0.4.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 +5 -2
- package/dist/cli.js +66 -46
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[![NPM Version][npm-image]][npm-url]
|
|
4
4
|
[![Github License][license-image]](LICENSE)
|
|
5
5
|
[![NPM Downloads][downloads-image]][npm-url]
|
|
6
|
+
[![Codecov][codecov-image]][codecov-url]
|
|
6
7
|
|
|
7
8
|
🔐 Git-native encrypted environments powered by your SSH keys
|
|
8
9
|
|
|
@@ -103,7 +104,7 @@ Your SSH private keys never leave `~/.ssh/`. dotenc reads them in place - nothin
|
|
|
103
104
|
|
|
104
105
|
After setup, your project will look like:
|
|
105
106
|
|
|
106
|
-
```
|
|
107
|
+
```plaintext
|
|
107
108
|
.
|
|
108
109
|
├── .dotenc/
|
|
109
110
|
│ ├── alice.pub
|
|
@@ -332,7 +333,7 @@ Copy the **entire** contents of the private key file and store it as a secret en
|
|
|
332
333
|
cat ci_key
|
|
333
334
|
```
|
|
334
335
|
|
|
335
|
-
```
|
|
336
|
+
```plaintext
|
|
336
337
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
|
337
338
|
b3BlbnNzaC1rZXktdjEAAAAABG5vbm...
|
|
338
339
|
-----END OPENSSH PRIVATE KEY-----
|
|
@@ -478,3 +479,5 @@ It does not aim to replace centralized secret managers like Vault or Doppler —
|
|
|
478
479
|
[license-image]: https://img.shields.io/github/license/ivanfilhoz/dotenc.svg
|
|
479
480
|
[downloads-image]: https://img.shields.io/npm/dm/@dotenc/cli.svg
|
|
480
481
|
[npm-url]: https://npmjs.org/package/@dotenc/cli
|
|
482
|
+
[codecov-image]: https://codecov.io/gh/ivanfilhoz/dotenc/graph/badge.svg?token=U2MKXVGBA0
|
|
483
|
+
[codecov-url]: https://codecov.io/gh/ivanfilhoz/dotenc
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ var package_default;
|
|
|
7
7
|
var init_package = __esm(() => {
|
|
8
8
|
package_default = {
|
|
9
9
|
name: "@dotenc/cli",
|
|
10
|
-
version: "0.4.
|
|
10
|
+
version: "0.4.4",
|
|
11
11
|
description: "🔐 Git-native encrypted environments powered by your SSH keys",
|
|
12
12
|
author: "Ivan Filho <i@ivanfilho.com>",
|
|
13
13
|
license: "MIT",
|
|
@@ -579,8 +579,8 @@ var init_getPrivateKeys = __esm(() => {
|
|
|
579
579
|
|
|
580
580
|
// src/helpers/decryptEnvironment.ts
|
|
581
581
|
import chalk2 from "chalk";
|
|
582
|
-
var decryptEnvironmentData = async (environment) => {
|
|
583
|
-
const { keys: availablePrivateKeys, passphraseProtectedKeys } = await getPrivateKeys();
|
|
582
|
+
var defaultDecryptEnvironmentDataDeps, decryptEnvironmentData = async (environment, deps = defaultDecryptEnvironmentDataDeps) => {
|
|
583
|
+
const { keys: availablePrivateKeys, passphraseProtectedKeys } = await deps.getPrivateKeys();
|
|
584
584
|
if (!availablePrivateKeys.length) {
|
|
585
585
|
if (passphraseProtectedKeys.length > 0) {
|
|
586
586
|
throw new Error(passphraseProtectedKeyError(passphraseProtectedKeys));
|
|
@@ -603,20 +603,20 @@ var decryptEnvironmentData = async (environment) => {
|
|
|
603
603
|
}
|
|
604
604
|
let dataKey;
|
|
605
605
|
try {
|
|
606
|
-
dataKey = decryptDataKey(selectedPrivateKey, Buffer.from(grantedKey.encryptedDataKey, "base64"));
|
|
606
|
+
dataKey = deps.decryptDataKey(selectedPrivateKey, Buffer.from(grantedKey.encryptedDataKey, "base64"));
|
|
607
607
|
} catch (error) {
|
|
608
608
|
throw new Error("Failed to decrypt the data key.", { cause: error });
|
|
609
609
|
}
|
|
610
|
-
const decryptedContent = await decryptData(dataKey, Buffer.from(environment.encryptedContent, "base64"));
|
|
610
|
+
const decryptedContent = await deps.decryptData(dataKey, Buffer.from(environment.encryptedContent, "base64"));
|
|
611
611
|
return decryptedContent;
|
|
612
|
-
}, decryptEnvironment = async (name) => {
|
|
613
|
-
const { keys: availablePrivateKeys } = await getPrivateKeys();
|
|
614
|
-
const environmentJson = await getEnvironmentByName(name);
|
|
612
|
+
}, defaultDecryptEnvironmentDeps, decryptEnvironment = async (name, deps = defaultDecryptEnvironmentDeps) => {
|
|
613
|
+
const { keys: availablePrivateKeys } = await deps.getPrivateKeys();
|
|
614
|
+
const environmentJson = await deps.getEnvironmentByName(name);
|
|
615
615
|
try {
|
|
616
|
-
return await decryptEnvironmentData(environmentJson);
|
|
616
|
+
return await decryptEnvironmentData(environmentJson, deps);
|
|
617
617
|
} catch (error) {
|
|
618
618
|
if (error instanceof Error && error.message === "Access denied to the environment.") {
|
|
619
|
-
|
|
619
|
+
deps.logError(`You do not have access to this environment.
|
|
620
620
|
|
|
621
621
|
These are your available private keys:
|
|
622
622
|
|
|
@@ -630,7 +630,7 @@ var decryptEnvironmentData = async (environment) => {
|
|
|
630
630
|
`);
|
|
631
631
|
}
|
|
632
632
|
if (error instanceof Error && error.message === "Failed to decrypt the data key.") {
|
|
633
|
-
|
|
633
|
+
deps.logError(`${chalk2.red("Error:")} failed to decrypt the data key. Please ensure you have the correct private key.`);
|
|
634
634
|
}
|
|
635
635
|
throw error;
|
|
636
636
|
}
|
|
@@ -641,6 +641,16 @@ var init_decryptEnvironment = __esm(() => {
|
|
|
641
641
|
init_errors();
|
|
642
642
|
init_getEnvironmentByName();
|
|
643
643
|
init_getPrivateKeys();
|
|
644
|
+
defaultDecryptEnvironmentDataDeps = {
|
|
645
|
+
getPrivateKeys,
|
|
646
|
+
decryptDataKey,
|
|
647
|
+
decryptData
|
|
648
|
+
};
|
|
649
|
+
defaultDecryptEnvironmentDeps = {
|
|
650
|
+
...defaultDecryptEnvironmentDataDeps,
|
|
651
|
+
getEnvironmentByName,
|
|
652
|
+
logError: (message) => console.error(message)
|
|
653
|
+
};
|
|
644
654
|
});
|
|
645
655
|
|
|
646
656
|
// src/helpers/encryptDataKey.ts
|
|
@@ -1000,10 +1010,12 @@ import fs7 from "node:fs/promises";
|
|
|
1000
1010
|
import os2 from "node:os";
|
|
1001
1011
|
import path6 from "node:path";
|
|
1002
1012
|
import { z as z2 } from "zod";
|
|
1003
|
-
var homeConfigSchema,
|
|
1013
|
+
var homeConfigSchema, getConfigPath = () => path6.join(os2.homedir(), ".dotenc", "config.json"), setHomeConfig = async (config) => {
|
|
1004
1014
|
const parsedConfig = homeConfigSchema.parse(config);
|
|
1015
|
+
const configPath = getConfigPath();
|
|
1005
1016
|
await fs7.writeFile(configPath, JSON.stringify(parsedConfig, null, 2), "utf-8");
|
|
1006
1017
|
}, getHomeConfig = async () => {
|
|
1018
|
+
const configPath = getConfigPath();
|
|
1007
1019
|
if (existsSync3(configPath)) {
|
|
1008
1020
|
const config = JSON.parse(await fs7.readFile(configPath, "utf-8"));
|
|
1009
1021
|
return homeConfigSchema.parse(config);
|
|
@@ -1014,7 +1026,6 @@ var init_homeConfig = __esm(() => {
|
|
|
1014
1026
|
homeConfigSchema = z2.object({
|
|
1015
1027
|
editor: z2.string().nullish()
|
|
1016
1028
|
});
|
|
1017
|
-
configPath = path6.join(os2.homedir(), ".dotenc", "config.json");
|
|
1018
1029
|
});
|
|
1019
1030
|
|
|
1020
1031
|
// src/commands/config.ts
|
|
@@ -1077,66 +1088,69 @@ var init_parseEnv = __esm(() => {
|
|
|
1077
1088
|
// src/commands/run.ts
|
|
1078
1089
|
import { spawn } from "node:child_process";
|
|
1079
1090
|
import chalk7 from "chalk";
|
|
1080
|
-
var runCommand = async (command, args, options) => {
|
|
1091
|
+
var defaultRunCommandDeps, runCommand = async (command, args, options, _command, deps = defaultRunCommandDeps) => {
|
|
1081
1092
|
const environmentName = options.env || process.env.DOTENC_ENV;
|
|
1082
1093
|
if (!environmentName) {
|
|
1083
|
-
|
|
1094
|
+
deps.logError(`No environment provided. Use -e or set DOTENC_ENV to the environment you want to run the command in.
|
|
1084
1095
|
To start a new environment, use "dotenc init [environment]".`);
|
|
1085
|
-
|
|
1096
|
+
deps.exit(1);
|
|
1086
1097
|
}
|
|
1087
1098
|
const environments = environmentName.split(",");
|
|
1088
1099
|
for (const env of environments) {
|
|
1089
|
-
const validation = validateEnvironmentName(env);
|
|
1100
|
+
const validation = deps.validateEnvironmentName(env);
|
|
1090
1101
|
if (!validation.valid) {
|
|
1091
|
-
|
|
1092
|
-
|
|
1102
|
+
deps.logError(`${chalk7.red("Error:")} ${validation.reason}`);
|
|
1103
|
+
deps.exit(1);
|
|
1093
1104
|
}
|
|
1094
1105
|
}
|
|
1095
1106
|
let failureCount = 0;
|
|
1096
1107
|
const decryptedEnvs = await Promise.all(environments.map(async (environment) => {
|
|
1097
1108
|
let content;
|
|
1098
1109
|
try {
|
|
1099
|
-
content = await decryptEnvironment(environment);
|
|
1110
|
+
content = await deps.decryptEnvironment(environment);
|
|
1100
1111
|
} catch (error) {
|
|
1101
|
-
|
|
1112
|
+
deps.logError(error instanceof Error ? error.message : `Unknown error occurred while decrypting the environment ${environment}.`);
|
|
1102
1113
|
failureCount++;
|
|
1103
1114
|
return {};
|
|
1104
1115
|
}
|
|
1105
|
-
const decryptedEnv2 = parseEnv(content);
|
|
1116
|
+
const decryptedEnv2 = deps.parseEnv(content);
|
|
1106
1117
|
return decryptedEnv2;
|
|
1107
1118
|
}));
|
|
1108
1119
|
if (failureCount === environments.length) {
|
|
1109
|
-
|
|
1110
|
-
|
|
1120
|
+
deps.logError(`${chalk7.red("Error:")} All environments failed to load.`);
|
|
1121
|
+
deps.exit(1);
|
|
1111
1122
|
}
|
|
1112
1123
|
if (failureCount > 0) {
|
|
1113
|
-
|
|
1124
|
+
deps.logError(`${chalk7.yellow("Warning:")} ${failureCount} of ${environments.length} environment(s) failed to load.`);
|
|
1114
1125
|
}
|
|
1115
1126
|
const decryptedEnv = decryptedEnvs.reduce((acc, env) => {
|
|
1116
1127
|
return { ...acc, ...env };
|
|
1117
1128
|
}, {});
|
|
1118
1129
|
const mergedEnv = { ...process.env, ...decryptedEnv };
|
|
1119
|
-
const child = spawn(command, args, {
|
|
1130
|
+
const child = deps.spawn(command, args, {
|
|
1120
1131
|
env: mergedEnv,
|
|
1121
1132
|
stdio: "inherit"
|
|
1122
1133
|
});
|
|
1123
1134
|
child.on("exit", (code) => {
|
|
1124
|
-
|
|
1135
|
+
deps.exit(code ?? 0);
|
|
1125
1136
|
});
|
|
1126
1137
|
};
|
|
1127
1138
|
var init_run = __esm(() => {
|
|
1128
1139
|
init_decryptEnvironment();
|
|
1129
1140
|
init_parseEnv();
|
|
1141
|
+
defaultRunCommandDeps = {
|
|
1142
|
+
decryptEnvironment,
|
|
1143
|
+
parseEnv,
|
|
1144
|
+
validateEnvironmentName,
|
|
1145
|
+
spawn,
|
|
1146
|
+
logError: (message) => console.error(message),
|
|
1147
|
+
exit: (code) => process.exit(code)
|
|
1148
|
+
};
|
|
1130
1149
|
});
|
|
1131
1150
|
|
|
1132
1151
|
// src/commands/dev.ts
|
|
1133
1152
|
import chalk8 from "chalk";
|
|
1134
|
-
var devCommand = async (command, args, deps = {
|
|
1135
|
-
getCurrentKeyName,
|
|
1136
|
-
runCommand,
|
|
1137
|
-
logError: (message) => console.error(message),
|
|
1138
|
-
exit: (code) => process.exit(code)
|
|
1139
|
-
}) => {
|
|
1153
|
+
var defaultDevCommandDeps, devCommand = async (command, args, deps = defaultDevCommandDeps) => {
|
|
1140
1154
|
const keyName = await deps.getCurrentKeyName();
|
|
1141
1155
|
if (!keyName) {
|
|
1142
1156
|
deps.logError(`${chalk8.red("Error:")} could not resolve your identity. Run ${chalk8.gray("dotenc init")} first.`);
|
|
@@ -1147,6 +1161,12 @@ var devCommand = async (command, args, deps = {
|
|
|
1147
1161
|
var init_dev = __esm(() => {
|
|
1148
1162
|
init_getCurrentKeyName();
|
|
1149
1163
|
init_run();
|
|
1164
|
+
defaultDevCommandDeps = {
|
|
1165
|
+
getCurrentKeyName,
|
|
1166
|
+
runCommand,
|
|
1167
|
+
logError: console.error,
|
|
1168
|
+
exit: process.exit
|
|
1169
|
+
};
|
|
1150
1170
|
});
|
|
1151
1171
|
|
|
1152
1172
|
// src/helpers/environmentExists.ts
|
|
@@ -1172,9 +1192,9 @@ import { existsSync as existsSync5 } from "node:fs";
|
|
|
1172
1192
|
import fs8 from "node:fs/promises";
|
|
1173
1193
|
import path8 from "node:path";
|
|
1174
1194
|
import { z as z3 } from "zod";
|
|
1175
|
-
var projectConfigSchema,
|
|
1176
|
-
if (existsSync5(
|
|
1177
|
-
const config = JSON.parse(await fs8.readFile(
|
|
1195
|
+
var projectConfigSchema, configPath, getProjectConfig = async () => {
|
|
1196
|
+
if (existsSync5(configPath)) {
|
|
1197
|
+
const config = JSON.parse(await fs8.readFile(configPath, "utf-8"));
|
|
1178
1198
|
return projectConfigSchema.parse(config);
|
|
1179
1199
|
}
|
|
1180
1200
|
return {};
|
|
@@ -1183,7 +1203,7 @@ var init_projectConfig = __esm(() => {
|
|
|
1183
1203
|
projectConfigSchema = z3.object({
|
|
1184
1204
|
projectId: z3.string()
|
|
1185
1205
|
});
|
|
1186
|
-
|
|
1206
|
+
configPath = path8.join(process.cwd(), "dotenc.json");
|
|
1187
1207
|
});
|
|
1188
1208
|
|
|
1189
1209
|
// src/prompts/createEnvironment.ts
|
|
@@ -1287,7 +1307,14 @@ var init_createHash = () => {};
|
|
|
1287
1307
|
|
|
1288
1308
|
// src/helpers/getDefaultEditor.ts
|
|
1289
1309
|
import { execSync } from "node:child_process";
|
|
1290
|
-
var
|
|
1310
|
+
var commandExists = (command) => {
|
|
1311
|
+
try {
|
|
1312
|
+
execSync(`command -v ${command}`, { stdio: "ignore" });
|
|
1313
|
+
return true;
|
|
1314
|
+
} catch {
|
|
1315
|
+
return false;
|
|
1316
|
+
}
|
|
1317
|
+
}, defaultGetDefaultEditorDeps, getDefaultEditor = async (deps = defaultGetDefaultEditorDeps) => {
|
|
1291
1318
|
const config = await deps.getHomeConfig();
|
|
1292
1319
|
if (config.editor) {
|
|
1293
1320
|
return config.editor;
|
|
@@ -1314,14 +1341,7 @@ var init_getDefaultEditor = __esm(() => {
|
|
|
1314
1341
|
init_homeConfig();
|
|
1315
1342
|
defaultGetDefaultEditorDeps = {
|
|
1316
1343
|
getHomeConfig,
|
|
1317
|
-
commandExists
|
|
1318
|
-
try {
|
|
1319
|
-
execSync(`command -v ${command}`, { stdio: "ignore" });
|
|
1320
|
-
return true;
|
|
1321
|
-
} catch {
|
|
1322
|
-
return false;
|
|
1323
|
-
}
|
|
1324
|
-
},
|
|
1344
|
+
commandExists,
|
|
1325
1345
|
platform: process.platform
|
|
1326
1346
|
};
|
|
1327
1347
|
});
|