cyberia 3.2.12 → 3.2.22

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 (55) hide show
  1. package/.github/workflows/engine-cyberia.cd.yml +1 -0
  2. package/.github/workflows/engine-cyberia.ci.yml +14 -2
  3. package/.github/workflows/ghpkg.ci.yml +1 -0
  4. package/.github/workflows/npmpkg.ci.yml +9 -5
  5. package/CHANGELOG.md +151 -1
  6. package/CLI-HELP.md +975 -1130
  7. package/bin/build.js +97 -136
  8. package/bin/build.template.js +25 -179
  9. package/bin/cyberia.js +11 -6
  10. package/bin/deploy.js +4 -1
  11. package/bin/index.js +11 -6
  12. package/conf.js +1 -0
  13. package/deployment.yaml +74 -2
  14. package/hardhat/package-lock.json +4 -4
  15. package/hardhat/package.json +1 -1
  16. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
  17. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  18. package/manifests/deployment/dd-cyberia-development/deployment.yaml +74 -2
  19. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  20. package/package.json +7 -7
  21. package/scripts/link-local-underpost-cli.sh +6 -0
  22. package/scripts/test-monitor.sh +250 -0
  23. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +7 -0
  24. package/src/cli/deploy.js +200 -282
  25. package/src/cli/env.js +1 -4
  26. package/src/cli/image.js +58 -4
  27. package/src/cli/index.js +47 -0
  28. package/src/cli/monitor.js +387 -6
  29. package/src/cli/release.js +26 -11
  30. package/src/cli/repository.js +101 -7
  31. package/src/cli/run.js +159 -73
  32. package/src/client/components/core/PanelForm.js +44 -44
  33. package/src/client/components/cyberia/SharedDefaultsCyberia.js +1 -1
  34. package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +55 -1
  35. package/src/client/public/cyberia-docs/ARCHITECTURE.md +272 -50
  36. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +20 -11
  37. package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +23 -1
  38. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  39. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  40. package/src/db/mongo/MongooseDB.js +2 -1
  41. package/src/index.js +1 -1
  42. package/src/runtime/cyberia-client/Dockerfile +4 -22
  43. package/src/runtime/cyberia-client/Dockerfile.dev +3 -18
  44. package/src/runtime/cyberia-server/Dockerfile +3 -23
  45. package/src/runtime/cyberia-server/Dockerfile.dev +3 -27
  46. package/src/runtime/wp/Dockerfile +3 -3
  47. package/src/server/catalog-underpost.js +61 -0
  48. package/src/server/catalog.js +77 -0
  49. package/src/server/conf.js +414 -56
  50. package/src/server/ipfs-client.js +5 -3
  51. package/src/server/runtime-status.js +235 -0
  52. package/src/server/start.js +32 -11
  53. package/test/deploy-monitor.test.js +251 -0
  54. package/manifests/deployment/dd-test-development/deployment.yaml +0 -256
  55. package/manifests/deployment/dd-test-development/proxy.yaml +0 -102
@@ -264,18 +264,29 @@ const ISOLATED_ENV = 'env -i HOME="$HOME" PATH="$PATH" USER="$USER" LOGNAME="$LO
264
264
  *
265
265
  * @returns {boolean} true when the template started cleanly, false otherwise.
266
266
  */
