@envsync-cloud/deploy-cli 0.6.5 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/index.js +61 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ bunx @envsync-cloud/deploy-cli <command>
|
|
|
40
40
|
```text
|
|
41
41
|
envsync-deploy preinstall
|
|
42
42
|
envsync-deploy setup
|
|
43
|
-
envsync-deploy bootstrap [--dry-run]
|
|
43
|
+
envsync-deploy bootstrap [--dry-run] [--force]
|
|
44
44
|
envsync-deploy deploy [--dry-run]
|
|
45
45
|
envsync-deploy health [--json]
|
|
46
46
|
envsync-deploy upgrade [--dry-run]
|
|
@@ -71,7 +71,7 @@ Bootstrap infra, migrations, RustFS, and OpenFGA:
|
|
|
71
71
|
npx @envsync-cloud/deploy-cli bootstrap
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
`bootstrap` is destructive. It removes the existing EnvSync stack, matching containers, network, and managed volumes before rebuilding, and requires typing `
|
|
74
|
+
`bootstrap` is destructive. It removes the existing EnvSync stack, matching containers, network, and managed volumes before rebuilding, and requires typing `yes` to continue. Use `--force` to bypass the prompt in automation or other non-interactive environments.
|
|
75
75
|
|
|
76
76
|
Deploy the pending API and frontend services:
|
|
77
77
|
|
|
@@ -106,6 +106,7 @@ Preview mutating commands without changing the host:
|
|
|
106
106
|
|
|
107
107
|
```bash
|
|
108
108
|
npx @envsync-cloud/deploy-cli bootstrap --dry-run
|
|
109
|
+
npx @envsync-cloud/deploy-cli bootstrap --force
|
|
109
110
|
npx @envsync-cloud/deploy-cli deploy --dry-run
|
|
110
111
|
```
|
|
111
112
|
|
package/dist/index.js
CHANGED
|
@@ -48,7 +48,7 @@ var REQUIRED_BOOTSTRAP_ENV_KEYS = [
|
|
|
48
48
|
"OPENFGA_MODEL_ID"
|
|
49
49
|
];
|
|
50
50
|
var SEMVER_VERSION_RE = /^\d+\.\d+\.\d+$/;
|
|
51
|
-
var currentOptions = { dryRun: false };
|
|
51
|
+
var currentOptions = { dryRun: false, force: false };
|
|
52
52
|
function formatShellArg(arg) {
|
|
53
53
|
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(arg)) return arg;
|
|
54
54
|
return JSON.stringify(arg);
|
|
@@ -221,7 +221,7 @@ async function ask(question, fallback = "") {
|
|
|
221
221
|
}
|
|
222
222
|
async function askRequired(question) {
|
|
223
223
|
if (!process.stdin.isTTY) {
|
|
224
|
-
throw new Error(
|
|
224
|
+
throw new Error("Bootstrap confirmation requires an interactive terminal. Re-run with --force to bypass the prompt.");
|
|
225
225
|
}
|
|
226
226
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
227
227
|
return await new Promise((resolve) => {
|
|
@@ -635,6 +635,10 @@ function renderTraefikDynamicConfig(config) {
|
|
|
635
635
|
" loadBalancer:",
|
|
636
636
|
" servers:",
|
|
637
637
|
" - url: http://web_nginx:8080",
|
|
638
|
+
" browser-otlp:",
|
|
639
|
+
" loadBalancer:",
|
|
640
|
+
" servers:",
|
|
641
|
+
` - url: http://clickstack:${config.services.clickstack_otlp_http_port}`,
|
|
638
642
|
" routers:",
|
|
639
643
|
" landing-router:",
|
|
640
644
|
` rule: Host(\`${hosts.landing}\`)`,
|
|
@@ -646,6 +650,18 @@ function renderTraefikDynamicConfig(config) {
|
|
|
646
650
|
" service: web",
|
|
647
651
|
" entryPoints: [websecure]",
|
|
648
652
|
" tls: {}",
|
|
653
|
+
" landing-otlp-router:",
|
|
654
|
+
` rule: Host(\`${hosts.landing}\`) && (PathPrefix(\`/v1/traces\`) || PathPrefix(\`/v1/logs\`) || PathPrefix(\`/v1/metrics\`))`,
|
|
655
|
+
" service: browser-otlp",
|
|
656
|
+
" priority: 100",
|
|
657
|
+
" entryPoints: [websecure]",
|
|
658
|
+
" tls: {}",
|
|
659
|
+
" web-otlp-router:",
|
|
660
|
+
` rule: Host(\`${hosts.app}\`) && (PathPrefix(\`/v1/traces\`) || PathPrefix(\`/v1/logs\`) || PathPrefix(\`/v1/metrics\`))`,
|
|
661
|
+
" service: browser-otlp",
|
|
662
|
+
" priority: 100",
|
|
663
|
+
" entryPoints: [websecure]",
|
|
664
|
+
" tls: {}",
|
|
649
665
|
" api-router:",
|
|
650
666
|
` rule: Host(\`${hosts.api}\`)`,
|
|
651
667
|
" service: envsync-api",
|
|
@@ -666,15 +682,17 @@ function renderNginxConf(kind) {
|
|
|
666
682
|
"}"
|
|
667
683
|
].join("\n") + "\n";
|
|
668
684
|
}
|
|
669
|
-
function renderFrontendRuntimeConfig(config) {
|
|
685
|
+
function renderFrontendRuntimeConfig(config, kind) {
|
|
670
686
|
const hosts = domainMap(config.domain.root_domain);
|
|
687
|
+
const otelEndpoint = kind === "web" ? `https://${hosts.app}` : `https://${hosts.landing}`;
|
|
671
688
|
return `window.__ENVSYNC_RUNTIME_CONFIG__ = ${JSON.stringify({
|
|
672
689
|
apiBaseUrl: `https://${hosts.api}`,
|
|
673
690
|
appBaseUrl: `https://${hosts.app}`,
|
|
674
691
|
authBaseUrl: `https://${hosts.auth}`,
|
|
675
692
|
keycloakRealm: config.auth.keycloak_realm,
|
|
676
693
|
webClientId: config.auth.web_client_id,
|
|
677
|
-
apiDocsUrl: `https://${hosts.api}/docs
|
|
694
|
+
apiDocsUrl: `https://${hosts.api}/docs`,
|
|
695
|
+
otelEndpoint
|
|
678
696
|
}, null, 2)};
|
|
679
697
|
`;
|
|
680
698
|
}
|
|
@@ -1090,7 +1108,29 @@ function waitForTcpService(config, label, host, port, timeoutSeconds = 120) {
|
|
|
1090
1108
|
throw new Error(`Timed out waiting for ${label} at ${host}:${port}`);
|
|
1091
1109
|
}
|
|
1092
1110
|
function waitForHttpService(config, label, url, timeoutSeconds = 120) {
|
|
1093
|
-
|
|
1111
|
+
if (currentOptions.dryRun) {
|
|
1112
|
+
logDryRun(`Would wait for ${label} at ${url}`);
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
logStep(`Waiting for ${label} on ${url}`);
|
|
1116
|
+
const deadline = Date.now() + timeoutSeconds * 1e3;
|
|
1117
|
+
while (Date.now() < deadline) {
|
|
1118
|
+
if (commandSucceeds("docker", [
|
|
1119
|
+
"run",
|
|
1120
|
+
"--rm",
|
|
1121
|
+
"--network",
|
|
1122
|
+
stackNetworkName(config),
|
|
1123
|
+
"alpine:3.20",
|
|
1124
|
+
"sh",
|
|
1125
|
+
"-lc",
|
|
1126
|
+
`wget -q -O /dev/null ${JSON.stringify(url)}`
|
|
1127
|
+
])) {
|
|
1128
|
+
logSuccess(`${label} is ready`);
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
sleepSeconds(2);
|
|
1132
|
+
}
|
|
1133
|
+
throw new Error(`Timed out waiting for ${label} at ${url}`);
|
|
1094
1134
|
}
|
|
1095
1135
|
function runOpenFgaMigrate(config, runtimeEnv) {
|
|
1096
1136
|
logStep("Running OpenFGA datastore migrations");
|
|
@@ -1307,9 +1347,14 @@ async function confirmBootstrapReset(config) {
|
|
|
1307
1347
|
logWarn("Containers: none currently matched");
|
|
1308
1348
|
}
|
|
1309
1349
|
logWarn("This removes existing deployment data for the managed EnvSync services.");
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1350
|
+
if (currentOptions.force) {
|
|
1351
|
+
logWarn("Skipping confirmation because --force was provided.");
|
|
1352
|
+
logSuccess("Destructive bootstrap reset confirmed");
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
const response = await askRequired(chalk.bold.red('Type "yes" to continue:'));
|
|
1356
|
+
if (response !== "yes") {
|
|
1357
|
+
throw new Error("Bootstrap aborted. Confirmation did not match 'yes'.");
|
|
1313
1358
|
}
|
|
1314
1359
|
logSuccess("Destructive bootstrap reset confirmed");
|
|
1315
1360
|
}
|
|
@@ -1480,9 +1525,8 @@ async function cmdBootstrap() {
|
|
|
1480
1525
|
assertSwarmManager();
|
|
1481
1526
|
if (currentOptions.dryRun) {
|
|
1482
1527
|
logWarn("Dry-run mode: bootstrap reset will be previewed but not executed.");
|
|
1483
|
-
} else {
|
|
1484
|
-
await confirmBootstrapReset(config);
|
|
1485
1528
|
}
|
|
1529
|
+
await confirmBootstrapReset(config);
|
|
1486
1530
|
cleanupBootstrapState(config);
|
|
1487
1531
|
ensureRepoCheckout(config);
|
|
1488
1532
|
writeDeployArtifacts(config, nextGenerated);
|
|
@@ -1513,7 +1557,7 @@ async function cmdBootstrap() {
|
|
|
1513
1557
|
run("docker", ["stack", "deploy", "-c", BOOTSTRAP_STACK_FILE, config.services.stack_name]);
|
|
1514
1558
|
logSuccess("Runtime bootstrap stack deployed");
|
|
1515
1559
|
}
|
|
1516
|
-
waitForHttpService(config, "keycloak", "http://keycloak:
|
|
1560
|
+
waitForHttpService(config, "keycloak management readiness", "http://keycloak:9000/health/ready", 180);
|
|
1517
1561
|
waitForHttpService(config, "openfga", "http://openfga:8090/stores");
|
|
1518
1562
|
waitForTcpService(config, "minikms", "minikms", 50051);
|
|
1519
1563
|
const initResult = runBootstrapInit(config);
|
|
@@ -1561,8 +1605,8 @@ async function cmdDeploy() {
|
|
|
1561
1605
|
}
|
|
1562
1606
|
extractStaticBundle(config.images.web, `${RELEASES_ROOT}/web/current`);
|
|
1563
1607
|
extractStaticBundle(config.images.landing, `${RELEASES_ROOT}/landing/current`);
|
|
1564
|
-
writeFileMaybe(`${RELEASES_ROOT}/web/current/runtime-config.js`, renderFrontendRuntimeConfig(config));
|
|
1565
|
-
writeFileMaybe(`${RELEASES_ROOT}/landing/current/runtime-config.js`, renderFrontendRuntimeConfig(config));
|
|
1608
|
+
writeFileMaybe(`${RELEASES_ROOT}/web/current/runtime-config.js`, renderFrontendRuntimeConfig(config, "web"));
|
|
1609
|
+
writeFileMaybe(`${RELEASES_ROOT}/landing/current/runtime-config.js`, renderFrontendRuntimeConfig(config, "landing"));
|
|
1566
1610
|
if (currentOptions.dryRun) {
|
|
1567
1611
|
logDryRun(`Would deploy full stack for ${config.services.stack_name}`);
|
|
1568
1612
|
logCommand("docker", ["stack", "deploy", "-c", STACK_FILE, config.services.stack_name]);
|
|
@@ -1770,9 +1814,10 @@ async function main() {
|
|
|
1770
1814
|
const command = argv[0];
|
|
1771
1815
|
const args = argv.slice(1);
|
|
1772
1816
|
currentOptions = {
|
|
1773
|
-
dryRun: args.includes("--dry-run")
|
|
1817
|
+
dryRun: args.includes("--dry-run"),
|
|
1818
|
+
force: args.includes("--force")
|
|
1774
1819
|
};
|
|
1775
|
-
const positionals = args.filter((arg) => arg !== "--dry-run");
|
|
1820
|
+
const positionals = args.filter((arg) => arg !== "--dry-run" && arg !== "--force");
|
|
1776
1821
|
switch (command) {
|
|
1777
1822
|
case "preinstall":
|
|
1778
1823
|
await cmdPreinstall();
|
|
@@ -1803,7 +1848,7 @@ async function main() {
|
|
|
1803
1848
|
break;
|
|
1804
1849
|
default:
|
|
1805
1850
|
console.log(
|
|
1806
|
-
"Usage: envsync-deploy <preinstall|setup|bootstrap|deploy|health|upgrade|upgrade-deps|backup|restore> [--dry-run]"
|
|
1851
|
+
"Usage: envsync-deploy <preinstall|setup|bootstrap|deploy|health|upgrade|upgrade-deps|backup|restore> [--dry-run] [--force]"
|
|
1807
1852
|
);
|
|
1808
1853
|
process.exit(command ? 1 : 0);
|
|
1809
1854
|
}
|