@rowlabs/ev 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +90 -54
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6309,6 +6309,7 @@ function parseGitEnvConfig(content) {
|
|
|
6309
6309
|
return {
|
|
6310
6310
|
project: parsed.project,
|
|
6311
6311
|
defaultEnv: parsed.default_env,
|
|
6312
|
+
envFiles: parsed.env_files ?? [".env", ".env.local"],
|
|
6312
6313
|
apps: parsed.apps,
|
|
6313
6314
|
backend: parsed.backend,
|
|
6314
6315
|
environments: parsed.environments
|
|
@@ -6459,7 +6460,7 @@ async function resolveCurrentContext(explicitTarget) {
|
|
|
6459
6460
|
if (!config) return null;
|
|
6460
6461
|
const context = resolveContext(config, cwd, root, explicitTarget);
|
|
6461
6462
|
const backendConfig = resolveBackendConfig(config, context.env);
|
|
6462
|
-
return { context, repoRoot: root, backendConfig };
|
|
6463
|
+
return { context, repoRoot: root, backendConfig, envFiles: config.envFiles };
|
|
6463
6464
|
}
|
|
6464
6465
|
var CONFIG_DIR, AUTH_FILE;
|
|
6465
6466
|
var init_config = __esm({
|
|
@@ -6717,7 +6718,7 @@ var init_auth = __esm({
|
|
|
6717
6718
|
|
|
6718
6719
|
// src/index.ts
|
|
6719
6720
|
init_dist();
|
|
6720
|
-
import { Command as
|
|
6721
|
+
import { Command as Command19 } from "commander";
|
|
6721
6722
|
|
|
6722
6723
|
// src/commands/login.ts
|
|
6723
6724
|
init_dist();
|
|
@@ -6874,7 +6875,11 @@ var initCommand = new Command2("init").description("Initialize ev in the current
|
|
|
6874
6875
|
const client = await createApiClient();
|
|
6875
6876
|
const project = await client.createProject(name, sealedKey);
|
|
6876
6877
|
await saveProjectKey(project.id, sealedKey);
|
|
6877
|
-
const configData = {
|
|
6878
|
+
const configData = {
|
|
6879
|
+
project: project.id,
|
|
6880
|
+
default_env: "dev",
|
|
6881
|
+
env_files: [".env", ".env.local"]
|
|
6882
|
+
};
|
|
6878
6883
|
const yamlContent = "# ev project configuration\n# Docs: https://github.com/blaze-rowland/git-env\n\n" + YAML2.stringify(configData);
|
|
6879
6884
|
await writeFile3(configPath, yamlContent, "utf-8");
|
|
6880
6885
|
const appsDir = join3(cwd, "apps");
|
|
@@ -6943,9 +6948,6 @@ init_api_client();
|
|
|
6943
6948
|
init_config();
|
|
6944
6949
|
init_auth();
|
|
6945
6950
|
import { Command as Command3 } from "commander";
|
|
6946
|
-
import { readFile as readFile4 } from "fs/promises";
|
|
6947
|
-
import { existsSync as existsSync4 } from "fs";
|
|
6948
|
-
import { join as join4 } from "path";
|
|
6949
6951
|
import chalk4 from "chalk";
|
|
6950
6952
|
import ora3 from "ora";
|
|
6951
6953
|
|
|
@@ -6965,8 +6967,28 @@ function maskValue(value) {
|
|
|
6965
6967
|
return value.slice(0, 2) + "****" + value.slice(-2);
|
|
6966
6968
|
}
|
|
6967
6969
|
|
|
6970
|
+
// src/lib/env-files.ts
|
|
6971
|
+
init_dist();
|
|
6972
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
6973
|
+
import { existsSync as existsSync4 } from "fs";
|
|
6974
|
+
import { join as join4 } from "path";
|
|
6975
|
+
async function readEnvFiles(dir, fileNames) {
|
|
6976
|
+
const vars = {};
|
|
6977
|
+
const files = [];
|
|
6978
|
+
for (const name of fileNames) {
|
|
6979
|
+
const filePath = join4(dir, name);
|
|
6980
|
+
if (existsSync4(filePath)) {
|
|
6981
|
+
const content = await readFile4(filePath, "utf-8");
|
|
6982
|
+
const parsed = parseEnvFile(content);
|
|
6983
|
+
Object.assign(vars, parsed);
|
|
6984
|
+
files.push(name);
|
|
6985
|
+
}
|
|
6986
|
+
}
|
|
6987
|
+
return { vars, files };
|
|
6988
|
+
}
|
|
6989
|
+
|
|
6968
6990
|
// src/commands/push.ts
|
|
6969
|
-
var pushCommand = new Command3("push").description("Push local .env to remote").argument("[target]", "app:env or env to push to").option("--prune", "Remove remote keys not present locally", false).option("-m, --message <message>", "Push message").option("-y, --yes", "Skip confirmation prompt", false).action(async (target, options) => {
|
|
6991
|
+
var pushCommand = new Command3("push").description("Push local .env files to remote").argument("[target]", "app:env or env to push to").option("--prune", "Remove remote keys not present locally", false).option("-m, --message <message>", "Push message").option("-y, --yes", "Skip confirmation prompt", false).action(async (target, options) => {
|
|
6970
6992
|
const spinner = ora3("Pushing secrets...").start();
|
|
6971
6993
|
try {
|
|
6972
6994
|
const resolved = await resolveCurrentContext(target);
|
|
@@ -6974,20 +6996,15 @@ var pushCommand = new Command3("push").description("Push local .env to remote").
|
|
|
6974
6996
|
spinner.fail(chalk4.red("No ev.yaml found. Run `ev init` first."));
|
|
6975
6997
|
process.exit(1);
|
|
6976
6998
|
}
|
|
6977
|
-
const { context, backendConfig } = resolved;
|
|
6999
|
+
const { context, backendConfig, envFiles } = resolved;
|
|
6978
7000
|
const isEvBackend = !backendConfig || backendConfig.type === "ev";
|
|
6979
|
-
const
|
|
6980
|
-
if (!existsSync4(envPath)) {
|
|
6981
|
-
spinner.fail(chalk4.red("No .env file found in current directory"));
|
|
6982
|
-
process.exit(1);
|
|
6983
|
-
}
|
|
6984
|
-
const envContent = await readFile4(envPath, "utf-8");
|
|
6985
|
-
const localEnv = parseEnvFile(envContent);
|
|
7001
|
+
const { vars: localEnv, files } = await readEnvFiles(process.cwd(), envFiles);
|
|
6986
7002
|
const keys = Object.keys(localEnv);
|
|
6987
7003
|
if (keys.length === 0) {
|
|
6988
|
-
spinner.fail(chalk4.red("No
|
|
7004
|
+
spinner.fail(chalk4.red("No env files found (.env, .env.local)"));
|
|
6989
7005
|
process.exit(1);
|
|
6990
7006
|
}
|
|
7007
|
+
spinner.text = `Reading from ${files.join(", ")}...`;
|
|
6991
7008
|
spinner.text = "Fetching remote secrets...";
|
|
6992
7009
|
const envId = await resolveEnvironmentId(context.project, context.app, context.env);
|
|
6993
7010
|
const client = await createApiClient(backendConfig);
|
|
@@ -7079,8 +7096,7 @@ init_api_client();
|
|
|
7079
7096
|
init_config();
|
|
7080
7097
|
init_auth();
|
|
7081
7098
|
import { Command as Command4 } from "commander";
|
|
7082
|
-
import {
|
|
7083
|
-
import { existsSync as existsSync5 } from "fs";
|
|
7099
|
+
import { writeFile as writeFile4, copyFile } from "fs/promises";
|
|
7084
7100
|
import { join as join5 } from "path";
|
|
7085
7101
|
import chalk5 from "chalk";
|
|
7086
7102
|
import ora4 from "ora";
|
|
@@ -7092,7 +7108,7 @@ var pullCommand = new Command4("pull").description("Pull remote secrets into loc
|
|
|
7092
7108
|
spinner.fail(chalk5.red("No ev.yaml found. Run `ev init` first."));
|
|
7093
7109
|
process.exit(1);
|
|
7094
7110
|
}
|
|
7095
|
-
const { context, backendConfig } = resolved;
|
|
7111
|
+
const { context, backendConfig, envFiles } = resolved;
|
|
7096
7112
|
const isEvBackend = !backendConfig || backendConfig.type === "ev";
|
|
7097
7113
|
const envId = await resolveEnvironmentId(context.project, context.app, context.env);
|
|
7098
7114
|
const client = await createApiClient(backendConfig);
|
|
@@ -7117,9 +7133,8 @@ var pullCommand = new Command4("pull").description("Pull remote secrets into loc
|
|
|
7117
7133
|
}
|
|
7118
7134
|
const envPath = join5(process.cwd(), ".env");
|
|
7119
7135
|
const backupPath = join5(process.cwd(), ".env.backup");
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
const localEnv = parseEnvFile(localContent);
|
|
7136
|
+
const { vars: localEnv, files: localFiles } = await readEnvFiles(process.cwd(), envFiles);
|
|
7137
|
+
if (localFiles.length > 0) {
|
|
7123
7138
|
const diff = diffEnvs(remoteEnv, localEnv);
|
|
7124
7139
|
const localOnlyKeys = diff.added;
|
|
7125
7140
|
const localChangedKeys = diff.changed;
|
|
@@ -7165,9 +7180,6 @@ init_api_client();
|
|
|
7165
7180
|
init_config();
|
|
7166
7181
|
init_auth();
|
|
7167
7182
|
import { Command as Command5 } from "commander";
|
|
7168
|
-
import { readFile as readFile6 } from "fs/promises";
|
|
7169
|
-
import { existsSync as existsSync6 } from "fs";
|
|
7170
|
-
import { join as join6 } from "path";
|
|
7171
7183
|
import chalk6 from "chalk";
|
|
7172
7184
|
import ora5 from "ora";
|
|
7173
7185
|
var diffCommand = new Command5("diff").description("Show differences between local and remote, or between two environments").argument("[env1]", "First environment (or local vs this env)").argument("[env2]", "Second environment (for env-to-env diff)").action(async (env1, env2) => {
|
|
@@ -7178,7 +7190,7 @@ var diffCommand = new Command5("diff").description("Show differences between loc
|
|
|
7178
7190
|
spinner.fail(chalk6.red("No ev.yaml found. Run `ev init` first."));
|
|
7179
7191
|
process.exit(1);
|
|
7180
7192
|
}
|
|
7181
|
-
const { context } = resolved;
|
|
7193
|
+
const { context, envFiles } = resolved;
|
|
7182
7194
|
const projectKey = await getDecryptedProjectKey(context.project);
|
|
7183
7195
|
const client = await createApiClient();
|
|
7184
7196
|
let base;
|
|
@@ -7199,13 +7211,12 @@ var diffCommand = new Command5("diff").description("Show differences between loc
|
|
|
7199
7211
|
baseLabel = env1;
|
|
7200
7212
|
targetLabel = env2;
|
|
7201
7213
|
} else {
|
|
7202
|
-
const
|
|
7203
|
-
if (
|
|
7204
|
-
spinner.fail(chalk6.red("No
|
|
7214
|
+
const { vars: localVars, files } = await readEnvFiles(process.cwd(), envFiles);
|
|
7215
|
+
if (files.length === 0) {
|
|
7216
|
+
spinner.fail(chalk6.red("No env files found"));
|
|
7205
7217
|
process.exit(1);
|
|
7206
7218
|
}
|
|
7207
|
-
|
|
7208
|
-
base = parseEnvFile(localContent);
|
|
7219
|
+
base = localVars;
|
|
7209
7220
|
const targetEnv = env1 ?? context.env;
|
|
7210
7221
|
const envId = await resolveEnvironmentId(context.project, context.app, targetEnv);
|
|
7211
7222
|
const remoteRes = await client.pullSecrets(envId);
|
|
@@ -7785,8 +7796,8 @@ var getCommand = new Command12("get").description("Get a single secret value").a
|
|
|
7785
7796
|
// src/commands/backend.ts
|
|
7786
7797
|
init_config();
|
|
7787
7798
|
import { Command as Command13 } from "commander";
|
|
7788
|
-
import { readFile as
|
|
7789
|
-
import { join as
|
|
7799
|
+
import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
7800
|
+
import { join as join6 } from "path";
|
|
7790
7801
|
import chalk15 from "chalk";
|
|
7791
7802
|
import ora9 from "ora";
|
|
7792
7803
|
import YAML3 from "yaml";
|
|
@@ -7799,7 +7810,7 @@ backendCommand.command("show").description("Show current backend configuration")
|
|
|
7799
7810
|
console.error(chalk15.red("No ev.yaml found."));
|
|
7800
7811
|
process.exit(1);
|
|
7801
7812
|
}
|
|
7802
|
-
const content = await
|
|
7813
|
+
const content = await readFile5(join6(root, "ev.yaml"), "utf-8");
|
|
7803
7814
|
const config = YAML3.parse(content);
|
|
7804
7815
|
console.log(chalk15.bold("\nBackend Configuration\n"));
|
|
7805
7816
|
const defaultBackend = config.backend ?? { type: "ev" };
|
|
@@ -7858,8 +7869,8 @@ backendCommand.command("set <type>").description("Set the default storage backen
|
|
|
7858
7869
|
spinner.fail(chalk15.red("No ev.yaml found."));
|
|
7859
7870
|
process.exit(1);
|
|
7860
7871
|
}
|
|
7861
|
-
const configPath =
|
|
7862
|
-
const content = await
|
|
7872
|
+
const configPath = join6(root, "ev.yaml");
|
|
7873
|
+
const content = await readFile5(configPath, "utf-8");
|
|
7863
7874
|
const config = YAML3.parse(content);
|
|
7864
7875
|
let backendConfig;
|
|
7865
7876
|
if (type === "ev") {
|
|
@@ -8205,6 +8216,7 @@ _ev() {
|
|
|
8205
8216
|
'import:Import from external provider'
|
|
8206
8217
|
'status:Show current context'
|
|
8207
8218
|
'scan:Scan codebase for env var references'
|
|
8219
|
+
'update:Update ev to latest version'
|
|
8208
8220
|
'doctor:Check setup for issues'
|
|
8209
8221
|
'completions:Generate shell completions'
|
|
8210
8222
|
)
|
|
@@ -8259,7 +8271,7 @@ var BASH_COMPLETION = `_ev() {
|
|
|
8259
8271
|
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
8260
8272
|
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
8261
8273
|
|
|
8262
|
-
commands="login init push pull diff log get rollback env promote access backend import status scan doctor completions"
|
|
8274
|
+
commands="login init push pull diff log get rollback env promote access backend import status scan doctor completions update"
|
|
8263
8275
|
|
|
8264
8276
|
case "\${prev}" in
|
|
8265
8277
|
ev)
|
|
@@ -8310,6 +8322,7 @@ complete -c ev -n '__fish_use_subcommand' -a backend -d 'Configure storage backe
|
|
|
8310
8322
|
complete -c ev -n '__fish_use_subcommand' -a import -d 'Import from external provider'
|
|
8311
8323
|
complete -c ev -n '__fish_use_subcommand' -a status -d 'Show current context'
|
|
8312
8324
|
complete -c ev -n '__fish_use_subcommand' -a scan -d 'Scan for env var references'
|
|
8325
|
+
complete -c ev -n '__fish_use_subcommand' -a update -d 'Update ev to latest version'
|
|
8313
8326
|
complete -c ev -n '__fish_use_subcommand' -a doctor -d 'Check setup for issues'
|
|
8314
8327
|
complete -c ev -n '__fish_use_subcommand' -a completions -d 'Generate shell completions'
|
|
8315
8328
|
|
|
@@ -8362,9 +8375,9 @@ init_config();
|
|
|
8362
8375
|
import { Command as Command17 } from "commander";
|
|
8363
8376
|
import chalk19 from "chalk";
|
|
8364
8377
|
import ora11 from "ora";
|
|
8365
|
-
import { readdir, readFile as
|
|
8366
|
-
import { existsSync as
|
|
8367
|
-
import { join as
|
|
8378
|
+
import { readdir, readFile as readFile6, stat } from "fs/promises";
|
|
8379
|
+
import { existsSync as existsSync5 } from "fs";
|
|
8380
|
+
import { join as join7, extname } from "path";
|
|
8368
8381
|
var SCAN_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
8369
8382
|
".ts",
|
|
8370
8383
|
".tsx",
|
|
@@ -8430,12 +8443,12 @@ async function scanDir(dir) {
|
|
|
8430
8443
|
}
|
|
8431
8444
|
for (const entry of entries) {
|
|
8432
8445
|
if (SKIP_DIRS.has(entry)) continue;
|
|
8433
|
-
const fullPath =
|
|
8446
|
+
const fullPath = join7(currentDir, entry);
|
|
8434
8447
|
const stats = await stat(fullPath);
|
|
8435
8448
|
if (stats.isDirectory()) {
|
|
8436
8449
|
await walk(fullPath);
|
|
8437
8450
|
} else if (SCAN_EXTENSIONS.has(extname(entry))) {
|
|
8438
|
-
const content = await
|
|
8451
|
+
const content = await readFile6(fullPath, "utf-8");
|
|
8439
8452
|
for (const pattern of ENV_PATTERNS) {
|
|
8440
8453
|
pattern.lastIndex = 0;
|
|
8441
8454
|
let match;
|
|
@@ -8452,13 +8465,12 @@ async function scanDir(dir) {
|
|
|
8452
8465
|
await walk(dir);
|
|
8453
8466
|
return results;
|
|
8454
8467
|
}
|
|
8455
|
-
async function getLocalEnvKeys(dir) {
|
|
8468
|
+
async function getLocalEnvKeys(dir, configEnvFiles) {
|
|
8456
8469
|
const keys = /* @__PURE__ */ new Set();
|
|
8457
|
-
const
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
const content = await readFile8(filePath, "utf-8");
|
|
8470
|
+
for (const file of configEnvFiles) {
|
|
8471
|
+
const filePath = join7(dir, file);
|
|
8472
|
+
if (existsSync5(filePath)) {
|
|
8473
|
+
const content = await readFile6(filePath, "utf-8");
|
|
8462
8474
|
const parsed = parseEnvFile(content);
|
|
8463
8475
|
for (const key of Object.keys(parsed)) {
|
|
8464
8476
|
keys.add(key);
|
|
@@ -8546,7 +8558,7 @@ var scanCommand = new Command17("scan").description("Scan codebase for env var r
|
|
|
8546
8558
|
let totalMissing = 0;
|
|
8547
8559
|
let totalVars = 0;
|
|
8548
8560
|
for (const [appName, appConfig] of Object.entries(config.apps)) {
|
|
8549
|
-
const appDir =
|
|
8561
|
+
const appDir = join7(repoRoot, appConfig.path);
|
|
8550
8562
|
try {
|
|
8551
8563
|
await stat(appDir);
|
|
8552
8564
|
} catch {
|
|
@@ -8569,7 +8581,7 @@ var scanCommand = new Command17("scan").description("Scan codebase for env var r
|
|
|
8569
8581
|
} else {
|
|
8570
8582
|
envData = allEnvData;
|
|
8571
8583
|
}
|
|
8572
|
-
const localKeys = await getLocalEnvKeys(appDir);
|
|
8584
|
+
const localKeys = await getLocalEnvKeys(appDir, config.envFiles);
|
|
8573
8585
|
spinner.stop();
|
|
8574
8586
|
const missing = printTable(
|
|
8575
8587
|
appName,
|
|
@@ -8600,7 +8612,7 @@ var scanCommand = new Command17("scan").description("Scan codebase for env var r
|
|
|
8600
8612
|
}
|
|
8601
8613
|
console.log();
|
|
8602
8614
|
} else {
|
|
8603
|
-
const scanRoot = context.app && config.apps?.[context.app] ?
|
|
8615
|
+
const scanRoot = context.app && config.apps?.[context.app] ? join7(repoRoot, config.apps[context.app].path) : cwd;
|
|
8604
8616
|
const refs = await scanDir(scanRoot);
|
|
8605
8617
|
if (refs.size === 0) {
|
|
8606
8618
|
spinner.succeed(
|
|
@@ -8609,7 +8621,7 @@ var scanCommand = new Command17("scan").description("Scan codebase for env var r
|
|
|
8609
8621
|
return;
|
|
8610
8622
|
}
|
|
8611
8623
|
spinner.text = `Found ${refs.size} variables. Checking environments...`;
|
|
8612
|
-
const localKeys = await getLocalEnvKeys(scanRoot);
|
|
8624
|
+
const localKeys = await getLocalEnvKeys(scanRoot, config.envFiles);
|
|
8613
8625
|
spinner.stop();
|
|
8614
8626
|
const label = context.app ?? "project";
|
|
8615
8627
|
const missingCount = printTable(
|
|
@@ -8640,9 +8652,32 @@ var scanCommand = new Command17("scan").description("Scan codebase for env var r
|
|
|
8640
8652
|
}
|
|
8641
8653
|
});
|
|
8642
8654
|
|
|
8655
|
+
// src/commands/update.ts
|
|
8656
|
+
import { Command as Command18 } from "commander";
|
|
8657
|
+
import { execSync as execSync2 } from "child_process";
|
|
8658
|
+
import chalk20 from "chalk";
|
|
8659
|
+
import ora12 from "ora";
|
|
8660
|
+
var updateCommand = new Command18("update").description("Update ev to the latest version").action(async () => {
|
|
8661
|
+
const spinner = ora12("Checking for updates...").start();
|
|
8662
|
+
try {
|
|
8663
|
+
const latest = execSync2("npm view @rowlabs/ev version", { encoding: "utf-8" }).trim();
|
|
8664
|
+
const current = "0.3.1";
|
|
8665
|
+
if (current === latest) {
|
|
8666
|
+
spinner.succeed(chalk20.green(`Already on the latest version (${current})`));
|
|
8667
|
+
return;
|
|
8668
|
+
}
|
|
8669
|
+
spinner.text = `Updating ${current} \u2192 ${latest}...`;
|
|
8670
|
+
execSync2("npm i -g @rowlabs/ev@latest", { stdio: "pipe" });
|
|
8671
|
+
spinner.succeed(chalk20.green(`Updated to ${latest}`));
|
|
8672
|
+
} catch (err) {
|
|
8673
|
+
spinner.fail(chalk20.red(`Update failed: ${err.message}`));
|
|
8674
|
+
process.exit(1);
|
|
8675
|
+
}
|
|
8676
|
+
});
|
|
8677
|
+
|
|
8643
8678
|
// src/index.ts
|
|
8644
|
-
var program = new
|
|
8645
|
-
program.name("ev").description("Git for env vars \u2014 sync environment variables across teams securely").version("0.3.
|
|
8679
|
+
var program = new Command19();
|
|
8680
|
+
program.name("ev").description("Git for env vars \u2014 sync environment variables across teams securely").version("0.3.1");
|
|
8646
8681
|
program.addCommand(loginCommand);
|
|
8647
8682
|
program.addCommand(initCommand);
|
|
8648
8683
|
program.addCommand(pushCommand);
|
|
@@ -8660,6 +8695,7 @@ program.addCommand(importCommand);
|
|
|
8660
8695
|
program.addCommand(doctorCommand);
|
|
8661
8696
|
program.addCommand(completionsCommand);
|
|
8662
8697
|
program.addCommand(scanCommand);
|
|
8698
|
+
program.addCommand(updateCommand);
|
|
8663
8699
|
async function main() {
|
|
8664
8700
|
await initCrypto();
|
|
8665
8701
|
program.parse();
|