@raisenow/tamaro-cli 1.9.0-dev.1 → 1.9.0-dev.2
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/dist/cli.js +171 -70
- package/dist/{env-JzPIlctP.js → env-DBP70I1v.js} +12 -16
- package/dist/webpack.config.js +17 -1
- package/package.json +12 -12
- package/eslint.config.ts +0 -30
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as
|
|
2
|
+
import { A as HTTPS_CRT_FILE, B as logSuccess, C as DEFAULT_PORT, D as EPMS_AUTH_BASE_URL_PROD, E as EPMS_API_BASE_URL_STAGE, F as createTerminalLink, I as logCommand, L as logDataTable, M as halt, N as promptConfirmation, O as EPMS_AUTH_BASE_URL_STAGE, P as runCommandSync, R as logError, S as DEFAULT_HTTP_TIMEOUT, T as EPMS_API_BASE_URL_PROD, V as logTitle, _ as AWS_S3_BUCKET_TAMARO, a as getIfCoreFns, b as CACHE_DIR, c as getWidgetUuid, d as resolveBin, f as resolveCoreVersion, g as AWS_CLOUDFRONT_DISTRIBUTION_ID, h as AUTH_PORT, j as HTTPS_KEY_FILE, k as EPMS_OAUTH_CLIENT_ID, l as resolveAccountUuidFromConfig, m as resolveOwn, n as assertEnvValid, o as getPaths, t as applyEnv, v as AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD, w as DEFAULT_TAG, x as CORE_CONFIG_NAME, y as AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE, z as logInfo } from "./env-DBP70I1v.js";
|
|
3
3
|
import { createCommand } from "commander";
|
|
4
4
|
import fs, { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
@@ -364,29 +364,32 @@ const AuthResponseSchema = z.object({
|
|
|
364
364
|
expires_in: z.number(),
|
|
365
365
|
token_type: z.string()
|
|
366
366
|
});
|
|
367
|
-
const getCacheFilePath = (
|
|
368
|
-
const cacheKey = Buffer.from(
|
|
367
|
+
const getCacheFilePath = (key) => {
|
|
368
|
+
const cacheKey = Buffer.from(key).toString("base64url");
|
|
369
369
|
return path.join(CACHE_DIR, `token-${cacheKey}.json`);
|
|
370
370
|
};
|
|
371
|
-
const
|
|
371
|
+
const getTokenCacheKey = (baseUrl, clientId) => {
|
|
372
|
+
return `${baseUrl}|${clientId}`;
|
|
373
|
+
};
|
|
374
|
+
const readCachedToken = (key) => {
|
|
372
375
|
try {
|
|
373
|
-
const filePath = getCacheFilePath(
|
|
376
|
+
const filePath = getCacheFilePath(key);
|
|
374
377
|
if (!existsSync(filePath)) return;
|
|
375
378
|
const data = JSON.parse(readFileSync(filePath, "utf8"));
|
|
376
379
|
const parsed = CachedTokenSchema.safeParse(data);
|
|
377
380
|
if (parsed.success) return parsed.data;
|
|
378
381
|
} catch {}
|
|
379
382
|
};
|
|
380
|
-
const writeCachedToken = (
|
|
383
|
+
const writeCachedToken = (key, cached) => {
|
|
381
384
|
try {
|
|
382
|
-
const filePath = getCacheFilePath(
|
|
385
|
+
const filePath = getCacheFilePath(key);
|
|
383
386
|
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
384
387
|
writeFileSync(filePath, JSON.stringify(cached), { mode: 384 });
|
|
385
388
|
} catch {}
|
|
386
389
|
};
|
|
387
|
-
const removeCachedToken = (
|
|
390
|
+
const removeCachedToken = (key) => {
|
|
388
391
|
try {
|
|
389
|
-
const filePath = getCacheFilePath(
|
|
392
|
+
const filePath = getCacheFilePath(key);
|
|
390
393
|
if (existsSync(filePath)) writeFileSync(filePath, "", { flag: "w" });
|
|
391
394
|
} catch {}
|
|
392
395
|
};
|
|
@@ -396,11 +399,13 @@ const createAuthorizer = (baseUrl, clientId, clientSecret) => {
|
|
|
396
399
|
const apiUnauthorized = ky.extend({
|
|
397
400
|
headers: { "Access-Control-Allow-Origin": "*" },
|
|
398
401
|
prefixUrl: baseUrl,
|
|
402
|
+
retry: { limit: 3 },
|
|
399
403
|
timeout: DEFAULT_HTTP_TIMEOUT
|
|
400
404
|
});
|
|
401
405
|
let expirationTime = void 0;
|
|
402
406
|
let token = void 0;
|
|
403
|
-
const
|
|
407
|
+
const cacheKey = getTokenCacheKey(baseUrl, clientId);
|
|
408
|
+
const cached = readCachedToken(cacheKey);
|
|
404
409
|
if (cached) {
|
|
405
410
|
token = cached.token;
|
|
406
411
|
expirationTime = cached.expirationTime;
|
|
@@ -415,7 +420,7 @@ const createAuthorizer = (baseUrl, clientId, clientSecret) => {
|
|
|
415
420
|
} }).json();
|
|
416
421
|
token = `${data.token_type} ${data.access_token}`;
|
|
417
422
|
expirationTime = Date.now() + data.expires_in * 1e3;
|
|
418
|
-
writeCachedToken(
|
|
423
|
+
writeCachedToken(cacheKey, {
|
|
419
424
|
expirationTime,
|
|
420
425
|
token
|
|
421
426
|
});
|
|
@@ -475,7 +480,9 @@ const doBrowserOAuthFlow = (authBaseUrl, clientId, port) => {
|
|
|
475
480
|
server.listen(port, () => {
|
|
476
481
|
const redirectUri = `http://localhost:${port}`;
|
|
477
482
|
const authUrl = `${authBaseUrl}/oauth2/authorize?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=token`;
|
|
478
|
-
|
|
483
|
+
logInfo("\nℹ️ Log in with your super admin account in the browser to continue.");
|
|
484
|
+
console.log(`Opening browser for authentication...`);
|
|
485
|
+
console.log(createTerminalLink(authUrl, authUrl));
|
|
479
486
|
open(authUrl).catch((error) => {
|
|
480
487
|
reject(error instanceof Error ? error : new Error(String(error)));
|
|
481
488
|
});
|
|
@@ -485,12 +492,13 @@ const doBrowserOAuthFlow = (authBaseUrl, clientId, port) => {
|
|
|
485
492
|
const createBrowserAuthorizer = (authBaseUrl, clientId, epmsBaseUrl) => {
|
|
486
493
|
const apiBase = ky.extend({
|
|
487
494
|
prefixUrl: epmsBaseUrl,
|
|
495
|
+
retry: { limit: 3 },
|
|
488
496
|
timeout: DEFAULT_HTTP_TIMEOUT
|
|
489
497
|
});
|
|
490
498
|
let expirationTime = void 0;
|
|
491
499
|
let token = void 0;
|
|
492
|
-
const
|
|
493
|
-
const cached = readCachedToken(
|
|
500
|
+
const cacheKey = getTokenCacheKey(authBaseUrl, clientId);
|
|
501
|
+
const cached = readCachedToken(cacheKey);
|
|
494
502
|
if (cached) {
|
|
495
503
|
token = cached.token;
|
|
496
504
|
expirationTime = cached.expirationTime;
|
|
@@ -498,7 +506,7 @@ const createBrowserAuthorizer = (authBaseUrl, clientId, epmsBaseUrl) => {
|
|
|
498
506
|
const resetAuth = () => {
|
|
499
507
|
token = void 0;
|
|
500
508
|
expirationTime = void 0;
|
|
501
|
-
removeCachedToken(
|
|
509
|
+
removeCachedToken(cacheKey);
|
|
502
510
|
};
|
|
503
511
|
const authorize = async () => {
|
|
504
512
|
const isExpired = (expirationTime ?? 0) - Date.now() < 300 * 1e3;
|
|
@@ -506,7 +514,7 @@ const createBrowserAuthorizer = (authBaseUrl, clientId, epmsBaseUrl) => {
|
|
|
506
514
|
const data = await doBrowserOAuthFlow(authBaseUrl, clientId, AUTH_PORT);
|
|
507
515
|
token = `${data.token_type} ${data.access_token}`;
|
|
508
516
|
expirationTime = Date.now() + data.expires_in * 1e3;
|
|
509
|
-
writeCachedToken(
|
|
517
|
+
writeCachedToken(cacheKey, {
|
|
510
518
|
expirationTime,
|
|
511
519
|
token
|
|
512
520
|
});
|
|
@@ -530,17 +538,54 @@ const createBrowserAuthorizer = (authBaseUrl, clientId, epmsBaseUrl) => {
|
|
|
530
538
|
} });
|
|
531
539
|
};
|
|
532
540
|
//#endregion
|
|
533
|
-
//#region src/lib/epms/
|
|
541
|
+
//#region src/lib/epms/myselfSchema.ts
|
|
542
|
+
const myselfSchema = z.object({
|
|
543
|
+
email: z.string(),
|
|
544
|
+
identity_roles: z.array(z.object({
|
|
545
|
+
identity_uuid: z.string(),
|
|
546
|
+
role: z.object({
|
|
547
|
+
name: z.string(),
|
|
548
|
+
uuid: z.string()
|
|
549
|
+
})
|
|
550
|
+
})),
|
|
551
|
+
uuid: z.string()
|
|
552
|
+
});
|
|
553
|
+
//#endregion
|
|
554
|
+
//#region src/lib/epms/epmsClient.ts
|
|
555
|
+
/**
|
|
556
|
+
* Manages authentication and API interactions with EPMS.
|
|
557
|
+
* It provides factory methods for creating instances based on different authentication flows (client credentials or browser-based) and handles token caching and renewal transparently.
|
|
558
|
+
*/
|
|
534
559
|
var EpmsClient = class EpmsClient {
|
|
560
|
+
#cacheKey;
|
|
535
561
|
#epmsClient;
|
|
536
|
-
|
|
562
|
+
#authType;
|
|
563
|
+
/**
|
|
564
|
+
* Creates an instance of EpmsClient.
|
|
565
|
+
*
|
|
566
|
+
* @param authType The type of authentication used by the client.
|
|
567
|
+
* @param cacheKey They key used for caching the token so that the client can manage the cache (e.g. clear it on logout). It should be unique per EPMS environment and client ID.
|
|
568
|
+
* @param epmsClient The underlying KyInstance used for making API requests.
|
|
569
|
+
*/
|
|
570
|
+
constructor(authType, cacheKey, epmsClient) {
|
|
571
|
+
this.#authType = authType;
|
|
572
|
+
this.#cacheKey = cacheKey;
|
|
537
573
|
this.#epmsClient = epmsClient;
|
|
538
574
|
}
|
|
539
575
|
static fromClientCredentials(baseUrl, clientId, clientSecret) {
|
|
540
|
-
return new EpmsClient(createAuthorizer(baseUrl, clientId, clientSecret));
|
|
576
|
+
return new EpmsClient("client_credentials", getTokenCacheKey(baseUrl, clientId), createAuthorizer(baseUrl, clientId, clientSecret));
|
|
541
577
|
}
|
|
542
578
|
static fromBrowserAuth(epmsBaseUrl, authBaseUrl, clientId) {
|
|
543
|
-
return new EpmsClient(createBrowserAuthorizer(authBaseUrl, clientId, epmsBaseUrl));
|
|
579
|
+
return new EpmsClient("browser", getTokenCacheKey(authBaseUrl, clientId), createBrowserAuthorizer(authBaseUrl, clientId, epmsBaseUrl));
|
|
580
|
+
}
|
|
581
|
+
get authType() {
|
|
582
|
+
return this.#authType;
|
|
583
|
+
}
|
|
584
|
+
logout() {
|
|
585
|
+
removeCachedToken(this.#cacheKey);
|
|
586
|
+
}
|
|
587
|
+
async myself() {
|
|
588
|
+
return this.#epmsClient.get("users/myself").json().then((response) => myselfSchema.parse(response));
|
|
544
589
|
}
|
|
545
590
|
async getOrganisationIdByAccountUuid(accountUuid) {
|
|
546
591
|
const query = {
|
|
@@ -549,7 +594,7 @@ var EpmsClient = class EpmsClient {
|
|
|
549
594
|
size: 1
|
|
550
595
|
};
|
|
551
596
|
const response = await this.#epmsClient.post("search/events", { json: query }).json();
|
|
552
|
-
if (response.hits.length === 0) throw new Error(`No
|
|
597
|
+
if (response.hits.length === 0) throw new Error(`No organisation found for account UUID: ${accountUuid}`);
|
|
553
598
|
const organisationId = response.hits[0].organisation_uuid;
|
|
554
599
|
if (!organisationId) throw new Error(`Organisation UUID not found for account UUID: ${accountUuid}`);
|
|
555
600
|
return organisationId;
|
|
@@ -557,6 +602,57 @@ var EpmsClient = class EpmsClient {
|
|
|
557
602
|
async updateTamaro(configName, tag, json) {
|
|
558
603
|
return this.#epmsClient.put(`products/tamaro/${configName}/tags/${tag}`, { json }).json();
|
|
559
604
|
}
|
|
605
|
+
async archiveTamaro(configName, tag) {
|
|
606
|
+
return this.#epmsClient.post(`products/tamaro/${configName}/tags/${tag}/archive`).json();
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
//#endregion
|
|
610
|
+
//#region src/lib/epms/helpers.ts
|
|
611
|
+
const epmsAuthenticate = (env) => {
|
|
612
|
+
const { authBaseUrl, baseUrl, clientId, clientSecret } = {
|
|
613
|
+
prod: {
|
|
614
|
+
authBaseUrl: EPMS_AUTH_BASE_URL_PROD,
|
|
615
|
+
baseUrl: EPMS_API_BASE_URL_PROD,
|
|
616
|
+
clientId: process.env.EPMS_CLIENT_ID,
|
|
617
|
+
clientSecret: process.env.EPMS_CLIENT_SECRET
|
|
618
|
+
},
|
|
619
|
+
stage: {
|
|
620
|
+
authBaseUrl: EPMS_AUTH_BASE_URL_STAGE,
|
|
621
|
+
baseUrl: EPMS_API_BASE_URL_STAGE,
|
|
622
|
+
clientId: process.env.EPMS_CLIENT_ID_STAGE,
|
|
623
|
+
clientSecret: process.env.EPMS_CLIENT_SECRET_STAGE
|
|
624
|
+
}
|
|
625
|
+
}[env];
|
|
626
|
+
if (clientId && clientSecret) return EpmsClient.fromClientCredentials(baseUrl, clientId, clientSecret);
|
|
627
|
+
return EpmsClient.fromBrowserAuth(baseUrl, authBaseUrl, EPMS_OAUTH_CLIENT_ID);
|
|
628
|
+
};
|
|
629
|
+
const loadAccountUuidsFromConfig = (paths) => {
|
|
630
|
+
if (!("configYml" in paths)) {
|
|
631
|
+
halt("Fatal error, this should never happen");
|
|
632
|
+
throw new Error("Fatal error");
|
|
633
|
+
}
|
|
634
|
+
const configContent = yaml.load(readFileSync(paths.configYml, "utf8"));
|
|
635
|
+
const accountUuidStage = resolveAccountUuidFromConfig(configContent, "epms_stage");
|
|
636
|
+
return {
|
|
637
|
+
accountUuidProd: resolveAccountUuidFromConfig(configContent, "epms"),
|
|
638
|
+
accountUuidStage
|
|
639
|
+
};
|
|
640
|
+
};
|
|
641
|
+
const assertUserIsSuperAdmin = async (epmsClient) => {
|
|
642
|
+
if (epmsClient.authType === "client_credentials") return;
|
|
643
|
+
const myself = await epmsClient.myself();
|
|
644
|
+
if (!myself.identity_roles.some(({ role }) => role.name === "super_admin")) {
|
|
645
|
+
epmsClient.logout();
|
|
646
|
+
logError("\nYou must be a super admin to perform this action.");
|
|
647
|
+
logTitle("\nYour EPMS user details:");
|
|
648
|
+
logDataTable({
|
|
649
|
+
email: myself.email,
|
|
650
|
+
roles: myself.identity_roles.map(({ role }) => role.name).join(", "),
|
|
651
|
+
uuid: myself.uuid
|
|
652
|
+
});
|
|
653
|
+
logError("If this keeps happening, go to Configurator / Hub and logout before trying again.");
|
|
654
|
+
halt();
|
|
655
|
+
}
|
|
560
656
|
};
|
|
561
657
|
//#endregion
|
|
562
658
|
//#region src/commands/update-epms.ts
|
|
@@ -576,7 +672,11 @@ const updateEpms = async (options) => {
|
|
|
576
672
|
const { dryrun, tag } = options;
|
|
577
673
|
const configName = getWidgetUuid();
|
|
578
674
|
loadEnv(paths);
|
|
579
|
-
const {
|
|
675
|
+
const { accountUuidProd, accountUuidStage } = loadAccountUuidsFromConfig(paths);
|
|
676
|
+
if (!accountUuidProd && !accountUuidStage) {
|
|
677
|
+
logTitle("No EPMS account UUIDs found in config.yml. Skipping EPMS update.");
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
580
680
|
if (dryrun) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
|
|
581
681
|
const configuredVersion = resolveCoreVersion();
|
|
582
682
|
if (!configuredVersion) {
|
|
@@ -588,15 +688,26 @@ const updateEpms = async (options) => {
|
|
|
588
688
|
last_deployment: Math.trunc(Date.now() / 1e3),
|
|
589
689
|
read_only: false
|
|
590
690
|
};
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
691
|
+
const environments = [{
|
|
692
|
+
accountUuid: accountUuidStage,
|
|
693
|
+
env: "stage",
|
|
694
|
+
label: "Stage"
|
|
695
|
+
}, {
|
|
696
|
+
accountUuid: accountUuidProd,
|
|
697
|
+
env: "prod",
|
|
698
|
+
label: "Prod"
|
|
699
|
+
}];
|
|
700
|
+
for (const { accountUuid, env, label } of environments) {
|
|
701
|
+
if (!accountUuid) continue;
|
|
702
|
+
const epmsClient = epmsAuthenticate(env);
|
|
703
|
+
await assertUserIsSuperAdmin(epmsClient);
|
|
704
|
+
const organisationUuid = await epmsClient.getOrganisationIdByAccountUuid(accountUuid);
|
|
594
705
|
const body = {
|
|
595
706
|
...baseInfo,
|
|
596
|
-
account_uuid:
|
|
707
|
+
account_uuid: accountUuid,
|
|
597
708
|
organisation_uuid: organisationUuid
|
|
598
709
|
};
|
|
599
|
-
logTitle(
|
|
710
|
+
logTitle(`\nUpdating EPMS ${label}:`);
|
|
600
711
|
logDataTable({
|
|
601
712
|
name: configName,
|
|
602
713
|
tag,
|
|
@@ -604,48 +715,9 @@ const updateEpms = async (options) => {
|
|
|
604
715
|
});
|
|
605
716
|
if (!dryrun) {
|
|
606
717
|
await epmsClient.updateTamaro(configName, tag, body);
|
|
607
|
-
logSuccess(
|
|
608
|
-
} else logSuccess(
|
|
718
|
+
logSuccess(`✅ EPMS ${label} is updated.`);
|
|
719
|
+
} else logSuccess(`⏭️ EPMS ${label} would be updated (dry run).`);
|
|
609
720
|
}
|
|
610
|
-
for (const accountUuidProd of accountUuidsProd) if (accountUuidProd) {
|
|
611
|
-
const epmsClient = epmsAuthenticate("prod");
|
|
612
|
-
const organisationUuid = await epmsClient.getOrganisationIdByAccountUuid(accountUuidProd);
|
|
613
|
-
logTitle(`Updating EPMS Prod for tag "${tag}":`);
|
|
614
|
-
const body = {
|
|
615
|
-
...baseInfo,
|
|
616
|
-
account_uuid: accountUuidProd,
|
|
617
|
-
organisation_uuid: organisationUuid
|
|
618
|
-
};
|
|
619
|
-
logDataTable({
|
|
620
|
-
name: configName,
|
|
621
|
-
tag,
|
|
622
|
-
...body
|
|
623
|
-
});
|
|
624
|
-
if (!dryrun) {
|
|
625
|
-
await epmsClient.updateTamaro(configName, tag, body);
|
|
626
|
-
logSuccess("✅ EPMS Prod is updated.");
|
|
627
|
-
} else logSuccess("⏭️ EPMS Prod would be updated (dry run).");
|
|
628
|
-
}
|
|
629
|
-
};
|
|
630
|
-
const epmsAuthenticate = (env) => {
|
|
631
|
-
if (env === "stage") {
|
|
632
|
-
if (process.env.EPMS_CLIENT_ID_STAGE && process.env.EPMS_CLIENT_SECRET_STAGE) return EpmsClient.fromClientCredentials(EPMS_API_BASE_URL_STAGE, process.env.EPMS_CLIENT_ID_STAGE, process.env.EPMS_CLIENT_SECRET_STAGE);
|
|
633
|
-
return EpmsClient.fromBrowserAuth(EPMS_API_BASE_URL_STAGE, EPMS_AUTH_BASE_URL_STAGE, EPMS_AUTH_CLIENT_ID_STAGE);
|
|
634
|
-
}
|
|
635
|
-
if (process.env.EPMS_CLIENT_ID && process.env.EPMS_CLIENT_SECRET) return EpmsClient.fromClientCredentials(EPMS_API_BASE_URL_PROD, process.env.EPMS_CLIENT_ID, process.env.EPMS_CLIENT_SECRET);
|
|
636
|
-
return EpmsClient.fromBrowserAuth(EPMS_API_BASE_URL_PROD, EPMS_AUTH_BASE_URL_PROD, EPMS_AUTH_CLIENT_ID_PROD);
|
|
637
|
-
};
|
|
638
|
-
const loadAccountUuidsFromConfig = (paths) => {
|
|
639
|
-
if (!("configYml" in paths)) {
|
|
640
|
-
halt("Fatal error, this should never happen");
|
|
641
|
-
throw new Error("Fatal error");
|
|
642
|
-
}
|
|
643
|
-
const configContent = yaml.load(readFileSync(paths.configYml, "utf8"));
|
|
644
|
-
const accountUuidsStage = resolveAccountUuidFromConfig(configContent, "epms_stage");
|
|
645
|
-
return {
|
|
646
|
-
accountUuidsProd: resolveAccountUuidFromConfig(configContent, "epms"),
|
|
647
|
-
accountUuidsStage
|
|
648
|
-
};
|
|
649
721
|
};
|
|
650
722
|
const loadEnv = (paths) => {
|
|
651
723
|
const filePath = globSync(paths.appEnv).find((file) => path.basename(file) === ".env");
|
|
@@ -1037,6 +1109,8 @@ const undeploy = async (options) => {
|
|
|
1037
1109
|
halt(error.stderr);
|
|
1038
1110
|
}
|
|
1039
1111
|
}
|
|
1112
|
+
if (!dryRunFlag && !all && !ifCore() && !options.skipEpmsSyncOnlyUseInEmergenciesOrYouWillBeFired) await archiveEpms(configName, tag, false);
|
|
1113
|
+
else if (all && !ifCore()) logTitle("⚠️ EPMS archive skipped: --all flag used. Archive entries in EPMS manually if needed.");
|
|
1040
1114
|
logTitle(`\n${description} has been undeployed.`);
|
|
1041
1115
|
logDataTable({ "Removed URL:": deployUrl });
|
|
1042
1116
|
process.on("exit", () => {
|
|
@@ -1046,6 +1120,33 @@ const undeploy = async (options) => {
|
|
|
1046
1120
|
});
|
|
1047
1121
|
});
|
|
1048
1122
|
};
|
|
1123
|
+
const archiveEpms = async (configName, tag, dryrun) => {
|
|
1124
|
+
const { ifCore } = getIfCoreFns();
|
|
1125
|
+
const { accountUuidProd, accountUuidStage } = loadAccountUuidsFromConfig(getPaths(ifCore));
|
|
1126
|
+
if (!accountUuidProd && !accountUuidStage) {
|
|
1127
|
+
logTitle("No EPMS account UUIDs found in config.yml. Skipping EPMS archive.");
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
const environments = [{
|
|
1131
|
+
accountUuid: accountUuidStage,
|
|
1132
|
+
env: "stage",
|
|
1133
|
+
label: "Stage"
|
|
1134
|
+
}, {
|
|
1135
|
+
accountUuid: accountUuidProd,
|
|
1136
|
+
env: "prod",
|
|
1137
|
+
label: "Prod"
|
|
1138
|
+
}];
|
|
1139
|
+
for (const { accountUuid, env, label } of environments) {
|
|
1140
|
+
if (!accountUuid) continue;
|
|
1141
|
+
const epmsClient = epmsAuthenticate(env);
|
|
1142
|
+
await assertUserIsSuperAdmin(epmsClient);
|
|
1143
|
+
logTitle(`\nArchiving EPMS ${label}: ${configName}/${tag}`);
|
|
1144
|
+
if (!dryrun) {
|
|
1145
|
+
await epmsClient.archiveTamaro(configName, tag);
|
|
1146
|
+
logSuccess(`✅ EPMS ${label} entry archived.`);
|
|
1147
|
+
} else logSuccess(`⏭️ EPMS ${label} entry would be archived (dry run).`);
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
1049
1150
|
const assertOptionsValid$1 = (options) => {
|
|
1050
1151
|
const { all, profile, tag } = options;
|
|
1051
1152
|
const { ifCore } = getIfCoreFns();
|
|
@@ -1128,7 +1229,7 @@ const promptBucketTamaroEmailConfig = async () => {
|
|
|
1128
1229
|
//#endregion
|
|
1129
1230
|
//#region package.json
|
|
1130
1231
|
var name = "@raisenow/tamaro-cli";
|
|
1131
|
-
var version = "1.9.0-dev.
|
|
1232
|
+
var version = "1.9.0-dev.2";
|
|
1132
1233
|
//#endregion
|
|
1133
1234
|
//#region src/cli.ts
|
|
1134
1235
|
/**
|
|
@@ -1163,7 +1264,7 @@ cli.command("list-deployed").description("List deployments of Tamaro Core or a p
|
|
|
1163
1264
|
cli.command("deploy-email-config").description("Deploy a widget's email configuration to AWS S3").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--bucket <bucket>", "AWS bucket for email configs").option("--dryrun", "Displays the operations that would be performed without actually running them", false).action(async (options) => {
|
|
1164
1265
|
await deployEmailConfig(options);
|
|
1165
1266
|
});
|
|
1166
|
-
cli.command("undeploy").description("Undeploy a Tamaro Core or customer configuration bundle from AWS S3").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--tag <tag>", "Tag which should be undeployed").option("--all", "Undeploy all tags", false).option("--dryrun", "Displays the operations that would be performed without actually running them", false).action(async (options) => {
|
|
1267
|
+
cli.command("undeploy").description("Undeploy a Tamaro Core or customer configuration bundle from AWS S3").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--tag <tag>", "Tag which should be undeployed").option("--all", "Undeploy all tags", false).option("--dryrun", "Displays the operations that would be performed without actually running them", false).option("--skip-epms-sync-only-use-in-emergencies-or-you-will-be-fired", "Skip EPMS archive sync (emergency use only)", false).action(async (options) => {
|
|
1167
1268
|
await undeploy(options);
|
|
1168
1269
|
});
|
|
1169
1270
|
cli.command("undeploy-email-config").description("Undeploy a widget's email configuration from AWS S3").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--bucket <bucket>", "AWS bucket for email configs").option("--stage", "Undeploy from stage environment (alternative to --bucket)").option("--prod", "Undeploy from prod environment (alternative to --bucket)").option("--dryrun", "Displays the operations that would be performed without actually running them", false).action(async (options) => {
|
|
@@ -22,6 +22,9 @@ const logError = (message) => {
|
|
|
22
22
|
const logSuccess = (message) => {
|
|
23
23
|
if (message) console.log(chalk.green(message));
|
|
24
24
|
};
|
|
25
|
+
const logInfo = (message) => {
|
|
26
|
+
if (message) console.log(chalk.blue(message));
|
|
27
|
+
};
|
|
25
28
|
const logCommand = (cmd) => {
|
|
26
29
|
console.log(`\n${chalk.dim(stripIndent(cmd).trim())}\n`);
|
|
27
30
|
};
|
|
@@ -66,16 +69,15 @@ const EPMS_API_BASE_URL_STAGE = "https://api.stage.mesos.raisenow.net";
|
|
|
66
69
|
const EPMS_API_BASE_URL_PROD = "https://api.raisenow.io";
|
|
67
70
|
const EPMS_AUTH_BASE_URL_STAGE = "https://login.stage.mesos.raisenow.net";
|
|
68
71
|
const EPMS_AUTH_BASE_URL_PROD = "https://login.raisenow.com";
|
|
69
|
-
const
|
|
70
|
-
const EPMS_AUTH_CLIENT_ID_PROD = "luna-local";
|
|
72
|
+
const EPMS_OAUTH_CLIENT_ID = "tamaro-cli";
|
|
71
73
|
const CORE_CONFIG_NAME = "tamaro-core";
|
|
72
|
-
const AUTH_PORT =
|
|
74
|
+
const AUTH_PORT = 4571;
|
|
73
75
|
const AWS_CLOUDFRONT_DISTRIBUTION_ID = "EHJ1OM458YQ0I";
|
|
74
76
|
const HTTPS_CRT_FILE = "localhost.crt";
|
|
75
77
|
const HTTPS_KEY_FILE = "localhost.key";
|
|
76
78
|
const CACHE_DIR = envPaths("tamaro-cli", { suffix: "" }).cache;
|
|
77
79
|
//#endregion
|
|
78
|
-
//#region node_modules/.pnpm/tsdown@0.21.
|
|
80
|
+
//#region node_modules/.pnpm/tsdown@0.21.10_synckit@0.11.11_typescript@6.0.3/node_modules/tsdown/esm-shims.js
|
|
79
81
|
const getFilename = () => fileURLToPath(import.meta.url);
|
|
80
82
|
const getDirname = () => path.dirname(getFilename());
|
|
81
83
|
const __dirname = /* @__PURE__ */ getDirname();
|
|
@@ -199,25 +201,19 @@ const getIfCoreFns = ({ allowArchived = false } = {}) => {
|
|
|
199
201
|
};
|
|
200
202
|
const resolveAccountUuidFromConfig = (rawConfig, field) => {
|
|
201
203
|
const epmsConfig = z.object({ [field]: z.record(z.string(), z.unknown()) }).safeParse(rawConfig);
|
|
202
|
-
if (!epmsConfig.success) return
|
|
204
|
+
if (!epmsConfig.success) return;
|
|
203
205
|
const configContent = epmsConfig.data[field];
|
|
204
206
|
const accountUuid = configContent.account_mapping ?? configContent.account_uuid;
|
|
205
207
|
const accountUuidValidation = z.uuid().safeParse(accountUuid);
|
|
206
|
-
|
|
207
|
-
if (!accountUuidValidation.success && !accontUuidArrayValidation.success) halt(stripIndent(`Could not extract EPMS account UUID(s) from config.yml. Please ensure that the "${field}" field is correctly formatted. Expected formats:
|
|
208
|
+
if (!accountUuidValidation.success) halt(stripIndent(`Could not extract EPMS account UUID from config.yml. Please ensure that the "${field}" field is correctly formatted. Expected formats:
|
|
208
209
|
1. ${field}:
|
|
209
210
|
account_uuid: <UUID>
|
|
210
|
-
|
|
211
|
+
|
|
211
212
|
Alternatively, if the account_uuid is conditional using "if" / "then" / "else" statements, it must be provided using the "account_mapping" format:
|
|
212
213
|
2. ${field}:
|
|
213
|
-
account_mapping:
|
|
214
|
-
- <UUID_1>
|
|
215
|
-
- <UUID_2>
|
|
216
|
-
- <UUID_N>
|
|
214
|
+
account_mapping: <UUID>
|
|
217
215
|
`));
|
|
218
|
-
|
|
219
|
-
if (accountUuidValidation.success) return [accountUuidValidation.data];
|
|
220
|
-
throw new Error("This should never happen");
|
|
216
|
+
return accountUuidValidation.data;
|
|
221
217
|
};
|
|
222
218
|
const TAMARO_VERSION_IN_URL_REGEX = /tamaro-core\/(.*)\/index.js/;
|
|
223
219
|
const extractTamaroVersionFromUrl = (url) => {
|
|
@@ -343,4 +339,4 @@ const applyEnv = (env) => {
|
|
|
343
339
|
if (filePath) config({ path: filePath });
|
|
344
340
|
};
|
|
345
341
|
//#endregion
|
|
346
|
-
export {
|
|
342
|
+
export { HTTPS_CRT_FILE as A, logSuccess as B, DEFAULT_PORT as C, EPMS_AUTH_BASE_URL_PROD as D, EPMS_API_BASE_URL_STAGE as E, createTerminalLink as F, logCommand as I, logDataTable as L, halt as M, promptConfirmation as N, EPMS_AUTH_BASE_URL_STAGE as O, runCommandSync as P, logError as R, DEFAULT_HTTP_TIMEOUT as S, EPMS_API_BASE_URL_PROD as T, logTitle as V, AWS_S3_BUCKET_TAMARO as _, getIfCoreFns as a, CACHE_DIR as b, getWidgetUuid as c, resolveBin as d, resolveCoreVersion as f, AWS_CLOUDFRONT_DISTRIBUTION_ID as g, AUTH_PORT as h, extensions as i, HTTPS_KEY_FILE as j, EPMS_OAUTH_CLIENT_ID as k, resolveAccountUuidFromConfig as l, resolveOwn as m, assertEnvValid as n, getPaths as o, resolveEslintPluginConfig as p, getEnvVars as r, getRelativePaths as s, applyEnv as t, resolveApp as u, AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD as v, DEFAULT_TAG as w, CORE_CONFIG_NAME as x, AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE as y, logInfo as z };
|
package/dist/webpack.config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as HTTPS_CRT_FILE, L as logDataTable, V as logTitle, a as getIfCoreFns, i as extensions, j as HTTPS_KEY_FILE, o as getPaths, p as resolveEslintPluginConfig, r as getEnvVars, s as getRelativePaths, u as resolveApp } from "./env-DBP70I1v.js";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
@@ -164,6 +164,22 @@ const getWebpackConfig = (env) => {
|
|
|
164
164
|
debug: ifDebug(),
|
|
165
165
|
modules: false,
|
|
166
166
|
useBuiltIns: "usage",
|
|
167
|
+
/**
|
|
168
|
+
* Exclude plugins that interfere with MobX flow inference via makeAutoObservable.
|
|
169
|
+
*
|
|
170
|
+
* MobX detects generator methods on the prototype and wraps them as `flow` automatically.
|
|
171
|
+
* Several Babel transforms break this detection:
|
|
172
|
+
*
|
|
173
|
+
* - transform-parameters: rewrites generator methods with default params (e.g. `*update(data, skip = false)`)
|
|
174
|
+
* into plain functions returning an IIFE generator. makeAutoObservable then sees a regular function
|
|
175
|
+
* and wraps it as `action` instead of `flow`.
|
|
176
|
+
*
|
|
177
|
+
* - async-to-generator / regenerator / async-generator-functions: downcompile async/generator syntax
|
|
178
|
+
* to state-machine helpers, again making the methods invisible to MobX flow inference.
|
|
179
|
+
*
|
|
180
|
+
* All of these transforms are unnecessary — our browser targets support default parameters,
|
|
181
|
+
* generators, and async functions natively.
|
|
182
|
+
*/
|
|
167
183
|
exclude: [
|
|
168
184
|
"@babel/plugin-transform-async-to-generator",
|
|
169
185
|
"@babel/plugin-transform-regenerator",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raisenow/tamaro-cli",
|
|
3
|
-
"version": "1.9.0-dev.
|
|
3
|
+
"version": "1.9.0-dev.2",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "RaiseNow",
|
|
6
6
|
"email": "development@raisenow.com"
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"bin": "dist/cli.js",
|
|
10
10
|
"engines": {
|
|
11
11
|
"node": ">=22.22.2",
|
|
12
|
-
"pnpm": ">=10.33.
|
|
12
|
+
"pnpm": ">=10.33.2",
|
|
13
13
|
"npm": "please-use-pnpm",
|
|
14
14
|
"yarn": "please-use-pnpm"
|
|
15
15
|
},
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@babel/cli": "^7.28.6",
|
|
19
19
|
"@babel/core": "^7.29.0",
|
|
20
20
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
|
21
|
-
"@babel/preset-env": "^7.29.
|
|
21
|
+
"@babel/preset-env": "^7.29.3",
|
|
22
22
|
"@babel/preset-react": "^7.28.5",
|
|
23
23
|
"@babel/preset-typescript": "^7.28.5",
|
|
24
24
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"handlebars": "^4.7.9",
|
|
55
55
|
"handlebars-helpers": "^0.10.0",
|
|
56
56
|
"html-loader": "^5.1.0",
|
|
57
|
-
"html-webpack-plugin": "^5.6.
|
|
57
|
+
"html-webpack-plugin": "^5.6.7",
|
|
58
58
|
"js-yaml": "^4.1.1",
|
|
59
59
|
"json-loader": "^0.5.7",
|
|
60
60
|
"ky": "^1.14.3",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"node-notifier": "^10.0.1",
|
|
64
64
|
"open": "^11.0.0",
|
|
65
65
|
"portfinder": "^1.0.38",
|
|
66
|
-
"postcss": "^8.5.
|
|
66
|
+
"postcss": "^8.5.13",
|
|
67
67
|
"postcss-import": "^16.1.1",
|
|
68
68
|
"postcss-loader": "^8.2.1",
|
|
69
69
|
"postcss-nested": "^7.0.2",
|
|
@@ -74,13 +74,13 @@
|
|
|
74
74
|
"sass-loader": "^16.0.7",
|
|
75
75
|
"strip-indent": "^4.1.1",
|
|
76
76
|
"style-loader": "^4.0.0",
|
|
77
|
-
"terser-webpack-plugin": "^5.
|
|
77
|
+
"terser-webpack-plugin": "^5.5.0",
|
|
78
78
|
"tsconfig-paths-webpack-plugin": "^4.2.0",
|
|
79
|
-
"tsdown": "^0.21.
|
|
80
|
-
"typescript": "^6.0.
|
|
79
|
+
"tsdown": "^0.21.10",
|
|
80
|
+
"typescript": "^6.0.3",
|
|
81
81
|
"url-loader": "^4.1.1",
|
|
82
|
-
"vitest": "^4.1.
|
|
83
|
-
"webpack": "^5.106.
|
|
82
|
+
"vitest": "^4.1.5",
|
|
83
|
+
"webpack": "^5.106.2",
|
|
84
84
|
"webpack-cli": "^7.0.2",
|
|
85
85
|
"webpack-config-utils": "^2.3.1",
|
|
86
86
|
"webpack-dev-server": "^5.2.3",
|
|
@@ -90,11 +90,11 @@
|
|
|
90
90
|
"devDependencies": {
|
|
91
91
|
"@rnw-npm/eslint-config": "^2.0.0",
|
|
92
92
|
"@rnw-npm/prettier-config": "^1.0.0",
|
|
93
|
+
"eslint": "^10.3.0",
|
|
93
94
|
"@types/js-yaml": "^4.0.9",
|
|
94
|
-
"eslint": "^10.2.0",
|
|
95
95
|
"husky": "^9.1.7",
|
|
96
96
|
"lint-staged": "^16.4.0",
|
|
97
|
-
"prettier": "^3.8.
|
|
97
|
+
"prettier": "^3.8.3"
|
|
98
98
|
},
|
|
99
99
|
"prettier": "@rnw-npm/prettier-config",
|
|
100
100
|
"lint-staged": {
|
package/eslint.config.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
baseConfig,
|
|
3
|
-
defineConfig,
|
|
4
|
-
GLOB_MARKDOWN,
|
|
5
|
-
GLOB_SRC,
|
|
6
|
-
OFF,
|
|
7
|
-
} from '@rnw-npm/eslint-config'
|
|
8
|
-
|
|
9
|
-
export default defineConfig([
|
|
10
|
-
...baseConfig,
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* "tamaro-cli" specific overrides.
|
|
14
|
-
*/
|
|
15
|
-
{
|
|
16
|
-
files: [GLOB_SRC],
|
|
17
|
-
name: 'tamaro-cli/overrides/ts',
|
|
18
|
-
rules: {
|
|
19
|
-
'jsdoc/convert-to-jsdoc-comments': OFF,
|
|
20
|
-
'no-useless-assignment': OFF,
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
files: [GLOB_MARKDOWN],
|
|
25
|
-
name: 'tamaro-cli/overrides/md',
|
|
26
|
-
rules: {
|
|
27
|
-
'markdown/no-multiple-h1': OFF,
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
])
|