267
- async function buildAndTestTemplate() {
267
+ async function buildAndTestTemplate(opts = {}) {
268
268
  killDevServers();
269
269
  Underpost.repo.clean({ paths: ['/home/dd/engine', '/home/dd/engine/engine-private '] });
270
270
  shellExec(`node bin pull . ${process.env.GITHUB_USERNAME}/engine`);
271
- shellExec(`npm run update:template`);
271
+ fs.removeSync(TEMPLATE_PATH);
272
+ shellExec(`npm run build:template`);
272
273
  shellExec(`node bin run shared-dir ${TEMPLATE_PATH}`);
273
274
 
275
+ const upsertEnvVar = (content, key, value) => {
276
+ const re = new RegExp(`^(${key}=).*`, 'm');
277
+ if (re.test(content)) return content.replace(re, `$1${value}`);
278
+ return `${content.trimEnd()}\n${key}=${value}\n`;
279
+ };
280
+
274
281
  const dhcpHostIp = Dns.getLocalIPv4Address();
275
282
  logger.info(`DHCP host IP for template test: ${dhcpHostIp}`);
276
283
  let envContent = fs.readFileSync(`${TEMPLATE_PATH}/.env.example`, 'utf8');
277
284
  if (dhcpHostIp) envContent = envContent.replace(/127\.0\.0\.1/g, dhcpHostIp);
278
- envContent = envContent.replace(/^ENABLE_FILE_LOGS=.*/m, 'ENABLE_FILE_LOGS=true');
285
+ envContent = upsertEnvVar(envContent, 'ENABLE_FILE_LOGS', 'true');
286
+ if (opts.mongoHost) envContent = upsertEnvVar(envContent, 'DB_HOST', opts.mongoHost);
287
+ if (opts.mongoUser) envContent = upsertEnvVar(envContent, 'DB_USER', opts.mongoUser);
288
+ if (opts.mongoPassword) envContent = upsertEnvVar(envContent, 'DB_PASSWORD', opts.mongoPassword);
289
+ if (opts.valkeyHost) envContent = upsertEnvVar(envContent, 'VALKEY_HOST', opts.valkeyHost);
279
290
  // fs.writeFileSync(`${TEMPLATE_PATH}/.env`, envContent, 'utf8');
280
291
  fs.writeFileSync(`${TEMPLATE_PATH}/.env.example`, envContent, 'utf8');
281
292
  shellExec(`cd ${TEMPLATE_PATH} && npm install`);
@@ -337,7 +348,12 @@ class UnderpostRelease {
337
348
  *
338
349
  * @method build
339
350
  * @param {string} [newVersion] - The new version string to set. Defaults to current version if not provided.
340
- * @param {{dryRun?: boolean}} [options] - Commander options. `--dry-run` previews changes.
351
+ * @param {{dryRun?: boolean, mongoHost?: string, mongoUser?: string, mongoPassword?: string, valkeyHost?: string}} [options] - Commander options.
352
+ * `--dry-run` previews changes without writing files.
353
+ * `--mongo-host` overrides `DB_HOST` in the template `.env.example` smoke test.
354
+ * `--mongo-user` overrides `DB_USER` in the template `.env.example` smoke test.
355
+ * `--mongo-password` overrides `DB_PASSWORD` in the template `.env.example` smoke test.
356
+ * `--valkey-host` overrides `VALKEY_HOST` in the template `.env.example` smoke test.
341
357
  * @memberof UnderpostRelease
342
358
  */
343
359
  async build(newVersion, options = {}) {
@@ -352,7 +368,7 @@ class UnderpostRelease {
352
368
  logger.info(`Release build — bumping ${version} → ${newVersion}${dryRun ? ' (dry-run)' : ''}`);
353
369
 
354
370
  if (!dryRun) {
355
- const templateOk = await buildAndTestTemplate();
371
+ const templateOk = await buildAndTestTemplate(options);
356
372
  if (!templateOk) return;
357
373
  }
358
374
 
@@ -390,9 +406,8 @@ class UnderpostRelease {
390
406
  shellExec(`node bin/deploy cli-docs ${version} ${newVersion}`);
391
407
  shellExec(`node bin/deploy update-dependencies`);
392
408
  shellExec(`node bin/build dd`);
393
- shellExec(`node bin deploy --build-manifest --sync --info-router --replicas 1 dd production`);
394
- shellExec(`node bin deploy --build-manifest --sync --info-router --replicas 1 dd development`);
395
- shellExec(`node bin/deploy build-default-confs`);
409
+ shellExec(`node bin run build-cluster-deployment-manifests`);
410
+ shellExec(`node bin new --default-conf --conf-workflow-id template`);
396
411
  shellExec(`sudo rm -rf ./engine-private/conf/dd-default`);
397
412
  shellExec(`node bin new --deploy-id dd-default`);
398
413
  console.log(fs.existsSync(`./engine-private/conf/dd-default`));
@@ -460,7 +475,7 @@ class UnderpostRelease {
460
475
  * Runs the pwa-microservices-template update and push flow locally.
461
476
  *
462
477
  * Always removes and re-clones pwa-microservices-template, then:
463
- * 1. Runs update:template (node bin/build.template) to sync engine sources.
478
+ * 1. Runs build:template (node bin/build.template) to sync engine sources.
464
479
  * 2. Installs dependencies and builds the template.
465
480
  * 3. Commits and pushes to the pwa-microservices-template remote repository.
466
481
  *
@@ -488,7 +503,7 @@ class UnderpostRelease {
488
503
  shellExec(`sudo rm -rf /home/dd/pwa-microservices-template`);
489
504
  shellExec(`node engine/bin clone ${githubOrg}/pwa-microservices-template`);
490
505
  shellCd('/home/dd/engine');
491
- shellExec(`npm run update:template`);
506
+ shellExec(`npm run build:template`);
492
507
  shellExec(`cd ../pwa-microservices-template && npm install && npm run build`);
493
508
  shellCd('/home/dd/pwa-microservices-template');
494
509
  shellExec(`git add .`);
@@ -520,7 +535,7 @@ class UnderpostRelease {
520
535
  shellExec(
521
536
  `node bin secret underpost --create-from-file /home/dd/engine/engine-private/conf/dd-cron/.env.production`,
522
537
  );
523
- shellExec(`node bin/build dd conf`);
538
+ shellExec(`node bin/build dd --conf`);
524
539
  shellExec(`git add . && cd ./engine-private && git add .`);
525
540
  shellExec(`node bin cmt . ci package-pwa-microservices-template 'New release v:${version}'`);
526
541
  shellExec(`node bin cmt ./engine-private ci package-pwa-microservices-template`);
@@ -8,6 +8,7 @@ import dotenv from 'dotenv';
8
8
  import { commitData } from '../client/components/core/CommonJs.js';
9
9
  import { pbcopy, shellCd, shellExec } from '../server/process.js';
10
10
  import { actionInitLog, loggerFactory } from '../server/logger.js';
11
+ import path from 'path';
11
12
  import fs from 'fs-extra';
12
13
  import {
13
14
  getNpmRootPath,
@@ -133,10 +134,21 @@ class UnderpostRepository {
133
134
  p: undefined,
134
135
  bc: '',
135
136
  isRemoteRepo: '',
137
+ hasChanges: false,
136
138
  },
137
139
  ) {
138
140
  if (!repoPath) repoPath = '.';
139
141
 
142
+ if (options.hasChanges) {
143
+ const status = shellExec(`cd ${repoPath} && git status --porcelain`, {
144
+ stdout: true,
145
+ silent: true,
146
+ disableLog: true,
147
+ }).trim();
148
+ process.stdout.write(status ? '1' : '');
149
+ return;
150
+ }
151
+
140
152
  if (options.isRemoteRepo) {
141
153
  const accessible = Underpost.repo.isRemoteRepo(options.isRemoteRepo);
142
154
  console.log(accessible);
@@ -556,7 +568,7 @@ class UnderpostRepository {
556
568
  // Handle sync-conf operation
557
569
  if (options.syncConf) {
558
570
  logger.info(`Syncing configuration for deploy ID: ${deployId}`);
559
- shellExec(`node bin/build ${deployId} conf`);
571
+ shellExec(`node bin/build ${deployId} --conf`);
560
572
  logger.info('Configuration synced successfully');
561
573
  return resolve(true);
562
574
  }
@@ -608,7 +620,8 @@ class UnderpostRepository {
608
620
  const npmRoot = getNpmRootPath();
609
621
  const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
610
622
  const destFolder = `./${projectName}`;
611
- logger.info('build app', { destFolder });
623
+ const deployId = projectName.startsWith('dd-') ? projectName : `dd-${projectName}`;
624
+ logger.info('build app', { destFolder, deployId });
612
625
  if (fs.existsSync(destFolder)) fs.removeSync(destFolder);
613
626
  fs.mkdirSync(destFolder, { recursive: true });
614
627
  if (!options.dev) {
@@ -621,8 +634,9 @@ class UnderpostRepository {
621
634
  UnderpostRepository.API.initLocalRepo({ path: destFolder });
622
635
  shellExec(`cd ${destFolder} && git add . && git commit -m "Base template implementation"`);
623
636
  }
624
- shellExec(`cd ${destFolder} && npm run build`);
625
- shellExec(`cd ${destFolder} && npm run dev`);
637
+ shellExec(`cd ${destFolder} && node bin new --deploy-id ${deployId} --default-conf`);
638
+ shellExec(`cd ${destFolder} && node bin client ${deployId}`);
639
+ shellExec(`cd ${destFolder} && DEPLOY_ID=${deployId} npm run dev`);
626
640
  }
627
641
  return resolve(true);
628
642
  } catch (error) {
@@ -852,6 +866,7 @@ class UnderpostRepository {
852
866
  }
853
867
  }
854
868
  await buildClient({
869
+ deployId: resolvedDeployId,
855
870
  buildZip: options.buildZip || false,
856
871
  split: options.split || '',
857
872
  fullBuild: options.liteBuild ? false : true,
@@ -862,7 +877,12 @@ class UnderpostRepository {
862
877
  logger.warn('Skip replica client build: replica folder not found', { replicaDeployId });
863
878
  continue;
864
879
  }
865
- await Underpost.repo.client(replicaDeployId);
880
+ await Underpost.repo.client(replicaDeployId, '', '', '', {
881
+ buildZip: options.buildZip || false,
882
+ split: options.split || '',
883
+ liteBuild: options.liteBuild || false,
884
+ iconsBuild: options.iconsBuild || false,
885
+ });
866
886
  }
867
887
 
868
888
  return resolve(true);
@@ -937,7 +957,7 @@ Prevent build private config repo.`,
937
957
  deployVersion: packageJsonDeploy.version,
938
958
  };
939
959
  }
940
- shellExec(`node bin/build ${deployId} conf`);
960
+ shellExec(`node bin/build ${deployId} --conf`);
941
961
  return {
942
962
  validVersion: true,
943
963
  engineVersion: packageJsonEngine.version,
@@ -1374,8 +1394,9 @@ Prevent build private config repo.`,
1374
1394
  const gitEmail = process.env.GITHUB_EMAIL || `development@underpost.net`;
1375
1395
 
1376
1396
  if (!fs.existsSync(`${repoPath}/.git`)) {
1377
- shellExec(`cd "${repoPath}" && git init`);
1397
+ shellExec(`mkdir -p "${repoPath}" && git init "${repoPath}"`);
1378
1398
  }
1399
+
1379
1400
  shellExec(`cd "${repoPath}" && git config user.name '${gitUsername}'`);
1380
1401
  shellExec(`cd "${repoPath}" && git config user.email '${gitEmail}'`);
1381
1402
  shellExec(`cd "${repoPath}" && git config core.filemode false`);
@@ -1653,6 +1674,79 @@ Prevent build private config repo.`,
1653
1674
  }
1654
1675
  return fallback;
1655
1676
  },
1677
+
1678
+ /**
1679
+ * Performs a shallow sparse Git checkout of a single subdirectory from any
1680
+ * GitHub repository into a local target directory.
1681
+ *
1682
+ * Uses `--depth 1 --no-checkout` + `git sparse-checkout` so only the
1683
+ * requested path is fetched — no full clone of the remote repo.
1684
+ * Skips the clone entirely when `<targetDir>/<subPath>` already exists on
1685
+ * disk (idempotent).
1686
+ *
1687
+ * Requires `GITHUB_TOKEN` to be set in the environment for authenticated
1688
+ * access to private repositories.
1689
+ *
1690
+ * @param {string} subPath - The subdirectory path within the remote repo to
1691
+ * check out (e.g. `'conf/dd-prototype'`, `'src/api/payments'`).
1692
+ * @param {object} [options]
1693
+ * @param {string} [options.repoOwner='underpostnet'] - GitHub organisation or
1694
+ * user that owns the repository.
1695
+ * @param {string} [options.repoName='engine-private'] - Name of the
1696
+ * repository on GitHub.
1697
+ * @param {string} [options.targetDir='./engine-private'] - Local directory
1698
+ * where the repo will be cloned.
1699
+ * @returns {boolean} `true` when the checkout was performed, `false` when it
1700
+ * was skipped because the target path already existed.
1701
+ * @memberof UnderpostRepository
1702
+ */
1703
+ sparseCheckoutDirectory(
1704
+ subPath,
1705
+ options = { repoOwner: 'underpostnet', repoName: 'engine-private', targetDir: './engine-private' },
1706
+ ) {
1707
+ const { repoOwner = 'underpostnet', repoName = 'engine-private', targetDir = './engine-private' } = options;
1708
+ const localPath = `${targetDir}/${subPath}`;
1709
+ if (fs.existsSync(localPath)) {
1710
+ logger.info('[sparseCheckoutDirectory] path already present, skipping', localPath);
1711
+ return false;
1712
+ }
1713
+ const authUrl = `https://${process.env.GITHUB_TOKEN}@github.com/${repoOwner}/${repoName}.git`;
1714
+ shellExec(`git clone --depth 1 --no-checkout ${authUrl} ${targetDir}`, { disableLog: true });
1715
+ shellExec(`cd ${targetDir} && git sparse-checkout set ${subPath} && git checkout`, { disableLog: true });
1716
+ logger.info('[sparseCheckoutDirectory] sparse checkout complete', localPath);
1717
+ return true;
1718
+ },
1719
+
1720
+ /**
1721
+ * Ensures a deploy's public source repo (e.g. `engine-prototype`) is present
1722
+ * next to the engine and reset to a pristine HEAD, so catalog `sourceMoves`
1723
+ * can (re)pull custom sources even after a previous build moved them out of
1724
+ * the source tree.
1725
+ *
1726
+ * Clones `../<repoName>` when missing; otherwise restores a clean checkout
1727
+ * (`git checkout .` brings back any moved-out tracked files) and pulls latest.
1728
+ * Mirrors the sibling-repo handling used by `syncPrivateConf`.
1729
+ *
1730
+ * @param {string} repoName - Public source repo name (e.g. `engine-prototype`).
1731
+ * @returns {boolean} `true` when the repo is available on disk.
1732
+ * @memberof UnderpostRepository
1733
+ */
1734
+ pullSourceRepo(repoName) {
1735
+ const username = process.env.GITHUB_USERNAME;
1736
+ if (!username || !repoName) return false;
1737
+ const repoPath = `../${repoName}`;
1738
+ const gitUri = `${username}/${repoName}`;
1739
+ if (!fs.existsSync(repoPath)) {
1740
+ shellExec(`cd .. && underpost clone ${gitUri}`, { silent: true });
1741
+ } else {
1742
+ const repoAbsPath = path.resolve(repoPath);
1743
+ shellExec(`git config --global --add safe.directory '${repoAbsPath}'`);
1744
+ shellExec(`cd ${repoPath} && git checkout . && git clean -f -d && underpost pull . ${gitUri}`, {
1745
+ silent: true,
1746
+ });
1747
+ }
1748
+ return fs.existsSync(repoPath);
1749
+ },
1656
1750
  };
1657
1751
  }
1658
1752