@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.
Files changed (3) hide show
  1. package/dist/cli.js +88 -71
  2. package/dist/index.js +88 -71
  3. 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 createHash2, generateKeyPairSync, randomBytes as randomBytes2, randomInt } from "crypto";
4
+ import { createHash as createHash3, generateKeyPairSync, randomBytes as randomBytes2, randomInt } from "crypto";
5
5
  import { spawnSync } from "child_process";
6
- import fs4 from "fs";
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
- ...landingEnabled ? publicHttpsOriginVariants(config, hosts.landing) : [],
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 fs2 from "fs";
1252
+ import fs3 from "fs";
1236
1253
  import path2 from "path";
1237
1254
  function exists(target) {
1238
- return fs2.existsSync(target);
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 = fs2.readdirSync(targetDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path2.join(targetDir, entry.name));
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 fs2.readdirSync(nestedRoot)) {
1252
- fs2.cpSync(path2.join(nestedRoot, entry), path2.join(targetDir, entry), { recursive: true });
1268
+ for (const entry of fs3.readdirSync(nestedRoot)) {
1269
+ fs3.cpSync(path2.join(nestedRoot, entry), path2.join(targetDir, entry), { recursive: true });
1253
1270
  }
1254
- fs2.rmSync(nestedRoot, { recursive: true, force: true });
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) ? fs2.readdirSync(targetDir).slice(0, 20).join(", ") : "";
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 fs3 from "fs";
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
- fs3.mkdirSync(dir2, { recursive: true, mode: 448 });
1279
- fs3.writeFileSync(tokenFile, `${existingToken.trim()}
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 (fs3.existsSync(tokenFile)) {
1284
- const fromFile = fs3.readFileSync(tokenFile, "utf8").trim();
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
- fs3.mkdirSync(dir, { recursive: true, mode: 448 });
1292
- fs3.writeFileSync(tokenFile, `${token}
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 (!fs3.existsSync(tokenFile)) {
1314
+ if (!fs4.existsSync(tokenFile)) {
1298
1315
  return null;
1299
1316
  }
1300
- const token = fs3.readFileSync(tokenFile, "utf8").trim();
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 createHash("sha256").update(token).digest("hex").slice(0, 12);
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
- fs4.mkdirSync(dir, { recursive: true });
1808
+ fs5.mkdirSync(dir, { recursive: true });
1792
1809
  }
1793
1810
  function writeFile(target, content, mode) {
1794
1811
  ensureDir(path4.dirname(target));
1795
- fs4.writeFileSync(target, content, "utf8");
1796
- if (mode != null) fs4.chmodSync(target, mode);
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
- fs4.chmodSync(LICENSE_ROOT, LICENSE_DIR_MODE);
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
- fs4.chmodSync(filePath, LICENSE_FILE_MODE);
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 fs4.existsSync(target);
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-${createHash2("sha256").update(`${rootDomain}:${stackName}`).digest("hex").slice(0, 32)}`;
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(fs4.readFileSync(WIREGUARD_STATE_FILE, "utf8"));
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 = fs4.readFileSync(packageJsonPath, "utf8");
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(fs4.readFileSync(INTERNAL_CONFIG_JSON, "utf8"));
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 = fs4.readFileSync(DEPLOY_YAML, "utf8");
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(fs4.readFileSync(DEPLOY_ENV, "utf8"));
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
- fs4.rmSync(targetDir, { recursive: true, force: true });
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 = fs4.readdirSync(sourceDir, { withFileTypes: true });
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
- fs4.cpSync(path4.join(sourceDir, entry.name), path4.join(targetDir, entry.name), {
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
- fs4.cpSync(stagedRuntimeConfig, path4.join(targetDir, "runtime-config.js"), { force: true });
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
- fs4.cpSync(stagedIndex, path4.join(targetDir, "index.html"), { force: true });
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 = fs4.readFileSync(filePath, "utf8").trim();
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
- fs4.rmSync(target, { recursive: true, force: true });
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
- fs4.rmSync(snapshotPath, { force: true });
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
- fs4.rmSync(snapshotPath, { force: true });
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) ? fs4.readFileSync(TRAEFIK_DYNAMIC_FILE, "utf8") : "";
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 = createHash2("sha256");
4686
- hash.update(fs4.readFileSync(filePath));
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(fs4.readFileSync(bundlePath, "utf8")));
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"), fs4.readFileSync(DEPLOY_ENV, "utf8"));
4978
- writeFile(path4.join(staged, "deploy.yaml"), fs4.readFileSync(DEPLOY_YAML, "utf8"));
4979
- writeFile(path4.join(staged, "config.json"), fs4.readFileSync(INTERNAL_CONFIG_JSON, "utf8"));
4980
- writeFile(path4.join(staged, "versions.lock.json"), fs4.readFileSync(VERSIONS_LOCK, "utf8"));
4981
- writeFile(path4.join(staged, "docker-stack.bootstrap.base.yaml"), fs4.readFileSync(BOOTSTRAP_BASE_STACK_FILE, "utf8"));
4982
- writeFile(path4.join(staged, "docker-stack.bootstrap.yaml"), fs4.readFileSync(BOOTSTRAP_STACK_FILE, "utf8"));
4983
- writeFile(path4.join(staged, "docker-stack.yaml"), fs4.readFileSync(STACK_FILE, "utf8"));
4984
- writeFile(path4.join(staged, "traefik-dynamic.yaml"), fs4.readFileSync(TRAEFIK_DYNAMIC_FILE, "utf8"));
4985
- writeFile(path4.join(staged, "keycloak-realm.envsync.json"), fs4.readFileSync(KEYCLOAK_REALM_FILE, "utf8"));
4986
- writeFile(path4.join(staged, "otel-agent.yaml"), fs4.readFileSync(OTEL_AGENT_CONF, "utf8"));
4987
- writeFile(path4.join(staged, "clickhouse-listen.xml"), fs4.readFileSync(CLICKSTACK_CLICKHOUSE_CONF, "utf8"));
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
- fs4.cpSync(LICENSE_ROOT, path4.join(staged, "license"), { recursive: true });
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, fs4.readFileSync(path4.join(restoreRoot, "deploy.env"), "utf8"), 384);
5057
- writeFile(DEPLOY_YAML, fs4.readFileSync(path4.join(restoreRoot, "deploy.yaml"), "utf8"));
5058
- writeFile(INTERNAL_CONFIG_JSON, fs4.readFileSync(path4.join(restoreRoot, "config.json"), "utf8"));
5059
- writeFile(VERSIONS_LOCK, fs4.readFileSync(path4.join(restoreRoot, "versions.lock.json"), "utf8"));
5060
- writeFile(BOOTSTRAP_BASE_STACK_FILE, fs4.readFileSync(path4.join(restoreRoot, "docker-stack.bootstrap.base.yaml"), "utf8"));
5061
- writeFile(BOOTSTRAP_STACK_FILE, fs4.readFileSync(path4.join(restoreRoot, "docker-stack.bootstrap.yaml"), "utf8"));
5062
- writeFile(STACK_FILE, fs4.readFileSync(path4.join(restoreRoot, "docker-stack.yaml"), "utf8"));
5063
- writeFile(TRAEFIK_DYNAMIC_FILE, fs4.readFileSync(path4.join(restoreRoot, "traefik-dynamic.yaml"), "utf8"));
5064
- writeFile(KEYCLOAK_REALM_FILE, fs4.readFileSync(path4.join(restoreRoot, "keycloak-realm.envsync.json"), "utf8"));
5065
- writeFile(OTEL_AGENT_CONF, fs4.readFileSync(path4.join(restoreRoot, "otel-agent.yaml"), "utf8"));
5066
- writeFile(CLICKSTACK_CLICKHOUSE_CONF, fs4.readFileSync(path4.join(restoreRoot, "clickhouse-listen.xml"), "utf8"));
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
- fs4.rmSync(LICENSE_ROOT, { recursive: true, force: true });
5070
- fs4.cpSync(restoredLicenseRoot, LICENSE_ROOT, { recursive: true });
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
- ...landingEnabled ? publicHttpsOriginVariants(config, hosts.landing) : [],
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 fs2 from "fs";
1259
+ import fs3 from "fs";
1243
1260
  import path2 from "path";
1244
1261
  function exists(target) {
1245
- return fs2.existsSync(target);
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 = fs2.readdirSync(targetDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path2.join(targetDir, entry.name));
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 fs2.readdirSync(nestedRoot)) {
1259
- fs2.cpSync(path2.join(nestedRoot, entry), path2.join(targetDir, entry), { recursive: true });
1275
+ for (const entry of fs3.readdirSync(nestedRoot)) {
1276
+ fs3.cpSync(path2.join(nestedRoot, entry), path2.join(targetDir, entry), { recursive: true });
1260
1277
  }
1261
- fs2.rmSync(nestedRoot, { recursive: true, force: true });
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) ? fs2.readdirSync(targetDir).slice(0, 20).join(", ") : "";
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 fs3 from "fs";
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
- fs3.mkdirSync(dir2, { recursive: true, mode: 448 });
1290
- fs3.writeFileSync(tokenFile, `${existingToken.trim()}
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 (fs3.existsSync(tokenFile)) {
1295
- const fromFile = fs3.readFileSync(tokenFile, "utf8").trim();
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
- fs3.mkdirSync(dir, { recursive: true, mode: 448 });
1303
- fs3.writeFileSync(tokenFile, `${token}
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 (!fs3.existsSync(tokenFile)) {
1325
+ if (!fs4.existsSync(tokenFile)) {
1309
1326
  return null;
1310
1327
  }
1311
- const token = fs3.readFileSync(tokenFile, "utf8").trim();
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 createHash("sha256").update(token).digest("hex").slice(0, 12);
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 createHash2, generateKeyPairSync, randomBytes as randomBytes2, randomInt } from "crypto";
1475
+ import { createHash as createHash3, generateKeyPairSync, randomBytes as randomBytes2, randomInt } from "crypto";
1459
1476
  import { spawnSync } from "child_process";
1460
- import fs4 from "fs";
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
- fs4.mkdirSync(dir, { recursive: true });
1671
+ fs5.mkdirSync(dir, { recursive: true });
1655
1672
  }
1656
1673
  function writeFile(target, content, mode) {
1657
1674
  ensureDir(path4.dirname(target));
1658
- fs4.writeFileSync(target, content, "utf8");
1659
- if (mode != null) fs4.chmodSync(target, mode);
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
- fs4.chmodSync(LICENSE_ROOT, LICENSE_DIR_MODE);
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
- fs4.chmodSync(filePath, LICENSE_FILE_MODE);
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 fs4.existsSync(target);
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-${createHash2("sha256").update(`${rootDomain}:${stackName}`).digest("hex").slice(0, 32)}`;
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(fs4.readFileSync(WIREGUARD_STATE_FILE, "utf8"));
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 = fs4.readFileSync(packageJsonPath, "utf8");
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(fs4.readFileSync(INTERNAL_CONFIG_JSON, "utf8"));
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 = fs4.readFileSync(DEPLOY_YAML, "utf8");
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(fs4.readFileSync(DEPLOY_ENV, "utf8"));
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
- fs4.rmSync(targetDir, { recursive: true, force: true });
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 = fs4.readdirSync(sourceDir, { withFileTypes: true });
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
- fs4.cpSync(path4.join(sourceDir, entry.name), path4.join(targetDir, entry.name), {
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
- fs4.cpSync(stagedRuntimeConfig, path4.join(targetDir, "runtime-config.js"), { force: true });
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
- fs4.cpSync(stagedIndex, path4.join(targetDir, "index.html"), { force: true });
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 = fs4.readFileSync(filePath, "utf8").trim();
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
- fs4.rmSync(target, { recursive: true, force: true });
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
- fs4.rmSync(snapshotPath, { force: true });
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
- fs4.rmSync(snapshotPath, { force: true });
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) ? fs4.readFileSync(TRAEFIK_DYNAMIC_FILE, "utf8") : "";
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 = createHash2("sha256");
4544
- hash.update(fs4.readFileSync(filePath));
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(fs4.readFileSync(bundlePath, "utf8")));
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"), fs4.readFileSync(DEPLOY_ENV, "utf8"));
4836
- writeFile(path4.join(staged, "deploy.yaml"), fs4.readFileSync(DEPLOY_YAML, "utf8"));
4837
- writeFile(path4.join(staged, "config.json"), fs4.readFileSync(INTERNAL_CONFIG_JSON, "utf8"));
4838
- writeFile(path4.join(staged, "versions.lock.json"), fs4.readFileSync(VERSIONS_LOCK, "utf8"));
4839
- writeFile(path4.join(staged, "docker-stack.bootstrap.base.yaml"), fs4.readFileSync(BOOTSTRAP_BASE_STACK_FILE, "utf8"));
4840
- writeFile(path4.join(staged, "docker-stack.bootstrap.yaml"), fs4.readFileSync(BOOTSTRAP_STACK_FILE, "utf8"));
4841
- writeFile(path4.join(staged, "docker-stack.yaml"), fs4.readFileSync(STACK_FILE, "utf8"));
4842
- writeFile(path4.join(staged, "traefik-dynamic.yaml"), fs4.readFileSync(TRAEFIK_DYNAMIC_FILE, "utf8"));
4843
- writeFile(path4.join(staged, "keycloak-realm.envsync.json"), fs4.readFileSync(KEYCLOAK_REALM_FILE, "utf8"));
4844
- writeFile(path4.join(staged, "otel-agent.yaml"), fs4.readFileSync(OTEL_AGENT_CONF, "utf8"));
4845
- writeFile(path4.join(staged, "clickhouse-listen.xml"), fs4.readFileSync(CLICKSTACK_CLICKHOUSE_CONF, "utf8"));
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
- fs4.cpSync(LICENSE_ROOT, path4.join(staged, "license"), { recursive: true });
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, fs4.readFileSync(path4.join(restoreRoot, "deploy.env"), "utf8"), 384);
4915
- writeFile(DEPLOY_YAML, fs4.readFileSync(path4.join(restoreRoot, "deploy.yaml"), "utf8"));
4916
- writeFile(INTERNAL_CONFIG_JSON, fs4.readFileSync(path4.join(restoreRoot, "config.json"), "utf8"));
4917
- writeFile(VERSIONS_LOCK, fs4.readFileSync(path4.join(restoreRoot, "versions.lock.json"), "utf8"));
4918
- writeFile(BOOTSTRAP_BASE_STACK_FILE, fs4.readFileSync(path4.join(restoreRoot, "docker-stack.bootstrap.base.yaml"), "utf8"));
4919
- writeFile(BOOTSTRAP_STACK_FILE, fs4.readFileSync(path4.join(restoreRoot, "docker-stack.bootstrap.yaml"), "utf8"));
4920
- writeFile(STACK_FILE, fs4.readFileSync(path4.join(restoreRoot, "docker-stack.yaml"), "utf8"));
4921
- writeFile(TRAEFIK_DYNAMIC_FILE, fs4.readFileSync(path4.join(restoreRoot, "traefik-dynamic.yaml"), "utf8"));
4922
- writeFile(KEYCLOAK_REALM_FILE, fs4.readFileSync(path4.join(restoreRoot, "keycloak-realm.envsync.json"), "utf8"));
4923
- writeFile(OTEL_AGENT_CONF, fs4.readFileSync(path4.join(restoreRoot, "otel-agent.yaml"), "utf8"));
4924
- writeFile(CLICKSTACK_CLICKHOUSE_CONF, fs4.readFileSync(path4.join(restoreRoot, "clickhouse-listen.xml"), "utf8"));
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
- fs4.rmSync(LICENSE_ROOT, { recursive: true, force: true });
4928
- fs4.cpSync(restoredLicenseRoot, LICENSE_ROOT, { recursive: true });
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.0",
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.0",
49
+ "@envsync-cloud/deploy-core": "0.21.2",
50
50
  "tsup": "^8.5.0",
51
51
  "typescript": "^5.9.2"
52
52
  },