cyberia 3.2.12 → 3.2.70
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/.env.example +127 -68
- package/.github/workflows/cyberia-client.cd.yml +40 -0
- package/.github/workflows/cyberia-server.cd.yml +40 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +49 -0
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +48 -0
- package/.github/workflows/docker-image.cyberia-server.ci.yml +69 -0
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +69 -0
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +52 -0
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +52 -0
- package/.github/workflows/engine-cyberia.cd.yml +34 -24
- package/.github/workflows/engine-cyberia.ci.yml +27 -5
- package/.github/workflows/ghpkg.ci.yml +89 -1
- package/.github/workflows/gitlab.ci.yml +1 -1
- package/.github/workflows/hardhat.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +19 -12
- package/.github/workflows/publish.ci.yml +2 -2
- package/.github/workflows/publish.cyberia.ci.yml +5 -16
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -8
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/CHANGELOG.md +451 -1
- package/CLI-HELP.md +1040 -1130
- package/Dockerfile +141 -43
- package/Dockerfile.dev +143 -0
- package/Dockerfile.test +165 -0
- package/README.md +1 -1
- package/baremetal/commission-workflows.json +1 -0
- package/bin/build.js +128 -136
- package/bin/build.template.js +25 -179
- package/bin/cyberia.js +1089 -188
- package/bin/deploy.js +6 -3
- package/bin/index.js +1089 -188
- package/bump.config.js +1 -0
- package/compose.env +131 -0
- package/conf.js +19 -1
- package/deployment.yaml +74 -2
- package/docker-compose.yml +316 -0
- package/hardhat/hardhat.config.js +2 -2
- package/hardhat/package-lock.json +620 -2041
- package/hardhat/package.json +7 -5
- package/hardhat/scripts/deployObjectLayerToken.js +18 -18
- package/hardhat/test/ObjectLayerToken.js +378 -274
- package/ipfs/configure-ipfs.sh +13 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +74 -2
- package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
- package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +32 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/mongodb/entrypoint.sh +76 -0
- package/nginx.conf +87 -0
- package/package.json +34 -22
- package/pv-pvc.yaml +32 -0
- package/scripts/disk-clean.sh +85 -60
- package/scripts/kubeadm-node-setup.sh +317 -0
- package/scripts/link-local-underpost-cli.sh +6 -0
- package/scripts/rocky-kickstart.sh +877 -185
- package/scripts/rpmfusion-ffmpeg-setup.sh +26 -50
- package/scripts/test-monitor.sh +248 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +43 -7
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +17 -25
- package/src/api/cyberia-action/cyberia-action.controller.js +19 -0
- package/src/api/cyberia-action/cyberia-action.model.js +21 -29
- package/src/api/cyberia-action/cyberia-action.router.js +42 -7
- package/src/api/cyberia-action/cyberia-action.service.js +20 -4
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +58 -57
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +37 -6
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +8 -2
- package/src/api/cyberia-entity/cyberia-entity.router.js +39 -7
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +74 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +63 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +62 -10
- package/src/api/cyberia-instance/cyberia-instance.model.js +32 -2
- package/src/api/cyberia-instance/cyberia-instance.router.js +6 -6
- package/src/api/cyberia-instance/cyberia-world-generator.js +116 -3
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +3 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +39 -7
- package/src/api/cyberia-map/cyberia-map.router.js +21 -6
- package/src/api/cyberia-quest/cyberia-quest.controller.js +38 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +47 -7
- package/src/api/cyberia-quest/cyberia-quest.service.js +59 -4
- package/src/api/cyberia-saga/cyberia-saga.controller.js +74 -0
- package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
- package/src/api/cyberia-saga/cyberia-saga.router.js +63 -0
- package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +551 -122
- package/src/api/cyberia-skill/cyberia-skill.controller.js +74 -0
- package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
- package/src/api/cyberia-skill/cyberia-skill.router.js +63 -0
- package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
- package/src/api/ipfs/ipfs.service.js +28 -15
- package/src/api/object-layer/object-layer.router.js +25 -15
- package/src/api/object-layer/object-layer.service.js +15 -20
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +39 -7
- package/src/cli/baremetal.js +717 -16
- package/src/cli/cluster.js +80 -5
- package/src/cli/deploy.js +326 -292
- package/src/cli/docker-compose.js +648 -0
- package/src/cli/env.js +12 -6
- package/src/cli/fs.js +75 -30
- package/src/cli/image.js +187 -55
- package/src/cli/index.js +157 -6
- package/src/cli/kickstart.js +142 -20
- package/src/cli/monitor.js +387 -6
- package/src/cli/release.js +72 -15
- package/src/cli/repository.js +339 -40
- package/src/cli/run.js +678 -186
- package/src/cli/secrets.js +82 -48
- package/src/cli/ssh.js +234 -0
- package/src/cli/static.js +2 -2
- package/src/client/components/core/PanelForm.js +44 -44
- package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
- package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
- package/src/client/components/cyberia/InstanceEngineCyberia.js +219 -13
- package/src/client/components/cyberia/MapEngineCyberia.js +154 -71
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +40 -63
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +34 -20
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +195 -4
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +66 -0
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +8 -0
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +8 -0
- package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +92 -20
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +272 -50
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +3 -3
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +39 -22
- package/src/client/public/cyberia-docs/CYBERIA-LORE.md +88 -0
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +28 -12
- package/src/client/public/cyberia-docs/CYBERIA.md +1 -1
- package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +27 -5
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +99 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +2 -2
- package/src/client/services/cyberia-map/cyberia-map.management.js +2 -2
- package/src/client/services/cyberia-saga/cyberia-saga.service.js +99 -0
- package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
- package/src/client/services/object-layer/object-layer.management.js +6 -6
- package/src/{server → client-builder}/client-build-docs.js +15 -5
- package/src/{server → client-builder}/client-build-live.js +3 -3
- package/src/{server → client-builder}/client-build.js +25 -22
- package/src/{server → client-builder}/client-dev-server.js +3 -3
- package/src/{server → client-builder}/client-icons.js +2 -2
- package/src/{server → client-builder}/ssr.js +5 -5
- package/src/client.build.js +1 -3
- package/src/client.dev.js +1 -1
- package/src/db/mongo/MongoBootstrap.js +12 -12
- package/src/db/mongo/MongooseDB.js +2 -1
- package/src/grpc/cyberia/grpc-server.js +255 -70
- package/src/index.js +12 -1
- package/src/mailer/EmailRender.js +1 -1
- package/src/{server → projects/cyberia}/atlas-sprite-sheet-generator.js +2 -2
- package/src/{server → projects/cyberia}/besu-genesis-generator.js +3 -3
- package/src/projects/cyberia/catalog-cyberia.js +81 -0
- package/src/projects/cyberia/gemini-client.js +175 -0
- package/src/projects/cyberia/generate-saga.js +2107 -0
- package/src/{server → projects/cyberia}/ipfs-client.js +6 -4
- package/src/{server → projects/cyberia}/object-layer.js +12 -108
- package/src/{server → projects/cyberia}/semantic-layer-generator-floor.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator-resource.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator-skin.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator.js +2 -2
- package/src/{server → projects/cyberia}/shape-generator.js +2 -2
- package/src/projects/underpost/catalog-underpost.js +60 -0
- package/src/runtime/cyberia-client/Dockerfile +31 -83
- package/src/runtime/cyberia-client/Dockerfile.dev +23 -30
- package/src/runtime/cyberia-server/Dockerfile +23 -42
- package/src/runtime/cyberia-server/Dockerfile.dev +20 -35
- package/src/runtime/engine-cyberia/Dockerfile +143 -0
- package/src/runtime/engine-cyberia/Dockerfile.dev +143 -0
- package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
- package/src/runtime/engine-cyberia/compose.env +131 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +316 -0
- package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
- package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
- package/src/runtime/engine-cyberia/nginx.conf +87 -0
- package/src/runtime/express/Express.js +2 -2
- package/src/runtime/nginx/Nginx.js +250 -0
- package/src/runtime/wp/Dockerfile +3 -3
- package/src/server/catalog.js +72 -0
- package/src/server/conf.js +415 -60
- package/src/server/runtime-status.js +252 -0
- package/src/server/start.js +42 -11
- package/src/server.js +6 -2
- package/test/cyberia-instance-conf-defaults.test.js +140 -0
- package/test/deploy-monitor.test.js +267 -0
- package/test/shape-generator.test.js +7 -1
- package/typedoc.dd-cyberia.json +3 -1
- package/typedoc.json +3 -1
- package/manifests/deployment/dd-test-development/deployment.yaml +0 -256
- package/manifests/deployment/dd-test-development/proxy.yaml +0 -102
- /package/src/client/ssr/{Render.js → RootDocument.js} +0 -0
- /package/src/{server → client-builder}/client-formatted.js +0 -0
package/src/cli/monitor.js
CHANGED
|
@@ -10,10 +10,19 @@ import {
|
|
|
10
10
|
loadConfServerJson,
|
|
11
11
|
loadCronDeployEnv,
|
|
12
12
|
etcHostFactory,
|
|
13
|
+
deployRangePortFactory,
|
|
13
14
|
} from '../server/conf.js';
|
|
14
15
|
import { loggerFactory } from '../server/logger.js';
|
|
16
|
+
import { timer } from '../client/components/core/CommonJs.js';
|
|
17
|
+
import {
|
|
18
|
+
RUNTIME_STATUS,
|
|
19
|
+
INTERNAL_STATUS_PATH,
|
|
20
|
+
normalizeContainerStatus,
|
|
21
|
+
deployStatusPort,
|
|
22
|
+
} from '../server/runtime-status.js';
|
|
15
23
|
import axios from 'axios';
|
|
16
24
|
import fs from 'fs-extra';
|
|
25
|
+
import net from 'node:net';
|
|
17
26
|
import { shellExec } from '../server/process.js';
|
|
18
27
|
import Underpost from '../index.js';
|
|
19
28
|
|
|
@@ -93,13 +102,13 @@ class UnderpostMonitor {
|
|
|
93
102
|
}
|
|
94
103
|
|
|
95
104
|
if (options.readyDeployment) {
|
|
96
|
-
|
|
97
|
-
(async () => {
|
|
98
|
-
await Underpost.
|
|
105
|
+
await Promise.all(
|
|
106
|
+
options.versions.split(',').map(async (version) => {
|
|
107
|
+
await Underpost.monitor.monitorReadyRunner(deployId, env, version, [], options.namespace);
|
|
99
108
|
if (options.promote)
|
|
100
109
|
Underpost.deploy.switchTraffic(deployId, env, version, options.replicas, options.namespace, options);
|
|
101
|
-
})
|
|
102
|
-
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
103
112
|
return;
|
|
104
113
|
}
|
|
105
114
|
|
|
@@ -227,7 +236,7 @@ class UnderpostMonitor {
|
|
|
227
236
|
monitorPodName = undefined;
|
|
228
237
|
}
|
|
229
238
|
const checkDeploymentReadyStatus = async () => {
|
|
230
|
-
const { ready, notReadyPods, readyPods } = await Underpost.
|
|
239
|
+
const { ready, notReadyPods, readyPods } = await Underpost.monitor.checkDeploymentReadyStatus(
|
|
231
240
|
deployId,
|
|
232
241
|
env,
|
|
233
242
|
traffic,
|
|
@@ -272,6 +281,378 @@ class UnderpostMonitor {
|
|
|
272
281
|
};
|
|
273
282
|
return new Promise((...args) => monitorCallBack(...args));
|
|
274
283
|
},
|
|
284
|
+
/**
|
|
285
|
+
* Checks the status of a deployment.
|
|
286
|
+
* @param {string} deployId - Deployment ID for which the status is being checked.
|
|
287
|
+
* @param {string} env - Environment for which the status is being checked.
|
|
288
|
+
* @param {string} traffic - Current traffic status for the deployment.
|
|
289
|
+
* @param {Array<string>} ignoresNames - List of pod names to ignore.
|
|
290
|
+
* @param {string} [namespace='default'] - Kubernetes namespace for the deployment.
|
|
291
|
+
* @returns {object} - Object containing the status of the deployment.
|
|
292
|
+
* @memberof UnderpostMonitor
|
|
293
|
+
*/
|
|
294
|
+
async checkDeploymentReadyStatus(deployId, env, traffic, ignoresNames = [], namespace = 'default') {
|
|
295
|
+
const pods = Underpost.kubectl.get(`${deployId}-${env}-${traffic}`, 'pods', namespace);
|
|
296
|
+
const readyPods = [];
|
|
297
|
+
const notReadyPods = [];
|
|
298
|
+
|
|
299
|
+
// Readiness signal: the pod's Kubernetes `Ready` condition driven by the
|
|
300
|
+
// container's readinessProbe (TCP socket, HTTP get, or exec). Set by kubelet
|
|
301
|
+
// when the probe passes. A failed or crashing runtime never becomes Ready —
|
|
302
|
+
// kubelet surfaces CrashLoopBackOff and this gate stays closed.
|
|
303
|
+
for (const pod of pods) {
|
|
304
|
+
const { NAME } = pod;
|
|
305
|
+
if (ignoresNames && ignoresNames.find((t) => NAME.trim().toLowerCase().match(t.trim().toLowerCase()))) continue;
|
|
306
|
+
|
|
307
|
+
let podJson = null;
|
|
308
|
+
try {
|
|
309
|
+
// Pod may not exist yet (between deployment apply and pod
|
|
310
|
+
// scheduling). silentOnError lets the monitor loop continue
|
|
311
|
+
// instead of aborting on the transient NotFound exit.
|
|
312
|
+
const raw = shellExec(`sudo kubectl get pod ${NAME} -n ${namespace} -o json`, {
|
|
313
|
+
silent: true,
|
|
314
|
+
disableLog: true,
|
|
315
|
+
stdout: true,
|
|
316
|
+
silentOnError: true,
|
|
317
|
+
});
|
|
318
|
+
podJson = raw ? JSON.parse(raw) : null;
|
|
319
|
+
} catch (_) {
|
|
320
|
+
podJson = null;
|
|
321
|
+
}
|
|
322
|
+
const conditions = podJson?.status?.conditions || [];
|
|
323
|
+
const readyCondition = conditions.find((c) => c.type === 'Ready');
|
|
324
|
+
const k8sReady = readyCondition?.status === 'True';
|
|
325
|
+
|
|
326
|
+
pod.out = JSON.stringify({ k8sReady, condition: readyCondition ?? null });
|
|
327
|
+
|
|
328
|
+
if (k8sReady) readyPods.push(pod);
|
|
329
|
+
else notReadyPods.push(pod);
|
|
330
|
+
}
|
|
331
|
+
const consideredCount = readyPods.length + notReadyPods.length;
|
|
332
|
+
return {
|
|
333
|
+
ready: consideredCount > 0 && notReadyPods.length === 0,
|
|
334
|
+
notReadyPods,
|
|
335
|
+
readyPods,
|
|
336
|
+
};
|
|
337
|
+
},
|
|
338
|
+
/**
|
|
339
|
+
* Resolves a free ephemeral TCP port on the loopback interface, used as the
|
|
340
|
+
* local end of the `kubectl port-forward` tunnel so it never collides with
|
|
341
|
+
* host-local services.
|
|
342
|
+
* @returns {Promise<number>}
|
|
343
|
+
* @memberof UnderpostMonitor
|
|
344
|
+
*/
|
|
345
|
+
findFreePort() {
|
|
346
|
+
return new Promise((resolve) => {
|
|
347
|
+
const srv = net.createServer();
|
|
348
|
+
srv.once('error', () => resolve(20000 + Math.floor(Math.random() * 20000)));
|
|
349
|
+
srv.listen(0, '127.0.0.1', () => {
|
|
350
|
+
const { port } = srv.address();
|
|
351
|
+
srv.close(() => resolve(port));
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
},
|
|
355
|
+
/**
|
|
356
|
+
* Resolves the deployment's internal status port (Phase-2 transport target).
|
|
357
|
+
*
|
|
358
|
+
* Canonical value is `fromPort - 1` from the deployment router — the exact
|
|
359
|
+
* port `buildManifest` injects into the pod (UNDERPOST_INTERNAL_PORT) and
|
|
360
|
+
* uses for the probes — so the tunnel target always matches the in-pod bind.
|
|
361
|
+
* `UNDERPOST_INTERNAL_PORT` overrides; ambient resolution is the last resort.
|
|
362
|
+
*
|
|
363
|
+
* @param {string} deployId
|
|
364
|
+
* @param {string} env
|
|
365
|
+
* @returns {Promise<number>}
|
|
366
|
+
* @memberof UnderpostMonitor
|
|
367
|
+
*/
|
|
368
|
+
async deployInternalPort(deployId, env) {
|
|
369
|
+
const override = parseInt(process.env.UNDERPOST_INTERNAL_PORT);
|
|
370
|
+
if (!Number.isNaN(override)) return override;
|
|
371
|
+
try {
|
|
372
|
+
const router = await Underpost.deploy.routerFactory(deployId, env);
|
|
373
|
+
const { fromPort } = deployRangePortFactory(router);
|
|
374
|
+
if (Number.isFinite(fromPort) && fromPort > 0) return fromPort - 1;
|
|
375
|
+
} catch (_) {
|
|
376
|
+
/* fall through to ambient resolution */
|
|
377
|
+
}
|
|
378
|
+
return deployStatusPort(deployId, env) ?? 3000;
|
|
379
|
+
},
|
|
380
|
+
/**
|
|
381
|
+
* Reads Phase-2 runtime status from a single pod using the selected transport.
|
|
382
|
+
*
|
|
383
|
+
* - `exec` (default): `kubectl exec … underpost config get container-status`
|
|
384
|
+
* reads the env-file value. Synchronous, no background process — required
|
|
385
|
+
* for custom instances (cyberia-server/client) and the safe choice for
|
|
386
|
+
* CI/SSH. See `Deploy custom instance to K8S.md`.
|
|
387
|
+
* - `http`: port-forward to the in-pod `/_internal/status` endpoint served
|
|
388
|
+
* by the `underpost start` launcher (dd-* runtime deploys). Opt-in.
|
|
389
|
+
*
|
|
390
|
+
* Transport failures are reported as `{ ok: false }` and must never be read
|
|
391
|
+
* as success — they are retried, not promoted.
|
|
392
|
+
*
|
|
393
|
+
* @param {string} podName
|
|
394
|
+
* @param {string} namespace
|
|
395
|
+
* @param {number} internalPort
|
|
396
|
+
* @param {('http'|'exec')} [transport='exec']
|
|
397
|
+
* @returns {Promise<{ok: boolean, status?: (string|null), transportError?: string}>}
|
|
398
|
+
* @memberof UnderpostMonitor
|
|
399
|
+
*/
|
|
400
|
+
async readRuntimeStatus(podName, namespace, internalPort, transport = 'exec') {
|
|
401
|
+
return transport === 'exec'
|
|
402
|
+
? Underpost.monitor.readRuntimeStatusViaExec(podName, namespace)
|
|
403
|
+
: Underpost.monitor.readRuntimeStatusViaHttp(podName, namespace, internalPort);
|
|
404
|
+
},
|
|
405
|
+
/**
|
|
406
|
+
* Phase-2 read over `kubectl exec` (env-file transport). Works for any pod
|
|
407
|
+
* whose image bakes the underpost CLI — notably custom instances that stamp
|
|
408
|
+
* `container-status` from `lifecycle.postStart`/`preStop` hooks.
|
|
409
|
+
* @param {string} podName
|
|
410
|
+
* @param {string} namespace
|
|
411
|
+
* @returns {{ok: boolean, status?: (string|null), transportError?: string}}
|
|
412
|
+
* @memberof UnderpostMonitor
|
|
413
|
+
*/
|
|
414
|
+
readRuntimeStatusViaExec(podName, namespace) {
|
|
415
|
+
try {
|
|
416
|
+
const raw = shellExec(
|
|
417
|
+
`sudo kubectl exec ${podName} -n ${namespace} -- sh -c 'underpost config get container-status --plain'`,
|
|
418
|
+
{ silent: true, disableLog: true, stdout: true, silentOnError: true },
|
|
419
|
+
);
|
|
420
|
+
const status = normalizeContainerStatus(raw ? raw.toString().trim() : '');
|
|
421
|
+
return status === undefined ? { ok: false, transportError: 'empty_status' } : { ok: true, status };
|
|
422
|
+
} catch (error) {
|
|
423
|
+
return { ok: false, transportError: error?.code || error?.message || 'exec_failed' };
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
/**
|
|
427
|
+
* Phase-2 read over `kubectl port-forward` + HTTP `/_internal/status`.
|
|
428
|
+
*
|
|
429
|
+
* The local side of the tunnel MUST be an ephemeral free port: pinning it to
|
|
430
|
+
* internalPort collides with any host-local service on that number (e.g. a
|
|
431
|
+
* dev runtime on the same machine as the cluster), making port-forward fail
|
|
432
|
+
* to bind and every read return a false transport error.
|
|
433
|
+
*
|
|
434
|
+
* @param {string} podName
|
|
435
|
+
* @param {string} namespace
|
|
436
|
+
* @param {number} internalPort
|
|
437
|
+
* @returns {Promise<{ok: boolean, status?: (string|null), transportError?: string}>}
|
|
438
|
+
* @memberof UnderpostMonitor
|
|
439
|
+
*/
|
|
440
|
+
async readRuntimeStatusViaHttp(podName, namespace, internalPort) {
|
|
441
|
+
const override = parseInt(process.env.UNDERPOST_PF_LOCAL_PORT);
|
|
442
|
+
const localPort = Number.isNaN(override) ? await Underpost.monitor.findFreePort() : override;
|
|
443
|
+
const url = `http://127.0.0.1:${localPort}${INTERNAL_STATUS_PATH}`;
|
|
444
|
+
let portForward;
|
|
445
|
+
try {
|
|
446
|
+
// `exec` makes the tracked child the sudo/kubectl process (so kill
|
|
447
|
+
// reaches it); stdio is redirected to /dev/null so the tunnel never
|
|
448
|
+
// inherits — and therefore never holds open — a CI/SSH session's pipes,
|
|
449
|
+
// which would hang the job after a successful deploy.
|
|
450
|
+
portForward = shellExec(
|
|
451
|
+
`exec sudo kubectl port-forward pod/${podName} ${localPort}:${internalPort} -n ${namespace} </dev/null >/dev/null 2>&1`,
|
|
452
|
+
{ async: true, silent: true, disableLog: true, silentOnError: true },
|
|
453
|
+
);
|
|
454
|
+
} catch (_) {
|
|
455
|
+
portForward = undefined;
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
let lastError;
|
|
459
|
+
const attempts = parseInt(process.env.UNDERPOST_PF_ATTEMPTS) || 20;
|
|
460
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
461
|
+
try {
|
|
462
|
+
const res = await axios.get(url, { timeout: 2500 });
|
|
463
|
+
const raw = res?.data?.status ?? null;
|
|
464
|
+
return { ok: true, status: normalizeContainerStatus(raw) ?? raw, payload: res.data };
|
|
465
|
+
} catch (error) {
|
|
466
|
+
lastError = error;
|
|
467
|
+
await timer(350);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return { ok: false, transportError: lastError?.code || lastError?.message || 'transport_failed' };
|
|
471
|
+
} finally {
|
|
472
|
+
if (portForward && typeof portForward.kill === 'function') {
|
|
473
|
+
try {
|
|
474
|
+
portForward.kill('SIGTERM');
|
|
475
|
+
} catch (_) {
|
|
476
|
+
/* tunnel already gone */
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
/**
|
|
482
|
+
* Monitors a deployment to terminal readiness using a deterministic
|
|
483
|
+
* two-phase state machine.
|
|
484
|
+
*
|
|
485
|
+
* Phase 1 (Kubernetes): pod `Ready` condition via `checkDeploymentReadyStatus`.
|
|
486
|
+
* Phase 2 (Runtime): `container-status`, read via the selected transport.
|
|
487
|
+
*
|
|
488
|
+
* Two deployment shapes are supported via `options`:
|
|
489
|
+
* - `runtime` gate (default, dd-* deploys): the `underpost start` launcher
|
|
490
|
+
* stamps `running-deployment`. Success requires K8S Ready AND every pod
|
|
491
|
+
* reporting `running-deployment`.
|
|
492
|
+
* - `kubernetes` gate (custom instances, e.g. cyberia): the runtime is a
|
|
493
|
+
* bare binary; K8S `readinessProbe` (TCP) IS the running signal and
|
|
494
|
+
* `container-status` is stamped to `initializing`/`stopping` by lifecycle
|
|
495
|
+
* hooks. Success requires K8S Ready; the status read is used only for
|
|
496
|
+
* fast `error` detection and display.
|
|
497
|
+
*
|
|
498
|
+
* Phase-2 transport defaults to `exec` (`kubectl exec`, no background
|
|
499
|
+
* process). The `http` transport (`kubectl port-forward` → `/_internal/status`)
|
|
500
|
+
* is opt-in via `options.statusTransport='http'` or
|
|
501
|
+
* `UNDERPOST_STATUS_TRANSPORT=http`; it must not be used in CI/SSH sessions
|
|
502
|
+
* where a stray tunnel can hang the job.
|
|
503
|
+
*
|
|
504
|
+
* Contract (both shapes):
|
|
505
|
+
* - Runtime readiness is never declared before Kubernetes readiness.
|
|
506
|
+
* - An explicit runtime `error` (or a fatal pod status) transitions
|
|
507
|
+
* immediately to `failed` (throw → CD exit 1).
|
|
508
|
+
* - Transport failures never count as success and never advance state.
|
|
509
|
+
* - `timeout` is a distinct terminal state from `failed`.
|
|
510
|
+
* - Every transition emits a structured, secret-free event.
|
|
511
|
+
*
|
|
512
|
+
* @param {string} deployId - Deployment ID for which the ready status is being monitored.
|
|
513
|
+
* @param {string} env - Environment for which the ready status is being monitored.
|
|
514
|
+
* @param {string} targetTraffic - Target traffic status for the deployment.
|
|
515
|
+
* @param {Array<string>} ignorePods - List of pod names to ignore.
|
|
516
|
+
* @param {string} [namespace='default'] - Kubernetes namespace for the deployment.
|
|
517
|
+
* @param {object} [options] - Monitoring shape.
|
|
518
|
+
* @param {('runtime'|'kubernetes')} [options.readyGate='runtime'] - Running-signal owner.
|
|
519
|
+
* @param {('http'|'exec')} [options.statusTransport='http'] - Phase-2 read transport.
|
|
520
|
+
* @returns {object} - Object containing the ready status of the deployment.
|
|
521
|
+
* @memberof UnderpostMonitor
|
|
522
|
+
*/
|
|
523
|
+
async monitorReadyRunner(deployId, env, targetTraffic, ignorePods = [], namespace = 'default', options = {}) {
|
|
524
|
+
const delayMs = parseInt(process.env.UNDERPOST_MONITOR_DELAY_MS) || 1000;
|
|
525
|
+
const maxIterations = parseInt(process.env.UNDERPOST_MONITOR_MAX_ITERATIONS) || 3000;
|
|
526
|
+
const deploymentId = `${deployId}-${env}-${targetTraffic}`;
|
|
527
|
+
const tag = `[${deploymentId}]`;
|
|
528
|
+
const expectedStatus = RUNTIME_STATUS.RUNNING;
|
|
529
|
+
const readyGate = options.readyGate === 'kubernetes' ? 'kubernetes' : 'runtime';
|
|
530
|
+
// Default to `exec`: a single synchronous `kubectl exec` read leaves no
|
|
531
|
+
// background process behind. The `http` transport spawns `kubectl
|
|
532
|
+
// port-forward` children that, if orphaned, inherit a CI/SSH session's
|
|
533
|
+
// stdio and hang the job after a successful deploy — opt in explicitly.
|
|
534
|
+
const statusTransport =
|
|
535
|
+
(options.statusTransport || process.env.UNDERPOST_STATUS_TRANSPORT) === 'http' ? 'http' : 'exec';
|
|
536
|
+
const internalPort =
|
|
537
|
+
statusTransport === 'http' ? await Underpost.monitor.deployInternalPort(deployId, env) : null;
|
|
538
|
+
const podErrorStates = ['error', 'crashloopbackoff', 'oomkilled', 'imagepullbackoff', 'errimagepull'];
|
|
539
|
+
|
|
540
|
+
const emit = (state, status) =>
|
|
541
|
+
logger.info('deploy-monitor', {
|
|
542
|
+
deployId: deploymentId,
|
|
543
|
+
phase: state.startsWith('runtime') ? 'runtime' : 'kubernetes',
|
|
544
|
+
state,
|
|
545
|
+
status: status ?? null,
|
|
546
|
+
timestamp: new Date().toISOString(),
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
logger.info('Deployment init', {
|
|
550
|
+
deployId,
|
|
551
|
+
env,
|
|
552
|
+
targetTraffic,
|
|
553
|
+
namespace,
|
|
554
|
+
internalPort,
|
|
555
|
+
readyGate,
|
|
556
|
+
statusTransport,
|
|
557
|
+
});
|
|
558
|
+
emit('pending');
|
|
559
|
+
|
|
560
|
+
const runtimeStatusCache = new Map();
|
|
561
|
+
const advancedPods = new Set();
|
|
562
|
+
|
|
563
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
564
|
+
const result = await Underpost.monitor.checkDeploymentReadyStatus(
|
|
565
|
+
deployId,
|
|
566
|
+
env,
|
|
567
|
+
targetTraffic,
|
|
568
|
+
ignorePods,
|
|
569
|
+
namespace,
|
|
570
|
+
);
|
|
571
|
+
const allPods = [...result.readyPods, ...result.notReadyPods];
|
|
572
|
+
|
|
573
|
+
if (allPods.length === 0) {
|
|
574
|
+
emit('pending');
|
|
575
|
+
await timer(delayMs);
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
emit('pod_scheduled');
|
|
579
|
+
|
|
580
|
+
// Phase 1 fatal: a Kubernetes-level pod failure is terminal (failed,
|
|
581
|
+
// not timeout) — fail the CD runner immediately instead of waiting out
|
|
582
|
+
// the full window.
|
|
583
|
+
for (const pod of allPods) {
|
|
584
|
+
const podStatus = (pod.STATUS || '').toLowerCase().trim();
|
|
585
|
+
if (podErrorStates.find((s) => podStatus.includes(s)))
|
|
586
|
+
throw new Error(`Pod ${pod.NAME} has error pod status: ${pod.STATUS}`);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const allPodsK8sReady = result.notReadyPods.length === 0;
|
|
590
|
+
if (allPodsK8sReady) emit('pod_ready');
|
|
591
|
+
|
|
592
|
+
// Phase 2: runtime status via the selected transport. Transport failures
|
|
593
|
+
// neither advance state nor count as success; explicit `error` is terminal.
|
|
594
|
+
let allRuntimeRead = true;
|
|
595
|
+
for (const pod of allPods) {
|
|
596
|
+
if (!pod?.NAME) continue;
|
|
597
|
+
const read = await Underpost.monitor.readRuntimeStatus(pod.NAME, namespace, internalPort, statusTransport);
|
|
598
|
+
if (!read.ok) {
|
|
599
|
+
allRuntimeRead = false;
|
|
600
|
+
emit('runtime_booting', `transport:${read.transportError}`);
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
const status = read.status;
|
|
604
|
+
if (status === RUNTIME_STATUS.ERROR) throw new Error(`Pod ${pod.NAME} reported runtime status=error`);
|
|
605
|
+
// Regression (advanced → empty/build) means a pod restarted. Under the
|
|
606
|
+
// kubernetes gate the runtime never advances past `initializing`, so
|
|
607
|
+
// only treat a drop to empty/build as a regression there.
|
|
608
|
+
if (advancedPods.has(pod.NAME) && (!status || status === RUNTIME_STATUS.BUILD))
|
|
609
|
+
throw new Error(`Pod ${pod.NAME} runtime status regressed (${status ?? 'empty'}) — pod likely restarted`);
|
|
610
|
+
if (status && status !== RUNTIME_STATUS.BUILD) advancedPods.add(pod.NAME);
|
|
611
|
+
runtimeStatusCache.set(pod.NAME, status);
|
|
612
|
+
emit('runtime_booting', status);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Under the kubernetes gate the readinessProbe is the running signal, so
|
|
616
|
+
// K8S Ready alone confirms Phase 2; the status read above is kept only
|
|
617
|
+
// for `error` fast-fail and display.
|
|
618
|
+
const allRuntimeReady =
|
|
619
|
+
readyGate === 'kubernetes'
|
|
620
|
+
? true
|
|
621
|
+
: allRuntimeRead && allPods.every((pod) => runtimeStatusCache.get(pod.NAME) === expectedStatus);
|
|
622
|
+
|
|
623
|
+
for (const pod of allPods) {
|
|
624
|
+
const status = runtimeStatusCache.get(pod.NAME) || 'waiting for status';
|
|
625
|
+
const podStatus = pod.STATUS || 'Unknown';
|
|
626
|
+
const statusDisplay = status === expectedStatus ? status : `${status} (pending)`;
|
|
627
|
+
console.log(
|
|
628
|
+
'Target pod:',
|
|
629
|
+
pod.NAME[pod.NAME.includes('green') ? 'bgGreen' : 'bgBlue'].bold.black,
|
|
630
|
+
'| Pod status:',
|
|
631
|
+
podStatus.bold.yellow,
|
|
632
|
+
'| Runtime status:',
|
|
633
|
+
statusDisplay.bold.cyan,
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Terminal success requires both phases. runtime_ready cannot precede
|
|
638
|
+
// Kubernetes readiness.
|
|
639
|
+
if (allPodsK8sReady && allRuntimeReady) {
|
|
640
|
+
const readySignal = readyGate === 'kubernetes' ? 'K8S readinessProbe' : `runtime ${expectedStatus}`;
|
|
641
|
+
emit('runtime_ready', readyGate === 'kubernetes' ? 'k8s-ready' : expectedStatus);
|
|
642
|
+
logger.info(`${tag} | Deployment ready (K8S Ready + ${readySignal})`);
|
|
643
|
+
return result;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
await timer(delayMs);
|
|
647
|
+
if ((i + 1) % 10 === 0) logger.info(`${tag} | In progress... iteration ${i + 1}`);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
emit('timeout');
|
|
651
|
+
logger.error(`${tag} | Deployment timeout after ${maxIterations} iterations`);
|
|
652
|
+
throw new Error(
|
|
653
|
+
`monitorReadyRunner timeout: ${deploymentId} did not become Ready within ${maxIterations}*${delayMs}ms`,
|
|
654
|
+
);
|
|
655
|
+
},
|
|
275
656
|
};
|
|
276
657
|
}
|
|
277
658
|
|
package/src/cli/release.js
CHANGED
|
@@ -117,9 +117,53 @@ const buildVersionBumpTargets = () => [
|
|
|
117
117
|
/(underpost-engine:v)\d+\.\d+\.\d+/g,
|
|
118
118
|
/(type=raw,value=v)\d+\.\d+\.\d+/g,
|
|
119
119
|
/(UNDERPOST_VERSION=)\d+\.\d+\.\d+/g,
|
|
120
|
+
/(UNDERPOST_VERSION:\s*['"]?)\d+\.\d+\.\d+/g,
|
|
120
121
|
],
|
|
121
122
|
},
|
|
122
123
|
|
|
124
|
+
// ── Docker-compose image-tag defaults. Root compose + generator + cyberia runtime compose.
|
|
125
|
+
// Tags live in `${VAR:-vX.Y.Z}` / `VAR=vX.Y.Z` shapes bumpp cannot detect. ──
|
|
126
|
+
{
|
|
127
|
+
file: 'docker-compose.yml',
|
|
128
|
+
patterns: /(_TAG:-v)\d+\.\d+\.\d+/g,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
file: 'src/cli/docker-compose.js',
|
|
132
|
+
patterns: /(_TAG=v)\d+\.\d+\.\d+/g,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
file: 'src/runtime/engine-cyberia/docker-compose.yml',
|
|
136
|
+
patterns: /(_TAG:-v)\d+\.\d+\.\d+/g,
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
// ── Cyberia CLI dev image tar/name defaults (bin/cyberia.js). ──
|
|
140
|
+
{
|
|
141
|
+
file: 'bin/cyberia.js',
|
|
142
|
+
patterns: [/(-dev_v)\d+\.\d+\.\d+(?=\.tar)/g, /(-dev:v)\d+\.\d+\.\d+/g],
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// ── Runtime Dockerfiles: `ARG UNDERPOST_VERSION=X.Y.Z` build-arg defaults. ──
|
|
146
|
+
{
|
|
147
|
+
dir: 'src/runtime',
|
|
148
|
+
match: /^Dockerfile(\.\w+)?$/,
|
|
149
|
+
patterns: /(ARG UNDERPOST_VERSION=)\d+\.\d+\.\d+/g,
|
|
150
|
+
recursive: true,
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
// ── Runtime compose.env shipped tag defaults ──
|
|
154
|
+
{
|
|
155
|
+
dir: 'src/runtime',
|
|
156
|
+
match: /^compose\.env$/,
|
|
157
|
+
patterns: /(_TAG=v)\d+\.\d+\.\d+/g,
|
|
158
|
+
recursive: true,
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
// ── Deploy-monitor smoke-test image default (scripts/test-monitor.sh). ──
|
|
162
|
+
{
|
|
163
|
+
file: 'scripts/test-monitor.sh',
|
|
164
|
+
patterns: /(underpost\/[a-z0-9-]+:v)\d+\.\d+\.\d+/g,
|
|
165
|
+
},
|
|
166
|
+
|
|
123
167
|
// ── engine-private confs (gitignored — bumped only if present). ──
|
|
124
168
|
{
|
|
125
169
|
dir: 'engine-private/conf',
|
|
@@ -264,18 +308,29 @@ const ISOLATED_ENV = 'env -i HOME="$HOME" PATH="$PATH" USER="$USER" LOGNAME="$LO
|
|
|
264
308
|
*
|
|
265
309
|
* @returns {boolean} true when the template started cleanly, false otherwise.
|
|
266
310
|
*/
|
|
267
|
-
async function buildAndTestTemplate() {
|
|
311
|
+
async function buildAndTestTemplate(opts = {}) {
|
|
268
312
|
killDevServers();
|
|
269
313
|
Underpost.repo.clean({ paths: ['/home/dd/engine', '/home/dd/engine/engine-private '] });
|
|
270
314
|
shellExec(`node bin pull . ${process.env.GITHUB_USERNAME}/engine`);
|
|
271
|
-
|
|
315
|
+
fs.removeSync(TEMPLATE_PATH);
|
|
316
|
+
shellExec(`npm run build:template`);
|
|
272
317
|
shellExec(`node bin run shared-dir ${TEMPLATE_PATH}`);
|
|
273
318
|
|
|
319
|
+
const upsertEnvVar = (content, key, value) => {
|
|
320
|
+
const re = new RegExp(`^(${key}=).*`, 'm');
|
|
321
|
+
if (re.test(content)) return content.replace(re, `$1${value}`);
|
|
322
|
+
return `${content.trimEnd()}\n${key}=${value}\n`;
|
|
323
|
+
};
|
|
324
|
+
|
|
274
325
|
const dhcpHostIp = Dns.getLocalIPv4Address();
|
|
275
326
|
logger.info(`DHCP host IP for template test: ${dhcpHostIp}`);
|
|
276
327
|
let envContent = fs.readFileSync(`${TEMPLATE_PATH}/.env.example`, 'utf8');
|
|
277
328
|
if (dhcpHostIp) envContent = envContent.replace(/127\.0\.0\.1/g, dhcpHostIp);
|
|
278
|
-
envContent = envContent
|
|
329
|
+
envContent = upsertEnvVar(envContent, 'ENABLE_FILE_LOGS', 'true');
|
|
330
|
+
if (opts.mongoHost) envContent = upsertEnvVar(envContent, 'DB_HOST', opts.mongoHost);
|
|
331
|
+
if (opts.mongoUser) envContent = upsertEnvVar(envContent, 'DB_USER', opts.mongoUser);
|
|
332
|
+
if (opts.mongoPassword) envContent = upsertEnvVar(envContent, 'DB_PASSWORD', opts.mongoPassword);
|
|
333
|
+
if (opts.valkeyHost) envContent = upsertEnvVar(envContent, 'VALKEY_HOST', opts.valkeyHost);
|
|
279
334
|
// fs.writeFileSync(`${TEMPLATE_PATH}/.env`, envContent, 'utf8');
|
|
280
335
|
fs.writeFileSync(`${TEMPLATE_PATH}/.env.example`, envContent, 'utf8');
|
|
281
336
|
shellExec(`cd ${TEMPLATE_PATH} && npm install`);
|
|
@@ -337,7 +392,12 @@ class UnderpostRelease {
|
|
|
337
392
|
*
|
|
338
393
|
* @method build
|
|
339
394
|
* @param {string} [newVersion] - The new version string to set. Defaults to current version if not provided.
|
|
340
|
-
* @param {{dryRun?: boolean}} [options] - Commander options.
|
|
395
|
+
* @param {{dryRun?: boolean, mongoHost?: string, mongoUser?: string, mongoPassword?: string, valkeyHost?: string}} [options] - Commander options.
|
|
396
|
+
* `--dry-run` previews changes without writing files.
|
|
397
|
+
* `--mongo-host` overrides `DB_HOST` in the template `.env.example` smoke test.
|
|
398
|
+
* `--mongo-user` overrides `DB_USER` in the template `.env.example` smoke test.
|
|
399
|
+
* `--mongo-password` overrides `DB_PASSWORD` in the template `.env.example` smoke test.
|
|
400
|
+
* `--valkey-host` overrides `VALKEY_HOST` in the template `.env.example` smoke test.
|
|
341
401
|
* @memberof UnderpostRelease
|
|
342
402
|
*/
|
|
343
403
|
async build(newVersion, options = {}) {
|
|
@@ -352,7 +412,7 @@ class UnderpostRelease {
|
|
|
352
412
|
logger.info(`Release build — bumping ${version} → ${newVersion}${dryRun ? ' (dry-run)' : ''}`);
|
|
353
413
|
|
|
354
414
|
if (!dryRun) {
|
|
355
|
-
const templateOk = await buildAndTestTemplate();
|
|
415
|
+
const templateOk = await buildAndTestTemplate(options);
|
|
356
416
|
if (!templateOk) return;
|
|
357
417
|
}
|
|
358
418
|
|
|
@@ -390,9 +450,8 @@ class UnderpostRelease {
|
|
|
390
450
|
shellExec(`node bin/deploy cli-docs ${version} ${newVersion}`);
|
|
391
451
|
shellExec(`node bin/deploy update-dependencies`);
|
|
392
452
|
shellExec(`node bin/build dd`);
|
|
393
|
-
shellExec(`node bin
|
|
394
|
-
shellExec(`node bin
|
|
395
|
-
shellExec(`node bin/deploy build-default-confs`);
|
|
453
|
+
shellExec(`node bin run build-cluster-deployment-manifests`);
|
|
454
|
+
shellExec(`node bin new --default-conf --conf-workflow-id template`);
|
|
396
455
|
shellExec(`sudo rm -rf ./engine-private/conf/dd-default`);
|
|
397
456
|
shellExec(`node bin new --deploy-id dd-default`);
|
|
398
457
|
console.log(fs.existsSync(`./engine-private/conf/dd-default`));
|
|
@@ -433,11 +492,10 @@ class UnderpostRelease {
|
|
|
433
492
|
let commitMsg = message;
|
|
434
493
|
if (!commitMsg) {
|
|
435
494
|
shellCd('/home/dd/engine');
|
|
436
|
-
|
|
495
|
+
commitMsg = shellExec(`node bin cmt --changelog-msg --changelog-no-hash`, {
|
|
437
496
|
stdout: true,
|
|
438
497
|
silent: true,
|
|
439
498
|
}).trim();
|
|
440
|
-
commitMsg = Underpost.repo.sanitizeChangelogMessage(rawMsg);
|
|
441
499
|
shellCd('/home/dd');
|
|
442
500
|
}
|
|
443
501
|
commitMsg = (commitMsg || '').trim() || `Update ${repoName} repository`;
|
|
@@ -460,7 +518,7 @@ class UnderpostRelease {
|
|
|
460
518
|
* Runs the pwa-microservices-template update and push flow locally.
|
|
461
519
|
*
|
|
462
520
|
* Always removes and re-clones pwa-microservices-template, then:
|
|
463
|
-
* 1. Runs
|
|
521
|
+
* 1. Runs build:template (node bin/build.template) to sync engine sources.
|
|
464
522
|
* 2. Installs dependencies and builds the template.
|
|
465
523
|
* 3. Commits and pushes to the pwa-microservices-template remote repository.
|
|
466
524
|
*
|
|
@@ -477,18 +535,17 @@ class UnderpostRelease {
|
|
|
477
535
|
let commitMsg = message;
|
|
478
536
|
if (!commitMsg) {
|
|
479
537
|
shellCd('/home/dd/engine');
|
|
480
|
-
|
|
538
|
+
commitMsg = shellExec(`node bin cmt --changelog-msg --changelog-no-hash`, {
|
|
481
539
|
stdout: true,
|
|
482
540
|
silent: true,
|
|
483
541
|
}).trim();
|
|
484
|
-
commitMsg = Underpost.repo.sanitizeChangelogMessage(rawMsg);
|
|
485
542
|
}
|
|
486
543
|
commitMsg = (commitMsg || '').trim() || `Update pwa-microservices-template repository`;
|
|
487
544
|
shellCd('/home/dd');
|
|
488
545
|
shellExec(`sudo rm -rf /home/dd/pwa-microservices-template`);
|
|
489
546
|
shellExec(`node engine/bin clone ${githubOrg}/pwa-microservices-template`);
|
|
490
547
|
shellCd('/home/dd/engine');
|
|
491
|
-
shellExec(`npm run
|
|
548
|
+
shellExec(`npm run build:template`);
|
|
492
549
|
shellExec(`cd ../pwa-microservices-template && npm install && npm run build`);
|
|
493
550
|
shellCd('/home/dd/pwa-microservices-template');
|
|
494
551
|
shellExec(`git add .`);
|
|
@@ -520,7 +577,7 @@ class UnderpostRelease {
|
|
|
520
577
|
shellExec(
|
|
521
578
|
`node bin secret underpost --create-from-file /home/dd/engine/engine-private/conf/dd-cron/.env.production`,
|
|
522
579
|
);
|
|
523
|
-
shellExec(`node bin/build dd conf`);
|
|
580
|
+
shellExec(`node bin/build dd --conf`);
|
|
524
581
|
shellExec(`git add . && cd ./engine-private && git add .`);
|
|
525
582
|
shellExec(`node bin cmt . ci package-pwa-microservices-template 'New release v:${version}'`);
|
|
526
583
|
shellExec(`node bin cmt ./engine-private ci package-pwa-microservices-template`);
|