@catladder/cli 0.0.0
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/.nvmrc +1 -0
- package/CONTRIBUTING.md +83 -0
- package/README.md +31 -0
- package/bin/catenv.sh +1 -0
- package/bin/catladder +3 -0
- package/includes/envrc +35 -0
- package/package.json +65 -0
- package/src/apps/catenv/catenv.ts +41 -0
- package/src/apps/shell/commands/general/index.ts +132 -0
- package/src/apps/shell/commands/general/namespaceAutoCompletion.ts +7 -0
- package/src/apps/shell/commands/general/portForward.ts +47 -0
- package/src/apps/shell/commands/mongodb/index.ts +10 -0
- package/src/apps/shell/commands/mongodb/projectMongoDestroyMember.ts +134 -0
- package/src/apps/shell/commands/mongodb/projectMongoGetShell.ts +41 -0
- package/src/apps/shell/commands/mongodb/projectMongoPortForward.ts +42 -0
- package/src/apps/shell/commands/mongodb/utils/index.ts +84 -0
- package/src/apps/shell/commands/project/commandCloudSqlProxy.ts +65 -0
- package/src/apps/shell/commands/project/commandConfigSecrets.ts +245 -0
- package/src/apps/shell/commands/project/commandCopyDB.ts +93 -0
- package/src/apps/shell/commands/project/commandDeletePods.ts +50 -0
- package/src/apps/shell/commands/project/commandDeleteProject.ts +34 -0
- package/src/apps/shell/commands/project/commandEnvVars.ts +17 -0
- package/src/apps/shell/commands/project/commandGetMyTotalWorktime.ts +14 -0
- package/src/apps/shell/commands/project/commandGetShell.ts +35 -0
- package/src/apps/shell/commands/project/commandGitlabCi.ts +114 -0
- package/src/apps/shell/commands/project/commandInitGitlab.ts +157 -0
- package/src/apps/shell/commands/project/commandInitProject.ts +282 -0
- package/src/apps/shell/commands/project/commandListPods.ts +21 -0
- package/src/apps/shell/commands/project/commandMigrateHelm3.ts +53 -0
- package/src/apps/shell/commands/project/commandNamespace.ts +12 -0
- package/src/apps/shell/commands/project/commandOpenCostDashboard.ts +29 -0
- package/src/apps/shell/commands/project/commandOpenDashboard.ts +27 -0
- package/src/apps/shell/commands/project/commandOpenEnv.ts +18 -0
- package/src/apps/shell/commands/project/commandOpenGit.ts +12 -0
- package/src/apps/shell/commands/project/commandOpenGrafana.ts +31 -0
- package/src/apps/shell/commands/project/commandOpenGrafanaPod.ts +46 -0
- package/src/apps/shell/commands/project/commandOpenLogs.ts +23 -0
- package/src/apps/shell/commands/project/commandPauseProject.ts +31 -0
- package/src/apps/shell/commands/project/commandPortForward.ts +45 -0
- package/src/apps/shell/commands/project/commandTriggerCronjob.ts +61 -0
- package/src/apps/shell/commands/project/commandVariables.ts +13 -0
- package/src/apps/shell/commands/project/index.ts +62 -0
- package/src/apps/shell/commands/project/utils/autocompletions.ts +7 -0
- package/src/apps/shell/commands/project/utils/ensureCluster.ts +31 -0
- package/src/apps/shell/commands/project/utils/ensureNamespace.ts +32 -0
- package/src/apps/shell/commands/project/utils/monorepo.ts +45 -0
- package/src/apps/shell/commands/shared/index.ts +31 -0
- package/src/apps/shell/commands/theStuffThatReallyMatters/index.ts +51 -0
- package/src/apps/shell/shell.ts +30 -0
- package/src/apps/shell/utils/getGoogleAuthUserNumber.ts +23 -0
- package/src/config/clusters.ts +45 -0
- package/src/config/constants.ts +5 -0
- package/src/index.ts +17 -0
- package/src/k8sApi/index.ts +17 -0
- package/src/packageInfos.ts +4 -0
- package/src/types/child-process-promise.d.ts +1 -0
- package/src/types/command-exists-promise.d.ts +1 -0
- package/src/types/git-repo-name.d.ts +1 -0
- package/src/types/types.ts +20 -0
- package/src/types/yawn-yaml.d.ts +1 -0
- package/src/utils/cluster.ts +21 -0
- package/src/utils/dashboardToken.ts +20 -0
- package/src/utils/files.ts +18 -0
- package/src/utils/formatEnvVars.ts +7 -0
- package/src/utils/getEditor.ts +16 -0
- package/src/utils/gitlab.ts +80 -0
- package/src/utils/log.ts +13 -0
- package/src/utils/passwordstore/index.ts +192 -0
- package/src/utils/portForward.ts +52 -0
- package/src/utils/preferences/index.ts +33 -0
- package/src/utils/projects/index.ts +171 -0
- package/src/utils/promise.ts +11 -0
- package/src/utils/shell.ts +20 -0
- package/tsconfig.json +20 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import updateNotifier from "update-notifier";
|
|
2
|
+
import { argv } from "yargs";
|
|
3
|
+
import catenv from "./apps/catenv/catenv";
|
|
4
|
+
import shell from "./apps/shell/shell";
|
|
5
|
+
import packageInfos from "./packageInfos";
|
|
6
|
+
|
|
7
|
+
// tslint:disable-next-line:no-var-requires
|
|
8
|
+
|
|
9
|
+
updateNotifier({
|
|
10
|
+
pkg: packageInfos,
|
|
11
|
+
}).notify();
|
|
12
|
+
|
|
13
|
+
if (argv.catenv) {
|
|
14
|
+
catenv();
|
|
15
|
+
} else {
|
|
16
|
+
shell();
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
KubeConfig,
|
|
3
|
+
CoreV1Api,
|
|
4
|
+
BatchV1Api,
|
|
5
|
+
BatchV1beta1Api,
|
|
6
|
+
} from "@kubernetes/client-node";
|
|
7
|
+
|
|
8
|
+
const kc = new KubeConfig();
|
|
9
|
+
|
|
10
|
+
kc.loadFromDefault();
|
|
11
|
+
|
|
12
|
+
const k8sApi = kc.makeApiClient(CoreV1Api);
|
|
13
|
+
|
|
14
|
+
export const k8sApiBatch = kc.makeApiClient(BatchV1Api);
|
|
15
|
+
export const k8sApiBatchBeta = kc.makeApiClient(BatchV1beta1Api);
|
|
16
|
+
|
|
17
|
+
export default k8sApi;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module 'child-process-promise'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module "command-exists-promise";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module "git-repo-name";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type Env = "dev-local" | "prod" | "dev" | "stage" | "review";
|
|
2
|
+
|
|
3
|
+
export type KubernetesSecretName = string;
|
|
4
|
+
export interface ISecrets {
|
|
5
|
+
[key: string]: KubernetesSecretName;
|
|
6
|
+
}
|
|
7
|
+
export interface IValueFile {
|
|
8
|
+
env?: {
|
|
9
|
+
public?: {
|
|
10
|
+
[key: string]: string;
|
|
11
|
+
};
|
|
12
|
+
secret?: ISecrets;
|
|
13
|
+
};
|
|
14
|
+
cloudsql?: {
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
instanceId?: string;
|
|
17
|
+
region?: string;
|
|
18
|
+
projectId?: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module "yawn-yaml/cjs";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { exec } from "child-process-promise";
|
|
2
|
+
import { findKey } from "lodash";
|
|
3
|
+
import clusters from "../config/clusters";
|
|
4
|
+
export const getCurrentContext = async () =>
|
|
5
|
+
(await exec("kubectl config current-context")).stdout.trim();
|
|
6
|
+
|
|
7
|
+
export const getCurrentConnectedClusterName = async () => {
|
|
8
|
+
const currentContext = await getCurrentContext();
|
|
9
|
+
return findKey(clusters, { fullName: currentContext });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const getClusterByName = (name: string) => {
|
|
13
|
+
return clusters[name];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const getAllClusterNames = () => Object.keys(clusters);
|
|
17
|
+
|
|
18
|
+
export const connectToCluster = async (clusterName: string) => {
|
|
19
|
+
const { connect } = getClusterByName(clusterName);
|
|
20
|
+
await connect();
|
|
21
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { exec } from "child-process-promise";
|
|
2
|
+
import clipboardy from "clipboardy";
|
|
3
|
+
export const getToken = async () => {
|
|
4
|
+
const { stdout } = await exec(
|
|
5
|
+
`kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | awk '/^deployment-controller-token-/{print $1}') | awk '$1=="token:"{print $2}'`
|
|
6
|
+
);
|
|
7
|
+
return stdout;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export async function printToken() {
|
|
11
|
+
await this.log(
|
|
12
|
+
"you will need to pass a token to the dashboard. We will copy it to your clipboard!"
|
|
13
|
+
);
|
|
14
|
+
const token = await getToken();
|
|
15
|
+
await this.log("");
|
|
16
|
+
await this.log(token);
|
|
17
|
+
await this.log("");
|
|
18
|
+
await clipboardy.write(token);
|
|
19
|
+
await this.log("");
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import yaml from "js-yaml";
|
|
3
|
+
|
|
4
|
+
const readFile = async (file: string) =>
|
|
5
|
+
fs.readFile(file, { encoding: "utf-8" });
|
|
6
|
+
|
|
7
|
+
export const readFileOrError = async (filePath: string) => {
|
|
8
|
+
try {
|
|
9
|
+
const file = await readFile(filePath);
|
|
10
|
+
return [null, file];
|
|
11
|
+
} catch (e) {
|
|
12
|
+
return [e];
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const readYaml = async (filename: string) => {
|
|
17
|
+
return yaml.load(await readFile(filename));
|
|
18
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
2
|
+
import { spawn } from "child-process-promise";
|
|
3
|
+
import commandExists from "command-exists-promise";
|
|
4
|
+
|
|
5
|
+
let cmd: string;
|
|
6
|
+
export default async () => {
|
|
7
|
+
if (!cmd) {
|
|
8
|
+
cmd = (await commandExists("code")) ? "code --wait" : "vim";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
open: async (path: string) => {
|
|
13
|
+
await spawn(`${cmd} ${path}`, { shell: true, stdio: "inherit" });
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { exec } from "child-process-promise";
|
|
2
|
+
import fetch from "node-fetch";
|
|
3
|
+
import open from "open";
|
|
4
|
+
import { CommandInstance } from "vorpal";
|
|
5
|
+
import { getPreference, hasPreference, setPreference } from "./preferences";
|
|
6
|
+
|
|
7
|
+
const TOKEN_KEY = "gitlab-personal-access-token";
|
|
8
|
+
export const getGitlabToken = async (vorpal: CommandInstance) => {
|
|
9
|
+
if (!(await hasPreference(TOKEN_KEY))) {
|
|
10
|
+
vorpal.log("");
|
|
11
|
+
vorpal.log(
|
|
12
|
+
"☝ in order to access the api, we need a personal access token"
|
|
13
|
+
);
|
|
14
|
+
vorpal.log("Its best to create one specifically for catladder");
|
|
15
|
+
vorpal.log("");
|
|
16
|
+
vorpal.log("☝ we open up the settings page for you!");
|
|
17
|
+
vorpal.log("");
|
|
18
|
+
const { shouldContinue } = await vorpal.prompt({
|
|
19
|
+
default: true,
|
|
20
|
+
message: "Ok",
|
|
21
|
+
name: "shouldContinue",
|
|
22
|
+
type: "prompt",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
open("https://git.panter.ch/-/profile/personal_access_tokens");
|
|
26
|
+
|
|
27
|
+
vorpal.log("Please type in gitlab's personal access token");
|
|
28
|
+
|
|
29
|
+
const { personalToken } = await vorpal.prompt({
|
|
30
|
+
type: "string",
|
|
31
|
+
name: "personalToken",
|
|
32
|
+
default: "",
|
|
33
|
+
message: "Your personal access token ",
|
|
34
|
+
});
|
|
35
|
+
if (personalToken) {
|
|
36
|
+
await setPreference(TOKEN_KEY, personalToken);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return getPreference(TOKEN_KEY);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const doGitlabRequest = async (
|
|
43
|
+
vorpal: CommandInstance,
|
|
44
|
+
path: string,
|
|
45
|
+
data?: any
|
|
46
|
+
) => {
|
|
47
|
+
const rootToken = await getGitlabToken(vorpal);
|
|
48
|
+
const result = await fetch(`https://git.panter.ch/api/v4/${path}`, {
|
|
49
|
+
method: data ? "POST" : "GET",
|
|
50
|
+
headers: {
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
"Private-Token": rootToken,
|
|
53
|
+
},
|
|
54
|
+
body: JSON.stringify(data),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (result.status >= 200 && result.status < 400) {
|
|
58
|
+
return result.json();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Could not send request to gitlab api: ${result.status} "${
|
|
63
|
+
result.statusText
|
|
64
|
+
}".\nResponse: ${JSON.stringify(await result.json(), null, 2)}`
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const getProjectInfo = async (vorpal: CommandInstance): Promise<any> => {
|
|
69
|
+
const gitRemoteOriginUrl = (
|
|
70
|
+
await exec("git config --get remote.origin.url")
|
|
71
|
+
).stdout.trim();
|
|
72
|
+
const projectPath = /(https:\/\/|git@)git\.panter\.ch[:/](.*)\.git/g.exec(
|
|
73
|
+
gitRemoteOriginUrl
|
|
74
|
+
);
|
|
75
|
+
const project = await doGitlabRequest(
|
|
76
|
+
vorpal,
|
|
77
|
+
`projects/${encodeURIComponent(projectPath[2])}`
|
|
78
|
+
);
|
|
79
|
+
return project;
|
|
80
|
+
};
|
package/src/utils/log.ts
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { exec, spawn } from "child-process-promise";
|
|
2
|
+
import commandExists from "command-exists-promise";
|
|
3
|
+
import dayjs from "dayjs";
|
|
4
|
+
|
|
5
|
+
import { readFile, writeFile } from "fs-extra";
|
|
6
|
+
import yaml from "js-yaml";
|
|
7
|
+
import { withFile } from "tmp-promise";
|
|
8
|
+
import formatEnvVars from "../formatEnvVars";
|
|
9
|
+
import getEditor from "../getEditor";
|
|
10
|
+
import { getPreference, hasPreference, setPreference } from "../preferences";
|
|
11
|
+
|
|
12
|
+
const DEBUG = false;
|
|
13
|
+
|
|
14
|
+
const unlockBitwarden = async () => {
|
|
15
|
+
console.error("");
|
|
16
|
+
console.error("# Bitwarden is locked, please unlock:");
|
|
17
|
+
console.error("");
|
|
18
|
+
const promise = spawn("bw", ["unlock", "--raw"], {
|
|
19
|
+
stdio: ["inherit", "pipe", "inherit"],
|
|
20
|
+
});
|
|
21
|
+
let session = null;
|
|
22
|
+
promise.childProcess.stdout.on(
|
|
23
|
+
"data",
|
|
24
|
+
(d: any) => (session = d.toString("utf-8"))
|
|
25
|
+
);
|
|
26
|
+
await promise;
|
|
27
|
+
await setPreference("bwsession", session);
|
|
28
|
+
};
|
|
29
|
+
const loginBitwarden = async () => {
|
|
30
|
+
console.error("");
|
|
31
|
+
console.error("# Please login to Bitwarden:");
|
|
32
|
+
console.error("");
|
|
33
|
+
const promise = spawn("bw", ["login", "--raw"], {
|
|
34
|
+
stdio: ["inherit", "pipe", "inherit"],
|
|
35
|
+
});
|
|
36
|
+
let session = null;
|
|
37
|
+
promise.childProcess.stdout.on(
|
|
38
|
+
"data",
|
|
39
|
+
(d: any) => (session = d.toString("utf-8"))
|
|
40
|
+
);
|
|
41
|
+
await promise;
|
|
42
|
+
await setPreference("bwsession", session);
|
|
43
|
+
await syncBitwarden(true); // needs syncing to work properly afterwards
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const execBitwardenCommand = async (command: string): Promise<any> => {
|
|
47
|
+
if (!(await hasPreference("bwsession"))) {
|
|
48
|
+
await loginBitwarden();
|
|
49
|
+
}
|
|
50
|
+
const session = await getPreference("bwsession");
|
|
51
|
+
const fullCommand = `BW_SESSION='${session}' bw ${command} --raw --nointeraction`;
|
|
52
|
+
if (DEBUG) {
|
|
53
|
+
console.log(fullCommand);
|
|
54
|
+
console.time(fullCommand);
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const { stdout } = await exec(fullCommand);
|
|
58
|
+
if (DEBUG) {
|
|
59
|
+
console.timeEnd(fullCommand);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!stdout) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
return JSON.parse(stdout);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
// no json
|
|
69
|
+
return stdout;
|
|
70
|
+
}
|
|
71
|
+
} catch (e) {
|
|
72
|
+
const isLocked = e.toString().includes("Vault is locked");
|
|
73
|
+
const notLoggedIn = e.toString().includes("You are not logged in");
|
|
74
|
+
if (isLocked) {
|
|
75
|
+
await unlockBitwarden();
|
|
76
|
+
return execBitwardenCommand(command);
|
|
77
|
+
} else if (notLoggedIn) {
|
|
78
|
+
await loginBitwarden();
|
|
79
|
+
return execBitwardenCommand(command);
|
|
80
|
+
} else {
|
|
81
|
+
console.error(e);
|
|
82
|
+
|
|
83
|
+
console.log("wooops", e.message);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const getCollection = async (collectionName: string) => {
|
|
89
|
+
return execBitwardenCommand(`get collection ${collectionName}`);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const getOrganization = async (organizationName: string) => {
|
|
93
|
+
return execBitwardenCommand(`get organization ${organizationName}`);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
let catladderCollectionId: string;
|
|
97
|
+
|
|
98
|
+
const getCatladderCollectionId = async () => {
|
|
99
|
+
if (!catladderCollectionId) {
|
|
100
|
+
catladderCollectionId = (await getCollection("catladder")).id;
|
|
101
|
+
}
|
|
102
|
+
return catladderCollectionId;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
let panterOrganizationId: string;
|
|
106
|
+
const getPanterOrganizationId = async () => {
|
|
107
|
+
if (!panterOrganizationId) {
|
|
108
|
+
panterOrganizationId = (await getOrganization("Panter AG")).id;
|
|
109
|
+
}
|
|
110
|
+
return panterOrganizationId;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const encode = (data: any) =>
|
|
114
|
+
Buffer.from(JSON.stringify(data)).toString("base64");
|
|
115
|
+
|
|
116
|
+
export const hasBitwarden = () => commandExists("bw");
|
|
117
|
+
|
|
118
|
+
const getItem = async (path: string) => {
|
|
119
|
+
return execBitwardenCommand(`get item ${path}`);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const readPass = async (path: string) => {
|
|
123
|
+
const result = await getItem(path);
|
|
124
|
+
|
|
125
|
+
return result.notes || result.login?.password;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const update = async (type: string, itemId: string, value: any) => {
|
|
129
|
+
const result = await execBitwardenCommand(
|
|
130
|
+
`edit ${type} ${itemId} ${encode(value)}`
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return result;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const MAX_SYNC_AGE_IN_MINUTES = 30;
|
|
137
|
+
export const syncBitwarden = async (force = true) => {
|
|
138
|
+
const lastSync = (await hasPreference("bwLastSync"))
|
|
139
|
+
? await getPreference("bwLastSync")
|
|
140
|
+
: null;
|
|
141
|
+
if (
|
|
142
|
+
force ||
|
|
143
|
+
!lastSync ||
|
|
144
|
+
dayjs().diff(lastSync, "minutes") >= MAX_SYNC_AGE_IN_MINUTES
|
|
145
|
+
) {
|
|
146
|
+
await execBitwardenCommand("sync");
|
|
147
|
+
await setPreference("bwLastSync", new Date().toISOString());
|
|
148
|
+
} else {
|
|
149
|
+
// skip
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
export const insertPass = async (path: string, content: string) => {
|
|
153
|
+
const value = {
|
|
154
|
+
type: 2,
|
|
155
|
+
secureNote: { type: 0 },
|
|
156
|
+
name: path,
|
|
157
|
+
|
|
158
|
+
notes: content,
|
|
159
|
+
collectionIds: [await getCatladderCollectionId()],
|
|
160
|
+
};
|
|
161
|
+
const result = await execBitwardenCommand(`create item ${encode(value)}`);
|
|
162
|
+
await share(result.id);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const share = async (itemId: string) =>
|
|
166
|
+
execBitwardenCommand(
|
|
167
|
+
`share ${itemId} ${await getPanterOrganizationId()} ${encode([
|
|
168
|
+
await getCatladderCollectionId(),
|
|
169
|
+
])}`
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
export const editPass = async (path: string) => {
|
|
173
|
+
const item = await getItem(path);
|
|
174
|
+
|
|
175
|
+
await withFile(async ({ path: tmpFilePath }) => {
|
|
176
|
+
await writeFile(tmpFilePath, item.notes);
|
|
177
|
+
await (await getEditor()).open(tmpFilePath);
|
|
178
|
+
const newContent = (await readFile(tmpFilePath)).toString("utf-8");
|
|
179
|
+
|
|
180
|
+
await update("item", item.id, {
|
|
181
|
+
...item,
|
|
182
|
+
notes: newContent,
|
|
183
|
+
});
|
|
184
|
+
}, { postfix: ".yml" });
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export const readPassEnvVars = async (path: string) => {
|
|
188
|
+
// make sure that you have pulled pass beforehand
|
|
189
|
+
const yamlstring = await readPass(path);
|
|
190
|
+
// if a value is an object, we convert it to strings
|
|
191
|
+
return formatEnvVars(yaml.load(yamlstring));
|
|
192
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { spawn } from "child-process-promise";
|
|
2
|
+
|
|
3
|
+
const portForwards = new Map();
|
|
4
|
+
|
|
5
|
+
export const stopPortForward = async (name: string) => {
|
|
6
|
+
const old = portForwards.get(name);
|
|
7
|
+
|
|
8
|
+
if (old) {
|
|
9
|
+
try {
|
|
10
|
+
old.childProcess.kill();
|
|
11
|
+
await old;
|
|
12
|
+
} catch (e) {
|
|
13
|
+
//
|
|
14
|
+
}
|
|
15
|
+
portForwards.delete(name);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const getAllRunningPortForwards = () => {
|
|
20
|
+
return Array.from(portForwards.keys());
|
|
21
|
+
};
|
|
22
|
+
export const startPortForward = async (
|
|
23
|
+
podname: string,
|
|
24
|
+
localPort: number,
|
|
25
|
+
remotePort: number,
|
|
26
|
+
namespace: string
|
|
27
|
+
) => {
|
|
28
|
+
const name = `${namespace}/${podname}/${localPort}:${remotePort}`;
|
|
29
|
+
// stop if already there
|
|
30
|
+
if (portForwards.has(name)) {
|
|
31
|
+
stopPortForward(name);
|
|
32
|
+
}
|
|
33
|
+
const promise = spawn(
|
|
34
|
+
"kubectl",
|
|
35
|
+
["port-forward", podname, `${localPort}:${remotePort}`, "-n", namespace],
|
|
36
|
+
{
|
|
37
|
+
env: {
|
|
38
|
+
...process.env,
|
|
39
|
+
DEBUG: "",
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
portForwards.set(name, promise);
|
|
44
|
+
// wait a moment so that is surley started, unfortunatly we don't know that
|
|
45
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const stopAllPortForwards = async () => {
|
|
49
|
+
getAllRunningPortForwards().forEach((name) => {
|
|
50
|
+
stopPortForward(name);
|
|
51
|
+
});
|
|
52
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
|
|
4
|
+
import yaml from "js-yaml";
|
|
5
|
+
const directory = `${homedir()}/.catladder`;
|
|
6
|
+
const file = `${directory}/preferences.yml`;
|
|
7
|
+
|
|
8
|
+
const getPreferences = async () => {
|
|
9
|
+
if (!(await fs.pathExists(file))) {
|
|
10
|
+
await fs.createFile(file);
|
|
11
|
+
}
|
|
12
|
+
return yaml.load(await fs.readFile(file, { encoding: "utf-8" })) ?? {};
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const hasPreference = async (key: string) => {
|
|
16
|
+
const preferences = await getPreferences();
|
|
17
|
+
return key in preferences;
|
|
18
|
+
};
|
|
19
|
+
export const getPreference = async (key: string) => {
|
|
20
|
+
const preferences = await getPreferences();
|
|
21
|
+
return preferences[key];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const setPreference = async (key: string, value: string | number) => {
|
|
25
|
+
const preferences = await getPreferences();
|
|
26
|
+
|
|
27
|
+
const newPreferences = {
|
|
28
|
+
...preferences,
|
|
29
|
+
[key]: value,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
await fs.writeFile(file, yaml.safeDump(newPreferences));
|
|
33
|
+
};
|