@clicadigital/vault-sdk 0.3.0 → 0.4.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/README.md +13 -66
- package/dist/cjs/cli.js +72 -0
- package/dist/cjs/cli.js.map +1 -0
- package/dist/cjs/client.js +75 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/dotenv.js +24 -0
- package/dist/cjs/dotenv.js.map +1 -0
- package/dist/cjs/index.js +40 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cli.js +21 -68
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +0 -13
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +0 -18
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +5 -46
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -87
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
- package/src/cli.ts +23 -79
- package/src/client.ts +0 -24
- package/src/index.ts +19 -149
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @clicadigital/vault-sdk
|
|
2
2
|
|
|
3
|
-
SDK
|
|
3
|
+
SDK para carregar variáveis de ambiente do **Clica VPN Vault** em `process.env`. Projeto, environment e allow ficam na UI do vault.
|
|
4
4
|
|
|
5
5
|
Pensado para uso **entre pods no k3s/k8s**. Sem API key estática.
|
|
6
6
|
|
|
@@ -51,24 +51,6 @@ O SDK lê o token sozinho e manda `Authorization: Bearer …`.
|
|
|
51
51
|
|
|
52
52
|
## CLI
|
|
53
53
|
|
|
54
|
-
### Garantir projeto + environment
|
|
55
|
-
|
|
56
|
-
Cria o projeto (se não existir) e o environment (se não existir). Projetos novos já nascem com `production` e `staging` no vault; outros nomes são criados sob demanda.
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
npx vault-sdk ensure --project meu-app --env production
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Criar environment e gravar variáveis
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
npx vault-sdk create \
|
|
66
|
-
--project meu-app \
|
|
67
|
-
--env production \
|
|
68
|
-
--var DATABASE_URL=postgres://... \
|
|
69
|
-
--var API_TOKEN=secret
|
|
70
|
-
```
|
|
71
|
-
|
|
72
54
|
### Puxar variáveis no build (gera `.env`)
|
|
73
55
|
|
|
74
56
|
```bash
|
|
@@ -93,71 +75,36 @@ npx vault-sdk pull --url http://vault-api.vpn.svc.cluster.local:3001 \
|
|
|
93
75
|
|
|
94
76
|
### Carregar no boot / build script
|
|
95
77
|
|
|
96
|
-
|
|
97
|
-
import { loadVault, pullVault, createEnv, ensureEnvironment } from "@clicadigital/vault-sdk";
|
|
98
|
-
|
|
99
|
-
// injeta em process.env
|
|
100
|
-
await loadVault({ project: "meu-app", env: "production" });
|
|
101
|
-
|
|
102
|
-
// ou grava arquivo
|
|
103
|
-
await pullVault({
|
|
104
|
-
project: "meu-app",
|
|
105
|
-
env: "production",
|
|
106
|
-
out: ".env",
|
|
107
|
-
inject: false,
|
|
108
|
-
});
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### Criar environment + vars pela URL da VPN
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
await createEnv({
|
|
115
|
-
project: "meu-app",
|
|
116
|
-
env: "production",
|
|
117
|
-
vars: {
|
|
118
|
-
DATABASE_URL: "postgres://...",
|
|
119
|
-
REDIS_URL: "redis://...",
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Só garantir que o environment existe
|
|
125
|
-
|
|
126
|
-
```ts
|
|
127
|
-
await ensureEnvironment({ project: "meu-app", env: "qa" });
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## Exemplo no pod (boot)
|
|
78
|
+
O pacote publica ESM e CJS. Um `import` normal vira `require()` no build CJS e continua funcionando. Não use `new Function` nem `import()` dinâmico.
|
|
131
79
|
|
|
132
80
|
```ts
|
|
133
81
|
import { loadVault } from "@clicadigital/vault-sdk";
|
|
134
82
|
|
|
135
|
-
|
|
136
|
-
|
|
83
|
+
export async function loadProductionVault(): Promise<void> {
|
|
84
|
+
if (process.env.NODE_ENV !== "production") return;
|
|
85
|
+
await loadVault({ project: "meu-app", env: "production" });
|
|
86
|
+
}
|
|
137
87
|
```
|
|
138
88
|
|
|
139
|
-
|
|
89
|
+
Mantenha esse import num módulo separado do entry do Next, para o webpack não levar o SDK às outras rotas.
|
|
90
|
+
|
|
91
|
+
No pod, a URL default é a do Service e o token SA já vem montado. Manifests de referência: [`deploy/k3s/`](../deploy/k3s/) (NetworkPolicy, RBAC TokenReview, SA, exemplos).
|
|
140
92
|
|
|
141
93
|
```bash
|
|
142
94
|
kubectl apply -k deploy/k3s
|
|
143
95
|
kubectl apply -f deploy/k3s/secrets.yaml # a partir de secrets.example.yaml
|
|
144
96
|
```
|
|
145
97
|
|
|
146
|
-
|
|
98
|
+
O allow do app é feito no projeto, pela UI do vault. Informe a ServiceAccount `system:serviceaccount:<namespace>:<nome>`.
|
|
147
99
|
|
|
148
|
-
|
|
149
|
-
system:serviceaccount:vault:app-demo
|
|
150
|
-
```
|
|
100
|
+
`VAULT_ALLOWED_SAS` no deployment fica só para o operador da UI (`vault-web`).
|
|
151
101
|
|
|
152
102
|
## API do vault usada pelo SDK
|
|
153
103
|
|
|
154
104
|
| Método | Path | Uso |
|
|
155
105
|
|--------|------|-----|
|
|
156
|
-
| `GET` | `/v1/projects` |
|
|
157
|
-
| `
|
|
158
|
-
| `POST` | `/v1/projects/:id/environments` | Criar environment |
|
|
159
|
-
| `GET` | `/v1/environments/:id/vars` | Listar vars (já descriptografadas) |
|
|
160
|
-
| `PUT` | `/v1/environments/:id/vars` | Upsert var |
|
|
106
|
+
| `GET` | `/v1/projects` | Achar o projeto e o environment |
|
|
107
|
+
| `GET` | `/v1/environments/:id/vars` | Ler as vars já descriptografadas |
|
|
161
108
|
|
|
162
109
|
## Desenvolvimento local (neste monorepo)
|
|
163
110
|
|
package/dist/cjs/cli.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const index_js_1 = require("./index.js");
|
|
5
|
+
function parseArgs(argv) {
|
|
6
|
+
const [cmd = "help", ...rest] = argv;
|
|
7
|
+
const flags = {};
|
|
8
|
+
for (let i = 0; i < rest.length; i++) {
|
|
9
|
+
const token = rest[i];
|
|
10
|
+
if (!token?.startsWith("--"))
|
|
11
|
+
continue;
|
|
12
|
+
const key = token.slice(2);
|
|
13
|
+
const next = rest[i + 1];
|
|
14
|
+
if (!next || next.startsWith("--")) {
|
|
15
|
+
flags[key] = true;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
flags[key] = next;
|
|
19
|
+
i++;
|
|
20
|
+
}
|
|
21
|
+
return { cmd, flags };
|
|
22
|
+
}
|
|
23
|
+
function str(flags, key, fallback) {
|
|
24
|
+
const value = flags[key];
|
|
25
|
+
if (typeof value === "string" && value.length > 0)
|
|
26
|
+
return value;
|
|
27
|
+
if (fallback !== undefined)
|
|
28
|
+
return fallback;
|
|
29
|
+
throw new Error(`Missing --${key}`);
|
|
30
|
+
}
|
|
31
|
+
function printHelp() {
|
|
32
|
+
console.log(`@clicadigital/vault-sdk — carrega envs do vault
|
|
33
|
+
|
|
34
|
+
Usage:
|
|
35
|
+
vault-sdk pull --project <name> --env <name> [--out .env] [--no-inject] [--url <vpn-url>]
|
|
36
|
+
|
|
37
|
+
Env:
|
|
38
|
+
VAULT_URL opcional; default k8s: http://vault-api-service.vault.svc.cluster.local:3001
|
|
39
|
+
VAULT_PROJECT default --project
|
|
40
|
+
VAULT_ENV default --env
|
|
41
|
+
VAULT_SA_TOKEN_PATH path do token SA (default: /var/run/secrets/tokens/vault/token)
|
|
42
|
+
|
|
43
|
+
No código: loadVault({ project, env }) injeta em process.env.
|
|
44
|
+
Criar projeto, environment e allow é pela UI do vault.
|
|
45
|
+
`);
|
|
46
|
+
}
|
|
47
|
+
async function main() {
|
|
48
|
+
const { cmd, flags } = parseArgs(process.argv.slice(2));
|
|
49
|
+
if (cmd === "help" || flags.help === true) {
|
|
50
|
+
printHelp();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (typeof flags.url === "string")
|
|
54
|
+
process.env.VAULT_URL = flags.url;
|
|
55
|
+
if (cmd !== "pull") {
|
|
56
|
+
printHelp();
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const project = str(flags, "project", process.env.VAULT_PROJECT);
|
|
60
|
+
const env = str(flags, "env", process.env.VAULT_ENV);
|
|
61
|
+
const out = typeof flags.out === "string" ? flags.out : undefined;
|
|
62
|
+
const inject = flags["no-inject"] !== true;
|
|
63
|
+
const result = await (0, index_js_1.pullVault)({ project, env, out, inject });
|
|
64
|
+
console.log(`pulled ${Object.keys(result.vars).length} vars for ${project}/${env}` +
|
|
65
|
+
(result.out ? ` → ${result.out}` : "") +
|
|
66
|
+
(inject ? " (injected into process.env)" : ""));
|
|
67
|
+
}
|
|
68
|
+
main().catch((err) => {
|
|
69
|
+
console.error(err instanceof Error ? err.message : err);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VaultClient = exports.DEFAULT_VAULT_URL = void 0;
|
|
4
|
+
exports.createClientFromEnv = createClientFromEnv;
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const DEFAULT_TOKEN_PATHS = [
|
|
7
|
+
"/var/run/secrets/tokens/vault/token",
|
|
8
|
+
"/var/run/secrets/kubernetes.io/serviceaccount/token",
|
|
9
|
+
];
|
|
10
|
+
/** DNS interno do Service no k3s (namespace `vault`). Override com VAULT_URL. */
|
|
11
|
+
exports.DEFAULT_VAULT_URL = "http://vault-api-service.vault.svc.cluster.local:3001";
|
|
12
|
+
function resolveToken(opts) {
|
|
13
|
+
if (opts.token)
|
|
14
|
+
return opts.token;
|
|
15
|
+
const paths = opts.tokenPaths ?? DEFAULT_TOKEN_PATHS;
|
|
16
|
+
for (const path of paths) {
|
|
17
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
18
|
+
continue;
|
|
19
|
+
try {
|
|
20
|
+
const token = (0, node_fs_1.readFileSync)(path, "utf8").trim();
|
|
21
|
+
if (token)
|
|
22
|
+
return token;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// ignore unreadable path
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
class VaultClient {
|
|
31
|
+
baseUrl;
|
|
32
|
+
token;
|
|
33
|
+
fetchFn;
|
|
34
|
+
constructor(opts) {
|
|
35
|
+
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
36
|
+
this.token = resolveToken(opts);
|
|
37
|
+
this.fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
38
|
+
}
|
|
39
|
+
async request(path, init) {
|
|
40
|
+
const headers = {
|
|
41
|
+
"content-type": "application/json",
|
|
42
|
+
...init?.headers,
|
|
43
|
+
};
|
|
44
|
+
if (this.token)
|
|
45
|
+
headers.authorization = `Bearer ${this.token}`;
|
|
46
|
+
const res = await this.fetchFn(`${this.baseUrl}${path}`, {
|
|
47
|
+
...init,
|
|
48
|
+
headers,
|
|
49
|
+
});
|
|
50
|
+
if (!res.ok) {
|
|
51
|
+
const body = (await res.json().catch(() => null));
|
|
52
|
+
throw new Error(body?.error ?? `Vault API ${res.status} ${path}`);
|
|
53
|
+
}
|
|
54
|
+
if (res.status === 204)
|
|
55
|
+
return undefined;
|
|
56
|
+
return (await res.json());
|
|
57
|
+
}
|
|
58
|
+
listProjects() {
|
|
59
|
+
return this.request("/v1/projects");
|
|
60
|
+
}
|
|
61
|
+
listVars(environmentId) {
|
|
62
|
+
return this.request(`/v1/environments/${environmentId}/vars`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.VaultClient = VaultClient;
|
|
66
|
+
function createClientFromEnv(env = process.env) {
|
|
67
|
+
const baseUrl = env.VAULT_URL ?? env.VAULT_API_URL ?? env.API_URL ?? exports.DEFAULT_VAULT_URL;
|
|
68
|
+
const tokenPath = env.VAULT_SA_TOKEN_PATH;
|
|
69
|
+
return new VaultClient({
|
|
70
|
+
baseUrl,
|
|
71
|
+
token: env.VAULT_SA_TOKEN,
|
|
72
|
+
tokenPaths: tokenPath ? [tokenPath, ...DEFAULT_TOKEN_PATHS] : undefined,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toDotEnv = toDotEnv;
|
|
4
|
+
exports.varsToRecord = varsToRecord;
|
|
5
|
+
function toDotEnv(vars) {
|
|
6
|
+
return (Object.entries(vars)
|
|
7
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
8
|
+
.map(([key, value]) => `${key}=${escapeEnvValue(value)}`)
|
|
9
|
+
.join("\n") + "\n");
|
|
10
|
+
}
|
|
11
|
+
function escapeEnvValue(value) {
|
|
12
|
+
if (value === "")
|
|
13
|
+
return '""';
|
|
14
|
+
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value))
|
|
15
|
+
return value;
|
|
16
|
+
return JSON.stringify(value);
|
|
17
|
+
}
|
|
18
|
+
function varsToRecord(rows) {
|
|
19
|
+
const out = {};
|
|
20
|
+
for (const row of rows)
|
|
21
|
+
out[row.key] = row.value;
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=dotenv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dotenv.js","sourceRoot":"","sources":["../../src/dotenv.ts"],"names":[],"mappings":";;AAAA,4BAOC;AAQD,oCAMC;AArBD,SAAgB,QAAQ,CAAC,IAA4B;IACnD,OAAO,CACL,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;SACjB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;SACtC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;SACxD,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CACrB,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACzD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,SAAgB,YAAY,CAC1B,IAAsC;IAEtC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC;IACjD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadVault = loadVault;
|
|
4
|
+
exports.pullVault = pullVault;
|
|
5
|
+
const promises_1 = require("node:fs/promises");
|
|
6
|
+
const client_js_1 = require("./client.js");
|
|
7
|
+
const dotenv_js_1 = require("./dotenv.js");
|
|
8
|
+
async function readVars(opts) {
|
|
9
|
+
const client = (0, client_js_1.createClientFromEnv)();
|
|
10
|
+
const projects = await client.listProjects();
|
|
11
|
+
const project = projects.find((item) => item.name === opts.project);
|
|
12
|
+
if (!project)
|
|
13
|
+
throw new Error(`Project not found: ${opts.project}`);
|
|
14
|
+
const environment = project.environments.find((item) => item.name === opts.env);
|
|
15
|
+
if (!environment) {
|
|
16
|
+
throw new Error(`Environment "${opts.env}" not found in project "${opts.project}"`);
|
|
17
|
+
}
|
|
18
|
+
const rows = await client.listVars(environment.id);
|
|
19
|
+
return (0, dotenv_js_1.varsToRecord)(rows);
|
|
20
|
+
}
|
|
21
|
+
/** Lê o environment e injeta as variáveis em `process.env`. */
|
|
22
|
+
async function loadVault(opts) {
|
|
23
|
+
const vars = await readVars(opts);
|
|
24
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
25
|
+
process.env[key] = value;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function pullVault(opts) {
|
|
29
|
+
const vars = await readVars(opts);
|
|
30
|
+
if (opts.inject !== false) {
|
|
31
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
32
|
+
process.env[key] = value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const out = opts.out;
|
|
36
|
+
if (out)
|
|
37
|
+
await (0, promises_1.writeFile)(out, (0, dotenv_js_1.toDotEnv)(vars), "utf8");
|
|
38
|
+
return out ? { vars, out } : { vars };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;AAyBA,8BAKC;AAED,8BAcC;AA9CD,+CAA6C;AAC7C,2CAAkD;AAClD,2CAAqD;AAOrD,KAAK,UAAU,QAAQ,CAAC,IAAsB;IAC5C,MAAM,MAAM,GAAG,IAAA,+BAAmB,GAAE,CAAC;IACrC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;IAC7C,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC;IACpE,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAEpE,MAAM,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC;IAChF,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,GAAG,2BAA2B,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IACnD,OAAO,IAAA,wBAAY,EAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,+DAA+D;AACxD,KAAK,UAAU,SAAS,CAAC,IAAsB;IACpD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC3B,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,SAAS,CAC7B,IAA2D;IAE3D,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IAElC,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC1B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;IACrB,IAAI,GAAG;QAAE,MAAM,IAAA,oBAAS,EAAC,GAAG,EAAE,IAAA,oBAAQ,EAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;IACtD,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { createEnv, ensureEnvironment, pullVault } from "./index.js";
|
|
2
|
+
import { pullVault } from "./index.js";
|
|
4
3
|
function parseArgs(argv) {
|
|
5
4
|
const [cmd = "help", ...rest] = argv;
|
|
6
5
|
const flags = {};
|
|
@@ -14,33 +13,24 @@ function parseArgs(argv) {
|
|
|
14
13
|
flags[key] = true;
|
|
15
14
|
continue;
|
|
16
15
|
}
|
|
17
|
-
|
|
18
|
-
const list = Array.isArray(flags.var) ? flags.var : [];
|
|
19
|
-
list.push(next);
|
|
20
|
-
flags.var = list;
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
flags[key] = next;
|
|
24
|
-
}
|
|
16
|
+
flags[key] = next;
|
|
25
17
|
i++;
|
|
26
18
|
}
|
|
27
19
|
return { cmd, flags };
|
|
28
20
|
}
|
|
29
21
|
function str(flags, key, fallback) {
|
|
30
|
-
const
|
|
31
|
-
if (typeof
|
|
32
|
-
return
|
|
22
|
+
const value = flags[key];
|
|
23
|
+
if (typeof value === "string" && value.length > 0)
|
|
24
|
+
return value;
|
|
33
25
|
if (fallback !== undefined)
|
|
34
26
|
return fallback;
|
|
35
27
|
throw new Error(`Missing --${key}`);
|
|
36
28
|
}
|
|
37
29
|
function printHelp() {
|
|
38
|
-
console.log(`@clicadigital/vault-sdk —
|
|
30
|
+
console.log(`@clicadigital/vault-sdk — carrega envs do vault
|
|
39
31
|
|
|
40
32
|
Usage:
|
|
41
|
-
vault-sdk
|
|
42
|
-
vault-sdk ensure --project <name> --env <name> [--url <vpn-url>]
|
|
43
|
-
vault-sdk pull --project <name> --env <name> [--out .env] [--no-inject] [--url <vpn-url>]
|
|
33
|
+
vault-sdk pull --project <name> --env <name> [--out .env] [--no-inject] [--url <vpn-url>]
|
|
44
34
|
|
|
45
35
|
Env:
|
|
46
36
|
VAULT_URL opcional; default k8s: http://vault-api-service.vault.svc.cluster.local:3001
|
|
@@ -48,67 +38,30 @@ Env:
|
|
|
48
38
|
VAULT_ENV default --env
|
|
49
39
|
VAULT_SA_TOKEN_PATH path do token SA (default: /var/run/secrets/tokens/vault/token)
|
|
50
40
|
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
No código: loadVault({ project, env }) injeta em process.env.
|
|
42
|
+
Criar projeto, environment e allow é pela UI do vault.
|
|
53
43
|
`);
|
|
54
44
|
}
|
|
55
|
-
function applyUrl(flags) {
|
|
56
|
-
if (typeof flags.url === "string")
|
|
57
|
-
process.env.VAULT_URL = flags.url;
|
|
58
|
-
}
|
|
59
|
-
function parseVarList(flags) {
|
|
60
|
-
const out = {};
|
|
61
|
-
const list = Array.isArray(flags.var) ? flags.var : [];
|
|
62
|
-
for (const pair of list) {
|
|
63
|
-
const eq = pair.indexOf("=");
|
|
64
|
-
if (eq < 1)
|
|
65
|
-
throw new Error(`Invalid --var ${pair} (use KEY=VALUE)`);
|
|
66
|
-
out[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
67
|
-
}
|
|
68
|
-
return out;
|
|
69
|
-
}
|
|
70
45
|
async function main() {
|
|
71
46
|
const { cmd, flags } = parseArgs(process.argv.slice(2));
|
|
72
47
|
if (cmd === "help" || flags.help === true) {
|
|
73
48
|
printHelp();
|
|
74
49
|
return;
|
|
75
50
|
}
|
|
76
|
-
|
|
51
|
+
if (typeof flags.url === "string")
|
|
52
|
+
process.env.VAULT_URL = flags.url;
|
|
53
|
+
if (cmd !== "pull") {
|
|
54
|
+
printHelp();
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
77
57
|
const project = str(flags, "project", process.env.VAULT_PROJECT);
|
|
78
58
|
const env = str(flags, "env", process.env.VAULT_ENV);
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const result = await createEnv({ project, env, url, vars, client });
|
|
86
|
-
console.log(JSON.stringify({ project, env, ...result }, null, 2));
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
if (cmd === "ensure") {
|
|
90
|
-
const result = await ensureEnvironment({ project, env, client });
|
|
91
|
-
console.log(JSON.stringify({
|
|
92
|
-
project,
|
|
93
|
-
env,
|
|
94
|
-
projectId: result.projectId,
|
|
95
|
-
environmentId: result.environmentId,
|
|
96
|
-
createdProject: result.createdProject,
|
|
97
|
-
createdEnvironment: result.createdEnvironment,
|
|
98
|
-
}, null, 2));
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
if (cmd === "pull") {
|
|
102
|
-
const out = typeof flags.out === "string" ? flags.out : undefined;
|
|
103
|
-
const inject = flags["no-inject"] !== true;
|
|
104
|
-
const result = await pullVault({ project, env, out, inject, client });
|
|
105
|
-
console.log(`pulled ${Object.keys(result.vars).length} vars for ${project}/${env}` +
|
|
106
|
-
(result.out ? ` → ${result.out}` : "") +
|
|
107
|
-
(inject ? " (injected into process.env)" : ""));
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
printHelp();
|
|
111
|
-
process.exit(1);
|
|
59
|
+
const out = typeof flags.out === "string" ? flags.out : undefined;
|
|
60
|
+
const inject = flags["no-inject"] !== true;
|
|
61
|
+
const result = await pullVault({ project, env, out, inject });
|
|
62
|
+
console.log(`pulled ${Object.keys(result.vars).length} vars for ${project}/${env}` +
|
|
63
|
+
(result.out ? ` → ${result.out}` : "") +
|
|
64
|
+
(inject ? " (injected into process.env)" : ""));
|
|
112
65
|
}
|
|
113
66
|
main().catch((err) => {
|
|
114
67
|
console.error(err instanceof Error ? err.message : err);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
CHANGED
|
@@ -2,11 +2,8 @@ import type { EnvVar, ProjectTree } from "./types.js";
|
|
|
2
2
|
/** DNS interno do Service no k3s (namespace `vault`). Override com VAULT_URL. */
|
|
3
3
|
export declare const DEFAULT_VAULT_URL = "http://vault-api-service.vault.svc.cluster.local:3001";
|
|
4
4
|
export type VaultClientOptions = {
|
|
5
|
-
/** Base URL da API do vault na VPN. Default: DEFAULT_VAULT_URL */
|
|
6
5
|
baseUrl: string;
|
|
7
|
-
/** Bearer token (ServiceAccount projected). Se omitido, tenta ler do filesystem do pod. */
|
|
8
6
|
token?: string;
|
|
9
|
-
/** Paths para procurar o token SA (ordem de prioridade). */
|
|
10
7
|
tokenPaths?: string[];
|
|
11
8
|
fetch?: typeof globalThis.fetch;
|
|
12
9
|
};
|
|
@@ -17,17 +14,7 @@ export declare class VaultClient {
|
|
|
17
14
|
constructor(opts: VaultClientOptions);
|
|
18
15
|
private request;
|
|
19
16
|
listProjects(): Promise<ProjectTree[]>;
|
|
20
|
-
createProject(name: string): Promise<{
|
|
21
|
-
id: string;
|
|
22
|
-
name: string;
|
|
23
|
-
}>;
|
|
24
|
-
createEnvironment(projectId: string, name: string): Promise<{
|
|
25
|
-
ok: boolean;
|
|
26
|
-
}>;
|
|
27
17
|
listVars(environmentId: string): Promise<EnvVar[]>;
|
|
28
|
-
upsertVar(environmentId: string, key: string, value: string): Promise<{
|
|
29
|
-
ok: boolean;
|
|
30
|
-
}>;
|
|
31
18
|
}
|
|
32
19
|
export declare function createClientFromEnv(env?: NodeJS.ProcessEnv): VaultClient;
|
|
33
20
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAOtD,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,0DAC2B,CAAC;AAE1D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAOtD,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,0DAC2B,CAAC;AAE1D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC,CAAC;AAiBF,qBAAa,WAAW;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;gBAEtC,IAAI,EAAE,kBAAkB;YAMtB,OAAO;IAmBrB,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAItC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAGnD;AAED,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,WAAW,CASb"}
|
package/dist/client.js
CHANGED
|
@@ -54,27 +54,9 @@ export class VaultClient {
|
|
|
54
54
|
listProjects() {
|
|
55
55
|
return this.request("/v1/projects");
|
|
56
56
|
}
|
|
57
|
-
createProject(name) {
|
|
58
|
-
return this.request("/v1/projects", {
|
|
59
|
-
method: "POST",
|
|
60
|
-
body: JSON.stringify({ name }),
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
createEnvironment(projectId, name) {
|
|
64
|
-
return this.request(`/v1/projects/${projectId}/environments`, {
|
|
65
|
-
method: "POST",
|
|
66
|
-
body: JSON.stringify({ name }),
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
57
|
listVars(environmentId) {
|
|
70
58
|
return this.request(`/v1/environments/${environmentId}/vars`);
|
|
71
59
|
}
|
|
72
|
-
upsertVar(environmentId, key, value) {
|
|
73
|
-
return this.request(`/v1/environments/${environmentId}/vars`, {
|
|
74
|
-
method: "PUT",
|
|
75
|
-
body: JSON.stringify({ key, value }),
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
60
|
}
|
|
79
61
|
export function createClientFromEnv(env = process.env) {
|
|
80
62
|
const baseUrl = env.VAULT_URL ?? env.VAULT_API_URL ?? env.API_URL ?? DEFAULT_VAULT_URL;
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGnD,MAAM,mBAAmB,GAAG;IAC1B,qCAAqC;IACrC,qDAAqD;CACtD,CAAC;AAEF,iFAAiF;AACjF,MAAM,CAAC,MAAM,iBAAiB,GAC5B,uDAAuD,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,54 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
import { toDotEnv, varsToRecord } from "./dotenv.js";
|
|
3
|
-
import type { EnvVar } from "./types.js";
|
|
4
|
-
export type { EnvVar, ProjectTree, ProjectTreeEnv } from "./types.js";
|
|
5
|
-
export { VaultClient, createClientFromEnv, DEFAULT_VAULT_URL, type VaultClientOptions, };
|
|
6
|
-
export { toDotEnv, varsToRecord };
|
|
7
|
-
export type ResolveOptions = {
|
|
1
|
+
export type LoadVaultOptions = {
|
|
8
2
|
project: string;
|
|
9
3
|
env: string;
|
|
10
|
-
client?: VaultClient;
|
|
11
4
|
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export type PullOptions = ResolveOptions & {
|
|
5
|
+
/** Lê o environment e injeta as variáveis em `process.env`. */
|
|
6
|
+
export declare function loadVault(opts: LoadVaultOptions): Promise<void>;
|
|
7
|
+
export declare function pullVault(opts: LoadVaultOptions & {
|
|
16
8
|
out?: string;
|
|
17
9
|
inject?: boolean;
|
|
18
|
-
}
|
|
19
|
-
export type CreateEnvOptions = {
|
|
20
|
-
project: string;
|
|
21
|
-
env: string;
|
|
22
|
-
/** URL da API do vault na VPN (cluster-internal) */
|
|
23
|
-
url?: string;
|
|
24
|
-
vars?: Record<string, string>;
|
|
25
|
-
client?: VaultClient;
|
|
26
|
-
};
|
|
27
|
-
export declare function resolveEnvironment(opts: ResolveOptions): Promise<{
|
|
28
|
-
projectId: string;
|
|
29
|
-
environmentId: string;
|
|
30
|
-
}>;
|
|
31
|
-
/** Garante projeto + environment na VPN (cria se não existir). */
|
|
32
|
-
export declare function ensureEnvironment(opts: EnsureOptions): Promise<{
|
|
33
|
-
projectId: string;
|
|
34
|
-
environmentId: string;
|
|
35
|
-
createdProject: boolean;
|
|
36
|
-
createdEnvironment: boolean;
|
|
37
|
-
}>;
|
|
38
|
-
/**
|
|
39
|
-
* Cria (ou garante) o environment no vault via URL da VPN
|
|
40
|
-
* e opcionalmente faz upsert das variáveis.
|
|
41
|
-
*/
|
|
42
|
-
export declare function createEnv(opts: CreateEnvOptions): Promise<{
|
|
43
|
-
projectId: string;
|
|
44
|
-
environmentId: string;
|
|
45
|
-
createdProject: boolean;
|
|
46
|
-
createdEnvironment: boolean;
|
|
47
|
-
upserted: string[];
|
|
48
|
-
}>;
|
|
49
|
-
export declare function fetchVars(opts: ResolveOptions): Promise<EnvVar[]>;
|
|
50
|
-
export declare function loadVault(opts: ResolveOptions): Promise<Record<string, string>>;
|
|
51
|
-
export declare function pullVault(opts: PullOptions): Promise<{
|
|
10
|
+
}): Promise<{
|
|
52
11
|
vars: Record<string, string>;
|
|
53
12
|
out?: string;
|
|
54
13
|
}>;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAiBF,+DAA+D;AAC/D,wBAAsB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKrE;AAED,wBAAsB,SAAS,CAC7B,IAAI,EAAE,gBAAgB,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC1D,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAYzD"}
|
package/dist/index.js
CHANGED
|
@@ -1,110 +1,36 @@
|
|
|
1
1
|
import { writeFile } from "node:fs/promises";
|
|
2
|
-
import { createClientFromEnv
|
|
2
|
+
import { createClientFromEnv } from "./client.js";
|
|
3
3
|
import { toDotEnv, varsToRecord } from "./dotenv.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
return tree.find((p) => p.name === name);
|
|
9
|
-
}
|
|
10
|
-
function resolveClient(opts) {
|
|
11
|
-
if (opts.client)
|
|
12
|
-
return opts.client;
|
|
13
|
-
if (opts.url)
|
|
14
|
-
return new VaultClient({ baseUrl: opts.url });
|
|
15
|
-
return createClientFromEnv();
|
|
16
|
-
}
|
|
17
|
-
export async function resolveEnvironment(opts) {
|
|
18
|
-
const client = opts.client ?? createClientFromEnv();
|
|
19
|
-
const project = await findProject(client, opts.project);
|
|
4
|
+
async function readVars(opts) {
|
|
5
|
+
const client = createClientFromEnv();
|
|
6
|
+
const projects = await client.listProjects();
|
|
7
|
+
const project = projects.find((item) => item.name === opts.project);
|
|
20
8
|
if (!project)
|
|
21
9
|
throw new Error(`Project not found: ${opts.project}`);
|
|
22
|
-
const environment = project.environments.find((
|
|
10
|
+
const environment = project.environments.find((item) => item.name === opts.env);
|
|
23
11
|
if (!environment) {
|
|
24
12
|
throw new Error(`Environment "${opts.env}" not found in project "${opts.project}"`);
|
|
25
13
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
/** Garante projeto + environment na VPN (cria se não existir). */
|
|
29
|
-
export async function ensureEnvironment(opts) {
|
|
30
|
-
const client = opts.client ?? createClientFromEnv();
|
|
31
|
-
let createdProject = false;
|
|
32
|
-
let createdEnvironment = false;
|
|
33
|
-
let project = await findProject(client, opts.project);
|
|
34
|
-
if (!project) {
|
|
35
|
-
if (opts.createProject === false) {
|
|
36
|
-
throw new Error(`Project not found: ${opts.project}`);
|
|
37
|
-
}
|
|
38
|
-
await client.createProject(opts.project);
|
|
39
|
-
createdProject = true;
|
|
40
|
-
project = await findProject(client, opts.project);
|
|
41
|
-
if (!project)
|
|
42
|
-
throw new Error(`Failed to create project: ${opts.project}`);
|
|
43
|
-
}
|
|
44
|
-
let environment = project.environments.find((e) => e.name === opts.env);
|
|
45
|
-
if (!environment) {
|
|
46
|
-
await client.createEnvironment(project.id, opts.env);
|
|
47
|
-
createdEnvironment = true;
|
|
48
|
-
const refreshed = await findProject(client, opts.project);
|
|
49
|
-
if (!refreshed)
|
|
50
|
-
throw new Error(`Failed to reload project: ${opts.project}`);
|
|
51
|
-
project = refreshed;
|
|
52
|
-
environment = project.environments.find((e) => e.name === opts.env);
|
|
53
|
-
if (!environment)
|
|
54
|
-
throw new Error(`Failed to create environment: ${opts.env}`);
|
|
55
|
-
}
|
|
56
|
-
return {
|
|
57
|
-
projectId: project.id,
|
|
58
|
-
environmentId: environment.id,
|
|
59
|
-
createdProject,
|
|
60
|
-
createdEnvironment,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Cria (ou garante) o environment no vault via URL da VPN
|
|
65
|
-
* e opcionalmente faz upsert das variáveis.
|
|
66
|
-
*/
|
|
67
|
-
export async function createEnv(opts) {
|
|
68
|
-
const client = resolveClient(opts);
|
|
69
|
-
const ensured = await ensureEnvironment({
|
|
70
|
-
project: opts.project,
|
|
71
|
-
env: opts.env,
|
|
72
|
-
client,
|
|
73
|
-
});
|
|
74
|
-
const upserted = [];
|
|
75
|
-
if (opts.vars) {
|
|
76
|
-
for (const [key, value] of Object.entries(opts.vars)) {
|
|
77
|
-
await client.upsertVar(ensured.environmentId, key, value);
|
|
78
|
-
upserted.push(key);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return { ...ensured, upserted };
|
|
82
|
-
}
|
|
83
|
-
export async function fetchVars(opts) {
|
|
84
|
-
const client = opts.client ?? createClientFromEnv();
|
|
85
|
-
const { environmentId } = await resolveEnvironment({ ...opts, client });
|
|
86
|
-
return client.listVars(environmentId);
|
|
14
|
+
const rows = await client.listVars(environment.id);
|
|
15
|
+
return varsToRecord(rows);
|
|
87
16
|
}
|
|
17
|
+
/** Lê o environment e injeta as variáveis em `process.env`. */
|
|
88
18
|
export async function loadVault(opts) {
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
for (const [key, value] of Object.entries(record)) {
|
|
19
|
+
const vars = await readVars(opts);
|
|
20
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
92
21
|
process.env[key] = value;
|
|
93
22
|
}
|
|
94
|
-
return record;
|
|
95
23
|
}
|
|
96
24
|
export async function pullVault(opts) {
|
|
97
|
-
const
|
|
98
|
-
const vars = varsToRecord(rows);
|
|
25
|
+
const vars = await readVars(opts);
|
|
99
26
|
if (opts.inject !== false) {
|
|
100
27
|
for (const [key, value] of Object.entries(vars)) {
|
|
101
28
|
process.env[key] = value;
|
|
102
29
|
}
|
|
103
30
|
}
|
|
104
31
|
const out = opts.out;
|
|
105
|
-
if (out)
|
|
32
|
+
if (out)
|
|
106
33
|
await writeFile(out, toDotEnv(vars), "utf8");
|
|
107
|
-
}
|
|
108
34
|
return out ? { vars, out } : { vars };
|
|
109
35
|
}
|
|
110
36
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOrD,KAAK,UAAU,QAAQ,CAAC,IAAsB;IAC5C,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;IAC7C,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC;IACpE,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAEpE,MAAM,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC;IAChF,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,GAAG,2BAA2B,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IACnD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAsB;IACpD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,IAA2D;IAE3D,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IAElC,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC1B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;IACrB,IAAI,GAAG;QAAE,MAAM,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;IACtD,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;AACxC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clicadigital/vault-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Load vault env vars into process.env via cluster ServiceAccount (k3s/k8s)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
|
-
"main": "./dist/index.js",
|
|
7
|
+
"main": "./dist/cjs/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"bin": {
|
|
10
10
|
"vault-sdk": "./dist/cli.js"
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"bun": "./src/index.ts",
|
|
16
16
|
"import": "./dist/index.js",
|
|
17
|
-
"
|
|
17
|
+
"require": "./dist/cjs/index.js",
|
|
18
|
+
"default": "./dist/cjs/index.js"
|
|
18
19
|
}
|
|
19
20
|
},
|
|
20
21
|
"files": [
|
|
@@ -22,7 +23,7 @@
|
|
|
22
23
|
"src"
|
|
23
24
|
],
|
|
24
25
|
"scripts": {
|
|
25
|
-
"build": "tsc -p tsconfig.json",
|
|
26
|
+
"build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && bun scripts/mark-cjs.ts",
|
|
26
27
|
"prepublishOnly": "bun run build",
|
|
27
28
|
"vault": "bun run src/cli.ts"
|
|
28
29
|
},
|
package/src/cli.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { createEnv, ensureEnvironment, pullVault } from "./index.js";
|
|
2
|
+
import { pullVault } from "./index.js";
|
|
4
3
|
|
|
5
|
-
type Flags = Record<string, string | boolean
|
|
4
|
+
type Flags = Record<string, string | boolean>;
|
|
6
5
|
|
|
7
6
|
function parseArgs(argv: string[]): { cmd: string; flags: Flags } {
|
|
8
7
|
const [cmd = "help", ...rest] = argv;
|
|
@@ -16,32 +15,24 @@ function parseArgs(argv: string[]): { cmd: string; flags: Flags } {
|
|
|
16
15
|
flags[key] = true;
|
|
17
16
|
continue;
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
const list = Array.isArray(flags.var) ? flags.var : [];
|
|
21
|
-
list.push(next);
|
|
22
|
-
flags.var = list;
|
|
23
|
-
} else {
|
|
24
|
-
flags[key] = next;
|
|
25
|
-
}
|
|
18
|
+
flags[key] = next;
|
|
26
19
|
i++;
|
|
27
20
|
}
|
|
28
21
|
return { cmd, flags };
|
|
29
22
|
}
|
|
30
23
|
|
|
31
24
|
function str(flags: Flags, key: string, fallback?: string): string {
|
|
32
|
-
const
|
|
33
|
-
if (typeof
|
|
25
|
+
const value = flags[key];
|
|
26
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
34
27
|
if (fallback !== undefined) return fallback;
|
|
35
28
|
throw new Error(`Missing --${key}`);
|
|
36
29
|
}
|
|
37
30
|
|
|
38
31
|
function printHelp(): void {
|
|
39
|
-
console.log(`@clicadigital/vault-sdk —
|
|
32
|
+
console.log(`@clicadigital/vault-sdk — carrega envs do vault
|
|
40
33
|
|
|
41
34
|
Usage:
|
|
42
|
-
vault-sdk
|
|
43
|
-
vault-sdk ensure --project <name> --env <name> [--url <vpn-url>]
|
|
44
|
-
vault-sdk pull --project <name> --env <name> [--out .env] [--no-inject] [--url <vpn-url>]
|
|
35
|
+
vault-sdk pull --project <name> --env <name> [--out .env] [--no-inject] [--url <vpn-url>]
|
|
45
36
|
|
|
46
37
|
Env:
|
|
47
38
|
VAULT_URL opcional; default k8s: http://vault-api-service.vault.svc.cluster.local:3001
|
|
@@ -49,26 +40,11 @@ Env:
|
|
|
49
40
|
VAULT_ENV default --env
|
|
50
41
|
VAULT_SA_TOKEN_PATH path do token SA (default: /var/run/secrets/tokens/vault/token)
|
|
51
42
|
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
No código: loadVault({ project, env }) injeta em process.env.
|
|
44
|
+
Criar projeto, environment e allow é pela UI do vault.
|
|
54
45
|
`);
|
|
55
46
|
}
|
|
56
47
|
|
|
57
|
-
function applyUrl(flags: Flags): void {
|
|
58
|
-
if (typeof flags.url === "string") process.env.VAULT_URL = flags.url;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function parseVarList(flags: Flags): Record<string, string> {
|
|
62
|
-
const out: Record<string, string> = {};
|
|
63
|
-
const list = Array.isArray(flags.var) ? flags.var : [];
|
|
64
|
-
for (const pair of list) {
|
|
65
|
-
const eq = pair.indexOf("=");
|
|
66
|
-
if (eq < 1) throw new Error(`Invalid --var ${pair} (use KEY=VALUE)`);
|
|
67
|
-
out[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
68
|
-
}
|
|
69
|
-
return out;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
48
|
async function main(): Promise<void> {
|
|
73
49
|
const { cmd, flags } = parseArgs(process.argv.slice(2));
|
|
74
50
|
|
|
@@ -77,55 +53,23 @@ async function main(): Promise<void> {
|
|
|
77
53
|
return;
|
|
78
54
|
}
|
|
79
55
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const project = str(flags, "project", process.env.VAULT_PROJECT);
|
|
83
|
-
const env = str(flags, "env", process.env.VAULT_ENV);
|
|
84
|
-
const url = typeof flags.url === "string" ? flags.url : undefined;
|
|
85
|
-
const client = url
|
|
86
|
-
? createClientFromEnv({ ...process.env, VAULT_URL: url })
|
|
87
|
-
: undefined;
|
|
88
|
-
|
|
89
|
-
if (cmd === "create") {
|
|
90
|
-
const vars = parseVarList(flags);
|
|
91
|
-
const result = await createEnv({ project, env, url, vars, client });
|
|
92
|
-
console.log(JSON.stringify({ project, env, ...result }, null, 2));
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (cmd === "ensure") {
|
|
97
|
-
const result = await ensureEnvironment({ project, env, client });
|
|
98
|
-
console.log(
|
|
99
|
-
JSON.stringify(
|
|
100
|
-
{
|
|
101
|
-
project,
|
|
102
|
-
env,
|
|
103
|
-
projectId: result.projectId,
|
|
104
|
-
environmentId: result.environmentId,
|
|
105
|
-
createdProject: result.createdProject,
|
|
106
|
-
createdEnvironment: result.createdEnvironment,
|
|
107
|
-
},
|
|
108
|
-
null,
|
|
109
|
-
2,
|
|
110
|
-
),
|
|
111
|
-
);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
56
|
+
if (typeof flags.url === "string") process.env.VAULT_URL = flags.url;
|
|
114
57
|
|
|
115
|
-
if (cmd
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const result = await pullVault({ project, env, out, inject, client });
|
|
119
|
-
console.log(
|
|
120
|
-
`pulled ${Object.keys(result.vars).length} vars for ${project}/${env}` +
|
|
121
|
-
(result.out ? ` → ${result.out}` : "") +
|
|
122
|
-
(inject ? " (injected into process.env)" : ""),
|
|
123
|
-
);
|
|
124
|
-
return;
|
|
58
|
+
if (cmd !== "pull") {
|
|
59
|
+
printHelp();
|
|
60
|
+
process.exit(1);
|
|
125
61
|
}
|
|
126
62
|
|
|
127
|
-
|
|
128
|
-
process.
|
|
63
|
+
const project = str(flags, "project", process.env.VAULT_PROJECT);
|
|
64
|
+
const env = str(flags, "env", process.env.VAULT_ENV);
|
|
65
|
+
const out = typeof flags.out === "string" ? flags.out : undefined;
|
|
66
|
+
const inject = flags["no-inject"] !== true;
|
|
67
|
+
const result = await pullVault({ project, env, out, inject });
|
|
68
|
+
console.log(
|
|
69
|
+
`pulled ${Object.keys(result.vars).length} vars for ${project}/${env}` +
|
|
70
|
+
(result.out ? ` → ${result.out}` : "") +
|
|
71
|
+
(inject ? " (injected into process.env)" : ""),
|
|
72
|
+
);
|
|
129
73
|
}
|
|
130
74
|
|
|
131
75
|
main().catch((err) => {
|
package/src/client.ts
CHANGED
|
@@ -11,11 +11,8 @@ export const DEFAULT_VAULT_URL =
|
|
|
11
11
|
"http://vault-api-service.vault.svc.cluster.local:3001";
|
|
12
12
|
|
|
13
13
|
export type VaultClientOptions = {
|
|
14
|
-
/** Base URL da API do vault na VPN. Default: DEFAULT_VAULT_URL */
|
|
15
14
|
baseUrl: string;
|
|
16
|
-
/** Bearer token (ServiceAccount projected). Se omitido, tenta ler do filesystem do pod. */
|
|
17
15
|
token?: string;
|
|
18
|
-
/** Paths para procurar o token SA (ordem de prioridade). */
|
|
19
16
|
tokenPaths?: string[];
|
|
20
17
|
fetch?: typeof globalThis.fetch;
|
|
21
18
|
};
|
|
@@ -69,30 +66,9 @@ export class VaultClient {
|
|
|
69
66
|
return this.request("/v1/projects");
|
|
70
67
|
}
|
|
71
68
|
|
|
72
|
-
createProject(name: string): Promise<{ id: string; name: string }> {
|
|
73
|
-
return this.request("/v1/projects", {
|
|
74
|
-
method: "POST",
|
|
75
|
-
body: JSON.stringify({ name }),
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
createEnvironment(projectId: string, name: string): Promise<{ ok: boolean }> {
|
|
80
|
-
return this.request(`/v1/projects/${projectId}/environments`, {
|
|
81
|
-
method: "POST",
|
|
82
|
-
body: JSON.stringify({ name }),
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
69
|
listVars(environmentId: string): Promise<EnvVar[]> {
|
|
87
70
|
return this.request(`/v1/environments/${environmentId}/vars`);
|
|
88
71
|
}
|
|
89
|
-
|
|
90
|
-
upsertVar(environmentId: string, key: string, value: string): Promise<{ ok: boolean }> {
|
|
91
|
-
return this.request(`/v1/environments/${environmentId}/vars`, {
|
|
92
|
-
method: "PUT",
|
|
93
|
-
body: JSON.stringify({ key, value }),
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
72
|
}
|
|
97
73
|
|
|
98
74
|
export function createClientFromEnv(
|
package/src/index.ts
CHANGED
|
@@ -1,166 +1,39 @@
|
|
|
1
1
|
import { writeFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
3
|
-
createClientFromEnv,
|
|
4
|
-
DEFAULT_VAULT_URL,
|
|
5
|
-
VaultClient,
|
|
6
|
-
type VaultClientOptions,
|
|
7
|
-
} from "./client.js";
|
|
2
|
+
import { createClientFromEnv } from "./client.js";
|
|
8
3
|
import { toDotEnv, varsToRecord } from "./dotenv.js";
|
|
9
|
-
import type { EnvVar, ProjectTree } from "./types.js";
|
|
10
4
|
|
|
11
|
-
export type
|
|
12
|
-
export {
|
|
13
|
-
VaultClient,
|
|
14
|
-
createClientFromEnv,
|
|
15
|
-
DEFAULT_VAULT_URL,
|
|
16
|
-
type VaultClientOptions,
|
|
17
|
-
};
|
|
18
|
-
export { toDotEnv, varsToRecord };
|
|
19
|
-
|
|
20
|
-
export type ResolveOptions = {
|
|
5
|
+
export type LoadVaultOptions = {
|
|
21
6
|
project: string;
|
|
22
7
|
env: string;
|
|
23
|
-
client?: VaultClient;
|
|
24
8
|
};
|
|
25
9
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export type PullOptions = ResolveOptions & {
|
|
31
|
-
out?: string;
|
|
32
|
-
inject?: boolean;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export type CreateEnvOptions = {
|
|
36
|
-
project: string;
|
|
37
|
-
env: string;
|
|
38
|
-
/** URL da API do vault na VPN (cluster-internal) */
|
|
39
|
-
url?: string;
|
|
40
|
-
vars?: Record<string, string>;
|
|
41
|
-
client?: VaultClient;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
async function findProject(
|
|
45
|
-
client: VaultClient,
|
|
46
|
-
name: string,
|
|
47
|
-
): Promise<ProjectTree | undefined> {
|
|
48
|
-
const tree = await client.listProjects();
|
|
49
|
-
return tree.find((p) => p.name === name);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function resolveClient(opts: { client?: VaultClient; url?: string }): VaultClient {
|
|
53
|
-
if (opts.client) return opts.client;
|
|
54
|
-
if (opts.url) return new VaultClient({ baseUrl: opts.url });
|
|
55
|
-
return createClientFromEnv();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function resolveEnvironment(
|
|
59
|
-
opts: ResolveOptions,
|
|
60
|
-
): Promise<{ projectId: string; environmentId: string }> {
|
|
61
|
-
const client = opts.client ?? createClientFromEnv();
|
|
62
|
-
const project = await findProject(client, opts.project);
|
|
10
|
+
async function readVars(opts: LoadVaultOptions): Promise<Record<string, string>> {
|
|
11
|
+
const client = createClientFromEnv();
|
|
12
|
+
const projects = await client.listProjects();
|
|
13
|
+
const project = projects.find((item) => item.name === opts.project);
|
|
63
14
|
if (!project) throw new Error(`Project not found: ${opts.project}`);
|
|
64
|
-
const environment = project.environments.find((e) => e.name === opts.env);
|
|
65
|
-
if (!environment) {
|
|
66
|
-
throw new Error(`Environment "${opts.env}" not found in project "${opts.project}"`);
|
|
67
|
-
}
|
|
68
|
-
return { projectId: project.id, environmentId: environment.id };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** Garante projeto + environment na VPN (cria se não existir). */
|
|
72
|
-
export async function ensureEnvironment(opts: EnsureOptions): Promise<{
|
|
73
|
-
projectId: string;
|
|
74
|
-
environmentId: string;
|
|
75
|
-
createdProject: boolean;
|
|
76
|
-
createdEnvironment: boolean;
|
|
77
|
-
}> {
|
|
78
|
-
const client = opts.client ?? createClientFromEnv();
|
|
79
|
-
let createdProject = false;
|
|
80
|
-
let createdEnvironment = false;
|
|
81
|
-
|
|
82
|
-
let project = await findProject(client, opts.project);
|
|
83
|
-
if (!project) {
|
|
84
|
-
if (opts.createProject === false) {
|
|
85
|
-
throw new Error(`Project not found: ${opts.project}`);
|
|
86
|
-
}
|
|
87
|
-
await client.createProject(opts.project);
|
|
88
|
-
createdProject = true;
|
|
89
|
-
project = await findProject(client, opts.project);
|
|
90
|
-
if (!project) throw new Error(`Failed to create project: ${opts.project}`);
|
|
91
|
-
}
|
|
92
15
|
|
|
93
|
-
|
|
16
|
+
const environment = project.environments.find((item) => item.name === opts.env);
|
|
94
17
|
if (!environment) {
|
|
95
|
-
|
|
96
|
-
createdEnvironment = true;
|
|
97
|
-
const refreshed = await findProject(client, opts.project);
|
|
98
|
-
if (!refreshed) throw new Error(`Failed to reload project: ${opts.project}`);
|
|
99
|
-
project = refreshed;
|
|
100
|
-
environment = project.environments.find((e) => e.name === opts.env);
|
|
101
|
-
if (!environment) throw new Error(`Failed to create environment: ${opts.env}`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
projectId: project.id,
|
|
106
|
-
environmentId: environment.id,
|
|
107
|
-
createdProject,
|
|
108
|
-
createdEnvironment,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Cria (ou garante) o environment no vault via URL da VPN
|
|
114
|
-
* e opcionalmente faz upsert das variáveis.
|
|
115
|
-
*/
|
|
116
|
-
export async function createEnv(opts: CreateEnvOptions): Promise<{
|
|
117
|
-
projectId: string;
|
|
118
|
-
environmentId: string;
|
|
119
|
-
createdProject: boolean;
|
|
120
|
-
createdEnvironment: boolean;
|
|
121
|
-
upserted: string[];
|
|
122
|
-
}> {
|
|
123
|
-
const client = resolveClient(opts);
|
|
124
|
-
const ensured = await ensureEnvironment({
|
|
125
|
-
project: opts.project,
|
|
126
|
-
env: opts.env,
|
|
127
|
-
client,
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
const upserted: string[] = [];
|
|
131
|
-
if (opts.vars) {
|
|
132
|
-
for (const [key, value] of Object.entries(opts.vars)) {
|
|
133
|
-
await client.upsertVar(ensured.environmentId, key, value);
|
|
134
|
-
upserted.push(key);
|
|
135
|
-
}
|
|
18
|
+
throw new Error(`Environment "${opts.env}" not found in project "${opts.project}"`);
|
|
136
19
|
}
|
|
137
20
|
|
|
138
|
-
|
|
21
|
+
const rows = await client.listVars(environment.id);
|
|
22
|
+
return varsToRecord(rows);
|
|
139
23
|
}
|
|
140
24
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export async function loadVault(
|
|
148
|
-
opts: ResolveOptions,
|
|
149
|
-
): Promise<Record<string, string>> {
|
|
150
|
-
const rows = await fetchVars(opts);
|
|
151
|
-
const record = varsToRecord(rows);
|
|
152
|
-
for (const [key, value] of Object.entries(record)) {
|
|
25
|
+
/** Lê o environment e injeta as variáveis em `process.env`. */
|
|
26
|
+
export async function loadVault(opts: LoadVaultOptions): Promise<void> {
|
|
27
|
+
const vars = await readVars(opts);
|
|
28
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
153
29
|
process.env[key] = value;
|
|
154
30
|
}
|
|
155
|
-
return record;
|
|
156
31
|
}
|
|
157
32
|
|
|
158
|
-
export async function pullVault(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const rows = await fetchVars(opts);
|
|
163
|
-
const vars = varsToRecord(rows);
|
|
33
|
+
export async function pullVault(
|
|
34
|
+
opts: LoadVaultOptions & { out?: string; inject?: boolean },
|
|
35
|
+
): Promise<{ vars: Record<string, string>; out?: string }> {
|
|
36
|
+
const vars = await readVars(opts);
|
|
164
37
|
|
|
165
38
|
if (opts.inject !== false) {
|
|
166
39
|
for (const [key, value] of Object.entries(vars)) {
|
|
@@ -169,9 +42,6 @@ export async function pullVault(opts: PullOptions): Promise<{
|
|
|
169
42
|
}
|
|
170
43
|
|
|
171
44
|
const out = opts.out;
|
|
172
|
-
if (out)
|
|
173
|
-
await writeFile(out, toDotEnv(vars), "utf8");
|
|
174
|
-
}
|
|
175
|
-
|
|
45
|
+
if (out) await writeFile(out, toDotEnv(vars), "utf8");
|
|
176
46
|
return out ? { vars, out } : { vars };
|
|
177
47
|
}
|