@purpleschool/infisical-env 0.1.2 → 0.1.4
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/package.json +3 -2
- package/src/cli.js +266 -269
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@purpleschool/infisical-env",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"infisical-env": "src/cli.js"
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"picocolors": "^1.0.1",
|
|
16
|
+
"prompts": "^2.4.2",
|
|
16
17
|
"yaml": "^2.4.5"
|
|
17
18
|
}
|
|
18
|
-
}
|
|
19
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -3,10 +3,10 @@ import fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import readline from "node:readline/promises";
|
|
7
6
|
import YAML from "yaml";
|
|
8
7
|
import os from "node:os";
|
|
9
8
|
import pc from "picocolors";
|
|
9
|
+
import prompts from "prompts";
|
|
10
10
|
|
|
11
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
12
|
const __dirname = path.dirname(__filename);
|
|
@@ -16,348 +16,345 @@ const DEFAULT_INFISICAL_BASE_URL = "https://secret.purplecode.ru";
|
|
|
16
16
|
const DEFAULT_INFISICAL_CLIENT_ID = "f3e0d60c-a602-4296-bd03-3be228e89165";
|
|
17
17
|
|
|
18
18
|
async function main() {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const repoRoot = findRepoRoot(process.cwd());
|
|
27
|
-
const packageJson = readJson(path.join(repoRoot, "package.json"));
|
|
19
|
+
try {
|
|
20
|
+
const args = process.argv.slice(2);
|
|
21
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
22
|
+
printHelp();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
const repoRoot = findRepoRoot(process.cwd());
|
|
27
|
+
const packageJson = readJson(path.join(repoRoot, "package.json"));
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
const projectId = resolveInfisicalProject(packageJson);
|
|
30
|
+
const envName = await resolveEnvName(args);
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
32
|
+
const deploymentPath = resolveDeploymentPath(repoRoot);
|
|
33
|
+
const envRefs = collectEnvRefs(deploymentPath);
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
envName,
|
|
42
|
-
envRefs,
|
|
43
|
-
repoRoot,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const envFilePath = path.join(repoRoot, ".env");
|
|
47
|
-
const lines = values.map((item) => `${item.name}=${item.value}`);
|
|
48
|
-
fs.writeFileSync(envFilePath, lines.join(os.EOL), "utf8");
|
|
49
|
-
|
|
50
|
-
console.log(pc.green(`.env written to ${envFilePath}`));
|
|
51
|
-
} catch (err) {
|
|
52
|
-
console.error(pc.red(formatError(err)));
|
|
53
|
-
console.error(pc.yellow("Run with --help for usage."));
|
|
54
|
-
process.exit(1);
|
|
35
|
+
if (envRefs.length === 0) {
|
|
36
|
+
throw new Error(`No env refs found in ${deploymentPath}`);
|
|
55
37
|
}
|
|
38
|
+
|
|
39
|
+
const values = await resolveEnvValues({
|
|
40
|
+
projectId,
|
|
41
|
+
envName,
|
|
42
|
+
envRefs,
|
|
43
|
+
repoRoot,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const envFilePath = path.join(repoRoot, ".env");
|
|
47
|
+
const lines = values.map((item) => `${item.name}=${item.value}`);
|
|
48
|
+
fs.writeFileSync(envFilePath, lines.join(os.EOL), "utf8");
|
|
49
|
+
|
|
50
|
+
console.log(pc.green(`.env written to ${envFilePath}`));
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error(pc.red(formatError(err)));
|
|
53
|
+
console.error(pc.yellow("Run with --help for usage."));
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
function printHelp() {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
59
|
+
console.log(
|
|
60
|
+
[
|
|
61
|
+
pc.bold("Usage:"),
|
|
62
|
+
" infisical-env [--env stage|stage-2]",
|
|
63
|
+
"",
|
|
64
|
+
pc.bold("Options:"),
|
|
65
|
+
" --env <name> Environment name (stage or stage-2)",
|
|
66
|
+
" -h, --help Show this help",
|
|
67
|
+
"",
|
|
68
|
+
pc.bold("Environment variables:"),
|
|
69
|
+
" INFISICAL_CLIENT_SECRET Required (machine identity secret)",
|
|
70
|
+
" INFISICAL_CLIENT_ID Optional override",
|
|
71
|
+
" INFISICAL_BASE_URL Optional override (default https://secret.purplecode.ru)",
|
|
72
|
+
" INFISICAL_MOCK_PATH Optional mock json path for offline testing",
|
|
73
|
+
].join(os.EOL)
|
|
74
|
+
);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function findRepoRoot(startDir) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
current = parent;
|
|
78
|
+
let current = startDir;
|
|
79
|
+
while (true) {
|
|
80
|
+
const candidate = path.join(current, "package.json");
|
|
81
|
+
if (fs.existsSync(candidate)) {
|
|
82
|
+
return current;
|
|
83
|
+
}
|
|
84
|
+
const parent = path.dirname(current);
|
|
85
|
+
if (parent === current) {
|
|
86
|
+
throw new Error("package.json not found in any parent directory");
|
|
89
87
|
}
|
|
88
|
+
current = parent;
|
|
89
|
+
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
function readJson(filePath) {
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
94
|
+
return JSON.parse(raw);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
function resolveInfisicalProject(packageJson) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
98
|
+
const name = typeof packageJson.name === "string" ? packageJson.name.toLowerCase() : "";
|
|
99
|
+
if (name.includes("rugpt")) {
|
|
100
|
+
return "48854e39-7129-4786-87ac-cfcce296991c";
|
|
101
|
+
}
|
|
102
|
+
if (name.includes("studdy") || name.includes("student-works")) {
|
|
103
|
+
return "b4b0abbd-f8e6-428a-a296-6289369ca8dd";
|
|
104
|
+
}
|
|
105
|
+
if (name.includes("multisite")) {
|
|
106
|
+
return "6531c08b-45e0-4d75-a8ab-08d14d0387bf";
|
|
107
|
+
}
|
|
108
|
+
throw new Error(
|
|
109
|
+
"Unable to resolve Infisical project from package.json name. Expected name to include rugpt, studdy, student-works, or multisite."
|
|
110
|
+
);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
async function resolveEnvName(args) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
const flagValue = parseEnvFlag(args);
|
|
115
|
+
if (flagValue) {
|
|
116
|
+
return validateEnv(flagValue);
|
|
117
|
+
}
|
|
118
|
+
return promptEnv();
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
function parseEnvFlag(args) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
if (arg.startsWith("--env=")) {
|
|
128
|
-
return arg.slice("--env=".length);
|
|
129
|
-
}
|
|
122
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
123
|
+
const arg = args[i];
|
|
124
|
+
if (arg === "--env") {
|
|
125
|
+
return args[i + 1] || "";
|
|
130
126
|
}
|
|
131
|
-
|
|
127
|
+
if (arg.startsWith("--env=")) {
|
|
128
|
+
return arg.slice("--env=".length);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return "";
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
function validateEnv(value) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
if (!ALLOWED_ENVS.includes(value)) {
|
|
136
|
+
throw new Error(`Invalid environment \"${value}\". Allowed: ${ALLOWED_ENVS.join(", ")}`);
|
|
137
|
+
}
|
|
138
|
+
return value;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
async function promptEnv() {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (!Number.isInteger(index) || index < 0 || index >= ALLOWED_ENVS.length) {
|
|
155
|
-
throw new Error("Invalid environment selection");
|
|
156
|
-
}
|
|
157
|
-
return ALLOWED_ENVS[index];
|
|
158
|
-
} finally {
|
|
159
|
-
rl.close();
|
|
142
|
+
const response = await prompts(
|
|
143
|
+
{
|
|
144
|
+
type: "select",
|
|
145
|
+
name: "env",
|
|
146
|
+
message: "Select environment",
|
|
147
|
+
choices: ALLOWED_ENVS.map((value) => ({ title: value, value })),
|
|
148
|
+
initial: 0,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
onCancel: () => {
|
|
152
|
+
throw new Error("Environment selection canceled");
|
|
153
|
+
},
|
|
160
154
|
}
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
return validateEnv(response.env);
|
|
161
158
|
}
|
|
162
159
|
|
|
163
160
|
function resolveDeploymentPath(repoRoot) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
161
|
+
const candidates = [
|
|
162
|
+
path.join(repoRoot, ".kube", "deployment.yml"),
|
|
163
|
+
path.join(repoRoot, ".kube", "deployment.yaml"),
|
|
164
|
+
path.join(repoRoot, "deployment.yml"),
|
|
165
|
+
path.join(repoRoot, "deployment.yaml"),
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
for (const candidate of candidates) {
|
|
169
|
+
if (fs.existsSync(candidate)) {
|
|
170
|
+
return candidate;
|
|
175
171
|
}
|
|
172
|
+
}
|
|
176
173
|
|
|
177
|
-
|
|
174
|
+
throw new Error("Deployment file not found. Expected .kube/deployment.yml or .kube/deployment.yaml");
|
|
178
175
|
}
|
|
179
176
|
|
|
180
177
|
function collectEnvRefs(deploymentPath) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
}
|
|
178
|
+
const raw = fs.readFileSync(deploymentPath, "utf8");
|
|
179
|
+
const docs = YAML.parseAllDocuments(raw);
|
|
180
|
+
const refs = [];
|
|
181
|
+
|
|
182
|
+
for (const doc of docs) {
|
|
183
|
+
const data = doc.toJSON();
|
|
184
|
+
const containers = data?.spec?.template?.spec?.containers || [];
|
|
185
|
+
for (const container of containers) {
|
|
186
|
+
const envList = container?.env || [];
|
|
187
|
+
for (const envItem of envList) {
|
|
188
|
+
const name = envItem?.name;
|
|
189
|
+
const secretKeyRef = envItem?.valueFrom?.secretKeyRef;
|
|
190
|
+
const folder = secretKeyRef?.name;
|
|
191
|
+
const key = secretKeyRef?.key;
|
|
192
|
+
if (name && folder && key) {
|
|
193
|
+
refs.push({ name, folder, key });
|
|
199
194
|
}
|
|
195
|
+
}
|
|
200
196
|
}
|
|
197
|
+
}
|
|
201
198
|
|
|
202
|
-
|
|
199
|
+
return refs;
|
|
203
200
|
}
|
|
204
201
|
|
|
205
202
|
async function resolveEnvValues({ projectId, envName, envRefs, repoRoot }) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
203
|
+
const mockPath = process.env.INFISICAL_MOCK_PATH;
|
|
204
|
+
if (mockPath) {
|
|
205
|
+
return resolveFromMock({ projectId, envName, envRefs, mockPath, repoRoot });
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const clientId = process.env.INFISICAL_CLIENT_ID || DEFAULT_INFISICAL_CLIENT_ID;
|
|
209
|
+
const clientSecret = process.env.INFISICAL_CLIENT_SECRET;
|
|
210
|
+
if (!clientId || !clientSecret) {
|
|
211
|
+
throw new Error(
|
|
212
|
+
"Missing Infisical credentials. Set INFISICAL_CLIENT_SECRET or use INFISICAL_MOCK_PATH."
|
|
213
|
+
);
|
|
214
|
+
}
|
|
218
215
|
|
|
219
|
-
|
|
220
|
-
|
|
216
|
+
const baseUrl = normalizeBaseUrl(process.env.INFISICAL_BASE_URL || DEFAULT_INFISICAL_BASE_URL);
|
|
217
|
+
const accessToken = await loginUniversalAuth({ baseUrl, clientId, clientSecret });
|
|
221
218
|
|
|
222
|
-
|
|
219
|
+
return fetchSecretsFromInfisical({ baseUrl, accessToken, projectId, envName, envRefs });
|
|
223
220
|
}
|
|
224
221
|
|
|
225
222
|
function resolveFromMock({ projectId, envName, envRefs, mockPath, repoRoot }) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
223
|
+
const absPath = path.isAbsolute(mockPath) ? mockPath : path.join(repoRoot, mockPath);
|
|
224
|
+
if (!fs.existsSync(absPath)) {
|
|
225
|
+
throw new Error(`Mock file not found: ${absPath}`);
|
|
226
|
+
}
|
|
227
|
+
const data = readJson(absPath);
|
|
228
|
+
const projectData = data?.projects?.[projectId];
|
|
229
|
+
if (!projectData) {
|
|
230
|
+
throw new Error(`Project not found in mock: ${projectId}`);
|
|
231
|
+
}
|
|
232
|
+
const envData = projectData?.[envName];
|
|
233
|
+
if (!envData) {
|
|
234
|
+
throw new Error(`Environment not found in mock: ${envName}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return envRefs.map((ref) => {
|
|
238
|
+
const folderData = envData?.[ref.folder];
|
|
239
|
+
const value = folderData?.[ref.key];
|
|
240
|
+
if (value === undefined || value === null) {
|
|
241
|
+
throw new Error(`Missing value for ${ref.folder}.${ref.key}`);
|
|
234
242
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
throw new Error(`Environment not found in mock: ${envName}`);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return envRefs.map((ref) => {
|
|
241
|
-
const folderData = envData?.[ref.folder];
|
|
242
|
-
const value = folderData?.[ref.key];
|
|
243
|
-
if (value === undefined || value === null) {
|
|
244
|
-
throw new Error(`Missing value for ${ref.folder}.${ref.key}`);
|
|
245
|
-
}
|
|
246
|
-
return { name: ref.name, value: String(value) };
|
|
247
|
-
});
|
|
243
|
+
return { name: ref.name, value: String(value) };
|
|
244
|
+
});
|
|
248
245
|
}
|
|
249
246
|
|
|
250
247
|
function formatError(err) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
248
|
+
if (err instanceof Error) {
|
|
249
|
+
return err.message;
|
|
250
|
+
}
|
|
251
|
+
return String(err);
|
|
255
252
|
}
|
|
256
253
|
|
|
257
254
|
function normalizeBaseUrl(value) {
|
|
258
|
-
|
|
255
|
+
return value.replace(/\/+$/, "");
|
|
259
256
|
}
|
|
260
257
|
|
|
261
258
|
async function loginUniversalAuth({ baseUrl, clientId, clientSecret }) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
259
|
+
const url = `${baseUrl}/api/v1/auth/universal-auth/login`;
|
|
260
|
+
const body = new URLSearchParams();
|
|
261
|
+
body.set("clientId", clientId);
|
|
262
|
+
body.set("clientSecret", clientSecret);
|
|
263
|
+
|
|
264
|
+
const res = await fetch(url, {
|
|
265
|
+
method: "POST",
|
|
266
|
+
headers: {
|
|
267
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
268
|
+
},
|
|
269
|
+
body,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
if (!res.ok) {
|
|
273
|
+
const text = await safeReadText(res);
|
|
274
|
+
throw new Error(`Infisical login failed (${res.status}). ${text}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const data = await res.json();
|
|
278
|
+
if (!data?.accessToken) {
|
|
279
|
+
throw new Error("Infisical login response missing accessToken.");
|
|
280
|
+
}
|
|
281
|
+
return data.accessToken;
|
|
285
282
|
}
|
|
286
283
|
|
|
287
284
|
async function fetchSecretsFromInfisical({ baseUrl, accessToken, projectId, envName, envRefs }) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
refsByFolder.get(ref.folder).push(ref);
|
|
285
|
+
const refsByFolder = new Map();
|
|
286
|
+
for (const ref of envRefs) {
|
|
287
|
+
if (!refsByFolder.has(ref.folder)) {
|
|
288
|
+
refsByFolder.set(ref.folder, []);
|
|
294
289
|
}
|
|
290
|
+
refsByFolder.get(ref.folder).push(ref);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const secretCache = new Map();
|
|
294
|
+
for (const [folder, refs] of refsByFolder.entries()) {
|
|
295
|
+
const secretPath = folder.startsWith("/") ? folder : `/${folder}`;
|
|
296
|
+
const secrets = await listSecrets({
|
|
297
|
+
baseUrl,
|
|
298
|
+
accessToken,
|
|
299
|
+
projectId,
|
|
300
|
+
envName,
|
|
301
|
+
secretPath,
|
|
302
|
+
});
|
|
303
|
+
const map = new Map();
|
|
304
|
+
for (const item of secrets) {
|
|
305
|
+
if (item?.secretKey != null) {
|
|
306
|
+
map.set(item.secretKey, item.secretValue);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
secretCache.set(folder, map);
|
|
295
310
|
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
baseUrl,
|
|
301
|
-
accessToken,
|
|
302
|
-
projectId,
|
|
303
|
-
envName,
|
|
304
|
-
secretPath,
|
|
305
|
-
});
|
|
306
|
-
const map = new Map();
|
|
307
|
-
for (const item of secrets) {
|
|
308
|
-
if (item?.secretKey != null) {
|
|
309
|
-
map.set(item.secretKey, item.secretValue);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
secretCache.set(folder, map);
|
|
313
|
-
|
|
314
|
-
for (const ref of refs) {
|
|
315
|
-
if (!map.has(ref.key)) {
|
|
316
|
-
throw new Error(`Missing value for ${folder}.${ref.key} in environment ${envName}`);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
311
|
+
for (const ref of refs) {
|
|
312
|
+
if (!map.has(ref.key)) {
|
|
313
|
+
throw new Error(`Missing value for ${folder}.${ref.key} in environment ${envName}`);
|
|
314
|
+
}
|
|
319
315
|
}
|
|
316
|
+
}
|
|
320
317
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
318
|
+
return envRefs.map((ref) => {
|
|
319
|
+
const folderMap = secretCache.get(ref.folder);
|
|
320
|
+
const value = folderMap?.get(ref.key);
|
|
321
|
+
return { name: ref.name, value: String(value) };
|
|
322
|
+
});
|
|
326
323
|
}
|
|
327
324
|
|
|
328
325
|
async function listSecrets({ baseUrl, accessToken, projectId, envName, secretPath }) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
326
|
+
const params = new URLSearchParams({
|
|
327
|
+
projectId,
|
|
328
|
+
environment: envName,
|
|
329
|
+
secretPath,
|
|
330
|
+
viewSecretValue: "true",
|
|
331
|
+
expandSecretReferences: "true",
|
|
332
|
+
recursive: "false",
|
|
333
|
+
includeImports: "true",
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const url = `${baseUrl}/api/v4/secrets?${params.toString()}`;
|
|
337
|
+
const res = await fetch(url, {
|
|
338
|
+
headers: {
|
|
339
|
+
Authorization: `Bearer ${accessToken}`,
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
if (!res.ok) {
|
|
344
|
+
const text = await safeReadText(res);
|
|
345
|
+
throw new Error(`Infisical secrets list failed (${res.status}) for ${secretPath}. ${text}`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const data = await res.json();
|
|
349
|
+
return Array.isArray(data?.secrets) ? data.secrets : [];
|
|
353
350
|
}
|
|
354
351
|
|
|
355
352
|
async function safeReadText(res) {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
353
|
+
try {
|
|
354
|
+
return await res.text();
|
|
355
|
+
} catch {
|
|
356
|
+
return "";
|
|
357
|
+
}
|
|
361
358
|
}
|
|
362
359
|
|
|
363
360
|
await main();
|