@raisenow/tamaro-cli 2.0.0 → 3.0.0-dev.1
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/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/tamaro-cli.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/.idea/workspace.xml +58 -0
- package/README.md +155 -195
- package/dist/cli.js +348 -306
- package/package.json +18 -64
- package/pnpm-workspace.yaml +0 -2
- package/dist/env-DZ3cWM6p.js +0 -347
- package/dist/webpack.config.js +0 -347
package/dist/cli.js
CHANGED
|
@@ -1,24 +1,92 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
3
|
import { createCommand } from "commander";
|
|
4
|
-
import fs
|
|
4
|
+
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import
|
|
6
|
+
import dedent from "dedent";
|
|
7
7
|
import { S3 } from "@aws-sdk/client-s3";
|
|
8
8
|
import { execaCommandSync } from "execa";
|
|
9
9
|
import prompts from "prompts";
|
|
10
|
+
import chalk from "chalk";
|
|
10
11
|
import columnify from "columnify";
|
|
12
|
+
import envPaths from "env-paths";
|
|
11
13
|
import * as yaml from "js-yaml";
|
|
12
14
|
import { z } from "zod";
|
|
13
15
|
import ky from "ky";
|
|
14
16
|
import { createServer } from "node:http";
|
|
15
17
|
import open from "open";
|
|
16
18
|
import notifier from "node-notifier";
|
|
19
|
+
import console$1 from "node:console";
|
|
17
20
|
import { globSync } from "glob";
|
|
18
21
|
import Handlebars from "handlebars";
|
|
19
22
|
import helpers from "handlebars-helpers";
|
|
20
23
|
import { config } from "dotenv";
|
|
21
24
|
import { getPortPromise } from "portfinder";
|
|
25
|
+
//#region src/lib/logging.ts
|
|
26
|
+
const logTitle = (title) => {
|
|
27
|
+
console.log(chalk.bold(title));
|
|
28
|
+
};
|
|
29
|
+
const logError = (message) => {
|
|
30
|
+
if (message) console.log(chalk.red(message));
|
|
31
|
+
};
|
|
32
|
+
const logSuccess = (message) => {
|
|
33
|
+
if (message) console.log(chalk.green(message));
|
|
34
|
+
};
|
|
35
|
+
const logInfo = (message) => {
|
|
36
|
+
if (message) console.log(chalk.blue(message));
|
|
37
|
+
};
|
|
38
|
+
const logCommand = (cmd) => {
|
|
39
|
+
console.log(`\n${chalk.dim(dedent(cmd))}\n`);
|
|
40
|
+
};
|
|
41
|
+
const logDataTable = (data) => {
|
|
42
|
+
console.log(columnify(data, { showHeaders: false }));
|
|
43
|
+
console.log("");
|
|
44
|
+
};
|
|
45
|
+
const createTerminalLink = (text, url) => {
|
|
46
|
+
return `\u001B]8;;${url}\u001B\\${text}\u001B]8;;\u001B\\`;
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/lib/command.ts
|
|
50
|
+
const halt = (message) => {
|
|
51
|
+
logError(message);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
};
|
|
54
|
+
const prepareCommand = (cmd) => {
|
|
55
|
+
return cmd.replaceAll("\n", " ").replaceAll(/[ \t]{2,}/g, " ").trim();
|
|
56
|
+
};
|
|
57
|
+
const runCommandSync = (cmd, options) => {
|
|
58
|
+
return execaCommandSync(prepareCommand(cmd), options);
|
|
59
|
+
};
|
|
60
|
+
const promptConfirmation = async (message, skipConfirmation) => {
|
|
61
|
+
if (skipConfirmation) return;
|
|
62
|
+
const { confirmed } = await prompts([{
|
|
63
|
+
initial: false,
|
|
64
|
+
message,
|
|
65
|
+
name: "confirmed",
|
|
66
|
+
type: "confirm"
|
|
67
|
+
}], { onCancel: () => halt() });
|
|
68
|
+
if (!confirmed) halt("Operation cancelled.");
|
|
69
|
+
};
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/lib/constants.ts
|
|
72
|
+
const DEFAULT_TAG = "latest";
|
|
73
|
+
const DEFAULT_PORT = 1234;
|
|
74
|
+
const DEFAULT_HTTP_TIMEOUT = 1e4;
|
|
75
|
+
const AWS_S3_BUCKET_TAMARO = "tamaro.raisenow.com";
|
|
76
|
+
const AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_STAGE = "rnw-stage-email-service";
|
|
77
|
+
const AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD = "rnw-email-service";
|
|
78
|
+
const EPMS_API_BASE_URL_STAGE = "https://api.stage.mesos.raisenow.net";
|
|
79
|
+
const EPMS_API_BASE_URL_PROD = "https://api.raisenow.io";
|
|
80
|
+
const EPMS_AUTH_BASE_URL_STAGE = "https://login.stage.mesos.raisenow.net";
|
|
81
|
+
const EPMS_AUTH_BASE_URL_PROD = "https://login.raisenow.com";
|
|
82
|
+
const EPMS_OAUTH_CLIENT_ID = "tamaro-cli";
|
|
83
|
+
const AUTH_PORT = 4571;
|
|
84
|
+
const AWS_CLOUDFRONT_DISTRIBUTION_ID = "EHJ1OM458YQ0I";
|
|
85
|
+
const HTTPS_CRT_FILE = "localhost.crt";
|
|
86
|
+
const HTTPS_KEY_FILE = "localhost.key";
|
|
87
|
+
const CACHE_DIR = envPaths("tamaro-cli", { suffix: "" }).cache;
|
|
88
|
+
const TAMARO_VERSION_IN_URL_REGEX = /tamaro-core\/(.*)\/index.js/;
|
|
89
|
+
//#endregion
|
|
22
90
|
//#region src/lib/aws.ts
|
|
23
91
|
const awsAuthenticate = (options) => {
|
|
24
92
|
if (options.ci)
|
|
@@ -35,12 +103,12 @@ const awsAuthenticate = (options) => {
|
|
|
35
103
|
}
|
|
36
104
|
};
|
|
37
105
|
const assertProfilesPresent = () => {
|
|
38
|
-
if (getAvailableProfiles().length === 0) halt(
|
|
106
|
+
if (getAvailableProfiles().length === 0) halt(dedent`
|
|
39
107
|
No AWS profiles found.
|
|
40
108
|
Run "aws configure sso" to set up SSO-enabled profile.
|
|
41
109
|
Check the wiki for more information:
|
|
42
110
|
https://raisenow.atlassian.net/wiki/x/lIrWvg
|
|
43
|
-
`)
|
|
111
|
+
`);
|
|
44
112
|
};
|
|
45
113
|
const getAvailableProfiles = () => {
|
|
46
114
|
const { stdout } = execaCommandSync("aws configure list-profiles");
|
|
@@ -51,14 +119,14 @@ const getAvailableProfiles = () => {
|
|
|
51
119
|
* Fails in case of expired SSO session.
|
|
52
120
|
*/
|
|
53
121
|
const checkIdentity = (options) => {
|
|
54
|
-
execaCommandSync(`aws sts get-caller-identity ${prepareFlags
|
|
122
|
+
execaCommandSync(`aws sts get-caller-identity ${prepareFlags(options)}`);
|
|
55
123
|
};
|
|
56
124
|
/**
|
|
57
125
|
* Opens SSO authentication page in the browser.
|
|
58
126
|
* Fails if user canceled authentication process.
|
|
59
127
|
*/
|
|
60
128
|
const login = (options) => {
|
|
61
|
-
const flags = prepareFlags
|
|
129
|
+
const flags = prepareFlags(options);
|
|
62
130
|
try {
|
|
63
131
|
execaCommandSync(`aws sso login ${flags}`, { stdio: "inherit" });
|
|
64
132
|
console.log("");
|
|
@@ -66,7 +134,7 @@ const login = (options) => {
|
|
|
66
134
|
halt("Login failed.");
|
|
67
135
|
}
|
|
68
136
|
};
|
|
69
|
-
const prepareFlags
|
|
137
|
+
const prepareFlags = (options) => {
|
|
70
138
|
const flags = [];
|
|
71
139
|
const { profile } = options;
|
|
72
140
|
if (profile) flags.push(`--profile ${profile}`);
|
|
@@ -74,10 +142,10 @@ const prepareFlags$3 = (options) => {
|
|
|
74
142
|
};
|
|
75
143
|
const assertProfileValid = (profile) => {
|
|
76
144
|
const profiles = getAvailableProfiles();
|
|
77
|
-
if (!profiles.includes(profile)) halt(
|
|
145
|
+
if (!profiles.includes(profile)) halt(dedent`
|
|
78
146
|
AWS profile "${profile}" is not found.
|
|
79
147
|
Available profiles are: ${profiles.map((v) => `"${v}"`).join(", ")}.
|
|
80
|
-
`)
|
|
148
|
+
`);
|
|
81
149
|
};
|
|
82
150
|
const promptProfile = async () => {
|
|
83
151
|
const { profile } = await prompts([{
|
|
@@ -114,6 +182,38 @@ const getConfigDeployments = async (configName) => {
|
|
|
114
182
|
return deployments;
|
|
115
183
|
};
|
|
116
184
|
//#endregion
|
|
185
|
+
//#region src/lib/resolve.ts
|
|
186
|
+
const resolveAccountUuidFromConfig = (rawConfig, field) => {
|
|
187
|
+
const epmsConfig = z.object({ [field]: z.record(z.string(), z.unknown()) }).safeParse(rawConfig);
|
|
188
|
+
if (!epmsConfig.success) return;
|
|
189
|
+
const configContent = epmsConfig.data[field];
|
|
190
|
+
const accountUuid = configContent.account_mapping ?? configContent.account_uuid;
|
|
191
|
+
const accountUuidValidation = z.uuid().safeParse(accountUuid);
|
|
192
|
+
if (!accountUuidValidation.success) halt(dedent`
|
|
193
|
+
Could not extract EPMS account UUID from config.yml. Please ensure that the "${field}" field is correctly formatted. Expected formats:
|
|
194
|
+
1. ${field}:
|
|
195
|
+
account_uuid: <UUID>
|
|
196
|
+
|
|
197
|
+
Alternatively, if the account_uuid is conditional using "if" / "then" / "else" statements, it must be provided using the "account_mapping" format:
|
|
198
|
+
2. ${field}:
|
|
199
|
+
account_mapping: <UUID>
|
|
200
|
+
`);
|
|
201
|
+
return accountUuidValidation.data;
|
|
202
|
+
};
|
|
203
|
+
const resolveManagedByFromConfig = (rawConfig) => {
|
|
204
|
+
const managedByConfig = z.object({ managed_by: z.string().trim().min(1).max(255).optional() }).safeParse(rawConfig);
|
|
205
|
+
if (!managedByConfig.success) halt("Could not extract \"managed_by\" from config.yml. Please ensure it is a non-empty string.");
|
|
206
|
+
return managedByConfig.data?.managed_by ?? "RaiseNow";
|
|
207
|
+
};
|
|
208
|
+
const extractTamaroVersionFromUrl = (url) => {
|
|
209
|
+
const match = url.match(TAMARO_VERSION_IN_URL_REGEX);
|
|
210
|
+
if (match) return match[1].toString();
|
|
211
|
+
};
|
|
212
|
+
const resolveCoreVersion = () => {
|
|
213
|
+
if (process.env.CORE_VERSION) return process.env.CORE_VERSION;
|
|
214
|
+
if (process.env.CORE_URL) return extractTamaroVersionFromUrl(process.env.CORE_URL);
|
|
215
|
+
};
|
|
216
|
+
//#endregion
|
|
117
217
|
//#region src/lib/epms/auth/util.ts
|
|
118
218
|
const CachedTokenSchema = z.object({
|
|
119
219
|
expirationTime: z.number(),
|
|
@@ -134,8 +234,8 @@ const getTokenCacheKey = (baseUrl, clientId) => {
|
|
|
134
234
|
const readCachedToken = (key) => {
|
|
135
235
|
try {
|
|
136
236
|
const filePath = getCacheFilePath(key);
|
|
137
|
-
if (!existsSync(filePath)) return;
|
|
138
|
-
const data = JSON.parse(readFileSync(filePath, "utf8"));
|
|
237
|
+
if (!fs.existsSync(filePath)) return;
|
|
238
|
+
const data = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
139
239
|
const parsed = CachedTokenSchema.safeParse(data);
|
|
140
240
|
if (parsed.success) return parsed.data;
|
|
141
241
|
} catch {}
|
|
@@ -143,14 +243,14 @@ const readCachedToken = (key) => {
|
|
|
143
243
|
const writeCachedToken = (key, cached) => {
|
|
144
244
|
try {
|
|
145
245
|
const filePath = getCacheFilePath(key);
|
|
146
|
-
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
147
|
-
writeFileSync(filePath, JSON.stringify(cached), { mode: 384 });
|
|
246
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
247
|
+
fs.writeFileSync(filePath, JSON.stringify(cached), { mode: 384 });
|
|
148
248
|
} catch {}
|
|
149
249
|
};
|
|
150
250
|
const removeCachedToken = (key) => {
|
|
151
251
|
try {
|
|
152
252
|
const filePath = getCacheFilePath(key);
|
|
153
|
-
if (existsSync(filePath)) writeFileSync(filePath, "", { flag: "w" });
|
|
253
|
+
if (fs.existsSync(filePath)) fs.writeFileSync(filePath, "", { flag: "w" });
|
|
154
254
|
} catch {}
|
|
155
255
|
};
|
|
156
256
|
//#endregion
|
|
@@ -386,12 +486,12 @@ const epmsAuthenticate = (env) => {
|
|
|
386
486
|
if (clientId && clientSecret) return EpmsClient.fromClientCredentials(baseUrl, clientId, clientSecret);
|
|
387
487
|
return EpmsClient.fromBrowserAuth(baseUrl, authBaseUrl, EPMS_OAUTH_CLIENT_ID);
|
|
388
488
|
};
|
|
389
|
-
const loadTamaroMetadataFromConfig = (
|
|
390
|
-
if (!(
|
|
391
|
-
halt("
|
|
489
|
+
const loadTamaroMetadataFromConfig = (configYmlPath) => {
|
|
490
|
+
if (!fs.existsSync(configYmlPath)) {
|
|
491
|
+
halt("config.yml not found.");
|
|
392
492
|
throw new Error("Fatal error");
|
|
393
493
|
}
|
|
394
|
-
const configContent = yaml.load(readFileSync(
|
|
494
|
+
const configContent = yaml.load(fs.readFileSync(configYmlPath, "utf8"));
|
|
395
495
|
const accountUuidStage = resolveAccountUuidFromConfig(configContent, "epms_stage");
|
|
396
496
|
return {
|
|
397
497
|
accountUuidProd: resolveAccountUuidFromConfig(configContent, "epms"),
|
|
@@ -421,7 +521,7 @@ const notify = (args) => {
|
|
|
421
521
|
const { message = "", title } = args;
|
|
422
522
|
notifier.notify({
|
|
423
523
|
contentImage: "https://assets.raisenow.io/favicon.png",
|
|
424
|
-
message:
|
|
524
|
+
message: dedent(message),
|
|
425
525
|
sound: "Funk",
|
|
426
526
|
title
|
|
427
527
|
});
|
|
@@ -429,21 +529,19 @@ const notify = (args) => {
|
|
|
429
529
|
//#endregion
|
|
430
530
|
//#region src/commands/archive.ts
|
|
431
531
|
const archive = async (options) => {
|
|
432
|
-
const { ifCore } = getIfCoreFns();
|
|
433
|
-
assertIsNotCore$4(ifCore);
|
|
434
532
|
assertIsNotArchived();
|
|
435
|
-
const configName =
|
|
533
|
+
const configName = path.basename(process.cwd());
|
|
436
534
|
const cwd = process.cwd();
|
|
437
535
|
const parentDir = path.dirname(cwd);
|
|
438
536
|
const archiveDir = path.resolve(parentDir, "_archived");
|
|
439
537
|
const archiveTarget = path.resolve(archiveDir, configName);
|
|
440
538
|
assertArchiveTargetDoesNotExist(archiveTarget);
|
|
441
|
-
assertOptionsValid$
|
|
539
|
+
assertOptionsValid$7(options);
|
|
442
540
|
if (options.dryrun) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
|
|
443
|
-
if (!
|
|
541
|
+
if (!options.forceSkipEpmsSync) await archiveEpms(configName, !!options.dryrun);
|
|
444
542
|
if (!options.dryrun) {
|
|
445
|
-
if (!existsSync(archiveDir)) mkdirSync(archiveDir, { recursive: true });
|
|
446
|
-
renameSync(cwd, archiveTarget);
|
|
543
|
+
if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
|
|
544
|
+
fs.renameSync(cwd, archiveTarget);
|
|
447
545
|
} else logTitle(`Would move ${cwd} → ${archiveTarget}`);
|
|
448
546
|
logSuccess(`\n✅ "${configName}" has been archived and moved to ${archiveTarget}`);
|
|
449
547
|
process.on("exit", () => {
|
|
@@ -454,9 +552,8 @@ const archive = async (options) => {
|
|
|
454
552
|
});
|
|
455
553
|
};
|
|
456
554
|
const archiveEpms = async (configName, dryrun) => {
|
|
457
|
-
const {
|
|
458
|
-
|
|
459
|
-
if (!accountUuidProd && !accountUuidStage) halt(stripIndent(`
|
|
555
|
+
const { accountUuidProd, accountUuidStage } = loadTamaroMetadataFromConfig(path.resolve(process.cwd(), "config.yml"));
|
|
556
|
+
if (!accountUuidProd && !accountUuidStage) halt(dedent`
|
|
460
557
|
No EPMS account UUIDs found in config.yml.
|
|
461
558
|
Add them to your config.yml, for example:
|
|
462
559
|
epms:
|
|
@@ -465,7 +562,7 @@ const archiveEpms = async (configName, dryrun) => {
|
|
|
465
562
|
account_uuid: <UUID>
|
|
466
563
|
|
|
467
564
|
Or re-run with --force-skip-epms-sync to skip the EPMS sync entirely - ONLY use in emergencies / or if you know what you're doing.
|
|
468
|
-
`)
|
|
565
|
+
`);
|
|
469
566
|
const environments = [{
|
|
470
567
|
accountUuid: accountUuidStage,
|
|
471
568
|
env: "stage",
|
|
@@ -486,18 +583,15 @@ const archiveEpms = async (configName, dryrun) => {
|
|
|
486
583
|
} else logSuccess(`⏭️ EPMS ${label} entry would be archived (dry run).`);
|
|
487
584
|
}
|
|
488
585
|
};
|
|
489
|
-
const assertOptionsValid$
|
|
586
|
+
const assertOptionsValid$7 = (options) => {
|
|
490
587
|
const { profile } = options;
|
|
491
588
|
if (profile) assertProfileValid(profile);
|
|
492
589
|
};
|
|
493
|
-
const assertIsNotCore$4 = (ifCore) => {
|
|
494
|
-
if (ifCore()) halt("You cannot archive Tamaro Core.");
|
|
495
|
-
};
|
|
496
590
|
const assertIsNotArchived = () => {
|
|
497
591
|
if (path.basename(path.dirname(process.cwd())) === "_archived") halt("This configuration is already archived.");
|
|
498
592
|
};
|
|
499
593
|
const assertArchiveTargetDoesNotExist = (archiveTarget) => {
|
|
500
|
-
if (existsSync(archiveTarget)) halt(`Archive target already exists: ${archiveTarget}. Please remove it first.`);
|
|
594
|
+
if (fs.existsSync(archiveTarget)) halt(`Archive target already exists: ${archiveTarget}. Please remove it first.`);
|
|
501
595
|
};
|
|
502
596
|
//#endregion
|
|
503
597
|
//#region src/lib/validators/validateEmailTemplates.ts
|
|
@@ -617,71 +711,73 @@ const validate = async () => {
|
|
|
617
711
|
logTitle("Validated successfully");
|
|
618
712
|
};
|
|
619
713
|
//#endregion
|
|
620
|
-
//#region src/lib/
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
714
|
+
//#region src/lib/paths.ts
|
|
715
|
+
const require = createRequire(import.meta.url);
|
|
716
|
+
/**
|
|
717
|
+
* Assert that the current working directory is a valid config directory (e.g. "configs/epms-demo") and throw an error if not.
|
|
718
|
+
* This is used to ensure that commands that need to be run from a config directory are not run from an invalid location.
|
|
719
|
+
*/
|
|
720
|
+
const assertConfigDir = () => {
|
|
721
|
+
const cwd = process.cwd();
|
|
722
|
+
const parentDir = path.basename(path.dirname(cwd));
|
|
723
|
+
const pkgPath = path.resolve(cwd, "../../package.json");
|
|
724
|
+
const message = `Command must be run from a config folder (e.g. configs/epms-demo), not from "${cwd}".`;
|
|
725
|
+
if (!fs.existsSync(pkgPath) || parentDir !== "configs") {
|
|
726
|
+
halt(message);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
const { name } = require(pkgPath);
|
|
730
|
+
if (name !== "@raisenow/tamaro-configurations") {
|
|
731
|
+
halt(message);
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
632
734
|
};
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
735
|
+
/**
|
|
736
|
+
* Assert that the output directory for the given config name exists and throw an error if not.
|
|
737
|
+
* The output directory is where the build artifacts are stored after a successful build.
|
|
738
|
+
* For example, if the config name is "epms-demo", it checks that the "dist/epms-demo" directory exists.
|
|
739
|
+
*/
|
|
740
|
+
const assertOutDir = (config) => {
|
|
741
|
+
const outDir = getOutDir(config ?? "");
|
|
742
|
+
if (!config) {
|
|
743
|
+
halt(`Config name is not specified.`);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
if (!fs.existsSync(outDir)) {
|
|
747
|
+
halt(dedent`
|
|
748
|
+
Build path "${path.relative(getConfigsPackageRoot(), outDir)}" does not exist.
|
|
749
|
+
Ensure that the build was successful and the output directory was created.
|
|
750
|
+
`);
|
|
751
|
+
return;
|
|
641
752
|
}
|
|
642
|
-
assertOptionsValid$8(options);
|
|
643
|
-
await validate();
|
|
644
|
-
const flags = prepareFlags$2(options);
|
|
645
|
-
const { ifCore } = getIfCoreFns();
|
|
646
|
-
const paths = getPaths(ifCore);
|
|
647
|
-
const configName = ifCore(CORE_CONFIG_NAME, getWidgetUuid());
|
|
648
|
-
const title = ifCore("Building optimised (minified) bundle of Tamaro Core …", `Building optimised (minified) bundle of "${configName}" customer configuration …`);
|
|
649
|
-
const cmd = `
|
|
650
|
-
${resolveBin("webpack")}
|
|
651
|
-
--config ${resolveOwn("dist/webpack.config.js")}
|
|
652
|
-
--env min
|
|
653
|
-
${flags}
|
|
654
|
-
`;
|
|
655
|
-
logTitle(title);
|
|
656
|
-
logCommand(cmd);
|
|
657
|
-
runCommandSync(cmd, { stdio: "inherit" });
|
|
658
|
-
logTitle(`\nBundle for "${configName}" customer configuration is done.`);
|
|
659
|
-
console.log(`Path: ${paths.appDist}\n`);
|
|
660
|
-
process.on("exit", () => {
|
|
661
|
-
notify({
|
|
662
|
-
message: `Bundle for "${configName}" customer configuration is done.`,
|
|
663
|
-
title: "build"
|
|
664
|
-
});
|
|
665
|
-
});
|
|
666
753
|
};
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
assertEnvValid(env);
|
|
754
|
+
/**
|
|
755
|
+
* Returns the name of the current config, which is the name of the current working directory.
|
|
756
|
+
* Asserts that the current working directory is a valid config directory (e.g. "configs/epms-demo") before returning the name.
|
|
757
|
+
*/
|
|
758
|
+
const getConfigName = () => {
|
|
759
|
+
assertConfigDir();
|
|
760
|
+
return path.basename(process.cwd());
|
|
675
761
|
};
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
762
|
+
/**
|
|
763
|
+
* Returns the absolute path to the root of the configs package.
|
|
764
|
+
* Asserts that the current working directory is a valid config directory (e.g. "configs/epms-demo") before returning the path.
|
|
765
|
+
*/
|
|
766
|
+
const getConfigsPackageRoot = () => {
|
|
767
|
+
assertConfigDir();
|
|
768
|
+
return path.resolve(process.cwd(), "../..");
|
|
769
|
+
};
|
|
770
|
+
/**
|
|
771
|
+
* Returns the absolute path to the input directory for the given config name.
|
|
772
|
+
* For example, if the config name is "epms-demo", it returns the absolute path to "configs/epms-demo" directory.
|
|
773
|
+
*/
|
|
774
|
+
const getInputDir = (configName) => path.resolve(getConfigsPackageRoot(), "configs", configName);
|
|
775
|
+
/**
|
|
776
|
+
* Returns the absolute path to the output directory for the build artifacts based on the given config name.
|
|
777
|
+
* For example, if the config name is "epms-demo", it returns the absolute path to "dist/epms-demo" directory.
|
|
778
|
+
*/
|
|
779
|
+
const getOutDir = (configName) => {
|
|
780
|
+
return path.resolve(getConfigsPackageRoot(), "dist", configName);
|
|
685
781
|
};
|
|
686
782
|
//#endregion
|
|
687
783
|
//#region src/lib/validators/assertTagValid.ts
|
|
@@ -692,8 +788,7 @@ const assertTagValid = (tag) => {
|
|
|
692
788
|
//#endregion
|
|
693
789
|
//#region src/commands/update-epms.ts
|
|
694
790
|
const updateEpms = async (options) => {
|
|
695
|
-
const
|
|
696
|
-
assertIsNotCore$3(ifCore);
|
|
791
|
+
const configName = getConfigName();
|
|
697
792
|
if (!options.profile && !options.ci) {
|
|
698
793
|
assertProfilesPresent();
|
|
699
794
|
options.profile = await promptProfile();
|
|
@@ -703,11 +798,12 @@ const updateEpms = async (options) => {
|
|
|
703
798
|
process.env.AWS_PROFILE = options.profile;
|
|
704
799
|
}
|
|
705
800
|
awsAuthenticate(options);
|
|
706
|
-
const paths = getPaths(ifCore);
|
|
707
801
|
const { dryrun, tag } = options;
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
const
|
|
802
|
+
const inputDir = getInputDir(configName);
|
|
803
|
+
const dotEnvPath = path.resolve(inputDir, ".env");
|
|
804
|
+
const configYmlPath = path.resolve(inputDir, "config.yml");
|
|
805
|
+
loadEnv(dotEnvPath);
|
|
806
|
+
const { accountUuidProd, accountUuidStage, managedBy } = loadTamaroMetadataFromConfig(configYmlPath);
|
|
711
807
|
if (!accountUuidProd && !accountUuidStage) {
|
|
712
808
|
logTitle("No EPMS account UUIDs found in config.yml. Skipping EPMS update.");
|
|
713
809
|
return;
|
|
@@ -755,48 +851,43 @@ const updateEpms = async (options) => {
|
|
|
755
851
|
} else logSuccess(`⏭️ EPMS ${label} would be updated (dry run).`);
|
|
756
852
|
}
|
|
757
853
|
};
|
|
758
|
-
const loadEnv = (
|
|
759
|
-
|
|
760
|
-
if (filePath) config({
|
|
854
|
+
const loadEnv = (filePath) => {
|
|
855
|
+
if (fs.existsSync(filePath)) config({
|
|
761
856
|
path: filePath,
|
|
762
857
|
quiet: true
|
|
763
858
|
});
|
|
764
859
|
};
|
|
765
|
-
const assertIsNotCore$3 = (ifCore) => {
|
|
766
|
-
if (ifCore()) halt("You cannot update EPMS in the context of Tamaro Core.");
|
|
767
|
-
};
|
|
768
860
|
//#endregion
|
|
769
861
|
//#region src/commands/deploy.ts
|
|
770
862
|
const deploy = async (options) => {
|
|
771
|
-
const
|
|
772
|
-
const
|
|
773
|
-
|
|
863
|
+
const configName = getConfigName();
|
|
864
|
+
const outDir = getOutDir(configName);
|
|
865
|
+
assertOutDir(configName);
|
|
774
866
|
if (!options.profile && !options.ci) {
|
|
775
867
|
assertProfilesPresent();
|
|
776
868
|
options.profile = await promptProfile();
|
|
777
869
|
}
|
|
778
|
-
assertOptionsValid$
|
|
870
|
+
assertOptionsValid$6(options);
|
|
779
871
|
await validate();
|
|
780
872
|
awsAuthenticate(options);
|
|
781
|
-
const flags = prepareFlags
|
|
873
|
+
const flags = prepareFlags(options);
|
|
782
874
|
const { dryrun, tag } = options;
|
|
783
875
|
const dryRunFlag = dryrun ? `--dryrun` : "";
|
|
784
|
-
const configName = ifCore(CORE_CONFIG_NAME, getWidgetUuid());
|
|
785
876
|
const deployUrl = `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}`;
|
|
786
|
-
const entryFilename =
|
|
877
|
+
const entryFilename = "widget.js";
|
|
787
878
|
if (dryRunFlag) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
|
|
788
879
|
/**
|
|
789
880
|
* Sync all files except entrypoint (without deleting old files)
|
|
790
881
|
*/
|
|
791
882
|
const cmdSyncAll = `
|
|
792
|
-
aws s3 sync ${
|
|
883
|
+
aws s3 sync ${outDir} ${deployUrl}
|
|
793
884
|
--acl public-read
|
|
794
|
-
--exclude ${
|
|
885
|
+
--exclude ${outDir}/${entryFilename}
|
|
795
886
|
--cache-control max-age=31536000
|
|
796
887
|
${flags}
|
|
797
888
|
${dryRunFlag}
|
|
798
889
|
`;
|
|
799
|
-
logTitle("Deploying to AWS S3
|
|
890
|
+
logTitle("Deploying to AWS S3...");
|
|
800
891
|
logCommand(cmdSyncAll);
|
|
801
892
|
try {
|
|
802
893
|
runCommandSync(cmdSyncAll, { stdout: "inherit" });
|
|
@@ -807,7 +898,7 @@ const deploy = async (options) => {
|
|
|
807
898
|
* Copy entrypoint
|
|
808
899
|
*/
|
|
809
900
|
const cmdCpEntry = `
|
|
810
|
-
aws s3 cp ${
|
|
901
|
+
aws s3 cp ${outDir}/${entryFilename} ${deployUrl}/${entryFilename}
|
|
811
902
|
--acl public-read
|
|
812
903
|
--cache-control max-age=64800
|
|
813
904
|
${flags}
|
|
@@ -829,7 +920,7 @@ const deploy = async (options) => {
|
|
|
829
920
|
--paths /${configName}/${tag}/*
|
|
830
921
|
${flags}
|
|
831
922
|
`;
|
|
832
|
-
logTitle("\nInvalidating edge cache
|
|
923
|
+
logTitle("\nInvalidating edge cache...");
|
|
833
924
|
logCommand(cmdInvalidateCache);
|
|
834
925
|
try {
|
|
835
926
|
runCommandSync(cmdInvalidateCache);
|
|
@@ -838,7 +929,7 @@ const deploy = async (options) => {
|
|
|
838
929
|
}
|
|
839
930
|
}
|
|
840
931
|
if (options.forceSkipEpmsSync) logTitle("⚠️ EPMS sync skipped. You better know what you are doing.");
|
|
841
|
-
else
|
|
932
|
+
else await updateEpms(options);
|
|
842
933
|
const demoPage = `https://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/index.html`;
|
|
843
934
|
const entryPoint = `https://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/${entryFilename}`;
|
|
844
935
|
logTitle(`\nBundle for "${configName}" is deployed with tag "${tag}".`);
|
|
@@ -848,7 +939,7 @@ const deploy = async (options) => {
|
|
|
848
939
|
});
|
|
849
940
|
process.on("exit", () => {
|
|
850
941
|
notify({
|
|
851
|
-
message: `
|
|
942
|
+
message: dedent`
|
|
852
943
|
Bundle for "${configName}" is deployed with tag "${tag}".
|
|
853
944
|
Demo page: ${demoPage}
|
|
854
945
|
Entry point: ${entryPoint}
|
|
@@ -857,35 +948,99 @@ const deploy = async (options) => {
|
|
|
857
948
|
});
|
|
858
949
|
});
|
|
859
950
|
};
|
|
860
|
-
const assertOptionsValid$
|
|
951
|
+
const assertOptionsValid$6 = (options) => {
|
|
861
952
|
const { profile, tag } = options;
|
|
862
953
|
if (profile) assertProfileValid(profile);
|
|
863
954
|
assertTagValid(tag);
|
|
864
955
|
};
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
956
|
+
//#endregion
|
|
957
|
+
//#region src/lib/https.ts
|
|
958
|
+
const assertCrtExists = () => {
|
|
959
|
+
const configsPackageRoot = getConfigsPackageRoot();
|
|
960
|
+
const certFile = path.resolve(configsPackageRoot, HTTPS_CRT_FILE);
|
|
961
|
+
const keyFile = path.resolve(configsPackageRoot, HTTPS_KEY_FILE);
|
|
962
|
+
if (fs.existsSync(certFile) && fs.existsSync(keyFile)) return;
|
|
963
|
+
halt(dedent`
|
|
964
|
+
Files "${HTTPS_CRT_FILE}" and/or "${HTTPS_KEY_FILE}" are not found.
|
|
965
|
+
You need to generate local certificates first.
|
|
966
|
+
1. Install mkcert:
|
|
967
|
+
brew install mkcert
|
|
968
|
+
2. Generate the certificate and key in the repository root:
|
|
969
|
+
mkcert -install -cert-file ${HTTPS_CRT_FILE} -key-file ${HTTPS_KEY_FILE} localhost 127.0.0.1 0.0.0.0 ::1
|
|
970
|
+
`);
|
|
971
|
+
};
|
|
972
|
+
//#endregion
|
|
973
|
+
//#region src/commands/preview.ts
|
|
974
|
+
const preview = async () => {
|
|
975
|
+
const configName = getConfigName();
|
|
976
|
+
const configsPackageRoot = getConfigsPackageRoot();
|
|
977
|
+
assertOutDir(configName);
|
|
978
|
+
assertCrtExists();
|
|
979
|
+
const cmd = `pnpm sirv ${path.relative(configsPackageRoot, getOutDir(configName))} --cors --http2 --cert ${HTTPS_CRT_FILE} --key ${HTTPS_KEY_FILE} --port ${await getPortPromise({ port: Number(DEFAULT_PORT) })}`;
|
|
980
|
+
logTitle(`Running web-server for pre-built bundle...`);
|
|
981
|
+
logCommand(cmd);
|
|
982
|
+
runCommandSync(cmd, {
|
|
983
|
+
cwd: configsPackageRoot,
|
|
984
|
+
stdio: "inherit"
|
|
985
|
+
});
|
|
986
|
+
};
|
|
987
|
+
//#endregion
|
|
988
|
+
//#region src/commands/build.ts
|
|
989
|
+
const build = async (options) => {
|
|
990
|
+
const configName = getConfigName();
|
|
991
|
+
const configsPackageRoot = getConfigsPackageRoot();
|
|
992
|
+
const outDir = getOutDir(configName);
|
|
993
|
+
if (options.deploy && !options.profile && !options.ci) {
|
|
994
|
+
assertProfilesPresent();
|
|
995
|
+
options.profile = await promptProfile();
|
|
996
|
+
}
|
|
997
|
+
assertOptionsValid$5(options);
|
|
998
|
+
await validate();
|
|
999
|
+
const cmd = `pnpm vite build`;
|
|
1000
|
+
const env = {
|
|
1001
|
+
...process.env,
|
|
1002
|
+
BABEL: options.babel ? "true" : "false",
|
|
1003
|
+
CONFIG_NAME: configName,
|
|
1004
|
+
MINIFY: options.minify ? "true" : "false"
|
|
1005
|
+
};
|
|
1006
|
+
logTitle(`Building optimised (minified) bundle of "${configName}" customer configuration...`);
|
|
1007
|
+
logCommand(cmd);
|
|
1008
|
+
runCommandSync(cmd, {
|
|
1009
|
+
cwd: configsPackageRoot,
|
|
1010
|
+
env,
|
|
1011
|
+
stdio: "inherit"
|
|
1012
|
+
});
|
|
1013
|
+
logTitle(`\nBundle for "${configName}" customer configuration is done.`);
|
|
1014
|
+
console$1.log(`Path: ${outDir}\n`);
|
|
1015
|
+
process.on("exit", () => {
|
|
1016
|
+
notify({
|
|
1017
|
+
message: `Bundle for "${configName}" customer configuration is done.`,
|
|
1018
|
+
title: "build"
|
|
1019
|
+
});
|
|
1020
|
+
});
|
|
1021
|
+
if (options.preview) await preview();
|
|
1022
|
+
if (options.deploy) await deploy(options);
|
|
1023
|
+
};
|
|
1024
|
+
const assertOptionsValid$5 = (options) => {
|
|
1025
|
+
const { profile } = options;
|
|
1026
|
+
if (profile) assertProfileValid(profile);
|
|
870
1027
|
};
|
|
871
1028
|
//#endregion
|
|
872
1029
|
//#region src/commands/deploy-email-config.ts
|
|
873
1030
|
const deployEmailConfig = async (options) => {
|
|
874
|
-
const
|
|
875
|
-
const emailConfigPath = `${
|
|
876
|
-
assertIsNotCore$2();
|
|
1031
|
+
const configName = getConfigName();
|
|
1032
|
+
const emailConfigPath = `${getInputDir(configName)}/email-config`;
|
|
877
1033
|
assertEmailConfigPathExists(emailConfigPath);
|
|
878
1034
|
if (!options.profile && !options.ci) {
|
|
879
1035
|
assertProfilesPresent();
|
|
880
1036
|
options.profile = await promptProfile();
|
|
881
1037
|
}
|
|
882
1038
|
if (!options.bucket) options.bucket = options.ci ? AWS_S3_BUCKET_TAMARO_EMAIL_CONFIG_PROD : await promptBucketTamaroEmailConfig$1();
|
|
883
|
-
assertOptionsValid$
|
|
1039
|
+
assertOptionsValid$4(options);
|
|
884
1040
|
await validate();
|
|
885
1041
|
awsAuthenticate(options);
|
|
886
|
-
const flags = prepareFlags
|
|
1042
|
+
const flags = prepareFlags(options);
|
|
887
1043
|
const dryRunFlag = options.dryrun ? `--dryrun` : "";
|
|
888
|
-
const configName = getWidgetUuid();
|
|
889
1044
|
const deployUrl = `s3://${options.bucket}/${configName}`;
|
|
890
1045
|
if (dryRunFlag) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
|
|
891
1046
|
const cmd = `
|
|
@@ -895,7 +1050,7 @@ const deployEmailConfig = async (options) => {
|
|
|
895
1050
|
${flags}
|
|
896
1051
|
${dryRunFlag}
|
|
897
1052
|
`;
|
|
898
|
-
logTitle(`Deploying email configuration for "${configName}" customer configuration to AWS S3
|
|
1053
|
+
logTitle(`Deploying email configuration for "${configName}" customer configuration to AWS S3...`);
|
|
899
1054
|
logCommand(cmd);
|
|
900
1055
|
try {
|
|
901
1056
|
runCommandSync(cmd, { stdout: "inherit" });
|
|
@@ -911,17 +1066,13 @@ const deployEmailConfig = async (options) => {
|
|
|
911
1066
|
});
|
|
912
1067
|
});
|
|
913
1068
|
};
|
|
914
|
-
const assertOptionsValid$
|
|
1069
|
+
const assertOptionsValid$4 = (options) => {
|
|
915
1070
|
const { bucket, profile } = options;
|
|
916
1071
|
if (profile) assertProfileValid(profile);
|
|
917
1072
|
if (bucket) assertBucketValid$1(bucket);
|
|
918
1073
|
};
|
|
919
1074
|
const assertEmailConfigPathExists = (distPath) => {
|
|
920
|
-
if (!existsSync(distPath)) halt("Email config folder does not exists. Nothing to deploy.");
|
|
921
|
-
};
|
|
922
|
-
const assertIsNotCore$2 = () => {
|
|
923
|
-
const { ifCore } = getIfCoreFns();
|
|
924
|
-
if (ifCore()) halt("You cannot deploy widget email configuration for Tamaro Core.");
|
|
1075
|
+
if (!fs.existsSync(distPath)) halt("Email config folder does not exists. Nothing to deploy.");
|
|
925
1076
|
};
|
|
926
1077
|
const assertBucketValid$1 = (bucket) => {
|
|
927
1078
|
if (!["rnw-stage-email-service", "rnw-email-service"].includes(bucket)) halt("Invalid bucket name.");
|
|
@@ -944,53 +1095,34 @@ const promptBucketTamaroEmailConfig$1 = async () => {
|
|
|
944
1095
|
//#endregion
|
|
945
1096
|
//#region src/commands/dev.ts
|
|
946
1097
|
const dev = async (options) => {
|
|
947
|
-
const
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
const
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
${flags}
|
|
957
|
-
`;
|
|
958
|
-
logTitle(title);
|
|
1098
|
+
const configName = getConfigName();
|
|
1099
|
+
const configsPackageRoot = getConfigsPackageRoot();
|
|
1100
|
+
const cmd = `pnpm vite dev --port ${await getPortPromise({ port: Number(DEFAULT_PORT) })}`;
|
|
1101
|
+
const env = {
|
|
1102
|
+
...process.env,
|
|
1103
|
+
CONFIG_NAME: configName,
|
|
1104
|
+
LOCAL_CORE: options.localCore ? "true" : "false"
|
|
1105
|
+
};
|
|
1106
|
+
logTitle(`\nStarting Vite dev server for "${configName}" configuration...`);
|
|
959
1107
|
logCommand(cmd);
|
|
960
|
-
runCommandSync(cmd, {
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
if (ifCore() && localCore) halt("Flag \"--local-core\" is redundant if running in Tamaro Core context.");
|
|
966
|
-
if (Number.isNaN(Number(port))) halt("Flag \"--port\" should be a number.");
|
|
967
|
-
if (https) assertCrtExists();
|
|
968
|
-
assertEnvValid(env);
|
|
969
|
-
};
|
|
970
|
-
const prepareFlags$1 = async (options) => {
|
|
971
|
-
const flags = [];
|
|
972
|
-
const { debug, https, localCore, nolint, port: defaultPort } = options;
|
|
973
|
-
const port = await getPortPromise({ port: Number(defaultPort) });
|
|
974
|
-
if (localCore) flags.push("--env localCore");
|
|
975
|
-
flags.push(`--port ${port}`);
|
|
976
|
-
if (https) flags.push("--env https");
|
|
977
|
-
if (nolint) flags.push("--env nolint");
|
|
978
|
-
if (debug) flags.push("--env debug");
|
|
979
|
-
return flags.join(" ");
|
|
1108
|
+
runCommandSync(cmd, {
|
|
1109
|
+
cwd: configsPackageRoot,
|
|
1110
|
+
env,
|
|
1111
|
+
stdio: "inherit"
|
|
1112
|
+
});
|
|
980
1113
|
};
|
|
981
1114
|
//#endregion
|
|
982
1115
|
//#region src/commands/list-deployed.ts
|
|
983
1116
|
const listDeployed = async (options) => {
|
|
1117
|
+
const configName = getConfigName();
|
|
984
1118
|
if (!options.profile && !options.ci) {
|
|
985
1119
|
assertProfilesPresent();
|
|
986
1120
|
options.profile = await promptProfile();
|
|
987
1121
|
}
|
|
988
|
-
assertOptionsValid$
|
|
1122
|
+
assertOptionsValid$3(options);
|
|
989
1123
|
awsAuthenticate(options);
|
|
990
|
-
const {
|
|
991
|
-
|
|
992
|
-
const configName = config ?? ifCore("tamaro-core", getWidgetUuid());
|
|
993
|
-
logTitle(!config && ifCore() ? `Listing deployments of Tamaro Core …` : `Listing deployments of "${configName}" customer configuration …`);
|
|
1124
|
+
const { profile } = options;
|
|
1125
|
+
logTitle(`Listing deployments of "${configName}" customer configuration...`);
|
|
994
1126
|
if (profile) process.env.AWS_PROFILE = profile;
|
|
995
1127
|
const deployments = await getConfigDeployments(configName);
|
|
996
1128
|
if (deployments.length === 0) {
|
|
@@ -1014,68 +1146,25 @@ const listDeployed = async (options) => {
|
|
|
1014
1146
|
});
|
|
1015
1147
|
console.log(`\n${table}\n`);
|
|
1016
1148
|
};
|
|
1017
|
-
const assertOptionsValid$4 = (options) => {
|
|
1018
|
-
const { config, profile } = options;
|
|
1019
|
-
assertConfigValid(config);
|
|
1020
|
-
if (profile) assertProfileValid(profile);
|
|
1021
|
-
};
|
|
1022
|
-
const assertConfigValid = (config) => {
|
|
1023
|
-
if (!config) return;
|
|
1024
|
-
const regex = /^[\w-]+$/;
|
|
1025
|
-
if (!regex.test(config)) halt(`Flag "--config" has forbidden format. Allowed format: ${regex.toString()}.`);
|
|
1026
|
-
};
|
|
1027
|
-
//#endregion
|
|
1028
|
-
//#region src/commands/serve.ts
|
|
1029
|
-
const serve = async (options) => {
|
|
1030
|
-
assertOptionsValid$3(options);
|
|
1031
|
-
const flags = await prepareFlags(options);
|
|
1032
|
-
const { ifCore } = getIfCoreFns();
|
|
1033
|
-
const cmd = `
|
|
1034
|
-
npx -y serve ${getPaths(ifCore).appDist}
|
|
1035
|
-
--cors
|
|
1036
|
-
${flags}
|
|
1037
|
-
`;
|
|
1038
|
-
logTitle(`Running web-server for pre-built bundle …`);
|
|
1039
|
-
logCommand(cmd);
|
|
1040
|
-
runCommandSync(cmd, { stdio: "inherit" });
|
|
1041
|
-
};
|
|
1042
1149
|
const assertOptionsValid$3 = (options) => {
|
|
1043
|
-
const {
|
|
1044
|
-
if (
|
|
1045
|
-
if (https) assertCrtExists();
|
|
1046
|
-
};
|
|
1047
|
-
const prepareFlags = async (options) => {
|
|
1048
|
-
const flags = [];
|
|
1049
|
-
const { https, port: defaultPort } = options;
|
|
1050
|
-
const port = await getPortPromise({ port: Number(defaultPort) });
|
|
1051
|
-
flags.push(`-p ${port}`);
|
|
1052
|
-
if (https) {
|
|
1053
|
-
const { ifCore } = getIfCoreFns();
|
|
1054
|
-
const paths = getPaths(ifCore);
|
|
1055
|
-
const certFile = path.resolve(paths.root, HTTPS_CRT_FILE);
|
|
1056
|
-
const keyFile = path.resolve(paths.root, HTTPS_KEY_FILE);
|
|
1057
|
-
flags.push(`--ssl-cert ${certFile}`);
|
|
1058
|
-
flags.push(`--ssl-key ${keyFile}`);
|
|
1059
|
-
}
|
|
1060
|
-
return flags.join(" ");
|
|
1150
|
+
const { profile } = options;
|
|
1151
|
+
if (profile) assertProfileValid(profile);
|
|
1061
1152
|
};
|
|
1062
1153
|
//#endregion
|
|
1063
1154
|
//#region src/commands/unarchive.ts
|
|
1064
1155
|
const unarchive = async (options) => {
|
|
1065
|
-
const { ifCore } = getIfCoreFns({ allowArchived: true });
|
|
1066
|
-
assertIsNotCore$1(ifCore);
|
|
1067
1156
|
assertIsArchived();
|
|
1068
|
-
const configName =
|
|
1157
|
+
const configName = path.basename(process.cwd());
|
|
1069
1158
|
const cwd = process.cwd();
|
|
1070
1159
|
const archivedDir = path.dirname(cwd);
|
|
1071
1160
|
const configsDir = path.dirname(archivedDir);
|
|
1072
1161
|
const restoreTarget = path.resolve(configsDir, configName);
|
|
1073
1162
|
assertRestoreTargetDoesNotExist(restoreTarget);
|
|
1074
1163
|
assertOptionsValid$2(options);
|
|
1075
|
-
if (!
|
|
1164
|
+
if (!options.forceSkipEpmsSync) await unarchiveEpms(configName, !!options.dryrun);
|
|
1076
1165
|
if (options.dryrun) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
|
|
1077
1166
|
if (!options.dryrun) {
|
|
1078
|
-
renameSync(cwd, restoreTarget);
|
|
1167
|
+
fs.renameSync(cwd, restoreTarget);
|
|
1079
1168
|
process.chdir(restoreTarget);
|
|
1080
1169
|
} else logTitle(`Would move ${cwd} → ${restoreTarget}`);
|
|
1081
1170
|
logSuccess(`\n✅ "${configName}" has been unarchived and moved to ${restoreTarget}`);
|
|
@@ -1087,9 +1176,8 @@ const unarchive = async (options) => {
|
|
|
1087
1176
|
});
|
|
1088
1177
|
};
|
|
1089
1178
|
const unarchiveEpms = async (configName, dryrun) => {
|
|
1090
|
-
const {
|
|
1091
|
-
|
|
1092
|
-
if (!accountUuidProd && !accountUuidStage) halt(stripIndent(`
|
|
1179
|
+
const { accountUuidProd, accountUuidStage } = loadTamaroMetadataFromConfig(path.resolve(process.cwd(), "config.yml"));
|
|
1180
|
+
if (!accountUuidProd && !accountUuidStage) halt(dedent`
|
|
1093
1181
|
No EPMS account UUIDs found in config.yml.
|
|
1094
1182
|
Add them to your config.yml, for example:
|
|
1095
1183
|
epms:
|
|
@@ -1098,7 +1186,7 @@ const unarchiveEpms = async (configName, dryrun) => {
|
|
|
1098
1186
|
account_uuid: <UUID>
|
|
1099
1187
|
|
|
1100
1188
|
Or re-run with --force-skip-epms-sync to skip the EPMS sync entirely - ONLY use in emergencies / or if you know what you're doing.
|
|
1101
|
-
`)
|
|
1189
|
+
`);
|
|
1102
1190
|
const environments = [{
|
|
1103
1191
|
accountUuid: accountUuidStage,
|
|
1104
1192
|
env: "stage",
|
|
@@ -1126,24 +1214,20 @@ const assertOptionsValid$2 = (options) => {
|
|
|
1126
1214
|
const assertIsArchived = () => {
|
|
1127
1215
|
if (path.basename(path.dirname(process.cwd())) !== "_archived") halt("This configuration is not archived. Only archived configurations can be unarchived.");
|
|
1128
1216
|
};
|
|
1129
|
-
const assertIsNotCore$1 = (ifCore) => {
|
|
1130
|
-
if (ifCore()) halt("You cannot unarchive Tamaro Core.");
|
|
1131
|
-
};
|
|
1132
1217
|
const assertRestoreTargetDoesNotExist = (restoreTarget) => {
|
|
1133
|
-
if (existsSync(restoreTarget)) halt(`Restore target already exists: ${restoreTarget}. Please remove it first.`);
|
|
1218
|
+
if (fs.existsSync(restoreTarget)) halt(`Restore target already exists: ${restoreTarget}. Please remove it first.`);
|
|
1134
1219
|
};
|
|
1135
1220
|
//#endregion
|
|
1136
1221
|
//#region src/commands/undeploy.ts
|
|
1137
1222
|
const undeploy = async (options) => {
|
|
1138
|
-
const
|
|
1139
|
-
const configName = ifCore(CORE_CONFIG_NAME, getWidgetUuid());
|
|
1223
|
+
const configName = getConfigName();
|
|
1140
1224
|
if (!options.profile && !options.ci) {
|
|
1141
1225
|
assertProfilesPresent();
|
|
1142
1226
|
options.profile = await promptProfile();
|
|
1143
1227
|
}
|
|
1144
1228
|
assertOptionsValid$1(options);
|
|
1145
1229
|
awsAuthenticate(options);
|
|
1146
|
-
const flags = prepareFlags
|
|
1230
|
+
const flags = prepareFlags(options);
|
|
1147
1231
|
const { all, dryrun, tag = DEFAULT_TAG } = options;
|
|
1148
1232
|
const dryRunFlag = dryrun ? `--dryrun` : "";
|
|
1149
1233
|
const deployUrl = all ? `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/` : `s3://${AWS_S3_BUCKET_TAMARO}/${configName}/${tag}/`;
|
|
@@ -1156,7 +1240,7 @@ const undeploy = async (options) => {
|
|
|
1156
1240
|
${flags}
|
|
1157
1241
|
${dryRunFlag}
|
|
1158
1242
|
`;
|
|
1159
|
-
logTitle(`Undeploying ${description} from AWS S3
|
|
1243
|
+
logTitle(`Undeploying ${description} from AWS S3...`);
|
|
1160
1244
|
logCommand(cmd);
|
|
1161
1245
|
try {
|
|
1162
1246
|
runCommandSync(cmd, { stdout: "inherit" });
|
|
@@ -1170,7 +1254,7 @@ const undeploy = async (options) => {
|
|
|
1170
1254
|
--paths ${all ? `/${configName}/*` : `/${configName}/${tag}/*`}
|
|
1171
1255
|
${flags}
|
|
1172
1256
|
`;
|
|
1173
|
-
logTitle("\nInvalidating edge cache
|
|
1257
|
+
logTitle("\nInvalidating edge cache...");
|
|
1174
1258
|
logCommand(cmdInvalidateCache);
|
|
1175
1259
|
try {
|
|
1176
1260
|
runCommandSync(cmdInvalidateCache);
|
|
@@ -1189,9 +1273,6 @@ const undeploy = async (options) => {
|
|
|
1189
1273
|
};
|
|
1190
1274
|
const assertOptionsValid$1 = (options) => {
|
|
1191
1275
|
const { all, profile, tag } = options;
|
|
1192
|
-
const { ifCore } = getIfCoreFns();
|
|
1193
|
-
if (ifCore() && all) halt("Flag \"--all\" must not be used in Tamaro Core context.");
|
|
1194
|
-
if (ifCore() && tag === "latest") halt("You cannot undeploy the default tag for Tamaro Core.");
|
|
1195
1276
|
if (profile) assertProfileValid(profile);
|
|
1196
1277
|
if (all && tag) halt("Flags \"--tag\" and \"--all\" must not be used together.");
|
|
1197
1278
|
if (!all && tag) assertTagValid(tag);
|
|
@@ -1199,8 +1280,7 @@ const assertOptionsValid$1 = (options) => {
|
|
|
1199
1280
|
//#endregion
|
|
1200
1281
|
//#region src/commands/undeploy-email-config.ts
|
|
1201
1282
|
const undeployEmailConfig = async (options) => {
|
|
1202
|
-
const
|
|
1203
|
-
assertIsNotCore(ifCore);
|
|
1283
|
+
const configName = getConfigName();
|
|
1204
1284
|
if (!options.profile && !options.ci) {
|
|
1205
1285
|
assertProfilesPresent();
|
|
1206
1286
|
options.profile = await promptProfile();
|
|
@@ -1212,9 +1292,8 @@ const undeployEmailConfig = async (options) => {
|
|
|
1212
1292
|
else options.bucket = await promptBucketTamaroEmailConfig();
|
|
1213
1293
|
assertOptionsValid(options);
|
|
1214
1294
|
awsAuthenticate(options);
|
|
1215
|
-
const flags = prepareFlags
|
|
1295
|
+
const flags = prepareFlags(options);
|
|
1216
1296
|
const dryRunFlag = options.dryrun ? `--dryrun` : "";
|
|
1217
|
-
const configName = getWidgetUuid();
|
|
1218
1297
|
const deployUrl = `s3://${options.bucket}/${configName}/`;
|
|
1219
1298
|
await promptConfirmation(`Are you sure you want to undeploy the email configuration for "${configName}" from "${options.bucket}"?`, options.ci);
|
|
1220
1299
|
if (dryRunFlag) logTitle("🧪 DRY RUN MODE - No actual changes will be made 🧪");
|
|
@@ -1224,7 +1303,7 @@ const undeployEmailConfig = async (options) => {
|
|
|
1224
1303
|
${flags}
|
|
1225
1304
|
${dryRunFlag}
|
|
1226
1305
|
`;
|
|
1227
|
-
logTitle(`Undeploying email configuration for "${configName}" from AWS S3
|
|
1306
|
+
logTitle(`Undeploying email configuration for "${configName}" from AWS S3...`);
|
|
1228
1307
|
logCommand(cmd);
|
|
1229
1308
|
try {
|
|
1230
1309
|
runCommandSync(cmd, { stdout: "inherit" });
|
|
@@ -1245,9 +1324,6 @@ const assertOptionsValid = (options) => {
|
|
|
1245
1324
|
if (profile) assertProfileValid(profile);
|
|
1246
1325
|
if (bucket) assertBucketValid(bucket);
|
|
1247
1326
|
};
|
|
1248
|
-
const assertIsNotCore = (ifCore) => {
|
|
1249
|
-
if (ifCore()) halt("You cannot undeploy widget email configuration for Tamaro Core.");
|
|
1250
|
-
};
|
|
1251
1327
|
const assertBucketValid = (bucket) => {
|
|
1252
1328
|
if (!["rnw-stage-email-service", "rnw-email-service"].includes(bucket)) halt("Invalid bucket name.");
|
|
1253
1329
|
};
|
|
@@ -1269,7 +1345,7 @@ const promptBucketTamaroEmailConfig = async () => {
|
|
|
1269
1345
|
//#endregion
|
|
1270
1346
|
//#region package.json
|
|
1271
1347
|
var name = "@raisenow/tamaro-cli";
|
|
1272
|
-
var version = "
|
|
1348
|
+
var version = "3.0.0-dev.1";
|
|
1273
1349
|
//#endregion
|
|
1274
1350
|
//#region src/cli.ts
|
|
1275
1351
|
/**
|
|
@@ -1280,59 +1356,25 @@ var version = "2.0.0";
|
|
|
1280
1356
|
process.on("unhandledRejection", (error) => {
|
|
1281
1357
|
throw error;
|
|
1282
1358
|
});
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
cli.command("
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
cli.command("
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
cli.command("
|
|
1296
|
-
|
|
1297
|
-
});
|
|
1298
|
-
cli.
|
|
1299
|
-
|
|
1300
|
-
});
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
});
|
|
1304
|
-
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) => {
|
|
1305
|
-
await deployEmailConfig(options);
|
|
1306
|
-
});
|
|
1307
|
-
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) => {
|
|
1308
|
-
await undeploy(options);
|
|
1309
|
-
});
|
|
1310
|
-
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) => {
|
|
1311
|
-
await undeployEmailConfig(options);
|
|
1312
|
-
});
|
|
1313
|
-
cli.command("archive").description("Archive a customer configuration by moving it to the _archived folder").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--dryrun", "Displays the operations that would be performed without actually running them", false).option("--force-skip-epms-sync", "Skip EPMS archive sync (emergency use only)", false).action(async (options) => {
|
|
1314
|
-
await archive(options);
|
|
1315
|
-
});
|
|
1316
|
-
cli.command("unarchive").description("Unarchive a customer configuration by restoring it from the _archived folder").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--dryrun", "Displays the operations that would be performed without actually running them", false).option("--force-skip-epms-sync", "Skip EPMS unarchive sync (emergency use only)", false).action(async (options) => {
|
|
1317
|
-
await unarchive(options);
|
|
1318
|
-
});
|
|
1319
|
-
cli.command("update-epms").description("Update EPMS with the currently deployed widget tags.").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--dryrun", "Displays the operations that would be performed without actually running them", false).option("--tag <tag>", "Tag which should be set for the deployment", DEFAULT_TAG).action(async (options) => {
|
|
1320
|
-
await updateEpms(options);
|
|
1321
|
-
});
|
|
1322
|
-
cli.command("validate").description("Validate the current Tamaro configuration to avoid common errors").action(async () => {
|
|
1323
|
-
await validate();
|
|
1324
|
-
});
|
|
1325
|
-
(async () => {
|
|
1326
|
-
try {
|
|
1327
|
-
logTitle(`${name} ${version}\n`);
|
|
1328
|
-
await cli.parseAsync(process.argv);
|
|
1329
|
-
} catch (error) {
|
|
1330
|
-
if (error.signal === "SIGINT" && error.signalDescription) logError(`\n${error.signalDescription}`);
|
|
1331
|
-
else {
|
|
1332
|
-
console.log("");
|
|
1333
|
-
logError(error.stack);
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
})();
|
|
1359
|
+
const cli = createCommand().name(name).description("CLI for Tamaro Customer Configurations development").version(version, "-v, --version");
|
|
1360
|
+
cli.command("dev").description("Run the local dev-server").option("--local-core", "Load Tamaro Core from \"https://localhost:1234/src/index.ts\" rather than from CDN", false).action(dev);
|
|
1361
|
+
cli.command("build").description("Build an optimised (minified) bundle").option("--preview", "Run a local web server for pre-built bundle", false).option("--babel", "Use Babel for transpilation", true).option("--no-babel", "Do not use Babel for transpilation").option("--minify", "Minify the bundle", true).option("--no-minify", "Do not minify the bundle").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--deploy", "Deploy customer configuration bundle to AWS S3", false).option("--force-skip-epms-sync", "Skip the EPMS sync entirely. Only use this in emergencies.", false).option("--tag <tag>", "Tag which should be used for the deployment of the bundle", DEFAULT_TAG).action(build);
|
|
1362
|
+
cli.command("preview").description("Run a local web server for pre-built bundle").action(preview);
|
|
1363
|
+
cli.command("deploy").description("Deploy a pre-built Tamaro Core or customer configuration bundle to AWS S3").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--tag <tag>", "Tag which should be used for the deployment of the bundle", DEFAULT_TAG).option("--dryrun", "Displays the operations that would be performed without actually running them", false).option("--force-skip-epms-sync", "Skip the EPMS sync entirely. Only use this in emergencies.", false).action(deploy);
|
|
1364
|
+
cli.command("list-deployed").description("List deployments of customer configuration").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).action(listDeployed);
|
|
1365
|
+
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(deployEmailConfig);
|
|
1366
|
+
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(undeploy);
|
|
1367
|
+
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(undeployEmailConfig);
|
|
1368
|
+
cli.command("archive").description("Archive a customer configuration by moving it to the _archived folder").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--dryrun", "Displays the operations that would be performed without actually running them", false).option("--force-skip-epms-sync", "Skip EPMS archive sync (emergency use only)", false).action(archive);
|
|
1369
|
+
cli.command("unarchive").description("Unarchive a customer configuration by restoring it from the _archived folder").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--dryrun", "Displays the operations that would be performed without actually running them", false).option("--force-skip-epms-sync", "Skip EPMS unarchive sync (emergency use only)", false).action(unarchive);
|
|
1370
|
+
cli.command("update-epms").description("Update EPMS with the currently deployed widget tags.").option("--profile <profile>", "AWS profile").option("--ci", "CI environment", false).option("--dryrun", "Displays the operations that would be performed without actually running them", false).option("--tag <tag>", "Tag which should be set for the deployment", DEFAULT_TAG).action(updateEpms);
|
|
1371
|
+
cli.command("validate").description("Validate the current Tamaro configuration to avoid common errors").action(validate);
|
|
1372
|
+
try {
|
|
1373
|
+
logTitle(`${name} ${version}\n`);
|
|
1374
|
+
await cli.parseAsync(process.argv);
|
|
1375
|
+
} catch (error) {
|
|
1376
|
+
if (error.signal === "SIGINT" && error.signalDescription) logError(`\n${error.signalDescription}`);
|
|
1377
|
+
else logError(`\n${error}`);
|
|
1378
|
+
}
|
|
1337
1379
|
//#endregion
|
|
1338
1380
|
export {};
|