@tc-libs/env 5.11.1 → 5.12.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 +28 -10
- package/package.json +1 -1
- package/src/cli.js +6 -1
- package/src/cli.js.map +1 -1
- package/src/env.pull.d.ts +1 -0
- package/src/env.pull.js +6 -5
- package/src/env.pull.js.map +1 -1
package/README.md
CHANGED
|
@@ -55,7 +55,8 @@ Uno script per ambiente nel `package.json` del progetto, per esempio:
|
|
|
55
55
|
- legge il segreto con le credenziali AWS locali: `--profile`, oppure `AWS_PROFILE`, oppure la catena di default
|
|
56
56
|
dell'SDK. La regione arriva da `--region`, dal profilo o da `AWS_REGION`;
|
|
57
57
|
- si ferma prima di leggere il segreto se il file di output non è ignorato da git (un file versionato non lo è mai) o
|
|
58
|
-
se coincide con la configurazione
|
|
58
|
+
se coincide con la configurazione. `--skip-git-check` salta la verifica di git: serve solo dove il file non può
|
|
59
|
+
finire in un commit, come il server di deploy, e sul PC di uno sviluppatore non si usa;
|
|
59
60
|
- scrive la configurazione così com'è e poi i segreti, con permessi 600, e stampa solo dei conteggi;
|
|
60
61
|
- si ferma con un errore che nomina solo le chiavi, mai i valori, se una chiave sta sia nella configurazione sia nel
|
|
61
62
|
segreto, se il segreto non è un oggetto JSON di stringhe o se un valore non si può scrivere in un file dotenv.
|
|
@@ -78,6 +79,23 @@ rm .env.prod.local
|
|
|
78
79
|
|
|
79
80
|
`DOT_ENV_FILE` è il meccanismo di Borgarello per scegliere il file env; ogni progetto usa il suo.
|
|
80
81
|
|
|
82
|
+
## Sul server di deploy
|
|
83
|
+
|
|
84
|
+
Al deploy il `.env` si compone sulla VM, prima di avviare il container nuovo, eseguendo `tc-env` dentro l'immagine
|
|
85
|
+
dell'app: sulla VM non serve Node, e il segreto si legge con il ruolo dell'istanza (con IMDSv2 serve un hop limit di
|
|
86
|
+
almeno 2). Nell'immagine git non c'è, quindi si usa `--skip-git-check`:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
sudo docker run --rm --user 1000:1000 -v "$APP_DIR:/work" -w /work \
|
|
90
|
+
--entrypoint /usr/src/app/node_modules/.bin/tc-env "$IMAGE" \
|
|
91
|
+
pull --secret-id "borgarello/$ENV/core" --config .env --out .env.composed --region eu-south-1 --skip-git-check
|
|
92
|
+
mv "$APP_DIR/.env.composed" "$APP_DIR/.env"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`$APP_DIR` contiene la configurazione arrivata con l'artifact; `@tc-libs/env` deve essere tra le `dependencies` dell'app,
|
|
96
|
+
perché l'immagine di produzione installa solo quelle. Se il segreto manca o non è valido `tc-env` esce con 1 e lo script
|
|
97
|
+
di deploy deve fermarsi prima di toccare la versione in esercizio.
|
|
98
|
+
|
|
81
99
|
## Permessi AWS
|
|
82
100
|
|
|
83
101
|
Chi esegue `pull` ha bisogno di `secretsmanager:GetSecretValue` sul segreto, più `kms:Decrypt` se il segreto è cifrato
|
|
@@ -94,12 +112,12 @@ risorsa si indica con un carattere jolly:
|
|
|
94
112
|
|
|
95
113
|
## API
|
|
96
114
|
|
|
97
|
-
| Funzione
|
|
98
|
-
|
|
|
99
|
-
| `secret(schema)`
|
|
100
|
-
| `secretKeys(objectSchema)`
|
|
101
|
-
| `secretKeysIn(content, keys)`
|
|
102
|
-
| `composeEnv(configContent, secrets, source)`
|
|
103
|
-
| `readSecret(secretId, region?)`
|
|
104
|
-
| `pullEnv({ secretId, configFile, outFile, region? })` | scrive il `.env` e restituisce i conteggi |
|
|
105
|
-
| `EnvError`
|
|
115
|
+
| Funzione | Uso |
|
|
116
|
+
| -------------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
117
|
+
| `secret(schema)` | marca una variabile Joi come segreto |
|
|
118
|
+
| `secretKeys(objectSchema)` | chiavi segrete dello schema, in ordine alfabetico |
|
|
119
|
+
| `secretKeysIn(content, keys)` | chiavi segrete definite nel contenuto di un file env |
|
|
120
|
+
| `composeEnv(configContent, secrets, source)` | testo del `.env`: configurazione e segreti |
|
|
121
|
+
| `readSecret(secretId, region?)` | segreto JSON di Secrets Manager come oggetto di stringhe |
|
|
122
|
+
| `pullEnv({ secretId, configFile, outFile, region?, skipGitCheck? })` | scrive il `.env` e restituisce i conteggi |
|
|
123
|
+
| `EnvError` | errore con messaggi che nominano chiavi e file, mai valori |
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -6,9 +6,12 @@ const node_util_1 = require("node:util");
|
|
|
6
6
|
const env_error_1 = require("./env.error");
|
|
7
7
|
const env_pull_1 = require("./env.pull");
|
|
8
8
|
const USAGE = `Uso: tc-env pull --secret-id <id> --config <file> --out <file> [--region <regione>] [--profile <profilo>]
|
|
9
|
+
[--skip-git-check]
|
|
9
10
|
|
|
10
11
|
Scrive <out> unendo la configurazione versionata <config> e il segreto JSON <id> di AWS Secrets Manager, letto con le
|
|
11
|
-
credenziali AWS locali: il profilo indicato, AWS_PROFILE oppure la catena di default. <out> deve essere ignorato da git
|
|
12
|
+
credenziali AWS locali: il profilo indicato, AWS_PROFILE oppure la catena di default. <out> deve essere ignorato da git.
|
|
13
|
+
--skip-git-check salta questa verifica: solo dove <out> non può finire in un commit, come il server di deploy, mai sul
|
|
14
|
+
PC di uno sviluppatore.`;
|
|
12
15
|
async function main(argv, io = { out: console.log, err: console.error }) {
|
|
13
16
|
let parsed;
|
|
14
17
|
try {
|
|
@@ -21,6 +24,7 @@ async function main(argv, io = { out: console.log, err: console.error }) {
|
|
|
21
24
|
out: { type: 'string' },
|
|
22
25
|
region: { type: 'string' },
|
|
23
26
|
profile: { type: 'string' },
|
|
27
|
+
'skip-git-check': { type: 'boolean' },
|
|
24
28
|
},
|
|
25
29
|
});
|
|
26
30
|
}
|
|
@@ -45,6 +49,7 @@ async function main(argv, io = { out: console.log, err: console.error }) {
|
|
|
45
49
|
configFile: config,
|
|
46
50
|
outFile: out,
|
|
47
51
|
region,
|
|
52
|
+
skipGitCheck: values['skip-git-check'],
|
|
48
53
|
});
|
|
49
54
|
io.out(`${out}: ${result.configKeys} chiavi di configurazione e ${result.secretKeys} segreti da ${secretId}`);
|
|
50
55
|
return 0;
|
package/src/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../../packages/env/src/cli.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../../packages/env/src/cli.ts"],"names":[],"mappings":";;;AAeA,oBAmDC;AAjED,yCAAsC;AACtC,2CAAuC;AACvC,yCAAqC;AAErC,MAAM,KAAK,GAAG;;;;;;wBAMU,CAAC;AAIlB,KAAK,UAAU,IAAI,CACxB,IAAc,EACd,KAAS,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,KAAK,EAAE;IAEjD,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,IAAA,qBAAS,EAAC;YACjB,IAAI,EAAE,IAAI;YACV,gBAAgB,EAAE,IAAI;YACtB,OAAO,EAAE;gBACP,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC/B,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC1B,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACvB,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC1B,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC3B,gBAAgB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;aACtC;SACF,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,EAAE,CAAC,GAAG,CAAE,KAAe,CAAC,OAAO,CAAC,CAAC;QACjC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACd,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IACrC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAChD,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,MAAM,IAAI,CAAC,QAAQ,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACrE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACd,OAAO,CAAC,CAAC;IACX,CAAC;IACD,oEAAoE;IACpE,IAAI,OAAO;QAAE,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC;IAElD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,kBAAO,EAAC;YAC3B,QAAQ;YACR,UAAU,EAAE,MAAM;YAClB,OAAO,EAAE,GAAG;YACZ,MAAM;YACN,YAAY,EAAE,MAAM,CAAC,gBAAgB,CAAC;SACvC,CAAC,CAAC;QACH,EAAE,CAAC,GAAG,CACJ,GAAG,GAAG,KAAK,MAAM,CAAC,UAAU,+BAA+B,MAAM,CAAC,UAAU,eAAe,QAAQ,EAAE,CACtG,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,oBAAQ,CAAC;YAAE,MAAM,KAAK,CAAC;QAC9C,EAAE,CAAC,GAAG,CAAC,WAAW,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACnC,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC5B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QACxC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/src/env.pull.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type PullEnvOptions = {
|
|
|
3
3
|
configFile: string;
|
|
4
4
|
outFile: string;
|
|
5
5
|
region?: string;
|
|
6
|
+
skipGitCheck?: boolean;
|
|
6
7
|
};
|
|
7
8
|
export declare function readSecret(secretId: string, region?: string): Promise<Record<string, string>>;
|
|
8
9
|
export declare function pullEnv(options: PullEnvOptions): Promise<{
|
package/src/env.pull.js
CHANGED
|
@@ -50,9 +50,6 @@ async function readSecret(secretId, region) {
|
|
|
50
50
|
function assertGitIgnored(file) {
|
|
51
51
|
const path = (0, node_path_1.resolve)(file);
|
|
52
52
|
const dir = (0, node_path_1.dirname)(path);
|
|
53
|
-
if (!(0, node_fs_1.existsSync)(dir)) {
|
|
54
|
-
throw new env_error_1.EnvError(`la cartella di ${file} non esiste`);
|
|
55
|
-
}
|
|
56
53
|
try {
|
|
57
54
|
(0, node_child_process_1.execFileSync)('git', ['check-ignore', '-q', '--', path], {
|
|
58
55
|
cwd: dir,
|
|
@@ -74,14 +71,18 @@ function assertGitIgnored(file) {
|
|
|
74
71
|
}
|
|
75
72
|
// Compone outFile dalla configurazione versionata e dal segreto, con permessi ristretti. Restituisce solo conteggi.
|
|
76
73
|
async function pullEnv(options) {
|
|
77
|
-
const { secretId, configFile, outFile, region } = options;
|
|
74
|
+
const { secretId, configFile, outFile, region, skipGitCheck } = options;
|
|
78
75
|
if ((0, node_path_1.resolve)(configFile) === (0, node_path_1.resolve)(outFile)) {
|
|
79
76
|
throw new env_error_1.EnvError('il file di configurazione e quello di output coincidono');
|
|
80
77
|
}
|
|
81
78
|
if (!(0, node_fs_1.existsSync)(configFile)) {
|
|
82
79
|
throw new env_error_1.EnvError(`file di configurazione ${configFile} non trovato`);
|
|
83
80
|
}
|
|
84
|
-
|
|
81
|
+
if (!(0, node_fs_1.existsSync)((0, node_path_1.dirname)((0, node_path_1.resolve)(outFile)))) {
|
|
82
|
+
throw new env_error_1.EnvError(`la cartella di ${outFile} non esiste`);
|
|
83
|
+
}
|
|
84
|
+
if (!skipGitCheck)
|
|
85
|
+
assertGitIgnored(outFile);
|
|
85
86
|
const configContent = (0, node_fs_1.readFileSync)(configFile, 'utf8');
|
|
86
87
|
const secrets = await readSecret(secretId, region);
|
|
87
88
|
(0, node_fs_1.writeFileSync)(outFile, (0, env_file_1.composeEnv)(configContent, secrets, secretId), {
|
package/src/env.pull.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.pull.js","sourceRoot":"","sources":["../../../../packages/env/src/env.pull.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"env.pull.js","sourceRoot":"","sources":["../../../../packages/env/src/env.pull.ts"],"names":[],"mappings":"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"}
|