underpost 2.90.4 → 2.92.0

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/src/cli/deploy.js CHANGED
@@ -387,6 +387,8 @@ spec:
387
387
  * @param {boolean} options.etcHosts - Whether to display the /etc/hosts file.
388
388
  * @param {boolean} options.disableUpdateUnderpostConfig - Whether to disable Underpost config updates.
389
389
  * @param {string} [options.namespace] - Kubernetes namespace for the deployment.
390
+ * @param {string} [options.kindType] - Type of Kubernetes resource to retrieve information for.
391
+ * @param {number} [options.port] - Port number for exposing the deployment.
390
392
  * @returns {Promise<void>} - Promise that resolves when the deployment process is complete.
391
393
  * @memberof UnderpostDeploy
392
394
  */
@@ -416,6 +418,8 @@ spec:
416
418
  etcHosts: false,
417
419
  disableUpdateUnderpostConfig: false,
418
420
  namespace: '',
421
+ kindType: '',
422
+ port: 0,
419
423
  },
420
424
  ) {
421
425
  const namespace = options.namespace ? options.namespace : 'default';
@@ -495,13 +499,20 @@ EOF`);
495
499
  const deployId = _deployId.trim();
496
500
  if (!deployId) continue;
497
501
  if (options.expose === true) {
498
- const svc = UnderpostDeploy.API.get(deployId, 'svc')[0];
499
- const port = parseInt(svc[`PORT(S)`].split('/TCP')[0]);
502
+ const kindType = options.kindType ? options.kindType : 'svc';
503
+ const svc = UnderpostDeploy.API.get(deployId, kindType)[0];
504
+ const port = options.port
505
+ ? options.port
506
+ : kindType !== 'svc'
507
+ ? 80
508
+ : parseInt(svc[`PORT(S)`].split('/TCP')[0]);
500
509
  logger.info(deployId, {
501
510
  svc,
502
511
  port,
503
512
  });
504
- shellExec(`sudo kubectl port-forward -n default svc/${svc.NAME} ${port}:${port}`, { async: true });
513
+ shellExec(`sudo kubectl port-forward -n ${namespace} ${kindType}/${svc.NAME} ${port}:${port}`, {
514
+ async: true,
515
+ });
505
516
  continue;
506
517
  }
507
518
 
@@ -657,15 +668,9 @@ EOF`);
657
668
  for (const pod of pods) {
658
669
  const { NAME } = pod;
659
670
  if (ignoresNames && ignoresNames.find((t) => NAME.trim().toLowerCase().match(t.trim().toLowerCase()))) continue;
660
- if (
661
- shellExec(`sudo kubectl exec -i ${NAME} -- sh -c "${cmd}"`, { stdout: true }).match(
662
- `${deployId}-${env}-running-deployment`,
663
- )
664
- ) {
665
- readyPods.push(pod);
666
- } else {
667
- notReadyPods.push(pod);
668
- }
671
+ const out = shellExec(`sudo kubectl exec -i ${NAME} -- sh -c "${cmd}"`, { stdout: true, silent: true });
672
+ const ready = out.match(`${deployId}-${env}-running-deployment`);
673
+ ready ? readyPods.push(pod) : notReadyPods.push(pod);
669
674
  }
