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/.github/workflows/release.cd.yml +7 -7
- package/README.md +5 -5
- package/bin/deploy.js +0 -127
- package/cli.md +93 -26
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +1 -1
- package/scripts/rocky-setup.sh +1 -0
- package/src/cli/db.js +1148 -197
- package/src/cli/deploy.js +17 -12
- package/src/cli/env.js +2 -2
- package/src/cli/index.js +100 -11
- package/src/cli/repository.js +127 -3
- package/src/cli/run.js +40 -11
- package/src/cli/ssh.js +424 -13
- package/src/client/components/core/CommonJs.js +0 -1
- package/src/db/mongo/MongooseDB.js +5 -1
- package/src/index.js +1 -1
- package/src/server/dns.js +154 -0
- package/src/server/start.js +2 -0
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
|
|
499
|
-
const
|
|
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
|
|
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
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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.
|
|
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.
|
|
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(
|
|
398
|
-
|
|
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', '
|
|
403
|
-
.option('--hosts <hosts>', '
|
|
404
|
-
.option('--paths <paths>', '
|
|
405
|
-
.option('--ns <ns-name>', '
|
|
406
|
-
.
|
|
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
|
-
.
|
|
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
|
package/src/cli/repository.js
CHANGED
|
@@ -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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
254
|
-
shellExec(`${baseCommand} deploy --expose --disable-update-underpost-config valkey`, { async: true });
|
|
254
|
+
|
|
255
255
|
{
|
|
256
|
-
|
|
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
|
|
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 });
|