underpost 3.2.10 → 3.2.12

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 (54) hide show
  1. package/.vscode/extensions.json +9 -9
  2. package/.vscode/settings.json +12 -1
  3. package/CHANGELOG.md +92 -1
  4. package/CLI-HELP.md +80 -26
  5. package/README.md +6 -10
  6. package/bin/build.js +9 -6
  7. package/bin/build.template.js +187 -0
  8. package/bin/deploy.js +29 -18
  9. package/conf.js +1 -4
  10. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  11. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  12. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  13. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  14. package/manifests/lxd/lxd-admin-profile.yaml +12 -3
  15. package/manifests/mongodb-4.4/headless-service.yaml +10 -0
  16. package/manifests/mongodb-4.4/kustomization.yaml +3 -1
  17. package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
  18. package/manifests/mongodb-4.4/pv-pvc.yaml +10 -14
  19. package/manifests/mongodb-4.4/statefulset.yaml +79 -0
  20. package/manifests/mongodb-4.4/storage-class.yaml +9 -0
  21. package/manifests/valkey/statefulset.yaml +1 -1
  22. package/manifests/valkey/valkey-nodeport.yaml +17 -0
  23. package/package.json +3 -3
  24. package/scripts/ipxe-setup.sh +52 -49
  25. package/scripts/k3s-node-setup.sh +84 -68
  26. package/scripts/lxd-vm-setup.sh +193 -8
  27. package/scripts/maas-nat-firewalld.sh +145 -0
  28. package/src/cli/baremetal.js +115 -93
  29. package/src/cli/cluster.js +548 -221
  30. package/src/cli/deploy.js +131 -166
  31. package/src/cli/fs.js +11 -3
  32. package/src/cli/index.js +75 -17
  33. package/src/cli/lxd.js +1034 -240
  34. package/src/cli/monitor.js +9 -3
  35. package/src/cli/release.js +72 -36
  36. package/src/cli/repository.js +10 -16
  37. package/src/cli/run.js +72 -55
  38. package/src/cli/secrets.js +11 -2
  39. package/src/client/components/core/Auth.js +4 -3
  40. package/src/client/components/core/ClientEvents.js +76 -0
  41. package/src/client/components/core/EventBus.js +4 -0
  42. package/src/client/components/core/Modal.js +82 -41
  43. package/src/db/DataBaseProvider.js +9 -9
  44. package/src/db/mariadb/MariaDB.js +2 -1
  45. package/src/db/mongo/MongoBootstrap.js +592 -522
  46. package/src/db/mongo/MongooseDB.js +19 -15
  47. package/src/index.js +1 -1
  48. package/src/server/conf.js +67 -19
  49. package/src/server/proxy.js +9 -2
  50. package/src/server/start.js +8 -4
  51. package/src/server/valkey.js +2 -0
  52. package/bin/file.js +0 -220
  53. package/bin/vs.js +0 -74
  54. package/bin/zed.js +0 -84
