@multiplatform.one/cli 2.1.0 → 2.1.5
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/lib/bin/multiplatformOne.mjs +140 -0
- package/package.json +10 -6
- package/src/bin/multiplatformOne.ts +176 -25
- package/lib/bin/multiplatformOne.d.mts +0 -2
- package/lib/index.d.mts +0 -2
- package/lib/types.d.mts +0 -9
|
@@ -1,12 +1,19 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
1
4
|
// src/bin/multiplatformOne.ts
|
|
2
5
|
import fsSync from "node:fs";
|
|
3
6
|
import fs from "node:fs/promises";
|
|
4
7
|
import os from "node:os";
|
|
5
8
|
import path from "node:path";
|
|
6
9
|
import { fileURLToPath } from "node:url";
|
|
10
|
+
import axios from "axios";
|
|
7
11
|
import { program } from "commander";
|
|
12
|
+
import dotenv from "dotenv";
|
|
8
13
|
import { execa } from "execa";
|
|
9
14
|
import inquirer from "inquirer";
|
|
15
|
+
import ora from "ora";
|
|
16
|
+
import pg from "pg";
|
|
10
17
|
var availableBackends = [
|
|
11
18
|
"api",
|
|
12
19
|
"frappe"
|
|
@@ -182,4 +189,137 @@ program.command("update").option("-r, --remote <remote>", "the remote to use", "
|
|
|
182
189
|
});
|
|
183
190
|
}
|
|
184
191
|
});
|
|
192
|
+
var waitServices = [
|
|
193
|
+
"api",
|
|
194
|
+
"frappe",
|
|
195
|
+
"postgres",
|
|
196
|
+
"keycloak"
|
|
197
|
+
];
|
|
198
|
+
program.command("wait").description("wait for a service to be ready").option("-i, --interval <interval>", "interval to wait for", 1e3).option("-t, --timeout <timeout>", "timeout to wait for", 6e5).option("-e, --dotenv <dotenv>", "dotenv file path", ".env").argument("<services>", `the services to wait for (${waitServices.join(", ")})`).action(async (servicesString, options) => {
|
|
199
|
+
dotenv.config({
|
|
200
|
+
path: options.dotenv
|
|
201
|
+
});
|
|
202
|
+
const interval = Number.parseInt(options.interval);
|
|
203
|
+
const timeout = Number.parseInt(options.timeout);
|
|
204
|
+
const services = servicesString.split(",");
|
|
205
|
+
const unreadyServices = [
|
|
206
|
+
...services
|
|
207
|
+
];
|
|
208
|
+
const spinner = ora(`waiting for ${formatServiceList(unreadyServices)}`).start();
|
|
209
|
+
function updateSpinner(readyService) {
|
|
210
|
+
unreadyServices.splice(unreadyServices.indexOf(readyService), 1);
|
|
211
|
+
spinner.stop();
|
|
212
|
+
spinner.succeed(`${readyService} is ready`);
|
|
213
|
+
if (!unreadyServices.length) return;
|
|
214
|
+
spinner.start(`waiting for ${formatServiceList(unreadyServices)}`);
|
|
215
|
+
}
|
|
216
|
+
__name(updateSpinner, "updateSpinner");
|
|
217
|
+
let timeoutId;
|
|
218
|
+
try {
|
|
219
|
+
await Promise.race([
|
|
220
|
+
Promise.all(services.map(async (service) => {
|
|
221
|
+
switch (service) {
|
|
222
|
+
case "api": {
|
|
223
|
+
await waitForApi(interval);
|
|
224
|
+
updateSpinner("api");
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
case "frappe": {
|
|
228
|
+
await waitForFrappe(interval);
|
|
229
|
+
updateSpinner("frappe");
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
case "postgres": {
|
|
233
|
+
await waitForPostgres(interval);
|
|
234
|
+
updateSpinner("postgres");
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
case "keycloak": {
|
|
238
|
+
await waitForKeycloak(interval);
|
|
239
|
+
updateSpinner("keycloak");
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
ora(`available services are ${formatServiceList(waitServices)}`).fail();
|
|
244
|
+
})),
|
|
245
|
+
new Promise((_, reject) => {
|
|
246
|
+
timeoutId = setTimeout(() => reject(new Error("Timeout")), timeout);
|
|
247
|
+
})
|
|
248
|
+
]);
|
|
249
|
+
} catch (err) {
|
|
250
|
+
if (err instanceof Error && err.message === "Timeout") {
|
|
251
|
+
spinner.fail(`${formatServiceList(unreadyServices)} timed out after ${timeout}ms`);
|
|
252
|
+
} else {
|
|
253
|
+
spinner.fail(err);
|
|
254
|
+
}
|
|
255
|
+
process.exit(1);
|
|
256
|
+
} finally {
|
|
257
|
+
clearTimeout(timeoutId);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
function formatServiceList(services) {
|
|
261
|
+
if (services.length === 1) return services[0];
|
|
262
|
+
if (services.length === 2) return `${services[0]} and ${services[1]}`;
|
|
263
|
+
return `${services.slice(0, -1).join(", ")} and ${services[services.length - 1]}`;
|
|
264
|
+
}
|
|
265
|
+
__name(formatServiceList, "formatServiceList");
|
|
185
266
|
program.parse(process.argv);
|
|
267
|
+
async function waitForApi(interval) {
|
|
268
|
+
try {
|
|
269
|
+
const res = await axios.get(`http://localhost:${process.env.API_PORT || "5001"}/healthz`);
|
|
270
|
+
if ((res?.status || 500) < 300) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
} catch (err) {
|
|
274
|
+
}
|
|
275
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
276
|
+
return waitForApi(interval);
|
|
277
|
+
}
|
|
278
|
+
__name(waitForApi, "waitForApi");
|
|
279
|
+
async function waitForFrappe(interval) {
|
|
280
|
+
try {
|
|
281
|
+
const res = await axios.get(`${process.env.FRAPPE_BASE_URL || "http://frappe.localhost"}/api/method/ping`);
|
|
282
|
+
if ((res?.status || 500) < 300) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
} catch (err) {
|
|
286
|
+
}
|
|
287
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
288
|
+
return waitForFrappe(interval);
|
|
289
|
+
}
|
|
290
|
+
__name(waitForFrappe, "waitForFrappe");
|
|
291
|
+
async function waitForPostgres(interval) {
|
|
292
|
+
const client = new pg.Client({
|
|
293
|
+
database: process.env.POSTGRES_DATABASE || "postgres",
|
|
294
|
+
host: process.env.POSTGRES_HOSTNAME || "localhost",
|
|
295
|
+
password: process.env.POSTGRES_PASSWORD || "postgres",
|
|
296
|
+
port: Number.parseInt(process.env.POSTGRES_PORT || "5432"),
|
|
297
|
+
user: process.env.POSTGRES_USERNAME || "postgres"
|
|
298
|
+
});
|
|
299
|
+
try {
|
|
300
|
+
await client.connect();
|
|
301
|
+
while (true) {
|
|
302
|
+
try {
|
|
303
|
+
await client.query("SELECT 1");
|
|
304
|
+
return;
|
|
305
|
+
} catch (error) {
|
|
306
|
+
}
|
|
307
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
308
|
+
}
|
|
309
|
+
} finally {
|
|
310
|
+
await client.end();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
__name(waitForPostgres, "waitForPostgres");
|
|
314
|
+
async function waitForKeycloak(interval) {
|
|
315
|
+
try {
|
|
316
|
+
const res = await axios.get(`${process.env.KEYCLOAK_BASE_URL || "http://localhost:8080"}/realms/master/.well-known/openid-configuration`);
|
|
317
|
+
if ((res?.status || 500) < 300) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
} catch (err) {
|
|
321
|
+
}
|
|
322
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
323
|
+
return waitForKeycloak(interval);
|
|
324
|
+
}
|
|
325
|
+
__name(waitForKeycloak, "waitForKeycloak");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplatform.one/cli",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"author": "BitSpur <support@bitspur.com> (https://bitspur.com)",
|
|
5
5
|
"contributors": [
|
|
6
6
|
{
|
|
@@ -48,16 +48,20 @@
|
|
|
48
48
|
"node": ">= 16.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
+
"@multiplatform.one/utils": "0.1.6",
|
|
51
52
|
"@types/inquirer": "^9.0.7",
|
|
52
53
|
"@types/node": "~20.12.13",
|
|
53
|
-
"tsup": "^8.
|
|
54
|
-
"typescript": "
|
|
55
|
-
"@multiplatform.one/utils": "^0.1.0"
|
|
54
|
+
"tsup": "^8.3.0",
|
|
55
|
+
"typescript": "^5.6.2"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
+
"axios": "^1.7.7",
|
|
58
59
|
"commander": "^9.5.0",
|
|
59
|
-
"
|
|
60
|
-
"
|
|
60
|
+
"dotenv": "^16.4.5",
|
|
61
|
+
"execa": "^9.4.1",
|
|
62
|
+
"inquirer": "^9.3.7",
|
|
63
|
+
"ora": "^8.1.0",
|
|
64
|
+
"pg": "^8.13.0"
|
|
61
65
|
},
|
|
62
66
|
"transpileModules": [
|
|
63
67
|
"#ansi-styles",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
2
|
* File: /src/bin/multiplatformOne.ts
|
|
3
3
|
* Project: @multiplatform.one/cli
|
|
4
|
-
* File Created:
|
|
4
|
+
* File Created: 01-01-1970 00:00:00
|
|
5
5
|
* Author: Clay Risser
|
|
6
6
|
* -----
|
|
7
|
-
* BitSpur Copyright 2021 - 2024
|
|
7
|
+
* BitSpur (c) Copyright 2021 - 2024
|
|
8
8
|
*
|
|
9
9
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
10
|
* you may not use this file except in compliance with the License.
|
|
@@ -24,9 +24,13 @@ import fs from "node:fs/promises";
|
|
|
24
24
|
import os from "node:os";
|
|
25
25
|
import path from "node:path";
|
|
26
26
|
import { fileURLToPath } from "node:url";
|
|
27
|
+
import axios from "axios";
|
|
27
28
|
import { program } from "commander";
|
|
29
|
+
import dotenv from "dotenv";
|
|
28
30
|
import { execa } from "execa";
|
|
29
31
|
import inquirer from "inquirer";
|
|
32
|
+
import ora from "ora";
|
|
33
|
+
import pg from "pg";
|
|
30
34
|
import type { CookieCutterConfig } from "../types";
|
|
31
35
|
|
|
32
36
|
const availableBackends = ["api", "frappe"];
|
|
@@ -41,7 +45,7 @@ const availablePlatforms = [
|
|
|
41
45
|
|
|
42
46
|
process.env.COOKIECUTTER = `sh ${path.resolve(
|
|
43
47
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
44
|
-
"../../scripts/cookiecutter.sh"
|
|
48
|
+
"../../scripts/cookiecutter.sh",
|
|
45
49
|
)}`;
|
|
46
50
|
program.name("multiplatform.one");
|
|
47
51
|
program.version(
|
|
@@ -49,11 +53,11 @@ program.version(
|
|
|
49
53
|
fsSync.readFileSync(
|
|
50
54
|
path.resolve(
|
|
51
55
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
52
|
-
"../../package.json"
|
|
56
|
+
"../../package.json",
|
|
53
57
|
),
|
|
54
|
-
"utf8"
|
|
55
|
-
)
|
|
56
|
-
)?.version
|
|
58
|
+
"utf8",
|
|
59
|
+
),
|
|
60
|
+
)?.version,
|
|
57
61
|
);
|
|
58
62
|
|
|
59
63
|
program
|
|
@@ -61,12 +65,12 @@ program
|
|
|
61
65
|
.option(
|
|
62
66
|
"-r, --remote <remote>",
|
|
63
67
|
"the remote to use",
|
|
64
|
-
"https://gitlab.com/bitspur/multiplatform.one/cookiecutter"
|
|
68
|
+
"https://gitlab.com/bitspur/multiplatform.one/cookiecutter",
|
|
65
69
|
)
|
|
66
70
|
.option(
|
|
67
71
|
"-c, --checkout <branch>",
|
|
68
72
|
"branch, tag or commit to checkout",
|
|
69
|
-
"main"
|
|
73
|
+
"main",
|
|
70
74
|
)
|
|
71
75
|
.option("-p, --platforms <platforms>", "platforms to keep")
|
|
72
76
|
.option("-b, --backends <backends>", "backends to keep")
|
|
@@ -83,7 +87,7 @@ program
|
|
|
83
87
|
).exitCode === 0
|
|
84
88
|
) {
|
|
85
89
|
throw new Error(
|
|
86
|
-
"multiplatform.one cannot be initialized inside a git repository"
|
|
90
|
+
"multiplatform.one cannot be initialized inside a git repository",
|
|
87
91
|
);
|
|
88
92
|
}
|
|
89
93
|
if (!name) {
|
|
@@ -129,19 +133,19 @@ program
|
|
|
129
133
|
};
|
|
130
134
|
const cookieCutterConfigFile = path.join(
|
|
131
135
|
await fs.mkdtemp(path.join(os.tmpdir(), "multiplatform-")),
|
|
132
|
-
"config.json"
|
|
136
|
+
"config.json",
|
|
133
137
|
);
|
|
134
138
|
try {
|
|
135
139
|
await fs.writeFile(
|
|
136
140
|
cookieCutterConfigFile,
|
|
137
|
-
JSON.stringify(cookieCutterConfig, null, 2)
|
|
141
|
+
JSON.stringify(cookieCutterConfig, null, 2),
|
|
138
142
|
);
|
|
139
143
|
await execa(
|
|
140
144
|
"sh",
|
|
141
145
|
[
|
|
142
146
|
path.resolve(
|
|
143
147
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
144
|
-
"../../scripts/init.sh"
|
|
148
|
+
"../../scripts/init.sh",
|
|
145
149
|
),
|
|
146
150
|
"--no-input",
|
|
147
151
|
"-f",
|
|
@@ -153,7 +157,7 @@ program
|
|
|
153
157
|
],
|
|
154
158
|
{
|
|
155
159
|
stdio: "inherit",
|
|
156
|
-
}
|
|
160
|
+
},
|
|
157
161
|
);
|
|
158
162
|
} finally {
|
|
159
163
|
await fs.rm(cookieCutterConfigFile, { recursive: true, force: true });
|
|
@@ -165,12 +169,12 @@ program
|
|
|
165
169
|
.option(
|
|
166
170
|
"-r, --remote <remote>",
|
|
167
171
|
"the remote to use",
|
|
168
|
-
"https://gitlab.com/bitspur/multiplatform.one/cookiecutter"
|
|
172
|
+
"https://gitlab.com/bitspur/multiplatform.one/cookiecutter",
|
|
169
173
|
)
|
|
170
174
|
.option(
|
|
171
175
|
"-c, --checkout <branch>",
|
|
172
176
|
"branch, tag or commit to checkout",
|
|
173
|
-
"main"
|
|
177
|
+
"main",
|
|
174
178
|
)
|
|
175
179
|
.option("-p, --platforms <platforms>", "platforms to keep")
|
|
176
180
|
.option("-b, --backends <backends>", "backends to keep")
|
|
@@ -207,7 +211,7 @@ program
|
|
|
207
211
|
).exitCode !== 0
|
|
208
212
|
) {
|
|
209
213
|
throw new Error(
|
|
210
|
-
"multiplatform.one cannot be updated outside of a git repository"
|
|
214
|
+
"multiplatform.one cannot be updated outside of a git repository",
|
|
211
215
|
);
|
|
212
216
|
}
|
|
213
217
|
if (
|
|
@@ -218,7 +222,7 @@ program
|
|
|
218
222
|
).exitCode !== 0
|
|
219
223
|
) {
|
|
220
224
|
throw new Error(
|
|
221
|
-
"multiplatform.one cannot be updated with uncommitted changes"
|
|
225
|
+
"multiplatform.one cannot be updated with uncommitted changes",
|
|
222
226
|
);
|
|
223
227
|
}
|
|
224
228
|
const projectRoot = (
|
|
@@ -229,11 +233,14 @@ program
|
|
|
229
233
|
(await fs.stat(path.resolve(projectRoot, "package.json"))).isFile() &&
|
|
230
234
|
(await fs.stat(path.resolve(projectRoot, "app/package.json"))).isFile() &&
|
|
231
235
|
JSON.parse(
|
|
232
|
-
await fs.readFile(
|
|
236
|
+
await fs.readFile(
|
|
237
|
+
path.resolve(projectRoot, "app/package.json"),
|
|
238
|
+
"utf8",
|
|
239
|
+
),
|
|
233
240
|
)?.dependencies?.["multiplatform.one"]?.length
|
|
234
241
|
) {
|
|
235
242
|
const name = JSON.parse(
|
|
236
|
-
await fs.readFile(path.resolve(projectRoot, "package.json"), "utf8")
|
|
243
|
+
await fs.readFile(path.resolve(projectRoot, "package.json"), "utf8"),
|
|
237
244
|
)?.name;
|
|
238
245
|
if (name) {
|
|
239
246
|
cookieCutterConfig = { default_context: { name, backends, platforms } };
|
|
@@ -242,19 +249,19 @@ program
|
|
|
242
249
|
if (!cookieCutterConfig) throw new Error("not a multiplatform.one project");
|
|
243
250
|
const cookieCutterConfigFile = path.join(
|
|
244
251
|
await fs.mkdtemp(path.join(os.tmpdir(), "multiplatform-")),
|
|
245
|
-
"config.json"
|
|
252
|
+
"config.json",
|
|
246
253
|
);
|
|
247
254
|
try {
|
|
248
255
|
await fs.writeFile(
|
|
249
256
|
cookieCutterConfigFile,
|
|
250
|
-
JSON.stringify(cookieCutterConfig, null, 2)
|
|
257
|
+
JSON.stringify(cookieCutterConfig, null, 2),
|
|
251
258
|
);
|
|
252
259
|
await execa(
|
|
253
260
|
"sh",
|
|
254
261
|
[
|
|
255
262
|
path.resolve(
|
|
256
263
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
257
|
-
"../../scripts/update.sh"
|
|
264
|
+
"../../scripts/update.sh",
|
|
258
265
|
),
|
|
259
266
|
"--no-input",
|
|
260
267
|
"-f",
|
|
@@ -267,11 +274,155 @@ program
|
|
|
267
274
|
{
|
|
268
275
|
cwd: projectRoot,
|
|
269
276
|
stdio: "inherit",
|
|
270
|
-
}
|
|
277
|
+
},
|
|
271
278
|
);
|
|
272
279
|
} finally {
|
|
273
280
|
await fs.rm(cookieCutterConfigFile, { recursive: true, force: true });
|
|
274
281
|
}
|
|
275
282
|
});
|
|
276
283
|
|
|
284
|
+
const waitServices = ["api", "frappe", "postgres", "keycloak"];
|
|
285
|
+
program
|
|
286
|
+
.command("wait")
|
|
287
|
+
.description("wait for a service to be ready")
|
|
288
|
+
.option("-i, --interval <interval>", "interval to wait for", 1000)
|
|
289
|
+
.option("-t, --timeout <timeout>", "timeout to wait for", 600000)
|
|
290
|
+
.option("-e, --dotenv <dotenv>", "dotenv file path", ".env")
|
|
291
|
+
.argument(
|
|
292
|
+
"<services>",
|
|
293
|
+
`the services to wait for (${waitServices.join(", ")})`,
|
|
294
|
+
)
|
|
295
|
+
.action(async (servicesString, options) => {
|
|
296
|
+
dotenv.config({ path: options.dotenv });
|
|
297
|
+
const interval = Number.parseInt(options.interval);
|
|
298
|
+
const timeout = Number.parseInt(options.timeout);
|
|
299
|
+
const services: string[] = servicesString.split(",");
|
|
300
|
+
const unreadyServices = [...services];
|
|
301
|
+
const spinner = ora(
|
|
302
|
+
`waiting for ${formatServiceList(unreadyServices)}`,
|
|
303
|
+
).start();
|
|
304
|
+
function updateSpinner(readyService: string) {
|
|
305
|
+
unreadyServices.splice(unreadyServices.indexOf(readyService), 1);
|
|
306
|
+
spinner.stop();
|
|
307
|
+
spinner.succeed(`${readyService} is ready`);
|
|
308
|
+
if (!unreadyServices.length) return;
|
|
309
|
+
spinner.start(`waiting for ${formatServiceList(unreadyServices)}`);
|
|
310
|
+
}
|
|
311
|
+
let timeoutId: NodeJS.Timeout;
|
|
312
|
+
try {
|
|
313
|
+
await Promise.race([
|
|
314
|
+
Promise.all(
|
|
315
|
+
services.map(async (service) => {
|
|
316
|
+
switch (service) {
|
|
317
|
+
case "api": {
|
|
318
|
+
await waitForApi(interval);
|
|
319
|
+
updateSpinner("api");
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
case "frappe": {
|
|
323
|
+
await waitForFrappe(interval);
|
|
324
|
+
updateSpinner("frappe");
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
case "postgres": {
|
|
328
|
+
await waitForPostgres(interval);
|
|
329
|
+
updateSpinner("postgres");
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
case "keycloak": {
|
|
333
|
+
await waitForKeycloak(interval);
|
|
334
|
+
updateSpinner("keycloak");
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
ora(
|
|
339
|
+
`available services are ${formatServiceList(waitServices)}`,
|
|
340
|
+
).fail();
|
|
341
|
+
}),
|
|
342
|
+
),
|
|
343
|
+
new Promise((_, reject) => {
|
|
344
|
+
timeoutId = setTimeout(() => reject(new Error("Timeout")), timeout);
|
|
345
|
+
}),
|
|
346
|
+
]);
|
|
347
|
+
} catch (err) {
|
|
348
|
+
if (err instanceof Error && err.message === "Timeout") {
|
|
349
|
+
spinner.fail(
|
|
350
|
+
`${formatServiceList(unreadyServices)} timed out after ${timeout}ms`,
|
|
351
|
+
);
|
|
352
|
+
} else {
|
|
353
|
+
spinner.fail(err);
|
|
354
|
+
}
|
|
355
|
+
process.exit(1);
|
|
356
|
+
} finally {
|
|
357
|
+
clearTimeout(timeoutId);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
function formatServiceList(services: string[]): string {
|
|
362
|
+
if (services.length === 1) return services[0];
|
|
363
|
+
if (services.length === 2) return `${services[0]} and ${services[1]}`;
|
|
364
|
+
return `${services.slice(0, -1).join(", ")} and ${services[services.length - 1]}`;
|
|
365
|
+
}
|
|
366
|
+
|
|
277
367
|
program.parse(process.argv);
|
|
368
|
+
|
|
369
|
+
async function waitForApi(interval: number) {
|
|
370
|
+
try {
|
|
371
|
+
const res = await axios.get(
|
|
372
|
+
`http://localhost:${process.env.API_PORT || "5001"}/healthz`,
|
|
373
|
+
);
|
|
374
|
+
if ((res?.status || 500) < 300) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
} catch (err) {}
|
|
378
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
379
|
+
return waitForApi(interval);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async function waitForFrappe(interval: number) {
|
|
383
|
+
try {
|
|
384
|
+
const res = await axios.get(
|
|
385
|
+
`${process.env.FRAPPE_BASE_URL || "http://frappe.localhost"}/api/method/ping`,
|
|
386
|
+
);
|
|
387
|
+
if ((res?.status || 500) < 300) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
} catch (err) {}
|
|
391
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
392
|
+
return waitForFrappe(interval);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async function waitForPostgres(interval: number) {
|
|
396
|
+
const client = new pg.Client({
|
|
397
|
+
database: process.env.POSTGRES_DATABASE || "postgres",
|
|
398
|
+
host: process.env.POSTGRES_HOSTNAME || "localhost",
|
|
399
|
+
password: process.env.POSTGRES_PASSWORD || "postgres",
|
|
400
|
+
port: Number.parseInt(process.env.POSTGRES_PORT || "5432"),
|
|
401
|
+
user: process.env.POSTGRES_USERNAME || "postgres",
|
|
402
|
+
});
|
|
403
|
+
try {
|
|
404
|
+
await client.connect();
|
|
405
|
+
while (true) {
|
|
406
|
+
try {
|
|
407
|
+
await client.query("SELECT 1");
|
|
408
|
+
return;
|
|
409
|
+
} catch (error) {}
|
|
410
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
411
|
+
}
|
|
412
|
+
} finally {
|
|
413
|
+
await client.end();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
async function waitForKeycloak(interval: number) {
|
|
418
|
+
try {
|
|
419
|
+
const res = await axios.get(
|
|
420
|
+
`${process.env.KEYCLOAK_BASE_URL || "http://localhost:8080"}/realms/master/.well-known/openid-configuration`,
|
|
421
|
+
);
|
|
422
|
+
if ((res?.status || 500) < 300) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
} catch (err) {}
|
|
426
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
427
|
+
return waitForKeycloak(interval);
|
|
428
|
+
}
|
package/lib/index.d.mts
DELETED