670
675
  return {
671
676
  ready: pods.length > 0 && notReadyPods.length === 0,
package/src/cli/env.js CHANGED
@@ -63,7 +63,7 @@ class UnderpostRootEnv {
63
63
  const exeRootPath = `${getNpmRootPath()}/underpost`;
64
64
  const envPath = `${exeRootPath}/.env`;
65
65
  if (!fs.existsSync(envPath)) {
66
- logger.error(`Unable to find underpost root environment`);
66
+ logger.warn(`Empty environment variables`);
67
67
  return undefined;
68
68
  }
69
69
  const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
@@ -80,7 +80,7 @@ class UnderpostRootEnv {
80
80
  const exeRootPath = `${getNpmRootPath()}/underpost`;
81
81
  const envPath = `${exeRootPath}/.env`;
82
82
  if (!fs.existsSync(envPath)) {
83
- logger.error(`Unable to find underpost root environment`);
83
+ logger.warn(`Empty environment variables`);
84
84
  return {};
85
85
  }
86
86
  const env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
package/src/cli/index.js CHANGED
@@ -29,6 +29,11 @@ program
29
29
  .option('--deploy-id <deploy-id>', 'Crete deploy ID conf env files')
30
30
  .option('--sub-conf <sub-conf>', 'Create sub conf env files')
31
31
  .option('--cluster', 'Create deploy ID cluster files and sync to current cluster')
32
+ .option('--build-repos', 'Create deploy ID repositories')
33
+ .option('--build', 'Build the deployment to pwa-microservices-template (requires --deploy-id)')
34
+ .option('--clean-template', 'Clean the build directory (pwa-microservices-template)')
35
+ .option('--sync-conf', 'Sync configuration to private repositories (requires --deploy-id)')
36
+ .option('--purge', 'Remove deploy ID conf and all related repositories (requires --deploy-id)')
32
37
  .option('--dev', 'Sets the development cli context')
33
38
  .description('Initializes a new Underpost project, service, or configuration.')
34
39
  .action(Underpost.repo.new);
@@ -238,9 +243,66 @@ program
238
243
 
239
244
  program
240
245
  .command('ip')
246
+ .argument('[ips]', 'Optional args comma-separated list of IP to process.')
241
247
  .option('--copy', 'Copies the IP addresses to the clipboard.')
248
+ .option('--ban-ingress-add', 'Adds IP addresses to banned ingress list.')
249
+ .option('--ban-ingress-remove', 'Removes IP addresses from banned ingress list.')
250
+ .option('--ban-ingress-list', 'Lists all banned ingress IP addresses.')
251
+ .option('--ban-ingress-clear', 'Clears all banned ingress IP addresses.')
252
+ .option('--ban-egress-add', 'Adds IP addresses to banned egress list.')
253
+ .option('--ban-egress-remove', 'Removes IP addresses from banned egress list.')
254
+ .option('--ban-egress-list', 'Lists all banned egress IP addresses.')
255
+ .option('--ban-egress-clear', 'Clears all banned egress IP addresses.')
256
+ .option('--ban-both-add', 'Adds IP addresses to both banned ingress and egress lists.')
257
+ .option('--ban-both-remove', 'Removes IP addresses from both banned ingress and egress lists.')
242
258
  .description('Displays the current public machine IP addresses.')
243
- .action(async (options) => {
259
+ .action(async (ips = '', options) => {
260
+ const ipList = ips
261
+ ? ips
262
+ .split(',')
263
+ .map((i) => i.trim())
264
+ .filter(Boolean)
265
+ : [];
266
+
267
+ if (options.banIngressAdd) {
268
+ return ipList.forEach((ip) => Dns.banIngress(ip));
269
+ }
270
+ if (options.banIngressRemove) {
271
+ return ipList.forEach((ip) => Dns.unbanIngress(ip));
272
+ }
273
+ if (options.banIngressList) {
274
+ return Dns.listBannedIngress();
275
+ }
276
+ if (options.banIngressClear) {
277
+ return Dns.clearBannedIngress();
278
+ }
279
+
280
+ if (options.banEgressAdd) {
281
+ return ipList.forEach((ip) => Dns.banEgress(ip));
282
+ }
283
+ if (options.banEgressRemove) {
284
+ return ipList.forEach((ip) => Dns.unbanEgress(ip));
285
+ }
286
+ if (options.banEgressList) {
287
+ return Dns.listBannedEgress();
288
+ }
289
+ if (options.banEgressClear) {
290
+ return Dns.clearBannedEgress();
291
+ }
292
+
293
+ if (options.banBothAdd) {
294
+ return ipList.forEach((ip) => {
295
+ Dns.banIngress(ip);
296
+ Dns.banEgress(ip);
297
+ });
298
+ }
299
+ if (options.banBothRemove) {
300
+ return ipList.forEach((ip) => {
301
+ Dns.unbanIngress(ip);
302
+ Dns.unbanEgress(ip);
303
+ });
304
+ }
305
+
244
306
  const ip = await Dns.getPublicIp();
245
307
  if (options.copy) return pbcopy(ip);
246
308
  return console.log(ip);
@@ -331,6 +393,8 @@ program
331
393
  .option('--restore-hosts', 'Restores default `/etc/hosts` entries.')
332
394
  .option('--disable-update-underpost-config', 'Disables updates to Underpost configuration during deployment.')
333
395
  .option('--namespace <namespace>', 'Kubernetes namespace for deployment operations (defaults to "default").')
396
+ .option('--kind-type <kind-type>', 'Specifies the Kind cluster type for deployment operations.')
397
+ .option('--port <port>', 'Sets up port forwarding from local to remote ports.')
334
398
  .description('Manages application deployments, defaulting to deploying development pods.')
335
399
  .action(Underpost.deploy.callback);
336
400
 
@@ -394,16 +458,27 @@ program
394
458
  .argument('<deploy-list>', 'A comma-separated list of deployment IDs (e.g., "default-a,default-b").')
395
459
  .option('--import', 'Imports container backups from specified repositories.')
396
460
  .option('--export', 'Exports container backups to specified repositories.')
397
- .option('--pod-name <pod-name>', 'Optional: Specifies the pod context for database operations.')
398
- .option('--collections <collections>', 'A comma-separated list of database collections to operate on.')
461
+ .option(
462
+ '--pod-name <pod-name>',
463
+ 'Comma-separated list of pod names or patterns (supports wildcards like "mariadb-*").',
464
+ )
465
+ .option('--node-name <node-name>', 'Comma-separated list of node names to filter pods by their node placement.')
466
+ .option('--label-selector <selector>', 'Kubernetes label selector for filtering pods (e.g., "app=mariadb").')
467
+ .option('--all-pods', 'Target all matching pods instead of just the first one.')
468
+ .option('--primary-pod', 'Automatically detect and use MongoDB primary pod (MongoDB only).')
469
+ .option('--stats', 'Display database statistics (collection/table names with document/row counts).')
470
+ .option('--collections <collections>', 'Comma-separated list of database collections to operate on.')
399
471
  .option('--out-path <out-path>', 'Specifies a custom output path for backups.')
400
- .option('--drop', 'Drops the specified databases or collections.')
401
- .option('--preserveUUID', 'Preserves UUIDs during database operations.')
402
- .option('--git', 'Uploads database backups to GitHub.')
403
- .option('--hosts <hosts>', 'A comma-separated list of database hosts.')
404
- .option('--paths <paths>', 'A comma-separated list of paths for database files.')
405
- .option('--ns <ns-name>', 'Optional: Specifies the namespace context for database operations.')
406
- .description('Manages database operations, including import, export, and collection management.')
472
+ .option('--drop', 'Drops the specified databases or collections before importing.')
473
+ .option('--preserveUUID', 'Preserves UUIDs during database import operations.')
474
+ .option('--git', 'Enables Git integration for backup version control (clone, pull, commit, push to GitHub).')
475
+ .option('--hosts <hosts>', 'Comma-separated list of database hosts to filter operations.')
476
+ .option('--paths <paths>', 'Comma-separated list of paths to filter database operations.')
477
+ .option('--ns <ns-name>', 'Kubernetes namespace context for database operations (defaults to "default").')
478
+ .option('--dry-run', 'Simulates operations without executing them (useful for testing).')
479
+ .description(
480
+ 'Manages database operations with support for MariaDB and MongoDB, including import/export, multi-pod targeting, and Git integration.',
481
+ )
407
482
  .action(Underpost.db.callback);
408
483
 
409
484
  program
@@ -498,8 +573,22 @@ program
498
573
  // 'ssh' command: SSH management
499
574
  program
500
575
  .command('ssh')
576
+ .option('--deploy-id <deploy-id>', 'Sets deploy id context for ssh operations.')
501
577
  .option('--generate', 'Generates new ssh credential and stores it in current private keys file storage.')
502
- .description('Import and start ssh server and client based on current default deployment ID.')
578
+ .option('--user <user>', 'Sets custom ssh user')
579
+ .option('--password <password>', 'Sets custom ssh password')
580
+ .option('--host <host>', 'Sets custom ssh host')
581
+ .option('--port <port>', 'Sets custom ssh port')
582
+ .option('--filter <filter>', 'Filters ssh user credentials from current private keys file storage.')
583
+ .option('--groups <groups>', 'Sets comma-separated ssh user groups for the ssh user credential.')
584
+ .option('--user-add', 'Adds a new ssh user credential to current private keys file storage.')
585
+ .option('--user-remove', 'Removes an existing ssh user credential from current private keys file storage.')
586
+ .option('--user-ls', 'Lists all ssh user credentials from current private keys file storage.')
587
+ .option('--start', 'Starts an SSH session with the specified credentials.')
588
+ .option('--reset', 'Resets ssh configuration and deletes all stored credentials.')
589
+ .option('--keys-list', 'Lists all ssh keys from current private keys file storage.')
590
+ .option('--hosts-list', 'Lists all ssh hosts from current private keys file storage.')
591
+ .option('--disable-password', 'Disables password authentication for the SSH session.')
503
592
  .action(Underpost.ssh.callback);
504
593
 
505
594
  // 'run' command: Run a script
@@ -234,21 +234,145 @@ class UnderpostRepository {
234
234
  /**
235
235
  * Initializes a new Underpost repository, optionally setting up a deploy ID or sub-configuration.
236
236
  * @param {string} [projectName=''] - The name of the project to create.
237
- * @param {object} [options={ deployId: '', subConf: '', cluster: false, dev: false }] - Initialization options.
237
+ * @param {object} [options] - Initialization options.
238
238
  * @param {string} [options.deployId=''] - The deployment ID to set up.
239
239
  * @param {string} [options.subConf=''] - The sub-configuration to create.
240
240
  * @param {boolean} [options.cluster=false] - If true, sets up a clustered configuration.
241
241
  * @param {boolean} [options.dev=false] - If true, uses development settings.
242
+ * @param {boolean} [options.buildRepos=false] - If true, creates the deployment repositories (engine-*, engine-*-private, engine-*-cron-backups).
243
+ * @param {boolean} [options.purge=false] - If true, removes the deploy ID conf and all related repositories (requires deployId).
244
+ * @param {boolean} [options.cleanTemplate=false] - If true, cleans the pwa-microservices-template build directory.
245
+ * @param {boolean} [options.build=false] - If true, builds the deployment to pwa-microservices-template (requires deployId).
246
+ * @param {boolean} [options.syncConf=false] - If true, syncs configuration to private repositories (requires deployId).
242
247
  * @returns {Promise<boolean>} A promise that resolves when the initialization is complete.
243
248
  * @memberof UnderpostRepository
244
249
  */
245
- new(projectName, options = { deployId: '', subConf: '', cluster: false, dev: false }) {
250
+ new(
251
+ projectName,
252
+ options = {
253
+ deployId: '',
254
+ subConf: '',
255
+ cluster: false,
256
+ dev: false,
257
+ buildRepos: false,
258
+ purge: false,
259
+ cleanTemplate: false,
260
+ build: false,
261
+ syncConf: false,
262
+ },
263
+ ) {
246
264
  return new Promise(async (resolve, reject) => {
247
265
  try {
248
266
  await logger.setUpInfo();
249
267
  actionInitLog();
268
+
269
+ // Handle cleanTemplate operation
270
+ if (options.cleanTemplate) {
271
+ logger.info('Cleaning build directory');
272
+ const basePath = '../pwa-microservices-template';
273
+ shellExec(`cd ${basePath} && git reset`);
274
+ shellExec(`cd ${basePath} && git checkout .`);
275
+ shellExec(`cd ${basePath} && git clean -f -d`);
276
+ logger.info('Build directory cleaned successfully');
277
+ return resolve(true);
278
+ }
279
+
250
280
  if (options.deployId) {
251
- Config.deployIdFactory(options.deployId, options);
281
+ let deployId = options.deployId;
282
+ if (!deployId.startsWith('dd-')) deployId = `dd-${deployId}`;
283
+
284
+ // Handle purge operation
285
+ if (options.purge) {
286
+ logger.info(`Purging deploy ID: ${deployId}`);
287
+
288
+ const suffix = deployId.split('dd-')[1];
289
+ const repoName = `engine-${suffix}`;
290
+ const privateRepoName = `engine-${suffix}-private`;
291
+ const cronRepoName = `engine-${suffix}-cron-backups`;
292
+ const confFolder = `./engine-private/conf/${deployId}`;
293
+
294
+ // Remove conf folder
295
+ if (fs.existsSync(confFolder)) {
296
+ fs.removeSync(confFolder);
297
+ logger.info(`Removed conf folder: ${confFolder}`);
298
+ } else {
299
+ logger.warn(`Conf folder not found: ${confFolder}`);
300
+ }
301
+
302
+ // Remove repositories
303
+ const repos = [
304
+ { path: `../${repoName}`, name: repoName },
305
+ { path: `../${privateRepoName}`, name: privateRepoName },
306
+ { path: `../${cronRepoName}`, name: cronRepoName },
307
+ ];
308
+
309
+ for (const repo of repos) {
310
+ if (fs.existsSync(repo.path)) {
311
+ fs.removeSync(repo.path);
312
+ logger.info(`Removed repository: ${repo.path}`);
313
+ } else {
314
+ logger.warn(`Repository not found: ${repo.path}`);
315
+ }
316
+ }
317
+
318
+ logger.info(`Successfully purged deploy ID: ${deployId}`);
319
+ return resolve(true);
320
+ }
321
+
322
+ // Handle sync-conf operation
323
+ if (options.syncConf) {
324
+ logger.info(`Syncing configuration for deploy ID: ${deployId}`);
325
+ shellExec(`node bin/build ${deployId} conf`);
326
+ logger.info('Configuration synced successfully');
327
+ return resolve(true);
328
+ }
329
+
330
+ // Handle build operation
331
+ if (options.build) {
332
+ logger.info(`Building deployment for deploy ID: ${deployId}`);
333
+ shellExec(`node bin/build ${deployId}`);
334
+ logger.info('Build completed successfully');
335
+ return resolve(true);
336
+ }
337
+
338
+ // Normal deploy ID factory operation
339
+ const { deployId: normalizedDeployId } = Config.deployIdFactory(deployId, options);
340
+
341
+ if (options.buildRepos) {
342
+ const suffix = normalizedDeployId.split('dd-')[1];
343
+ const repoName = `engine-${suffix}`;
344
+ const privateRepoName = `engine-${suffix}-private`;
345
+ const cronRepoName = `engine-${suffix}-cron-backups`;
346
+ const repos = [
347
+ { path: `../${repoName}`, name: repoName },
348
+ { path: `../${privateRepoName}`, name: privateRepoName },
349
+ { path: `../${cronRepoName}`, name: cronRepoName },
350
+ ];
351
+
352
+ const username = process.env.GITHUB_USERNAME;
353
+ const token = process.env.GITHUB_TOKEN;
354
+
355
+ if (!username) {
356
+ logger.error('GITHUB_USERNAME environment variable not set');
357
+ return reject(false);
358
+ }
359
+
360
+ for (const repo of repos) {
361
+ if (!fs.existsSync(repo.path)) {
362
+ fs.mkdirSync(repo.path, { recursive: true });
363
+ logger.info(`Created repository directory: ${repo.path}`);
364
+ }
365
+
366
+ // Initialize git repository
367
+ shellExec(`cd ${repo.path} && git init`, { disableLog: false });
368
+ logger.info(`Initialized git repository in: ${repo.path}`);
369
+
370
+ // Add remote origin
371
+ const remoteUrl = `https://${token ? `${token}@` : ''}github.com/${username}/${repo.name}.git`;
372
+ shellExec(`cd ${repo.path} && git remote add origin ${remoteUrl}`, { disableLog: false });
373
+ logger.info(`Added remote origin for: ${repo.name}`);
374
+ }
375
+ }
252
376
  return resolve(true);
253
377
  }
254
378
  if (projectName) {
package/src/cli/run.js CHANGED
@@ -19,6 +19,7 @@ import UnderpostTest from './test.js';
19
19
  import fs from 'fs-extra';
20
20
  import { range, setPad, timer } from '../client/components/core/CommonJs.js';
21
21
  import UnderpostDeploy from './deploy.js';
22
+ import UnderpostDB from './db.js';
22
23
  import UnderpostRootEnv from './env.js';
23
24
  import UnderpostRepository from './repository.js';
24
25
  import os from 'os';
@@ -250,10 +251,29 @@ class UnderpostRun {
250
251
  );
251
252
  shellExec(`${baseCommand} cluster${options.dev ? ' --dev' : ''} --valkey --pull-image`);
252
253
  }
253
- shellExec(`${baseCommand} deploy --expose --disable-update-underpost-config mongo`, { async: true });
254
- shellExec(`${baseCommand} deploy --expose --disable-update-underpost-config valkey`, { async: true });
254
+
255
255
  {
256
- const hostListenResult = UnderpostDeploy.API.etcHostFactory(mongoHosts);
256
+ // Detect MongoDB primary pod using centralized method
257
+ let primaryMongoHost = 'mongodb-0.mongodb-service';
258
+ try {
259
+ const primaryPodName = UnderpostDB.API.getMongoPrimaryPodName({
260
+ namespace: options.namespace,
261
+ podName: 'mongodb-0',
262
+ });
263
+ // shellExec(`${baseCommand} deploy --expose --disable-update-underpost-config mongo`, { async: true });
264
+ shellExec(`kubectl port-forward -n ${options.namespace} pod/${primaryPodName} 27017:27017`, { async: true });
265
+ shellExec(
266
+ `${baseCommand} deploy --expose --namespace ${options.namespace} --disable-update-underpost-config valkey`,
267
+ { async: true },
268
+ );
269
+ } catch (error) {
270
+ logger.warn('Failed to detect MongoDB primary pod, using default', {
271
+ error: error.message,
272
+ default: primaryMongoHost,
273
+ });
274
+ }
275
+
276
+ const hostListenResult = UnderpostDeploy.API.etcHostFactory([primaryMongoHost]);
257
277
  logger.info(hostListenResult.renderHosts);
258
278
  }
259
279
  },
@@ -268,7 +288,7 @@ class UnderpostRun {
268
288
  metadata: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
269
289
  const ports = '6379,27017';
270
290
  shellExec(`node bin run kill '${ports}'`);
271
- shellExec(`node bin run dev-cluster --dev --expose`, { async: true });
291
+ shellExec(`node bin run dev-cluster --dev --expose --namespace ${options.namespace}`, { async: true });
272
292
  console.log('Loading fordward services...');
273
293
  await timer(5000);
274
294
  shellExec(`node bin metadata --generate ${path}`);
@@ -408,8 +428,8 @@ class UnderpostRun {
408
428
  * @param {Object} options - The default underpost runner options for customizing workflow
409
429
  * @memberof UnderpostRun
410
430
  */
411
- clean: (path, options = UnderpostRun.DEFAULT_OPTION) => {
412
- shellCd(path ?? `/home/dd/engine`);
431
+ clean: (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
432
+ shellCd(path ? path : `/home/dd/engine`);
413
433
  shellExec(`node bin/deploy clean-core-repo`);
414
434
  },
415
435
  /**
@@ -422,16 +442,25 @@ class UnderpostRun {
422
442
  pull: (path, options = UnderpostRun.DEFAULT_OPTION) => {
423
443
  if (!fs.existsSync(`/home/dd`) || !fs.existsSync(`/home/dd/engine`)) {
424
444
  fs.mkdirSync(`/home/dd`, { recursive: true });
425
- shellExec(`cd /home/dd && underpost clone ${process.env.GITHUB_USERNAME}/engine`);
445
+ shellExec(`cd /home/dd && underpost clone ${process.env.GITHUB_USERNAME}/engine`, {
446
+ silent: true,
447
+ });
426
448
  } else {
427
449
  shellExec(`underpost run clean`);
428
- shellExec(`cd /home/dd/engine && underpost pull . ${process.env.GITHUB_USERNAME}/engine`);
450
+ shellExec(`cd /home/dd/engine && underpost pull . ${process.env.GITHUB_USERNAME}/engine`, {
451
+ silent: true,
452
+ });
429
453
  }
430
454
  if (!fs.existsSync(`/home/dd/engine/engine-private`))
431
- shellExec(`cd /home/dd/engine && underpost clone ${process.env.GITHUB_USERNAME}/engine-private`);
455
+ shellExec(`cd /home/dd/engine && underpost clone ${process.env.GITHUB_USERNAME}/engine-private`, {
456
+ silent: true,
457
+ });
432
458
  else
433
459
  shellExec(
434
460
  `cd /home/dd/engine/engine-private && underpost pull . ${process.env.GITHUB_USERNAME}/engine-private`,
461
+ {
462
+ silent: true,
463
+ },
435
464
  );
436
465
  },
437
466
  /**
@@ -975,7 +1004,7 @@ EOF
975
1004
  const successInstance = await UnderpostTest.API.statusMonitor('adminer', 'Running', 'pods', 1000, 60 * 10);
976
1005
 
977
1006
  if (successInstance) {
978
- shellExec(`underpost deploy --expose adminer`);
1007
+ shellExec(`underpost deploy --expose adminer --namespace ${options.namespace}`);
979
1008
  }
980
1009
  },
981
1010
 
@@ -1174,7 +1203,7 @@ EOF
1174
1203
  envObj.DEV_PROXY_PORT_OFFSET = options.devProxyPortOffset;
1175
1204
  writeEnv(envPath, envObj);
1176
1205
  }
1177
- shellExec(`node bin run dev-cluster --expose`, { async: true });
1206
+ shellExec(`node bin run dev-cluster --expose --namespace ${options.namespace}`, { async: true });
1178
1207
  {
1179
1208
  const cmd = `npm run dev-api ${deployId} ${subConf} ${host} ${_path} ${clientHostPort}${options.tls ? ' tls' : ''}`;
1180
1209
  options.terminal ? openTerminal(cmd) : shellExec(cmd, { async: true });