@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.
Files changed (2) hide show
  1. package/dist/index.js +90 -54
  2. 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 Command18 } from "commander";
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 = { project: project.id, default_env: "dev" };
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 envPath = join4(process.cwd(), ".env");
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 variables found in .env"));
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 { readFile as readFile5, writeFile as writeFile4, copyFile } from "fs/promises";
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
- if (existsSync5(envPath)) {
7121
- const localContent = await readFile5(envPath, "utf-8");
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 envPath = join6(process.cwd(), ".env");
7203
- if (!existsSync6(envPath)) {
7204
- spinner.fail(chalk6.red("No .env file found"));
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
- const localContent = await readFile6(envPath, "utf-8");
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 readFile7, writeFile as writeFile5 } from "fs/promises";
7789
- import { join as join7 } from "path";
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 readFile7(join7(root, "ev.yaml"), "utf-8");
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 = join7(root, "ev.yaml");
7862
- const content = await readFile7(configPath, "utf-8");
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 readFile8, stat } from "fs/promises";
8366
- import { existsSync as existsSync7 } from "fs";
8367
- import { join as join8, extname } from "path";
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 = join8(currentDir, entry);
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 readFile8(fullPath, "utf-8");
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 envFiles = [".env", ".env.local"];
8458
- for (const file of envFiles) {
8459
- const filePath = join8(dir, file);
8460
- if (existsSync7(filePath)) {
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 = join8(repoRoot, appConfig.path);
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] ? join8(repoRoot, config.apps[context.app].path) : cwd;
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 Command18();
8645
- program.name("ev").description("Git for env vars \u2014 sync environment variables across teams securely").version("0.3.0");
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowlabs/ev",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Git for env vars — sync environment variables across teams securely",
5
5
  "type": "module",
6
6
  "bin": {