@rowlabs/ev 0.4.1 → 0.4.3
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 +191 -39
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6577,6 +6577,9 @@ var init_api_client = __esm({
|
|
|
6577
6577
|
githubRepo
|
|
6578
6578
|
});
|
|
6579
6579
|
}
|
|
6580
|
+
async deleteProject(projectId) {
|
|
6581
|
+
return this.request("DELETE", `/projects/${projectId}`);
|
|
6582
|
+
}
|
|
6580
6583
|
async updateProjectName(projectId, name) {
|
|
6581
6584
|
return this.request("PATCH", `/projects/${projectId}`, {
|
|
6582
6585
|
name
|
|
@@ -6650,6 +6653,9 @@ var init_api_client = __esm({
|
|
|
6650
6653
|
async deleteEnvironment(appId, envId) {
|
|
6651
6654
|
return this.request("DELETE", `/apps/${appId}/environments/${envId}`);
|
|
6652
6655
|
}
|
|
6656
|
+
async deleteApp(projectId, appId) {
|
|
6657
|
+
return this.request("DELETE", `/projects/${projectId}/apps/${appId}`);
|
|
6658
|
+
}
|
|
6653
6659
|
async rotateKeys(projectId, body) {
|
|
6654
6660
|
return this.request("POST", `/projects/${projectId}/rotate`, body);
|
|
6655
6661
|
}
|
|
@@ -6729,7 +6735,7 @@ var init_auth = __esm({
|
|
|
6729
6735
|
|
|
6730
6736
|
// src/index.ts
|
|
6731
6737
|
init_dist();
|
|
6732
|
-
import { Command as
|
|
6738
|
+
import { Command as Command20 } from "commander";
|
|
6733
6739
|
|
|
6734
6740
|
// src/commands/login.ts
|
|
6735
6741
|
init_dist();
|
|
@@ -6917,11 +6923,11 @@ var initCommand = new Command2("init").description("Initialize ev in the current
|
|
|
6917
6923
|
console.log(` ${app.name} \u2192 ${app.path}`);
|
|
6918
6924
|
}
|
|
6919
6925
|
const rl = await import("readline");
|
|
6920
|
-
const
|
|
6926
|
+
const readline3 = rl.createInterface({ input: process.stdin, output: process.stdout });
|
|
6921
6927
|
const answer = await new Promise((resolve) => {
|
|
6922
|
-
|
|
6928
|
+
readline3.question("\nAdd these as app mappings to ev.yaml? (y/N) ", resolve);
|
|
6923
6929
|
});
|
|
6924
|
-
|
|
6930
|
+
readline3.close();
|
|
6925
6931
|
if (answer.toLowerCase() === "y") {
|
|
6926
6932
|
const appsConfig = {};
|
|
6927
6933
|
for (const app of detectedApps) {
|
|
@@ -7579,16 +7585,16 @@ envCommand.command("create <name>").description("Create a new environment").acti
|
|
|
7579
7585
|
});
|
|
7580
7586
|
envCommand.command("delete <name>").description("Delete an environment").action(async (name) => {
|
|
7581
7587
|
const rl = await import("readline");
|
|
7582
|
-
const
|
|
7588
|
+
const readline3 = rl.createInterface({ input: process.stdin, output: process.stdout });
|
|
7583
7589
|
const answer = await new Promise((resolve) => {
|
|
7584
|
-
|
|
7590
|
+
readline3.question(
|
|
7585
7591
|
chalk10.red(
|
|
7586
7592
|
`Delete environment "${name}"? This removes all secrets and releases. Type the environment name to confirm: `
|
|
7587
7593
|
),
|
|
7588
7594
|
resolve
|
|
7589
7595
|
);
|
|
7590
7596
|
});
|
|
7591
|
-
|
|
7597
|
+
readline3.close();
|
|
7592
7598
|
if (answer !== name) {
|
|
7593
7599
|
console.log(chalk10.yellow("Cancelled."));
|
|
7594
7600
|
process.exit(0);
|
|
@@ -7722,11 +7728,11 @@ accessCommand.command("grant <email>").description("Invite a user and share the
|
|
|
7722
7728
|
});
|
|
7723
7729
|
accessCommand.command("revoke <email>").description("Remove a member's access").action(async (email) => {
|
|
7724
7730
|
const rl = await import("readline");
|
|
7725
|
-
const
|
|
7731
|
+
const readline3 = rl.createInterface({ input: process.stdin, output: process.stdout });
|
|
7726
7732
|
const answer = await new Promise((resolve) => {
|
|
7727
|
-
|
|
7733
|
+
readline3.question(chalk12.yellow(`Revoke access for ${email}? (y/N) `), resolve);
|
|
7728
7734
|
});
|
|
7729
|
-
|
|
7735
|
+
readline3.close();
|
|
7730
7736
|
if (answer.toLowerCase() !== "y") {
|
|
7731
7737
|
console.log(chalk12.yellow("Cancelled."));
|
|
7732
7738
|
process.exit(0);
|
|
@@ -7947,6 +7953,18 @@ init_auth();
|
|
|
7947
7953
|
import { Command as Command12 } from "commander";
|
|
7948
7954
|
import { execSync } from "child_process";
|
|
7949
7955
|
import chalk14 from "chalk";
|
|
7956
|
+
async function getSecretValue(projectId, appName, envName, key, isEvBackend, backendConfig) {
|
|
7957
|
+
const envId = await resolveEnvironmentId(projectId, appName, envName);
|
|
7958
|
+
const client = await createApiClient(backendConfig);
|
|
7959
|
+
const response = await client.pullSecrets(envId);
|
|
7960
|
+
const secret = response.secrets.find((s2) => s2.key === key);
|
|
7961
|
+
if (!secret) return null;
|
|
7962
|
+
if (isEvBackend) {
|
|
7963
|
+
const projectKey = await getDecryptedProjectKey(projectId);
|
|
7964
|
+
return decryptSecret(secret.encryptedValue, projectKey);
|
|
7965
|
+
}
|
|
7966
|
+
return secret.encryptedValue;
|
|
7967
|
+
}
|
|
7950
7968
|
var getCommand = new Command12("get").description("Get a single secret value").argument("<key>", "Secret key name").argument("[target]", "app:env or env").option("-c, --copy", "Copy to clipboard").action(async (key, target, options) => {
|
|
7951
7969
|
try {
|
|
7952
7970
|
const resolved = await resolveCurrentContext(target);
|
|
@@ -7954,40 +7972,94 @@ var getCommand = new Command12("get").description("Get a single secret value").a
|
|
|
7954
7972
|
console.error(chalk14.red("No ev.yaml found. Run `ev init` first."));
|
|
7955
7973
|
process.exit(1);
|
|
7956
7974
|
}
|
|
7957
|
-
const { context, backendConfig } = resolved;
|
|
7975
|
+
const { context, backendConfig, repoRoot } = resolved;
|
|
7958
7976
|
const isEvBackend = !backendConfig || backendConfig.type === "ev";
|
|
7959
|
-
const
|
|
7960
|
-
const
|
|
7961
|
-
const
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
|
|
7977
|
-
|
|
7978
|
-
|
|
7979
|
-
|
|
7980
|
-
|
|
7981
|
-
|
|
7977
|
+
const config = await loadEvConfig(process.cwd());
|
|
7978
|
+
const cwd = process.cwd();
|
|
7979
|
+
const isAtRoot = cwd === repoRoot;
|
|
7980
|
+
if (isAtRoot && !target && config?.apps && Object.keys(config.apps).length > 0) {
|
|
7981
|
+
const results = [];
|
|
7982
|
+
for (const appName of Object.keys(config.apps)) {
|
|
7983
|
+
try {
|
|
7984
|
+
const value = await getSecretValue(
|
|
7985
|
+
context.project,
|
|
7986
|
+
appName,
|
|
7987
|
+
context.env,
|
|
7988
|
+
key,
|
|
7989
|
+
isEvBackend,
|
|
7990
|
+
backendConfig
|
|
7991
|
+
);
|
|
7992
|
+
if (value !== null) {
|
|
7993
|
+
results.push({ app: appName, value });
|
|
7994
|
+
}
|
|
7995
|
+
} catch {
|
|
7996
|
+
}
|
|
7997
|
+
}
|
|
7998
|
+
if (results.length === 0) {
|
|
7999
|
+
console.error(chalk14.red(`Secret "${key}" not found in any app`));
|
|
8000
|
+
process.exit(1);
|
|
8001
|
+
}
|
|
8002
|
+
if (results.length === 1) {
|
|
8003
|
+
outputValue(results[0].value, key, options?.copy);
|
|
8004
|
+
} else {
|
|
8005
|
+
const allSame = results.every((r2) => r2.value === results[0].value);
|
|
8006
|
+
if (allSame && options?.copy) {
|
|
8007
|
+
outputValue(results[0].value, key, true);
|
|
8008
|
+
} else {
|
|
8009
|
+
console.error(chalk14.bold(`
|
|
8010
|
+
${key} found in ${results.length} apps:
|
|
8011
|
+
`));
|
|
8012
|
+
for (const { app, value } of results) {
|
|
8013
|
+
const masked = value.length <= 8 ? "****" : value.slice(0, 4) + "****" + value.slice(-4);
|
|
8014
|
+
console.error(` ${chalk14.cyan(app.padEnd(20))} ${chalk14.dim(masked)}`);
|
|
8015
|
+
}
|
|
8016
|
+
console.error();
|
|
8017
|
+
if (allSame) {
|
|
8018
|
+
console.error(chalk14.dim(" All values are identical."));
|
|
8019
|
+
outputValue(results[0].value, key, options?.copy);
|
|
8020
|
+
} else {
|
|
8021
|
+
console.error(
|
|
8022
|
+
chalk14.dim(" Values differ. Specify an app: ev get " + key + " AppName:dev")
|
|
8023
|
+
);
|
|
8024
|
+
}
|
|
8025
|
+
}
|
|
7982
8026
|
}
|
|
7983
8027
|
} else {
|
|
7984
|
-
|
|
8028
|
+
const value = await getSecretValue(
|
|
8029
|
+
context.project,
|
|
8030
|
+
context.app,
|
|
8031
|
+
context.env,
|
|
8032
|
+
key,
|
|
8033
|
+
isEvBackend,
|
|
8034
|
+
backendConfig
|
|
8035
|
+
);
|
|
8036
|
+
if (value === null) {
|
|
8037
|
+
console.error(
|
|
8038
|
+
chalk14.red(
|
|
8039
|
+
`Secret "${key}" not found in ${context.app ?? "default"}:${context.env}`
|
|
8040
|
+
)
|
|
8041
|
+
);
|
|
8042
|
+
process.exit(1);
|
|
8043
|
+
}
|
|
8044
|
+
outputValue(value, key, options?.copy);
|
|
7985
8045
|
}
|
|
7986
8046
|
} catch (err) {
|
|
7987
8047
|
console.error(chalk14.red(`Failed: ${err.message}`));
|
|
7988
8048
|
process.exit(1);
|
|
7989
8049
|
}
|
|
7990
8050
|
});
|
|
8051
|
+
function outputValue(value, key, copy) {
|
|
8052
|
+
if (copy) {
|
|
8053
|
+
try {
|
|
8054
|
+
execSync("pbcopy", { input: value });
|
|
8055
|
+
console.error(chalk14.green(`Copied ${key} to clipboard`));
|
|
8056
|
+
} catch {
|
|
8057
|
+
process.stdout.write(value);
|
|
8058
|
+
}
|
|
8059
|
+
} else {
|
|
8060
|
+
process.stdout.write(value);
|
|
8061
|
+
}
|
|
8062
|
+
}
|
|
7991
8063
|
|
|
7992
8064
|
// src/commands/backend.ts
|
|
7993
8065
|
init_config();
|
|
@@ -8415,6 +8487,7 @@ _ev() {
|
|
|
8415
8487
|
'update:Update ev to latest version'
|
|
8416
8488
|
'doctor:Check setup for issues'
|
|
8417
8489
|
'completions:Generate shell completions'
|
|
8490
|
+
'delete:Delete an app or environment'
|
|
8418
8491
|
)
|
|
8419
8492
|
|
|
8420
8493
|
_arguments -C \\
|
|
@@ -8467,7 +8540,7 @@ var BASH_COMPLETION = `_ev() {
|
|
|
8467
8540
|
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
8468
8541
|
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
8469
8542
|
|
|
8470
|
-
commands="login init push pull diff log get rollback env promote access backend import status scan doctor completions update"
|
|
8543
|
+
commands="login init push pull diff log get rollback env promote access backend import status scan doctor completions update delete"
|
|
8471
8544
|
|
|
8472
8545
|
case "\${prev}" in
|
|
8473
8546
|
ev)
|
|
@@ -8521,6 +8594,7 @@ complete -c ev -n '__fish_use_subcommand' -a scan -d 'Scan for env var reference
|
|
|
8521
8594
|
complete -c ev -n '__fish_use_subcommand' -a update -d 'Update ev to latest version'
|
|
8522
8595
|
complete -c ev -n '__fish_use_subcommand' -a doctor -d 'Check setup for issues'
|
|
8523
8596
|
complete -c ev -n '__fish_use_subcommand' -a completions -d 'Generate shell completions'
|
|
8597
|
+
complete -c ev -n '__fish_use_subcommand' -a delete -d 'Delete an app or environment'
|
|
8524
8598
|
|
|
8525
8599
|
# env subcommands
|
|
8526
8600
|
complete -c ev -n '__fish_seen_subcommand_from env' -a create -d 'Create environment'
|
|
@@ -8857,7 +8931,7 @@ var updateCommand = new Command18("update").description("Update ev to the latest
|
|
|
8857
8931
|
const spinner = ora12("Checking for updates...").start();
|
|
8858
8932
|
try {
|
|
8859
8933
|
const latest = execSync2("npm view @rowlabs/ev version", { encoding: "utf-8" }).trim();
|
|
8860
|
-
const current = "0.4.
|
|
8934
|
+
const current = "0.4.3";
|
|
8861
8935
|
if (current === latest) {
|
|
8862
8936
|
spinner.succeed(chalk20.green(`Already on the latest version (${current})`));
|
|
8863
8937
|
return;
|
|
@@ -8871,9 +8945,86 @@ var updateCommand = new Command18("update").description("Update ev to the latest
|
|
|
8871
8945
|
}
|
|
8872
8946
|
});
|
|
8873
8947
|
|
|
8948
|
+
// src/commands/delete.ts
|
|
8949
|
+
init_api_client();
|
|
8950
|
+
init_config();
|
|
8951
|
+
import { Command as Command19 } from "commander";
|
|
8952
|
+
import chalk21 from "chalk";
|
|
8953
|
+
import ora13 from "ora";
|
|
8954
|
+
import readline2 from "readline";
|
|
8955
|
+
var deleteCommand = new Command19("delete").description("Delete an app or environment").argument("<target>", "App name to delete, or app:env to delete a specific environment").action(async (target) => {
|
|
8956
|
+
try {
|
|
8957
|
+
const resolved = await resolveCurrentContext();
|
|
8958
|
+
if (!resolved) {
|
|
8959
|
+
console.error(chalk21.red("No ev.yaml found. Run `ev init` first."));
|
|
8960
|
+
process.exit(1);
|
|
8961
|
+
}
|
|
8962
|
+
const { context } = resolved;
|
|
8963
|
+
const client = await createApiClient();
|
|
8964
|
+
const project = await client.getProject(context.project);
|
|
8965
|
+
if (target.includes(":")) {
|
|
8966
|
+
const [appName, envName] = target.split(":");
|
|
8967
|
+
const app = project.apps.find((a2) => a2.name === appName);
|
|
8968
|
+
if (!app) {
|
|
8969
|
+
console.error(chalk21.red(`App "${appName}" not found`));
|
|
8970
|
+
process.exit(1);
|
|
8971
|
+
}
|
|
8972
|
+
const envs = await client.listEnvironments(app.id);
|
|
8973
|
+
const env = envs.find((e) => e.name === envName);
|
|
8974
|
+
if (!env) {
|
|
8975
|
+
console.error(chalk21.red(`Environment "${envName}" not found in app "${appName}"`));
|
|
8976
|
+
process.exit(1);
|
|
8977
|
+
}
|
|
8978
|
+
const answer = await prompt2(
|
|
8979
|
+
chalk21.red(`Delete environment "${appName}:${envName}"? This removes all secrets and releases. Type the environment name to confirm: `)
|
|
8980
|
+
);
|
|
8981
|
+
if (answer !== envName) {
|
|
8982
|
+
console.log(chalk21.yellow("Cancelled."));
|
|
8983
|
+
process.exit(0);
|
|
8984
|
+
}
|
|
8985
|
+
const spinner = ora13(`Deleting ${appName}:${envName}...`).start();
|
|
8986
|
+
await client.deleteEnvironment(app.id, env.id);
|
|
8987
|
+
spinner.succeed(chalk21.green(`Deleted environment "${appName}:${envName}"`));
|
|
8988
|
+
} else {
|
|
8989
|
+
const appName = target;
|
|
8990
|
+
const app = project.apps.find((a2) => a2.name === appName);
|
|
8991
|
+
if (!app) {
|
|
8992
|
+
console.error(chalk21.red(`App "${appName}" not found`));
|
|
8993
|
+
process.exit(1);
|
|
8994
|
+
}
|
|
8995
|
+
const envs = await client.listEnvironments(app.id);
|
|
8996
|
+
const secretCount = envs.reduce((sum, e) => sum + e.secretCount, 0);
|
|
8997
|
+
const answer = await prompt2(
|
|
8998
|
+
chalk21.red(
|
|
8999
|
+
`Delete app "${appName}"? This removes ${envs.length} environment${envs.length === 1 ? "" : "s"} and ${secretCount} secret${secretCount === 1 ? "" : "s"}. Type the app name to confirm: `
|
|
9000
|
+
)
|
|
9001
|
+
);
|
|
9002
|
+
if (answer !== appName) {
|
|
9003
|
+
console.log(chalk21.yellow("Cancelled."));
|
|
9004
|
+
process.exit(0);
|
|
9005
|
+
}
|
|
9006
|
+
const spinner = ora13(`Deleting ${appName}...`).start();
|
|
9007
|
+
await client.deleteApp(context.project, app.id);
|
|
9008
|
+
spinner.succeed(chalk21.green(`Deleted app "${appName}"`));
|
|
9009
|
+
}
|
|
9010
|
+
} catch (err) {
|
|
9011
|
+
console.error(chalk21.red(`Delete failed: ${err.message}`));
|
|
9012
|
+
process.exit(1);
|
|
9013
|
+
}
|
|
9014
|
+
});
|
|
9015
|
+
function prompt2(question) {
|
|
9016
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
9017
|
+
return new Promise((resolve) => {
|
|
9018
|
+
rl.question(question, (answer) => {
|
|
9019
|
+
rl.close();
|
|
9020
|
+
resolve(answer.trim());
|
|
9021
|
+
});
|
|
9022
|
+
});
|
|
9023
|
+
}
|
|
9024
|
+
|
|
8874
9025
|
// src/index.ts
|
|
8875
|
-
var program = new
|
|
8876
|
-
program.name("ev").description("Git for env vars \u2014 sync environment variables across teams securely").version("0.4.
|
|
9026
|
+
var program = new Command20();
|
|
9027
|
+
program.name("ev").description("Git for env vars \u2014 sync environment variables across teams securely").version("0.4.3");
|
|
8877
9028
|
program.addCommand(loginCommand);
|
|
8878
9029
|
program.addCommand(initCommand);
|
|
8879
9030
|
program.addCommand(pushCommand);
|
|
@@ -8892,6 +9043,7 @@ program.addCommand(doctorCommand);
|
|
|
8892
9043
|
program.addCommand(completionsCommand);
|
|
8893
9044
|
program.addCommand(scanCommand);
|
|
8894
9045
|
program.addCommand(updateCommand);
|
|
9046
|
+
program.addCommand(deleteCommand);
|
|
8895
9047
|
async function main() {
|
|
8896
9048
|
await initCrypto();
|
|
8897
9049
|
program.parse();
|