@envsync-cloud/deploy 0.21.0 → 0.21.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 +88 -71
- package/dist/index.js +88 -71
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { createHash as
|
|
4
|
+
import { createHash as createHash3, generateKeyPairSync, randomBytes as randomBytes2, randomInt } from "crypto";
|
|
5
5
|
import { spawnSync } from "child_process";
|
|
6
|
-
import
|
|
6
|
+
import fs5 from "fs";
|
|
7
7
|
import path4 from "path";
|
|
8
8
|
import readline2 from "readline";
|
|
9
9
|
import chalk from "chalk";
|
|
@@ -284,6 +284,16 @@ function formatDeploymentPlan(plan, format = "yaml") {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
// src/render.ts
|
|
287
|
+
import { createHash } from "crypto";
|
|
288
|
+
import fs2 from "fs";
|
|
289
|
+
function swarmConfigObjectName(stackName, logical, filePath, fallbackVersion) {
|
|
290
|
+
let token = fallbackVersion.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_|_$/g, "") || "v";
|
|
291
|
+
try {
|
|
292
|
+
token = createHash("sha256").update(fs2.readFileSync(filePath)).digest("hex").slice(0, 12);
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
295
|
+
return `${stackName}_${logical}_${token}`;
|
|
296
|
+
}
|
|
287
297
|
function domainMap(rootDomain) {
|
|
288
298
|
return {
|
|
289
299
|
landing: rootDomain,
|
|
@@ -537,7 +547,7 @@ function renderTraefikDynamicConfig(config, generated) {
|
|
|
537
547
|
const landingEnabled = false;
|
|
538
548
|
const managementEnabled = !isOssConfig(config);
|
|
539
549
|
const otelAllowedOrigins = [
|
|
540
|
-
...
|
|
550
|
+
...publicHttpsOriginVariants(config, hosts.landing),
|
|
541
551
|
...publicHttpsOriginVariants(config, hosts.app)
|
|
542
552
|
];
|
|
543
553
|
return [
|
|
@@ -925,6 +935,7 @@ function renderStack(config, runtimeEnv, generated, mode, paths) {
|
|
|
925
935
|
const apiLicenseVolume = managementEnabled ? "\n volumes:\n - /etc/envsync/license:/etc/envsync/license:ro" : "";
|
|
926
936
|
const deployment = createSteadyApiDeploymentState(config, generated);
|
|
927
937
|
const stackName = config.services.stack_name;
|
|
938
|
+
const configName = (logical, filePath) => swarmConfigObjectName(stackName, logical, filePath, config.release.version);
|
|
928
939
|
const s3RouterName = `${stackName}-s3-router`;
|
|
929
940
|
const s3ServiceName = `${stackName}-s3-service`;
|
|
930
941
|
const s3ConsoleRouterName = `${stackName}-s3-console-router`;
|
|
@@ -1216,56 +1227,62 @@ volumes:
|
|
|
1216
1227
|
|
|
1217
1228
|
configs:
|
|
1218
1229
|
keycloak_realm:
|
|
1230
|
+
name: ${configName("keycloak_realm", paths.keycloakRealmFile)}
|
|
1219
1231
|
file: ${paths.keycloakRealmFile}
|
|
1220
1232
|
clickstack_clickhouse_conf:
|
|
1233
|
+
name: ${configName("clickstack_clickhouse_conf", paths.clickstackClickhouseConf)}
|
|
1221
1234
|
file: ${paths.clickstackClickhouseConf}
|
|
1222
1235
|
otel_agent_conf:
|
|
1236
|
+
name: ${configName("otel_agent_conf", paths.otelAgentConf)}
|
|
1223
1237
|
file: ${paths.otelAgentConf}
|
|
1224
1238
|
${landingEnabled ? ` nginx_landing_conf:
|
|
1239
|
+
name: ${configName("nginx_landing_conf", paths.nginxLandingConf)}
|
|
1225
1240
|
file: ${paths.nginxLandingConf}
|
|
1226
1241
|
` : ""}
|
|
1227
1242
|
nginx_web_conf:
|
|
1243
|
+
name: ${configName("nginx_web_conf", paths.nginxWebConf)}
|
|
1228
1244
|
file: ${paths.nginxWebConf}
|
|
1229
1245
|
nginx_api_maintenance_conf:
|
|
1246
|
+
name: ${configName("nginx_api_maintenance_conf", paths.nginxApiMaintenanceConf)}
|
|
1230
1247
|
file: ${paths.nginxApiMaintenanceConf}
|
|
1231
1248
|
`.trimStart();
|
|
1232
1249
|
}
|
|
1233
1250
|
|
|
1234
1251
|
// src/static-bundle.ts
|
|
1235
|
-
import
|
|
1252
|
+
import fs3 from "fs";
|
|
1236
1253
|
import path2 from "path";
|
|
1237
1254
|
function exists(target) {
|
|
1238
|
-
return
|
|
1255
|
+
return fs3.existsSync(target);
|
|
1239
1256
|
}
|
|
1240
1257
|
function normalizeExtractedStaticBundle(kind, targetDir) {
|
|
1241
1258
|
const directIndex = path2.join(targetDir, "index.html");
|
|
1242
1259
|
if (exists(directIndex)) {
|
|
1243
1260
|
return;
|
|
1244
1261
|
}
|
|
1245
|
-
const childDirs =
|
|
1262
|
+
const childDirs = fs3.readdirSync(targetDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path2.join(targetDir, entry.name));
|
|
1246
1263
|
const candidateRoots = childDirs.filter((dir) => exists(path2.join(dir, "index.html")));
|
|
1247
1264
|
if (candidateRoots.length !== 1) {
|
|
1248
1265
|
return;
|
|
1249
1266
|
}
|
|
1250
1267
|
const nestedRoot = candidateRoots[0];
|
|
1251
|
-
for (const entry of
|
|
1252
|
-
|
|
1268
|
+
for (const entry of fs3.readdirSync(nestedRoot)) {
|
|
1269
|
+
fs3.cpSync(path2.join(nestedRoot, entry), path2.join(targetDir, entry), { recursive: true });
|
|
1253
1270
|
}
|
|
1254
|
-
|
|
1271
|
+
fs3.rmSync(nestedRoot, { recursive: true, force: true });
|
|
1255
1272
|
}
|
|
1256
1273
|
function validateStaticBundle(kind, targetDir) {
|
|
1257
1274
|
if (exists(path2.join(targetDir, "index.html"))) {
|
|
1258
1275
|
return;
|
|
1259
1276
|
}
|
|
1260
|
-
const entries = exists(targetDir) ?
|
|
1277
|
+
const entries = exists(targetDir) ? fs3.readdirSync(targetDir).slice(0, 20).join(", ") : "";
|
|
1261
1278
|
throw new Error(
|
|
1262
1279
|
`Invalid ${kind} static bundle at ${targetDir}: missing index.html${entries ? `. Found: ${entries}` : ""}`
|
|
1263
1280
|
);
|
|
1264
1281
|
}
|
|
1265
1282
|
|
|
1266
1283
|
// src/org-setup.ts
|
|
1267
|
-
import { createHash, randomBytes } from "crypto";
|
|
1268
|
-
import
|
|
1284
|
+
import { createHash as createHash2, randomBytes } from "crypto";
|
|
1285
|
+
import fs4 from "fs";
|
|
1269
1286
|
import path3 from "path";
|
|
1270
1287
|
import readline from "readline";
|
|
1271
1288
|
var SETUP_TOKEN_HEADER = "X-EnvSync-Setup-Token";
|
|
@@ -1275,33 +1292,33 @@ function generateSetupToken() {
|
|
|
1275
1292
|
function ensureSetupTokenFile(tokenFile, existingToken) {
|
|
1276
1293
|
if (existingToken?.trim()) {
|
|
1277
1294
|
const dir2 = path3.dirname(tokenFile);
|
|
1278
|
-
|
|
1279
|
-
|
|
1295
|
+
fs4.mkdirSync(dir2, { recursive: true, mode: 448 });
|
|
1296
|
+
fs4.writeFileSync(tokenFile, `${existingToken.trim()}
|
|
1280
1297
|
`, { mode: 384 });
|
|
1281
1298
|
return existingToken.trim();
|
|
1282
1299
|
}
|
|
1283
|
-
if (
|
|
1284
|
-
const fromFile =
|
|
1300
|
+
if (fs4.existsSync(tokenFile)) {
|
|
1301
|
+
const fromFile = fs4.readFileSync(tokenFile, "utf8").trim();
|
|
1285
1302
|
if (fromFile.length >= 16) {
|
|
1286
1303
|
return fromFile;
|
|
1287
1304
|
}
|
|
1288
1305
|
}
|
|
1289
1306
|
const token = generateSetupToken();
|
|
1290
1307
|
const dir = path3.dirname(tokenFile);
|
|
1291
|
-
|
|
1292
|
-
|
|
1308
|
+
fs4.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1309
|
+
fs4.writeFileSync(tokenFile, `${token}
|
|
1293
1310
|
`, { mode: 384 });
|
|
1294
1311
|
return token;
|
|
1295
1312
|
}
|
|
1296
1313
|
function readSetupTokenFile(tokenFile) {
|
|
1297
|
-
if (!
|
|
1314
|
+
if (!fs4.existsSync(tokenFile)) {
|
|
1298
1315
|
return null;
|
|
1299
1316
|
}
|
|
1300
|
-
const token =
|
|
1317
|
+
const token = fs4.readFileSync(tokenFile, "utf8").trim();
|
|
1301
1318
|
return token.length >= 16 ? token : null;
|
|
1302
1319
|
}
|
|
1303
1320
|
function setupTokenFingerprint(token) {
|
|
1304
|
-
return
|
|
1321
|
+
return createHash2("sha256").update(token).digest("hex").slice(0, 12);
|
|
1305
1322
|
}
|
|
1306
1323
|
async function fetchSetupStatus(apiBaseUrl, setupToken) {
|
|
1307
1324
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
@@ -1788,12 +1805,12 @@ ${typeof result.stderr === "string" ? result.stderr : ""}`.toLowerCase();
|
|
|
1788
1805
|
${stderr}` : ""}`);
|
|
1789
1806
|
}
|
|
1790
1807
|
function ensureDir(dir) {
|
|
1791
|
-
|
|
1808
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
1792
1809
|
}
|
|
1793
1810
|
function writeFile(target, content, mode) {
|
|
1794
1811
|
ensureDir(path4.dirname(target));
|
|
1795
|
-
|
|
1796
|
-
if (mode != null)
|
|
1812
|
+
fs5.writeFileSync(target, content, "utf8");
|
|
1813
|
+
if (mode != null) fs5.chmodSync(target, mode);
|
|
1797
1814
|
}
|
|
1798
1815
|
function writeFileMaybe(target, content, mode) {
|
|
1799
1816
|
if (currentOptions.dryRun) {
|
|
@@ -1805,7 +1822,7 @@ function writeFileMaybe(target, content, mode) {
|
|
|
1805
1822
|
function ensureEnterpriseLicenseFilesReadable() {
|
|
1806
1823
|
if (exists2(LICENSE_ROOT)) {
|
|
1807
1824
|
try {
|
|
1808
|
-
|
|
1825
|
+
fs5.chmodSync(LICENSE_ROOT, LICENSE_DIR_MODE);
|
|
1809
1826
|
} catch {
|
|
1810
1827
|
throw new Error(
|
|
1811
1828
|
`Cannot update directory mode for ${LICENSE_ROOT}. Fix by running: chmod ${LICENSE_DIR_MODE.toString(8)} ${LICENSE_ROOT}`
|
|
@@ -1815,7 +1832,7 @@ function ensureEnterpriseLicenseFilesReadable() {
|
|
|
1815
1832
|
for (const filePath of [LICENSE_BUNDLE_FILE, LICENSE_CERT_FILE, LICENSE_KEY_FILE, LICENSE_ROOT_CA_FILE]) {
|
|
1816
1833
|
if (!exists2(filePath)) continue;
|
|
1817
1834
|
try {
|
|
1818
|
-
|
|
1835
|
+
fs5.chmodSync(filePath, LICENSE_FILE_MODE);
|
|
1819
1836
|
} catch {
|
|
1820
1837
|
throw new Error(
|
|
1821
1838
|
`Cannot update file mode for ${filePath}. Fix by running: chmod ${LICENSE_FILE_MODE.toString(8)} ${filePath}`
|
|
@@ -1824,7 +1841,7 @@ function ensureEnterpriseLicenseFilesReadable() {
|
|
|
1824
1841
|
}
|
|
1825
1842
|
}
|
|
1826
1843
|
function exists2(target) {
|
|
1827
|
-
return
|
|
1844
|
+
return fs5.existsSync(target);
|
|
1828
1845
|
}
|
|
1829
1846
|
function randomSecret(bytes = 24) {
|
|
1830
1847
|
return randomBytes2(bytes).toString("hex");
|
|
@@ -1834,7 +1851,7 @@ function generateMinikmsSessionSigningKey() {
|
|
|
1834
1851
|
return privateKey.export({ type: "pkcs8", format: "pem" }).toString();
|
|
1835
1852
|
}
|
|
1836
1853
|
function deterministicInstallFingerprint(rootDomain, stackName) {
|
|
1837
|
-
return `envsync-${
|
|
1854
|
+
return `envsync-${createHash3("sha256").update(`${rootDomain}:${stackName}`).digest("hex").slice(0, 32)}`;
|
|
1838
1855
|
}
|
|
1839
1856
|
function randomStrongPassword() {
|
|
1840
1857
|
return `EnvSync!${randomBytes2(8).toString("hex")}Aa1`;
|
|
@@ -1938,7 +1955,7 @@ function normalizeNetutilsForwards(raw, stackName, enabled) {
|
|
|
1938
1955
|
}
|
|
1939
1956
|
function loadWireguardState() {
|
|
1940
1957
|
if (!exists2(WIREGUARD_STATE_FILE)) return null;
|
|
1941
|
-
const raw = JSON.parse(
|
|
1958
|
+
const raw = JSON.parse(fs5.readFileSync(WIREGUARD_STATE_FILE, "utf8"));
|
|
1942
1959
|
return {
|
|
1943
1960
|
private_key: raw.private_key,
|
|
1944
1961
|
public_key: raw.public_key,
|
|
@@ -2122,7 +2139,7 @@ function publicHttpsUrl2(config, host, path5 = "") {
|
|
|
2122
2139
|
function getDeployCliVersion() {
|
|
2123
2140
|
try {
|
|
2124
2141
|
const packageJsonPath = new URL("../package.json", import.meta.url);
|
|
2125
|
-
const raw =
|
|
2142
|
+
const raw = fs5.readFileSync(packageJsonPath, "utf8");
|
|
2126
2143
|
return JSON.parse(raw).version ?? "0.0.0";
|
|
2127
2144
|
} catch {
|
|
2128
2145
|
return process.env.npm_package_version ?? "0.0.0";
|
|
@@ -2543,7 +2560,7 @@ function normalizeGeneratedState(raw) {
|
|
|
2543
2560
|
}
|
|
2544
2561
|
function readInternalState() {
|
|
2545
2562
|
if (!exists2(INTERNAL_CONFIG_JSON)) return null;
|
|
2546
|
-
const raw = JSON.parse(
|
|
2563
|
+
const raw = JSON.parse(fs5.readFileSync(INTERNAL_CONFIG_JSON, "utf8"));
|
|
2547
2564
|
return {
|
|
2548
2565
|
config: raw.config ? normalizeConfig(raw.config) : void 0,
|
|
2549
2566
|
generated: normalizeGeneratedState(raw.generated)
|
|
@@ -2553,7 +2570,7 @@ function loadConfig() {
|
|
|
2553
2570
|
if (!exists2(DEPLOY_YAML)) {
|
|
2554
2571
|
throw new Error(`Missing deploy config at ${DEPLOY_YAML}. Run setup first.`);
|
|
2555
2572
|
}
|
|
2556
|
-
const raw =
|
|
2573
|
+
const raw = fs5.readFileSync(DEPLOY_YAML, "utf8");
|
|
2557
2574
|
if (raw.trimStart().startsWith("{")) {
|
|
2558
2575
|
return normalizeConfig(JSON.parse(raw));
|
|
2559
2576
|
}
|
|
@@ -2561,7 +2578,7 @@ function loadConfig() {
|
|
|
2561
2578
|
}
|
|
2562
2579
|
function loadGeneratedEnv() {
|
|
2563
2580
|
if (!exists2(DEPLOY_ENV)) return {};
|
|
2564
|
-
return parseEnvFile(
|
|
2581
|
+
return parseEnvFile(fs5.readFileSync(DEPLOY_ENV, "utf8"));
|
|
2565
2582
|
}
|
|
2566
2583
|
function mergeGeneratedState(env, generated) {
|
|
2567
2584
|
const normalized = normalizeGeneratedState(generated);
|
|
@@ -2849,7 +2866,7 @@ function extractStaticBundle(kind, image, targetDir) {
|
|
|
2849
2866
|
logDryRun(`Would extract ${kind} bundle from ${image} into ${targetDir}`);
|
|
2850
2867
|
return;
|
|
2851
2868
|
}
|
|
2852
|
-
|
|
2869
|
+
fs5.rmSync(targetDir, { recursive: true, force: true });
|
|
2853
2870
|
ensureDir(targetDir);
|
|
2854
2871
|
const containerId = run("docker", ["create", image], { quiet: true }).trim();
|
|
2855
2872
|
try {
|
|
@@ -2877,23 +2894,23 @@ function writeFrontendRuntimeConfig(targetDir, runtimeConfig) {
|
|
|
2877
2894
|
function syncFrontendReleaseContents(sourceDir, targetDir) {
|
|
2878
2895
|
ensureDir(targetDir);
|
|
2879
2896
|
const deferredEntries = /* @__PURE__ */ new Set(["index.html", "runtime-config.js"]);
|
|
2880
|
-
const entries =
|
|
2897
|
+
const entries = fs5.readdirSync(sourceDir, { withFileTypes: true });
|
|
2881
2898
|
for (const entry of entries) {
|
|
2882
2899
|
if (deferredEntries.has(entry.name)) {
|
|
2883
2900
|
continue;
|
|
2884
2901
|
}
|
|
2885
|
-
|
|
2902
|
+
fs5.cpSync(path4.join(sourceDir, entry.name), path4.join(targetDir, entry.name), {
|
|
2886
2903
|
recursive: true,
|
|
2887
2904
|
force: true
|
|
2888
2905
|
});
|
|
2889
2906
|
}
|
|
2890
2907
|
const stagedRuntimeConfig = path4.join(sourceDir, "runtime-config.js");
|
|
2891
2908
|
if (exists2(stagedRuntimeConfig)) {
|
|
2892
|
-
|
|
2909
|
+
fs5.cpSync(stagedRuntimeConfig, path4.join(targetDir, "runtime-config.js"), { force: true });
|
|
2893
2910
|
}
|
|
2894
2911
|
const stagedIndex = path4.join(sourceDir, "index.html");
|
|
2895
2912
|
if (exists2(stagedIndex)) {
|
|
2896
|
-
|
|
2913
|
+
fs5.cpSync(stagedIndex, path4.join(targetDir, "index.html"), { force: true });
|
|
2897
2914
|
}
|
|
2898
2915
|
}
|
|
2899
2916
|
function activateFrontendRelease(kind, version, runtimeConfig) {
|
|
@@ -3291,7 +3308,7 @@ ${trimmed}`
|
|
|
3291
3308
|
}
|
|
3292
3309
|
function readRenderedRuntimeConfig(filePath) {
|
|
3293
3310
|
if (!exists2(filePath)) return null;
|
|
3294
|
-
const raw =
|
|
3311
|
+
const raw = fs5.readFileSync(filePath, "utf8").trim();
|
|
3295
3312
|
const prefix = "window.__ENVSYNC_RUNTIME_CONFIG__ = ";
|
|
3296
3313
|
if (!raw.startsWith(prefix) || !raw.endsWith(";")) return null;
|
|
3297
3314
|
try {
|
|
@@ -3588,7 +3605,7 @@ function removeTarget(target) {
|
|
|
3588
3605
|
logDryRun(`Would remove ${target}`);
|
|
3589
3606
|
return;
|
|
3590
3607
|
}
|
|
3591
|
-
|
|
3608
|
+
fs5.rmSync(target, { recursive: true, force: true });
|
|
3592
3609
|
logInfo(`Removed ${target}`);
|
|
3593
3610
|
}
|
|
3594
3611
|
async function cmdRemove() {
|
|
@@ -4335,7 +4352,7 @@ async function cmdDeploy() {
|
|
|
4335
4352
|
sleepSeconds(3);
|
|
4336
4353
|
promoted = true;
|
|
4337
4354
|
if (snapshotPath && !currentOptions.dryRun) {
|
|
4338
|
-
|
|
4355
|
+
fs5.rmSync(snapshotPath, { force: true });
|
|
4339
4356
|
}
|
|
4340
4357
|
} catch (error) {
|
|
4341
4358
|
const rollbackTarget = preUpgradeHead ?? "zero";
|
|
@@ -4369,7 +4386,7 @@ ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
|
|
|
4369
4386
|
deployRenderedStack(config, "rollback recovery");
|
|
4370
4387
|
waitForApiSlotHealthy(config, recoveredState.deployment.active_slot);
|
|
4371
4388
|
if (snapshotPath && !config.upgrade.keep_failed_upgrade_db_snapshot && !currentOptions.dryRun) {
|
|
4372
|
-
|
|
4389
|
+
fs5.rmSync(snapshotPath, { force: true });
|
|
4373
4390
|
}
|
|
4374
4391
|
throw new Error(
|
|
4375
4392
|
usedSnapshotRestore ? `Deploy failed after DB migration. Previous release was restored using DB snapshot recovery. ${error instanceof Error ? error.message : String(error)}` : `Deploy failed after DB migration. Previous release was restored. ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -4490,7 +4507,7 @@ async function cmdHealth(asJson) {
|
|
|
4490
4507
|
const hosts = domainMap2(config.domain.root_domain);
|
|
4491
4508
|
const services = listStackServices(config);
|
|
4492
4509
|
const stackName = config.services.stack_name;
|
|
4493
|
-
const traefikDynamic = exists2(TRAEFIK_DYNAMIC_FILE) ?
|
|
4510
|
+
const traefikDynamic = exists2(TRAEFIK_DYNAMIC_FILE) ? fs5.readFileSync(TRAEFIK_DYNAMIC_FILE, "utf8") : "";
|
|
4494
4511
|
const webRuntimeConfig = readRenderedRuntimeConfig(path4.join(RELEASES_ROOT, "web", "current", "runtime-config.js"));
|
|
4495
4512
|
const landingRuntimeConfig = readRenderedRuntimeConfig(path4.join(RELEASES_ROOT, "landing", "current", "runtime-config.js"));
|
|
4496
4513
|
const clickstackSearchState = getClickstackSearchState(config);
|
|
@@ -4682,8 +4699,8 @@ async function cmdUpgradeDeps() {
|
|
|
4682
4699
|
await cmdDeploy();
|
|
4683
4700
|
}
|
|
4684
4701
|
function sha256File(filePath) {
|
|
4685
|
-
const hash =
|
|
4686
|
-
hash.update(
|
|
4702
|
+
const hash = createHash3("sha256");
|
|
4703
|
+
hash.update(fs5.readFileSync(filePath));
|
|
4687
4704
|
return hash.digest("hex");
|
|
4688
4705
|
}
|
|
4689
4706
|
function parseEnterpriseLicenseBundle(raw) {
|
|
@@ -4712,7 +4729,7 @@ function parseEnterpriseLicenseBundle(raw) {
|
|
|
4712
4729
|
return candidate;
|
|
4713
4730
|
}
|
|
4714
4731
|
function readEnterpriseLicenseBundle(bundlePath) {
|
|
4715
|
-
return parseEnterpriseLicenseBundle(JSON.parse(
|
|
4732
|
+
return parseEnterpriseLicenseBundle(JSON.parse(fs5.readFileSync(bundlePath, "utf8")));
|
|
4716
4733
|
}
|
|
4717
4734
|
function normalizeLicenseServerUrl(serverUrl, route) {
|
|
4718
4735
|
const trimmed = serverUrl.replace(/\/+$/, "");
|
|
@@ -4974,19 +4991,19 @@ async function cmdBackup() {
|
|
|
4974
4991
|
logWarn("Backup will temporarily stop the EnvSync stack to capture consistent volume data.");
|
|
4975
4992
|
stopManagedRuntime(config, "Stopping existing EnvSync services for consistent backup");
|
|
4976
4993
|
}
|
|
4977
|
-
writeFile(path4.join(staged, "deploy.env"),
|
|
4978
|
-
writeFile(path4.join(staged, "deploy.yaml"),
|
|
4979
|
-
writeFile(path4.join(staged, "config.json"),
|
|
4980
|
-
writeFile(path4.join(staged, "versions.lock.json"),
|
|
4981
|
-
writeFile(path4.join(staged, "docker-stack.bootstrap.base.yaml"),
|
|
4982
|
-
writeFile(path4.join(staged, "docker-stack.bootstrap.yaml"),
|
|
4983
|
-
writeFile(path4.join(staged, "docker-stack.yaml"),
|
|
4984
|
-
writeFile(path4.join(staged, "traefik-dynamic.yaml"),
|
|
4985
|
-
writeFile(path4.join(staged, "keycloak-realm.envsync.json"),
|
|
4986
|
-
writeFile(path4.join(staged, "otel-agent.yaml"),
|
|
4987
|
-
writeFile(path4.join(staged, "clickhouse-listen.xml"),
|
|
4994
|
+
writeFile(path4.join(staged, "deploy.env"), fs5.readFileSync(DEPLOY_ENV, "utf8"));
|
|
4995
|
+
writeFile(path4.join(staged, "deploy.yaml"), fs5.readFileSync(DEPLOY_YAML, "utf8"));
|
|
4996
|
+
writeFile(path4.join(staged, "config.json"), fs5.readFileSync(INTERNAL_CONFIG_JSON, "utf8"));
|
|
4997
|
+
writeFile(path4.join(staged, "versions.lock.json"), fs5.readFileSync(VERSIONS_LOCK, "utf8"));
|
|
4998
|
+
writeFile(path4.join(staged, "docker-stack.bootstrap.base.yaml"), fs5.readFileSync(BOOTSTRAP_BASE_STACK_FILE, "utf8"));
|
|
4999
|
+
writeFile(path4.join(staged, "docker-stack.bootstrap.yaml"), fs5.readFileSync(BOOTSTRAP_STACK_FILE, "utf8"));
|
|
5000
|
+
writeFile(path4.join(staged, "docker-stack.yaml"), fs5.readFileSync(STACK_FILE, "utf8"));
|
|
5001
|
+
writeFile(path4.join(staged, "traefik-dynamic.yaml"), fs5.readFileSync(TRAEFIK_DYNAMIC_FILE, "utf8"));
|
|
5002
|
+
writeFile(path4.join(staged, "keycloak-realm.envsync.json"), fs5.readFileSync(KEYCLOAK_REALM_FILE, "utf8"));
|
|
5003
|
+
writeFile(path4.join(staged, "otel-agent.yaml"), fs5.readFileSync(OTEL_AGENT_CONF, "utf8"));
|
|
5004
|
+
writeFile(path4.join(staged, "clickhouse-listen.xml"), fs5.readFileSync(CLICKSTACK_CLICKHOUSE_CONF, "utf8"));
|
|
4988
5005
|
if (!isOssConfig2(config) && exists2(LICENSE_ROOT)) {
|
|
4989
|
-
|
|
5006
|
+
fs5.cpSync(LICENSE_ROOT, path4.join(staged, "license"), { recursive: true });
|
|
4990
5007
|
}
|
|
4991
5008
|
const volumesDir = path4.join(staged, "volumes");
|
|
4992
5009
|
for (const volume of STACK_VOLUMES) {
|
|
@@ -5053,21 +5070,21 @@ async function cmdRestore(archivePath, autoDeploy = false) {
|
|
|
5053
5070
|
}
|
|
5054
5071
|
ensureDir(restoreRoot);
|
|
5055
5072
|
run("bash", ["-lc", `tar -xzf ${JSON.stringify(archivePath)} -C ${JSON.stringify(restoreRoot)}`]);
|
|
5056
|
-
writeFile(DEPLOY_ENV,
|
|
5057
|
-
writeFile(DEPLOY_YAML,
|
|
5058
|
-
writeFile(INTERNAL_CONFIG_JSON,
|
|
5059
|
-
writeFile(VERSIONS_LOCK,
|
|
5060
|
-
writeFile(BOOTSTRAP_BASE_STACK_FILE,
|
|
5061
|
-
writeFile(BOOTSTRAP_STACK_FILE,
|
|
5062
|
-
writeFile(STACK_FILE,
|
|
5063
|
-
writeFile(TRAEFIK_DYNAMIC_FILE,
|
|
5064
|
-
writeFile(KEYCLOAK_REALM_FILE,
|
|
5065
|
-
writeFile(OTEL_AGENT_CONF,
|
|
5066
|
-
writeFile(CLICKSTACK_CLICKHOUSE_CONF,
|
|
5073
|
+
writeFile(DEPLOY_ENV, fs5.readFileSync(path4.join(restoreRoot, "deploy.env"), "utf8"), 384);
|
|
5074
|
+
writeFile(DEPLOY_YAML, fs5.readFileSync(path4.join(restoreRoot, "deploy.yaml"), "utf8"));
|
|
5075
|
+
writeFile(INTERNAL_CONFIG_JSON, fs5.readFileSync(path4.join(restoreRoot, "config.json"), "utf8"));
|
|
5076
|
+
writeFile(VERSIONS_LOCK, fs5.readFileSync(path4.join(restoreRoot, "versions.lock.json"), "utf8"));
|
|
5077
|
+
writeFile(BOOTSTRAP_BASE_STACK_FILE, fs5.readFileSync(path4.join(restoreRoot, "docker-stack.bootstrap.base.yaml"), "utf8"));
|
|
5078
|
+
writeFile(BOOTSTRAP_STACK_FILE, fs5.readFileSync(path4.join(restoreRoot, "docker-stack.bootstrap.yaml"), "utf8"));
|
|
5079
|
+
writeFile(STACK_FILE, fs5.readFileSync(path4.join(restoreRoot, "docker-stack.yaml"), "utf8"));
|
|
5080
|
+
writeFile(TRAEFIK_DYNAMIC_FILE, fs5.readFileSync(path4.join(restoreRoot, "traefik-dynamic.yaml"), "utf8"));
|
|
5081
|
+
writeFile(KEYCLOAK_REALM_FILE, fs5.readFileSync(path4.join(restoreRoot, "keycloak-realm.envsync.json"), "utf8"));
|
|
5082
|
+
writeFile(OTEL_AGENT_CONF, fs5.readFileSync(path4.join(restoreRoot, "otel-agent.yaml"), "utf8"));
|
|
5083
|
+
writeFile(CLICKSTACK_CLICKHOUSE_CONF, fs5.readFileSync(path4.join(restoreRoot, "clickhouse-listen.xml"), "utf8"));
|
|
5067
5084
|
const restoredLicenseRoot = path4.join(restoreRoot, "license");
|
|
5068
5085
|
if (exists2(restoredLicenseRoot)) {
|
|
5069
|
-
|
|
5070
|
-
|
|
5086
|
+
fs5.rmSync(LICENSE_ROOT, { recursive: true, force: true });
|
|
5087
|
+
fs5.cpSync(restoredLicenseRoot, LICENSE_ROOT, { recursive: true });
|
|
5071
5088
|
ensureEnterpriseLicenseFilesReadable();
|
|
5072
5089
|
}
|
|
5073
5090
|
const config = loadConfig();
|
package/dist/index.js
CHANGED
|
@@ -285,6 +285,16 @@ var init_src = __esm({
|
|
|
285
285
|
});
|
|
286
286
|
|
|
287
287
|
// src/render.ts
|
|
288
|
+
import { createHash } from "crypto";
|
|
289
|
+
import fs2 from "fs";
|
|
290
|
+
function swarmConfigObjectName(stackName, logical, filePath, fallbackVersion) {
|
|
291
|
+
let token = fallbackVersion.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_|_$/g, "") || "v";
|
|
292
|
+
try {
|
|
293
|
+
token = createHash("sha256").update(fs2.readFileSync(filePath)).digest("hex").slice(0, 12);
|
|
294
|
+
} catch {
|
|
295
|
+
}
|
|
296
|
+
return `${stackName}_${logical}_${token}`;
|
|
297
|
+
}
|
|
288
298
|
function domainMap(rootDomain) {
|
|
289
299
|
return {
|
|
290
300
|
landing: rootDomain,
|
|
@@ -538,7 +548,7 @@ function renderTraefikDynamicConfig(config, generated) {
|
|
|
538
548
|
const landingEnabled = false;
|
|
539
549
|
const managementEnabled = !isOssConfig(config);
|
|
540
550
|
const otelAllowedOrigins = [
|
|
541
|
-
...
|
|
551
|
+
...publicHttpsOriginVariants(config, hosts.landing),
|
|
542
552
|
...publicHttpsOriginVariants(config, hosts.app)
|
|
543
553
|
];
|
|
544
554
|
return [
|
|
@@ -922,6 +932,7 @@ function renderStack(config, runtimeEnv, generated, mode, paths) {
|
|
|
922
932
|
const apiLicenseVolume = managementEnabled ? "\n volumes:\n - /etc/envsync/license:/etc/envsync/license:ro" : "";
|
|
923
933
|
const deployment = createSteadyApiDeploymentState(config, generated);
|
|
924
934
|
const stackName = config.services.stack_name;
|
|
935
|
+
const configName = (logical, filePath) => swarmConfigObjectName(stackName, logical, filePath, config.release.version);
|
|
925
936
|
const s3RouterName = `${stackName}-s3-router`;
|
|
926
937
|
const s3ServiceName = `${stackName}-s3-service`;
|
|
927
938
|
const s3ConsoleRouterName = `${stackName}-s3-console-router`;
|
|
@@ -1213,17 +1224,23 @@ volumes:
|
|
|
1213
1224
|
|
|
1214
1225
|
configs:
|
|
1215
1226
|
keycloak_realm:
|
|
1227
|
+
name: ${configName("keycloak_realm", paths.keycloakRealmFile)}
|
|
1216
1228
|
file: ${paths.keycloakRealmFile}
|
|
1217
1229
|
clickstack_clickhouse_conf:
|
|
1230
|
+
name: ${configName("clickstack_clickhouse_conf", paths.clickstackClickhouseConf)}
|
|
1218
1231
|
file: ${paths.clickstackClickhouseConf}
|
|
1219
1232
|
otel_agent_conf:
|
|
1233
|
+
name: ${configName("otel_agent_conf", paths.otelAgentConf)}
|
|
1220
1234
|
file: ${paths.otelAgentConf}
|
|
1221
1235
|
${landingEnabled ? ` nginx_landing_conf:
|
|
1236
|
+
name: ${configName("nginx_landing_conf", paths.nginxLandingConf)}
|
|
1222
1237
|
file: ${paths.nginxLandingConf}
|
|
1223
1238
|
` : ""}
|
|
1224
1239
|
nginx_web_conf:
|
|
1240
|
+
name: ${configName("nginx_web_conf", paths.nginxWebConf)}
|
|
1225
1241
|
file: ${paths.nginxWebConf}
|
|
1226
1242
|
nginx_api_maintenance_conf:
|
|
1243
|
+
name: ${configName("nginx_api_maintenance_conf", paths.nginxApiMaintenanceConf)}
|
|
1227
1244
|
file: ${paths.nginxApiMaintenanceConf}
|
|
1228
1245
|
`.trimStart();
|
|
1229
1246
|
}
|
|
@@ -1239,32 +1256,32 @@ var init_render = __esm({
|
|
|
1239
1256
|
});
|
|
1240
1257
|
|
|
1241
1258
|
// src/static-bundle.ts
|
|
1242
|
-
import
|
|
1259
|
+
import fs3 from "fs";
|
|
1243
1260
|
import path2 from "path";
|
|
1244
1261
|
function exists(target) {
|
|
1245
|
-
return
|
|
1262
|
+
return fs3.existsSync(target);
|
|
1246
1263
|
}
|
|
1247
1264
|
function normalizeExtractedStaticBundle(kind, targetDir) {
|
|
1248
1265
|
const directIndex = path2.join(targetDir, "index.html");
|
|
1249
1266
|
if (exists(directIndex)) {
|
|
1250
1267
|
return;
|
|
1251
1268
|
}
|
|
1252
|
-
const childDirs =
|
|
1269
|
+
const childDirs = fs3.readdirSync(targetDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path2.join(targetDir, entry.name));
|
|
1253
1270
|
const candidateRoots = childDirs.filter((dir) => exists(path2.join(dir, "index.html")));
|
|
1254
1271
|
if (candidateRoots.length !== 1) {
|
|
1255
1272
|
return;
|
|
1256
1273
|
}
|
|
1257
1274
|
const nestedRoot = candidateRoots[0];
|
|
1258
|
-
for (const entry of
|
|
1259
|
-
|
|
1275
|
+
for (const entry of fs3.readdirSync(nestedRoot)) {
|
|
1276
|
+
fs3.cpSync(path2.join(nestedRoot, entry), path2.join(targetDir, entry), { recursive: true });
|
|
1260
1277
|
}
|
|
1261
|
-
|
|
1278
|
+
fs3.rmSync(nestedRoot, { recursive: true, force: true });
|
|
1262
1279
|
}
|
|
1263
1280
|
function validateStaticBundle(kind, targetDir) {
|
|
1264
1281
|
if (exists(path2.join(targetDir, "index.html"))) {
|
|
1265
1282
|
return;
|
|
1266
1283
|
}
|
|
1267
|
-
const entries = exists(targetDir) ?
|
|
1284
|
+
const entries = exists(targetDir) ? fs3.readdirSync(targetDir).slice(0, 20).join(", ") : "";
|
|
1268
1285
|
throw new Error(
|
|
1269
1286
|
`Invalid ${kind} static bundle at ${targetDir}: missing index.html${entries ? `. Found: ${entries}` : ""}`
|
|
1270
1287
|
);
|
|
@@ -1276,8 +1293,8 @@ var init_static_bundle = __esm({
|
|
|
1276
1293
|
});
|
|
1277
1294
|
|
|
1278
1295
|
// src/org-setup.ts
|
|
1279
|
-
import { createHash, randomBytes } from "crypto";
|
|
1280
|
-
import
|
|
1296
|
+
import { createHash as createHash2, randomBytes } from "crypto";
|
|
1297
|
+
import fs4 from "fs";
|
|
1281
1298
|
import path3 from "path";
|
|
1282
1299
|
import readline from "readline";
|
|
1283
1300
|
function generateSetupToken() {
|
|
@@ -1286,33 +1303,33 @@ function generateSetupToken() {
|
|
|
1286
1303
|
function ensureSetupTokenFile(tokenFile, existingToken) {
|
|
1287
1304
|
if (existingToken?.trim()) {
|
|
1288
1305
|
const dir2 = path3.dirname(tokenFile);
|
|
1289
|
-
|
|
1290
|
-
|
|
1306
|
+
fs4.mkdirSync(dir2, { recursive: true, mode: 448 });
|
|
1307
|
+
fs4.writeFileSync(tokenFile, `${existingToken.trim()}
|
|
1291
1308
|
`, { mode: 384 });
|
|
1292
1309
|
return existingToken.trim();
|
|
1293
1310
|
}
|
|
1294
|
-
if (
|
|
1295
|
-
const fromFile =
|
|
1311
|
+
if (fs4.existsSync(tokenFile)) {
|
|
1312
|
+
const fromFile = fs4.readFileSync(tokenFile, "utf8").trim();
|
|
1296
1313
|
if (fromFile.length >= 16) {
|
|
1297
1314
|
return fromFile;
|
|
1298
1315
|
}
|
|
1299
1316
|
}
|
|
1300
1317
|
const token = generateSetupToken();
|
|
1301
1318
|
const dir = path3.dirname(tokenFile);
|
|
1302
|
-
|
|
1303
|
-
|
|
1319
|
+
fs4.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1320
|
+
fs4.writeFileSync(tokenFile, `${token}
|
|
1304
1321
|
`, { mode: 384 });
|
|
1305
1322
|
return token;
|
|
1306
1323
|
}
|
|
1307
1324
|
function readSetupTokenFile(tokenFile) {
|
|
1308
|
-
if (!
|
|
1325
|
+
if (!fs4.existsSync(tokenFile)) {
|
|
1309
1326
|
return null;
|
|
1310
1327
|
}
|
|
1311
|
-
const token =
|
|
1328
|
+
const token = fs4.readFileSync(tokenFile, "utf8").trim();
|
|
1312
1329
|
return token.length >= 16 ? token : null;
|
|
1313
1330
|
}
|
|
1314
1331
|
function setupTokenFingerprint(token) {
|
|
1315
|
-
return
|
|
1332
|
+
return createHash2("sha256").update(token).digest("hex").slice(0, 12);
|
|
1316
1333
|
}
|
|
1317
1334
|
async function fetchSetupStatus(apiBaseUrl, setupToken) {
|
|
1318
1335
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
@@ -1455,9 +1472,9 @@ var init_org_setup = __esm({
|
|
|
1455
1472
|
|
|
1456
1473
|
// src/cli.ts
|
|
1457
1474
|
var cli_exports = {};
|
|
1458
|
-
import { createHash as
|
|
1475
|
+
import { createHash as createHash3, generateKeyPairSync, randomBytes as randomBytes2, randomInt } from "crypto";
|
|
1459
1476
|
import { spawnSync } from "child_process";
|
|
1460
|
-
import
|
|
1477
|
+
import fs5 from "fs";
|
|
1461
1478
|
import path4 from "path";
|
|
1462
1479
|
import readline2 from "readline";
|
|
1463
1480
|
import chalk from "chalk";
|
|
@@ -1651,12 +1668,12 @@ ${typeof result.stderr === "string" ? result.stderr : ""}`.toLowerCase();
|
|
|
1651
1668
|
${stderr}` : ""}`);
|
|
1652
1669
|
}
|
|
1653
1670
|
function ensureDir(dir) {
|
|
1654
|
-
|
|
1671
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
1655
1672
|
}
|
|
1656
1673
|
function writeFile(target, content, mode) {
|
|
1657
1674
|
ensureDir(path4.dirname(target));
|
|
1658
|
-
|
|
1659
|
-
if (mode != null)
|
|
1675
|
+
fs5.writeFileSync(target, content, "utf8");
|
|
1676
|
+
if (mode != null) fs5.chmodSync(target, mode);
|
|
1660
1677
|
}
|
|
1661
1678
|
function writeFileMaybe(target, content, mode) {
|
|
1662
1679
|
if (currentOptions.dryRun) {
|
|
@@ -1668,7 +1685,7 @@ function writeFileMaybe(target, content, mode) {
|
|
|
1668
1685
|
function ensureEnterpriseLicenseFilesReadable() {
|
|
1669
1686
|
if (exists2(LICENSE_ROOT)) {
|
|
1670
1687
|
try {
|
|
1671
|
-
|
|
1688
|
+
fs5.chmodSync(LICENSE_ROOT, LICENSE_DIR_MODE);
|
|
1672
1689
|
} catch {
|
|
1673
1690
|
throw new Error(
|
|
1674
1691
|
`Cannot update directory mode for ${LICENSE_ROOT}. Fix by running: chmod ${LICENSE_DIR_MODE.toString(8)} ${LICENSE_ROOT}`
|
|
@@ -1678,7 +1695,7 @@ function ensureEnterpriseLicenseFilesReadable() {
|
|
|
1678
1695
|
for (const filePath of [LICENSE_BUNDLE_FILE, LICENSE_CERT_FILE, LICENSE_KEY_FILE, LICENSE_ROOT_CA_FILE]) {
|
|
1679
1696
|
if (!exists2(filePath)) continue;
|
|
1680
1697
|
try {
|
|
1681
|
-
|
|
1698
|
+
fs5.chmodSync(filePath, LICENSE_FILE_MODE);
|
|
1682
1699
|
} catch {
|
|
1683
1700
|
throw new Error(
|
|
1684
1701
|
`Cannot update file mode for ${filePath}. Fix by running: chmod ${LICENSE_FILE_MODE.toString(8)} ${filePath}`
|
|
@@ -1687,7 +1704,7 @@ function ensureEnterpriseLicenseFilesReadable() {
|
|
|
1687
1704
|
}
|
|
1688
1705
|
}
|
|
1689
1706
|
function exists2(target) {
|
|
1690
|
-
return
|
|
1707
|
+
return fs5.existsSync(target);
|
|
1691
1708
|
}
|
|
1692
1709
|
function randomSecret(bytes = 24) {
|
|
1693
1710
|
return randomBytes2(bytes).toString("hex");
|
|
@@ -1697,7 +1714,7 @@ function generateMinikmsSessionSigningKey() {
|
|
|
1697
1714
|
return privateKey.export({ type: "pkcs8", format: "pem" }).toString();
|
|
1698
1715
|
}
|
|
1699
1716
|
function deterministicInstallFingerprint(rootDomain, stackName) {
|
|
1700
|
-
return `envsync-${
|
|
1717
|
+
return `envsync-${createHash3("sha256").update(`${rootDomain}:${stackName}`).digest("hex").slice(0, 32)}`;
|
|
1701
1718
|
}
|
|
1702
1719
|
function randomStrongPassword() {
|
|
1703
1720
|
return `EnvSync!${randomBytes2(8).toString("hex")}Aa1`;
|
|
@@ -1801,7 +1818,7 @@ function normalizeNetutilsForwards(raw, stackName, enabled) {
|
|
|
1801
1818
|
}
|
|
1802
1819
|
function loadWireguardState() {
|
|
1803
1820
|
if (!exists2(WIREGUARD_STATE_FILE)) return null;
|
|
1804
|
-
const raw = JSON.parse(
|
|
1821
|
+
const raw = JSON.parse(fs5.readFileSync(WIREGUARD_STATE_FILE, "utf8"));
|
|
1805
1822
|
return {
|
|
1806
1823
|
private_key: raw.private_key,
|
|
1807
1824
|
public_key: raw.public_key,
|
|
@@ -1985,7 +2002,7 @@ function publicHttpsUrl2(config, host, path5 = "") {
|
|
|
1985
2002
|
function getDeployCliVersion() {
|
|
1986
2003
|
try {
|
|
1987
2004
|
const packageJsonPath = new URL("../package.json", import.meta.url);
|
|
1988
|
-
const raw =
|
|
2005
|
+
const raw = fs5.readFileSync(packageJsonPath, "utf8");
|
|
1989
2006
|
return JSON.parse(raw).version ?? "0.0.0";
|
|
1990
2007
|
} catch {
|
|
1991
2008
|
return process.env.npm_package_version ?? "0.0.0";
|
|
@@ -2406,7 +2423,7 @@ function normalizeGeneratedState(raw) {
|
|
|
2406
2423
|
}
|
|
2407
2424
|
function readInternalState() {
|
|
2408
2425
|
if (!exists2(INTERNAL_CONFIG_JSON)) return null;
|
|
2409
|
-
const raw = JSON.parse(
|
|
2426
|
+
const raw = JSON.parse(fs5.readFileSync(INTERNAL_CONFIG_JSON, "utf8"));
|
|
2410
2427
|
return {
|
|
2411
2428
|
config: raw.config ? normalizeConfig(raw.config) : void 0,
|
|
2412
2429
|
generated: normalizeGeneratedState(raw.generated)
|
|
@@ -2416,7 +2433,7 @@ function loadConfig() {
|
|
|
2416
2433
|
if (!exists2(DEPLOY_YAML)) {
|
|
2417
2434
|
throw new Error(`Missing deploy config at ${DEPLOY_YAML}. Run setup first.`);
|
|
2418
2435
|
}
|
|
2419
|
-
const raw =
|
|
2436
|
+
const raw = fs5.readFileSync(DEPLOY_YAML, "utf8");
|
|
2420
2437
|
if (raw.trimStart().startsWith("{")) {
|
|
2421
2438
|
return normalizeConfig(JSON.parse(raw));
|
|
2422
2439
|
}
|
|
@@ -2424,7 +2441,7 @@ function loadConfig() {
|
|
|
2424
2441
|
}
|
|
2425
2442
|
function loadGeneratedEnv() {
|
|
2426
2443
|
if (!exists2(DEPLOY_ENV)) return {};
|
|
2427
|
-
return parseEnvFile(
|
|
2444
|
+
return parseEnvFile(fs5.readFileSync(DEPLOY_ENV, "utf8"));
|
|
2428
2445
|
}
|
|
2429
2446
|
function mergeGeneratedState(env, generated) {
|
|
2430
2447
|
const normalized = normalizeGeneratedState(generated);
|
|
@@ -2712,7 +2729,7 @@ function extractStaticBundle(kind, image, targetDir) {
|
|
|
2712
2729
|
logDryRun(`Would extract ${kind} bundle from ${image} into ${targetDir}`);
|
|
2713
2730
|
return;
|
|
2714
2731
|
}
|
|
2715
|
-
|
|
2732
|
+
fs5.rmSync(targetDir, { recursive: true, force: true });
|
|
2716
2733
|
ensureDir(targetDir);
|
|
2717
2734
|
const containerId = run("docker", ["create", image], { quiet: true }).trim();
|
|
2718
2735
|
try {
|
|
@@ -2740,23 +2757,23 @@ function writeFrontendRuntimeConfig(targetDir, runtimeConfig) {
|
|
|
2740
2757
|
function syncFrontendReleaseContents(sourceDir, targetDir) {
|
|
2741
2758
|
ensureDir(targetDir);
|
|
2742
2759
|
const deferredEntries = /* @__PURE__ */ new Set(["index.html", "runtime-config.js"]);
|
|
2743
|
-
const entries =
|
|
2760
|
+
const entries = fs5.readdirSync(sourceDir, { withFileTypes: true });
|
|
2744
2761
|
for (const entry of entries) {
|
|
2745
2762
|
if (deferredEntries.has(entry.name)) {
|
|
2746
2763
|
continue;
|
|
2747
2764
|
}
|
|
2748
|
-
|
|
2765
|
+
fs5.cpSync(path4.join(sourceDir, entry.name), path4.join(targetDir, entry.name), {
|
|
2749
2766
|
recursive: true,
|
|
2750
2767
|
force: true
|
|
2751
2768
|
});
|
|
2752
2769
|
}
|
|
2753
2770
|
const stagedRuntimeConfig = path4.join(sourceDir, "runtime-config.js");
|
|
2754
2771
|
if (exists2(stagedRuntimeConfig)) {
|
|
2755
|
-
|
|
2772
|
+
fs5.cpSync(stagedRuntimeConfig, path4.join(targetDir, "runtime-config.js"), { force: true });
|
|
2756
2773
|
}
|
|
2757
2774
|
const stagedIndex = path4.join(sourceDir, "index.html");
|
|
2758
2775
|
if (exists2(stagedIndex)) {
|
|
2759
|
-
|
|
2776
|
+
fs5.cpSync(stagedIndex, path4.join(targetDir, "index.html"), { force: true });
|
|
2760
2777
|
}
|
|
2761
2778
|
}
|
|
2762
2779
|
function activateFrontendRelease(kind, version, runtimeConfig) {
|
|
@@ -3149,7 +3166,7 @@ ${trimmed}`
|
|
|
3149
3166
|
}
|
|
3150
3167
|
function readRenderedRuntimeConfig(filePath) {
|
|
3151
3168
|
if (!exists2(filePath)) return null;
|
|
3152
|
-
const raw =
|
|
3169
|
+
const raw = fs5.readFileSync(filePath, "utf8").trim();
|
|
3153
3170
|
const prefix = "window.__ENVSYNC_RUNTIME_CONFIG__ = ";
|
|
3154
3171
|
if (!raw.startsWith(prefix) || !raw.endsWith(";")) return null;
|
|
3155
3172
|
try {
|
|
@@ -3446,7 +3463,7 @@ function removeTarget(target) {
|
|
|
3446
3463
|
logDryRun(`Would remove ${target}`);
|
|
3447
3464
|
return;
|
|
3448
3465
|
}
|
|
3449
|
-
|
|
3466
|
+
fs5.rmSync(target, { recursive: true, force: true });
|
|
3450
3467
|
logInfo(`Removed ${target}`);
|
|
3451
3468
|
}
|
|
3452
3469
|
async function cmdRemove() {
|
|
@@ -4193,7 +4210,7 @@ async function cmdDeploy() {
|
|
|
4193
4210
|
sleepSeconds(3);
|
|
4194
4211
|
promoted = true;
|
|
4195
4212
|
if (snapshotPath && !currentOptions.dryRun) {
|
|
4196
|
-
|
|
4213
|
+
fs5.rmSync(snapshotPath, { force: true });
|
|
4197
4214
|
}
|
|
4198
4215
|
} catch (error) {
|
|
4199
4216
|
const rollbackTarget = preUpgradeHead ?? "zero";
|
|
@@ -4227,7 +4244,7 @@ ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
|
|
|
4227
4244
|
deployRenderedStack(config, "rollback recovery");
|
|
4228
4245
|
waitForApiSlotHealthy(config, recoveredState.deployment.active_slot);
|
|
4229
4246
|
if (snapshotPath && !config.upgrade.keep_failed_upgrade_db_snapshot && !currentOptions.dryRun) {
|
|
4230
|
-
|
|
4247
|
+
fs5.rmSync(snapshotPath, { force: true });
|
|
4231
4248
|
}
|
|
4232
4249
|
throw new Error(
|
|
4233
4250
|
usedSnapshotRestore ? `Deploy failed after DB migration. Previous release was restored using DB snapshot recovery. ${error instanceof Error ? error.message : String(error)}` : `Deploy failed after DB migration. Previous release was restored. ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -4348,7 +4365,7 @@ async function cmdHealth(asJson) {
|
|
|
4348
4365
|
const hosts = domainMap2(config.domain.root_domain);
|
|
4349
4366
|
const services = listStackServices(config);
|
|
4350
4367
|
const stackName = config.services.stack_name;
|
|
4351
|
-
const traefikDynamic = exists2(TRAEFIK_DYNAMIC_FILE) ?
|
|
4368
|
+
const traefikDynamic = exists2(TRAEFIK_DYNAMIC_FILE) ? fs5.readFileSync(TRAEFIK_DYNAMIC_FILE, "utf8") : "";
|
|
4352
4369
|
const webRuntimeConfig = readRenderedRuntimeConfig(path4.join(RELEASES_ROOT, "web", "current", "runtime-config.js"));
|
|
4353
4370
|
const landingRuntimeConfig = readRenderedRuntimeConfig(path4.join(RELEASES_ROOT, "landing", "current", "runtime-config.js"));
|
|
4354
4371
|
const clickstackSearchState = getClickstackSearchState(config);
|
|
@@ -4540,8 +4557,8 @@ async function cmdUpgradeDeps() {
|
|
|
4540
4557
|
await cmdDeploy();
|
|
4541
4558
|
}
|
|
4542
4559
|
function sha256File(filePath) {
|
|
4543
|
-
const hash =
|
|
4544
|
-
hash.update(
|
|
4560
|
+
const hash = createHash3("sha256");
|
|
4561
|
+
hash.update(fs5.readFileSync(filePath));
|
|
4545
4562
|
return hash.digest("hex");
|
|
4546
4563
|
}
|
|
4547
4564
|
function parseEnterpriseLicenseBundle(raw) {
|
|
@@ -4570,7 +4587,7 @@ function parseEnterpriseLicenseBundle(raw) {
|
|
|
4570
4587
|
return candidate;
|
|
4571
4588
|
}
|
|
4572
4589
|
function readEnterpriseLicenseBundle(bundlePath) {
|
|
4573
|
-
return parseEnterpriseLicenseBundle(JSON.parse(
|
|
4590
|
+
return parseEnterpriseLicenseBundle(JSON.parse(fs5.readFileSync(bundlePath, "utf8")));
|
|
4574
4591
|
}
|
|
4575
4592
|
function normalizeLicenseServerUrl(serverUrl, route) {
|
|
4576
4593
|
const trimmed = serverUrl.replace(/\/+$/, "");
|
|
@@ -4832,19 +4849,19 @@ async function cmdBackup() {
|
|
|
4832
4849
|
logWarn("Backup will temporarily stop the EnvSync stack to capture consistent volume data.");
|
|
4833
4850
|
stopManagedRuntime(config, "Stopping existing EnvSync services for consistent backup");
|
|
4834
4851
|
}
|
|
4835
|
-
writeFile(path4.join(staged, "deploy.env"),
|
|
4836
|
-
writeFile(path4.join(staged, "deploy.yaml"),
|
|
4837
|
-
writeFile(path4.join(staged, "config.json"),
|
|
4838
|
-
writeFile(path4.join(staged, "versions.lock.json"),
|
|
4839
|
-
writeFile(path4.join(staged, "docker-stack.bootstrap.base.yaml"),
|
|
4840
|
-
writeFile(path4.join(staged, "docker-stack.bootstrap.yaml"),
|
|
4841
|
-
writeFile(path4.join(staged, "docker-stack.yaml"),
|
|
4842
|
-
writeFile(path4.join(staged, "traefik-dynamic.yaml"),
|
|
4843
|
-
writeFile(path4.join(staged, "keycloak-realm.envsync.json"),
|
|
4844
|
-
writeFile(path4.join(staged, "otel-agent.yaml"),
|
|
4845
|
-
writeFile(path4.join(staged, "clickhouse-listen.xml"),
|
|
4852
|
+
writeFile(path4.join(staged, "deploy.env"), fs5.readFileSync(DEPLOY_ENV, "utf8"));
|
|
4853
|
+
writeFile(path4.join(staged, "deploy.yaml"), fs5.readFileSync(DEPLOY_YAML, "utf8"));
|
|
4854
|
+
writeFile(path4.join(staged, "config.json"), fs5.readFileSync(INTERNAL_CONFIG_JSON, "utf8"));
|
|
4855
|
+
writeFile(path4.join(staged, "versions.lock.json"), fs5.readFileSync(VERSIONS_LOCK, "utf8"));
|
|
4856
|
+
writeFile(path4.join(staged, "docker-stack.bootstrap.base.yaml"), fs5.readFileSync(BOOTSTRAP_BASE_STACK_FILE, "utf8"));
|
|
4857
|
+
writeFile(path4.join(staged, "docker-stack.bootstrap.yaml"), fs5.readFileSync(BOOTSTRAP_STACK_FILE, "utf8"));
|
|
4858
|
+
writeFile(path4.join(staged, "docker-stack.yaml"), fs5.readFileSync(STACK_FILE, "utf8"));
|
|
4859
|
+
writeFile(path4.join(staged, "traefik-dynamic.yaml"), fs5.readFileSync(TRAEFIK_DYNAMIC_FILE, "utf8"));
|
|
4860
|
+
writeFile(path4.join(staged, "keycloak-realm.envsync.json"), fs5.readFileSync(KEYCLOAK_REALM_FILE, "utf8"));
|
|
4861
|
+
writeFile(path4.join(staged, "otel-agent.yaml"), fs5.readFileSync(OTEL_AGENT_CONF, "utf8"));
|
|
4862
|
+
writeFile(path4.join(staged, "clickhouse-listen.xml"), fs5.readFileSync(CLICKSTACK_CLICKHOUSE_CONF, "utf8"));
|
|
4846
4863
|
if (!isOssConfig2(config) && exists2(LICENSE_ROOT)) {
|
|
4847
|
-
|
|
4864
|
+
fs5.cpSync(LICENSE_ROOT, path4.join(staged, "license"), { recursive: true });
|
|
4848
4865
|
}
|
|
4849
4866
|
const volumesDir = path4.join(staged, "volumes");
|
|
4850
4867
|
for (const volume of STACK_VOLUMES) {
|
|
@@ -4911,21 +4928,21 @@ async function cmdRestore(archivePath, autoDeploy = false) {
|
|
|
4911
4928
|
}
|
|
4912
4929
|
ensureDir(restoreRoot);
|
|
4913
4930
|
run("bash", ["-lc", `tar -xzf ${JSON.stringify(archivePath)} -C ${JSON.stringify(restoreRoot)}`]);
|
|
4914
|
-
writeFile(DEPLOY_ENV,
|
|
4915
|
-
writeFile(DEPLOY_YAML,
|
|
4916
|
-
writeFile(INTERNAL_CONFIG_JSON,
|
|
4917
|
-
writeFile(VERSIONS_LOCK,
|
|
4918
|
-
writeFile(BOOTSTRAP_BASE_STACK_FILE,
|
|
4919
|
-
writeFile(BOOTSTRAP_STACK_FILE,
|
|
4920
|
-
writeFile(STACK_FILE,
|
|
4921
|
-
writeFile(TRAEFIK_DYNAMIC_FILE,
|
|
4922
|
-
writeFile(KEYCLOAK_REALM_FILE,
|
|
4923
|
-
writeFile(OTEL_AGENT_CONF,
|
|
4924
|
-
writeFile(CLICKSTACK_CLICKHOUSE_CONF,
|
|
4931
|
+
writeFile(DEPLOY_ENV, fs5.readFileSync(path4.join(restoreRoot, "deploy.env"), "utf8"), 384);
|
|
4932
|
+
writeFile(DEPLOY_YAML, fs5.readFileSync(path4.join(restoreRoot, "deploy.yaml"), "utf8"));
|
|
4933
|
+
writeFile(INTERNAL_CONFIG_JSON, fs5.readFileSync(path4.join(restoreRoot, "config.json"), "utf8"));
|
|
4934
|
+
writeFile(VERSIONS_LOCK, fs5.readFileSync(path4.join(restoreRoot, "versions.lock.json"), "utf8"));
|
|
4935
|
+
writeFile(BOOTSTRAP_BASE_STACK_FILE, fs5.readFileSync(path4.join(restoreRoot, "docker-stack.bootstrap.base.yaml"), "utf8"));
|
|
4936
|
+
writeFile(BOOTSTRAP_STACK_FILE, fs5.readFileSync(path4.join(restoreRoot, "docker-stack.bootstrap.yaml"), "utf8"));
|
|
4937
|
+
writeFile(STACK_FILE, fs5.readFileSync(path4.join(restoreRoot, "docker-stack.yaml"), "utf8"));
|
|
4938
|
+
writeFile(TRAEFIK_DYNAMIC_FILE, fs5.readFileSync(path4.join(restoreRoot, "traefik-dynamic.yaml"), "utf8"));
|
|
4939
|
+
writeFile(KEYCLOAK_REALM_FILE, fs5.readFileSync(path4.join(restoreRoot, "keycloak-realm.envsync.json"), "utf8"));
|
|
4940
|
+
writeFile(OTEL_AGENT_CONF, fs5.readFileSync(path4.join(restoreRoot, "otel-agent.yaml"), "utf8"));
|
|
4941
|
+
writeFile(CLICKSTACK_CLICKHOUSE_CONF, fs5.readFileSync(path4.join(restoreRoot, "clickhouse-listen.xml"), "utf8"));
|
|
4925
4942
|
const restoredLicenseRoot = path4.join(restoreRoot, "license");
|
|
4926
4943
|
if (exists2(restoredLicenseRoot)) {
|
|
4927
|
-
|
|
4928
|
-
|
|
4944
|
+
fs5.rmSync(LICENSE_ROOT, { recursive: true, force: true });
|
|
4945
|
+
fs5.cpSync(restoredLicenseRoot, LICENSE_ROOT, { recursive: true });
|
|
4929
4946
|
ensureEnterpriseLicenseFilesReadable();
|
|
4930
4947
|
}
|
|
4931
4948
|
const config = loadConfig();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envsync-cloud/deploy",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.2",
|
|
4
4
|
"description": "OSS CLI for self-hosted EnvSync deployment on Docker Swarm",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"zod": "^3.25.76"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@envsync-cloud/deploy-core": "0.21.
|
|
49
|
+
"@envsync-cloud/deploy-core": "0.21.2",
|
|
50
50
|
"tsup": "^8.5.0",
|
|
51
51
|
"typescript": "^5.9.2"
|
|
52
52
|
},
|