package/src/cli/deploy.js CHANGED
@@ -78,7 +78,8 @@ class UnderpostDeploy {
78
78
  return `
79
79
  - conditions:
80
80
  - prefix: ${path}
81
- ${pathRewritePolicy
81
+ ${
82
+ pathRewritePolicy
82
83
  ? `pathRewritePolicy:
83
84
  replacePrefix:
84
85
  ${pathRewritePolicy.map(
@@ -88,26 +89,30 @@ class UnderpostDeploy {
88
89
  ).join(`
89
90
  `)}`
90
91
  : ''
91
- }${timeoutPolicy
92
- ? `\n timeoutPolicy:\n${timeoutPolicy.response ? ` response: ${timeoutPolicy.response}\n` : ''}${timeoutPolicy.idle ? ` idle: ${timeoutPolicy.idle}\n` : ''
93
- }`
92
+ }${
93
+ timeoutPolicy
94
+ ? `\n timeoutPolicy:\n${timeoutPolicy.response ? ` response: ${timeoutPolicy.response}\n` : ''}${
95
+ timeoutPolicy.idle ? ` idle: ${timeoutPolicy.idle}\n` : ''
96
+ }`
94
97
  : ''
95
- }${retryPolicy
96
- ? `\n retryPolicy:\n${retryPolicy.count !== undefined ? ` count: ${retryPolicy.count}\n` : ''}${retryPolicy.perTryTimeout ? ` perTryTimeout: ${retryPolicy.perTryTimeout}\n` : ''
97
- }`
98
+ }${
99
+ retryPolicy
100
+ ? `\n retryPolicy:\n${retryPolicy.count !== undefined ? ` count: ${retryPolicy.count}\n` : ''}${
101
+ retryPolicy.perTryTimeout ? ` perTryTimeout: ${retryPolicy.perTryTimeout}\n` : ''
102
+ }`
98
103
  : ''
99
- }
104
+ }
100
105
  enableWebsockets: true
101
106
  services:
102
107
  ${deploymentVersions
103
- .map(
104
- (version, i) =>
105
- ` - name: ${serviceId ? serviceId : `${deployId}-${env}-${version}-service`}
108
+ .map(
109
+ (version, i) =>
110
+ ` - name: ${serviceId ? serviceId : `${deployId}-${env}-${version}-service`}
106
111
  port: ${port}
107
112
  weight: ${i === 0 ? 100 : 0}
108
113
  `,
109
- )
110
- .join('')}`;
114
+ )
115
+ .join('')}`;
111
116
  },
112
117
  /**
113
118
  * Creates a YAML deployment configuration for a deployment.
@@ -154,17 +159,17 @@ class UnderpostDeploy {
154
159
  cmd =
155
160
  pullBundle || skipFullBuild
156
161
  ? [
157
- // When pullBundle (or skipFullBuild) is set the container pulls the pre-built client
158
- // bundle from Cloudinary (push-bundle must have been run on the dev machine beforehand).
159
- `underpost secret underpost --create-from-env`,
160
- `underpost start --build --run --pull-bundle --skip-full-build ${deployId} ${env}`,
161
- ]
162
+ // When pullBundle (or skipFullBuild) is set the container pulls the pre-built client
163
+ // bundle from Cloudinary (push-bundle must have been run on the dev machine beforehand).
164
+ `underpost secret underpost --create-from-env`,
165
+ `underpost start --build --run --pull-bundle --skip-full-build ${deployId} ${env}`,
166
+ ]
162
167
  : [
163
- // `npm install -g npm@11.2.0`,
164
- // `npm install -g underpost`,
165
- `underpost secret underpost --create-from-env`,
166
- `underpost start --build --run ${deployId} ${env}`,
167
- ];
168
+ // `npm install -g npm@11.2.0`,
169
+ // `npm install -g underpost`,
170
+ `underpost secret underpost --create-from-env`,
171
+ `underpost start --build --run ${deployId} ${env}`,
172
+ ];
168
173
  const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
169
174
  if (!volumes) volumes = [];
170
175
  const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
@@ -199,12 +204,14 @@ spec:
199
204
  envFrom:
200
205
  - secretRef:
201
206
  name: underpost-config
202
- ${containerPort
203
- ? ` ports:
207
+ ${
208
+ containerPort
209
+ ? ` ports:
204
210
  - containerPort: ${containerPort}
205
211
  `
206
- : ''
207
- }${resources
212
+ : ''
213
+ }${
214
+ resources
208
215
  ? ` resources:
209
216
  requests:
210
217
  memory: "${resources.requests.memory}"
@@ -213,46 +220,50 @@ ${containerPort
213
220
  memory: "${resources.limits.memory}"
214
221
  cpu: "${resources.limits.cpu}"`
215
222
  : ''
216
- }
223
+ }
217
224
  command:
218
225
  - /bin/sh
219
226
  - -c
220
227
  - >
221
228
  ${cmd.join(' &&\n ')}
222
- ${readinessProbe
223
- ? ` readinessProbe:
229
+ ${
230
+ readinessProbe
231
+ ? ` readinessProbe:
224
232
  ${JSON.stringify(readinessProbe, null, 2)
225
- .split('\n')
226
- .map((l) => ' ' + l)
227
- .join('\n')}
233
+ .split('\n')
234
+ .map((l) => ' ' + l)
235
+ .join('\n')}
228
236
  `
229
- : ''
230
- }${livenessProbe
237
+ : ''
238
+ }${
239
+ livenessProbe
231
240
  ? ` livenessProbe:
232
241
  ${JSON.stringify(livenessProbe, null, 2)
233
- .split('\n')
234
- .map((l) => ' ' + l)
235
- .join('\n')}
242
+ .split('\n')
243
+ .map((l) => ' ' + l)
244
+ .join('\n')}
236
245
  `
237
246
  : ''
238
- }${lifecycle
247
+ }${
248
+ lifecycle
239
249
  ? ` lifecycle:
240
250
  ${JSON.stringify(lifecycle, null, 2)
241
- .split('\n')
242
- .map((l) => ' ' + l)
243
- .join('\n')}
251
+ .split('\n')
252
+ .map((l) => ' ' + l)
253
+ .join('\n')}
244
254
  `
245
255
  : ''
246
- }
256
+ }
247
257
 
248
- ${volumes.length > 0
249
- ? Underpost.deploy
250
- .volumeFactory(volumes.map((v) => ((v.version = `${deployId}-${env}-${suffix}`), v)))
251
- .render.split(`\n`)
252
- .map((l) => ' ' + l)
253
- .join(`\n`)
254
- : ''
255
- }
258
+ ${
259
+ volumes.length > 0
260
+ ? Underpost.deploy
261
+ .volumeFactory(volumes.map((v) => ((v.version = `${deployId}-${env}-${suffix}`), v)))
262
+ .render.split(`\n`)
263
+ .map((l) => ' ' + l)
264
+ .join(`\n`)
265
+ : ''
266
+ }
256
267
  ---
257
268
  apiVersion: v1
258
269
  kind: Service
@@ -312,19 +323,19 @@ spec:
312
323
  for (const deploymentVersion of deploymentVersions) {
313
324
  deploymentYamlParts += `---
314
325
  ${Underpost.deploy
315
- .deploymentYamlPartsFactory({
316
- deployId,
317
- env,
318
- suffix: deploymentVersion,
319
- replicas,
320
- image,
321
- namespace: options.namespace,
322
- cmd: options.cmd ? options.cmd.split(',').map((c) => c.trim()) : undefined,
323
- skipFullBuild: options.skipFullBuild,
324
- pullBundle: options.pullBundle,
325
- imagePullPolicy: options.imagePullPolicy,
326
- })
327
- .replace('{{ports}}', buildKindPorts(fromPort, toPort))}
326
+ .deploymentYamlPartsFactory({
327
+ deployId,
328
+ env,
329
+ suffix: deploymentVersion,
330
+ replicas,
331
+ image,
332
+ namespace: options.namespace,
333
+ cmd: options.cmd ? options.cmd.split(',').map((c) => c.trim()) : undefined,
334
+ skipFullBuild: options.skipFullBuild,
335
+ pullBundle: options.pullBundle,
336
+ imagePullPolicy: options.imagePullPolicy,
337
+ })
338
+ .replace('{{ports}}', buildKindPorts(fromPort, toPort))}
328
339
  `;
329
340
  }
330
341
  fs.writeFileSync(`./engine-private/conf/${deployId}/build/${env}/deployment.yaml`, deploymentYamlParts, 'utf8');
@@ -376,20 +387,20 @@ ${Underpost.deploy
376
387
  let proxyRoutes = '';
377
388
  const globalTimeoutPolicy =
378
389
  (options.timeoutResponse && options.timeoutResponse !== '') ||
379
- (options.timeoutIdle && options.timeoutIdle !== '')
390
+ (options.timeoutIdle && options.timeoutIdle !== '')
380
391
  ? {
381
- response: options.timeoutResponse,
382
- idle: options.timeoutIdle,
383
- }
392
+ response: options.timeoutResponse,
393
+ idle: options.timeoutIdle,
394
+ }
384
395
  : undefined;
385
396
  const globalRetryPolicy =
386
397
  options.retryCount ||
387
- options.retryCount === 0 ||
388
- (options.retryPerTryTimeout && options.retryPerTryTimeout !== '')
398
+ options.retryCount === 0 ||
399
+ (options.retryPerTryTimeout && options.retryPerTryTimeout !== '')
389
400
  ? {
390
- count: options.retryCount,
391
- perTryTimeout: options.retryPerTryTimeout,
392
- }
401
+ count: options.retryCount,
402
+ perTryTimeout: options.retryPerTryTimeout,
403
+ }
393
404
  : undefined;
394
405
  if (!options.disableDeploymentProxy)
395
406
  for (const conditionObj of pathPortAssignment) {
@@ -577,12 +588,13 @@ metadata:
577
588
  namespace: ${options.namespace}
578
589
  spec:
579
590
  virtualhost:
580
- fqdn: ${host}${env === 'development'
581
- ? ''
582
- : `
591
+ fqdn: ${host}${
592
+ env === 'development'
593
+ ? ''
594
+ : `
583
595
  tls:
584
596
  secretName: ${host}`
585
- }
597
+ }
586
598
  routes:`;
587
599
  },
588
600
 
@@ -604,13 +616,11 @@ spec:
604
616
  * @param {string} options.traffic - Traffic status for the deployment.
605
617
  * @param {string} options.replicas - Number of replicas for the deployment.
606
618
  * @param {string} options.node - Node name for resource allocation.
607
- * @param {boolean} options.restoreHosts - Whether to restore the hosts file.
608
619
  * @param {boolean} options.disableUpdateDeployment - Whether to disable deployment updates.
609
620
  * @param {boolean} options.disableUpdateProxy - Whether to disable proxy updates.
610
621
  * @param {boolean} options.disableDeploymentProxy - Whether to disable deployment proxy.
611
622
  * @param {boolean} options.disableUpdateVolume - Whether to disable volume updates.
612
623
  * @param {boolean} options.status - Whether to display deployment status.
613
- * @param {boolean} options.etcHosts - Whether to display the /etc/hosts file.
614
624
  * @param {boolean} options.disableUpdateUnderpostConfig - Whether to disable Underpost config updates.
615
625
  * @param {string} [options.namespace] - Kubernetes namespace for the deployment.
616
626
  * @param {string} [options.timeoutResponse] - Timeout response setting for the deployment.
@@ -648,13 +658,11 @@ spec:
648
658
  traffic: '',
649
659
  replicas: '',
650
660
  node: '',
651
- restoreHosts: false,
652
661
  disableUpdateDeployment: false,
653
662
  disableUpdateProxy: false,
654
663
  disableDeploymentProxy: false,
655
664
  disableUpdateVolume: false,
656
665
  status: false,
657
- etcHosts: false,
658
666
  disableUpdateUnderpostConfig: false,
659
667
  namespace: '',
660
668
  timeoutResponse: '',
@@ -738,14 +746,6 @@ EOF`);
738
746
  return;
739
747
  }
740
748
  if (!options.disableUpdateUnderpostConfig) Underpost.deploy.configMap(env);
741
- let renderHosts = '';
742
- let etcHosts = [];
743
- if (options.restoreHosts === true) {
744
- const factoryResult = Underpost.deploy.etcHostFactory(etcHosts);
745
- renderHosts = factoryResult.renderHosts;
746
- logger.info(renderHosts);
747
- return;
748
- }
749
749
 
750
750
  for (const _deployId of deployList.split(',')) {
751
751
  const deployId = _deployId.trim();
@@ -753,6 +753,10 @@ EOF`);
753
753
  if (options.expose === true) {
754
754
  const kindType = options.kindType ? options.kindType : 'svc';
755
755
  const svc = Underpost.kubectl.get(deployId, kindType)[0];
756
+ if (!svc) {
757
+ logger.error(`No ${kindType} found matching '${deployId}', skipping expose`);
758
+ continue;
759
+ }
756
760
  const port = options.exposePort
757
761
  ? parseInt(options.exposePort)
758
762
  : options.port
@@ -802,7 +806,6 @@ EOF`);
802
806
  if (Underpost.deploy.isValidTLSContext({ host, env, options }))
803
807
  shellExec(`sudo kubectl delete Certificate ${host} -n ${namespace} --ignore-not-found`);
804
808
  }
805
- if (!options.remove) etcHosts.push(host);
806
809
  }
807
810
 
808
811
  const manifestsPath =
@@ -823,15 +826,6 @@ EOF`);
823
826
  shellExec(`sudo kubectl apply -f ./${manifestsPath}/secret.yaml -n ${namespace}`);
824
827
  }
825
828
  }
826
- if (options.etcHosts === true) {
827
- const factoryResult = Underpost.deploy.etcHostFactory(etcHosts);
828
- renderHosts = factoryResult.renderHosts;
829
- }
830
- if (renderHosts)
831
- logger.info(
832
- `
833
- ` + renderHosts,
834
- );
835
829
  },
836
830
  /**
837
831
  * Checks the status of a deployment.
@@ -1002,10 +996,10 @@ EOF`);
1002
996
  shellExec(`kubectl delete pv ${pvId} --ignore-not-found`);
1003
997
  shellExec(`kubectl apply -f - -n ${namespace} <<EOF
1004
998
  ${Underpost.deploy.persistentVolumeFactory({
1005
- hostPath: rootVolumeHostPath,
1006
- pvcId,
1007
- namespace,
1008
- })}
999
+ hostPath: rootVolumeHostPath,
1000
+ pvcId,
1001
+ namespace,
1002
+ })}
1009
1003
  EOF
1010
1004
  `);
1011
1005
  },
@@ -1064,22 +1058,23 @@ ${secret ? ` readOnly: true\n` : ''}`;
1064
1058
 
1065
1059
  _volumes += `
1066
1060
  - name: ${volumeName}
1067
- ${emptyDir
1068
- ? ` emptyDir: {}`
1069
- : secret
1070
- ? ` secret:
1061
+ ${
1062
+ emptyDir
1063
+ ? ` emptyDir: {}`
1064
+ : secret
1065
+ ? ` secret:
1071
1066
  secretName: ${secret}`
1072
- : configMap
1073
- ? ` configMap:
1067
+ : configMap
1068
+ ? ` configMap:
1074
1069
  name: ${configMap}`
1075
- : claimName
1076
- ? ` persistentVolumeClaim:
1070
+ : claimName
1071
+ ? ` persistentVolumeClaim:
1077
1072
  claimName: ${claimName}`
1078
- : ` hostPath:
1073
+ : ` hostPath:
1079
1074
  path: ${volumeHostPath}
1080
1075
  type: ${volumeType}
1081
1076
  `
1082
- }
1077
+ }
1083
1078
 
1084
1079
  `;
1085
1080
  });
@@ -1131,42 +1126,6 @@ spec:
1131
1126
  storage: 5Gi`;
1132
1127
  },
1133
1128
 
1134
- /**
1135
- * Creates a hosts file for a deployment.
1136
- * @param {Array<string>} hosts - List of hosts to be added to the hosts file.
1137
- * @param {object} options - Options for the hosts file creation.
1138
- * @param {boolean} options.append - Whether to append to the existing hosts file.
1139
- * @returns {object} - Object containing the rendered hosts file.
1140
- * @memberof UnderpostDeploy
1141
- */
1142
- etcHostFactory(hosts = [], options = { append: false }) {
1143
- hosts = hosts.map((host) => {
1144
- try {
1145
- if (!host.startsWith('http')) host = `http://${host}`;
1146
- const hostname = new URL(host).hostname;
1147
- logger.info('Hostname extract valid', { host, hostname });
1148
- return hostname;
1149
- } catch (e) {
1150
- logger.warn('No hostname extract valid', host);
1151
- return host;
1152
- }
1153
- });
1154
- const renderHosts = `127.0.0.1 ${hosts.join(
1155
- ' ',
1156
- )} localhost localhost.localdomain localhost4 localhost4.localdomain4
1157
- ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6`;
1158
-
1159
- if (options && options.append && fs.existsSync(`/etc/hosts`)) {
1160
- fs.writeFileSync(
1161
- `/etc/hosts`,
1162
- fs.readFileSync(`/etc/hosts`, 'utf8') +
1163
- `
1164
- ${renderHosts}`,
1165
- 'utf8',
1166
- );
1167
- } else fs.writeFileSync(`/etc/hosts`, renderHosts, 'utf8');
1168
- return { renderHosts };
1169
- },
1170
1129
  /**
1171
1130
  * Checks if a TLS context is valid.
1172
1131
  * @param {object} options - Options for the check.
@@ -1191,13 +1150,13 @@ ${renderHosts}`,
1191
1150
  * `src/client/public/nexodev/docs/references/Deploy custom instance to K8S.md`.
1192
1151
  *
1193
1152
  * Container-status:
1194
- * `underpost config get container-status` is still read from each pod for
1195
- * the display column and for early-abort on `error`, but it is no longer
1196
- * required to equal `<deploy>-running-deployment` to finish the monitor.
1197
- * Older implementations gated on it; that produced false timeouts for
1198
- * runtimes (e.g. cyberia-server's Go binary, cyberia-client's server.py)
1199
- * whose startup sequence didn't reliably overwrite the
1200
- * `initializing-deployment` stamp set by the postStart lifecycle hook.
1153
+ * `underpost config get container-status` is read from each pod for both
1154
+ * the display column and as a second ready gate alongside the K8S Ready
1155
+ * condition. Both must be satisfied before the monitor exits:
1156
+ * 1. K8S readinessProbe (TCP socket) ensures the port is bound.
1157
+ * 2. container-status == `<deploy>-<env>-running-deployment` — ensures
1158
+ * the application has completed its own startup sequence.
1159
+ * Early-abort on `error` container-status remains in effect.
1201
1160
  *
1202
1161
  * @param {string} deployId - Deployment ID for which the ready status is being monitored.
1203
1162
  * @param {string} env - Environment for which the ready status is being monitored.
@@ -1234,10 +1193,13 @@ ${renderHosts}`,
1234
1193
  }
1235
1194
  };
1236
1195
 
1237
-
1238
1196
  for (let i = 0; i < maxIterations; i++) {
1239
1197
  const result = await Underpost.deploy.checkDeploymentReadyStatus(
1240
- deployId, env, targetTraffic, ignorePods, namespace,
1198
+ deployId,
1199
+ env,
1200
+ targetTraffic,
1201
+ ignorePods,
1202
+ namespace,
1241
1203
  );
1242
1204
 
1243
1205
  const allPods = [...result.readyPods, ...result.notReadyPods];
@@ -1246,20 +1208,22 @@ ${renderHosts}`,
1246
1208
  for (const pod of allPods) {
1247
1209
  if (!pod?.NAME) continue;
1248
1210
  const status = readContainerStatus(pod.NAME);
1249
- if (status === 'error')
1250
- throw new Error(`Pod ${pod.NAME} has error status`);
1211
+ if (status === 'error') throw new Error(`Pod ${pod.NAME} has error status`);
1251
1212
  podStatusCache.set(pod.NAME, status);
1252
1213
  }
1253
1214
 
1254
1215
  const allPodsK8sReady = allPods.length > 0 && result.notReadyPods.length === 0;
1255
1216
 
1217
+ const allPodsStatusReady =
1218
+ allPods.length > 0 && allPods.every((pod) => podStatusCache.get(pod.NAME) === expectedContainerStatus);
1219
+
1256
1220
  // Print snapshot for every pod — annotate when container-status hasn't caught
1257
- // up to the K8S Ready condition (informational only; no longer gates exit).
1221
+ // up to the K8S Ready condition yet.
1258
1222
  for (const pod of allPods) {
1259
1223
  const status = podStatusCache.get(pod.NAME) || containerStatusDefault;
1260
1224
  const podStatus = pod.STATUS || 'Unknown';
1261
1225
  const statusMatchesExpected = status === expectedContainerStatus;
1262
- const statusDisplay = statusMatchesExpected ? status : `${status} (advisory)`;
1226
+ const statusDisplay = statusMatchesExpected ? status : `${status} (pending)`;
1263
1227
 
1264
1228
  console.log(
1265
1229
  'Target pod:',
@@ -1271,10 +1235,11 @@ ${renderHosts}`,
1271
1235
  );
1272
1236
  }
1273
1237
 
1274
- // Finish as soon as every pod is K8S-Ready. The readinessProbe (TCP
1275
- // socket on the listening port) is the source of truth a runtime
1276
- // that can't bind never reaches Ready and the monitor will time out.
1277
- if (allPodsK8sReady) {
1238
+ // Both K8S readinessProbe AND container-status must be satisfied before
1239
+ // declaring the deployment ready. The TCP probe ensures the port is bound;
1240
+ // container-status == running-deployment ensures the application has
1241
+ // completed its own startup sequence so traffic is not switched prematurely.
1242
+ if (allPodsK8sReady && allPodsStatusReady) {
1278
1243
  logger.info(`${tag} | All pods Ready (K8S readinessProbe satisfied)`);
1279
1244
  return result;
1280
1245
  }
package/src/cli/fs.js CHANGED
@@ -127,7 +127,11 @@ class UnderpostFileStorage {
127
127
  if (options.git === true) {
128
128
  const gitPath = hasPathFilter ? basePath : '.';
129
129
  shellExec(`cd ${gitPath} && git add .`);
130
- shellExec(`underpost cmt ${gitPath} feat`);
130
+ shellExec(`underpost cmt ${gitPath} feat`, {
131
+ silentOnError: true,
132
+ silent: true,
133
+ disableLog: true,
134
+ });
131
135
  }
132
136
 
133
137
  return;
@@ -155,7 +159,7 @@ class UnderpostFileStorage {
155
159
  if (options.git === true) {
156
160
  Underpost.repo.initLocalRepo({ path });
157
161
  shellExec(`cd ${path} && git add . && git commit -m "Base pull state"`, {
158
- silentOnError: true
162
+ silentOnError: true,
159
163
  });
160
164
  }
161
165
  } else {
@@ -175,7 +179,11 @@ class UnderpostFileStorage {
175
179
  Underpost.fs.writeStorageConf(storage, storageConf);
176
180
  if (options.git === true) {
177
181
  shellExec(`cd ${path} && git add .`);
178
- shellExec(`underpost cmt ${path} feat`);
182
+ shellExec(`underpost cmt ${path} feat`, {
183
+ silentOnError: true,
184
+ silent: true,
185
+ disableLog: true,
186
+ });
179
187
  }
180
188
  },
181
189
  /**