underpost 2.8.838 → 2.8.839
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/README.md +4 -2
- package/cli.md +13 -21
- package/docker-compose.yml +1 -1
- package/manifests/deployment/dd-template-development/deployment.yaml +2 -2
- package/manifests/maas/gpu-diag.sh +19 -0
- package/package.json +1 -1
- package/src/cli/cluster.js +13 -7
- package/src/cli/deploy.js +9 -0
- package/src/cli/index.js +2 -1
- package/src/cli/run.js +54 -31
- package/src/index.js +1 -1
- package/src/server/process.js +9 -2
package/README.md
CHANGED
|
@@ -25,10 +25,11 @@ template
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
|
|
28
29
|
<!-- badges -->
|
|
29
30
|
|
|
30
31
|
|
|
31
|
-
[](https://github.com/underpostnet/engine/actions/workflows/docker-image.yml) [](https://github.com/underpostnet/engine/actions/workflows/coverall.yml) [](https://www.npmjs.com/package/underpost) [](https://github.com/underpostnet/engine/actions/workflows/docker-image.yml) [](https://github.com/underpostnet/engine/actions/workflows/coverall.yml) [](https://www.npmjs.com/package/underpost) [](https://socket.dev/npm/package/underpost/overview/2.8.839) [](https://coveralls.io/github/underpostnet/engine?branch=master) [](https://www.npmjs.org/package/underpost) [](https://www.npmjs.com/package/underpost)
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
<!-- end-badges -->
|
|
@@ -36,6 +37,7 @@ template
|
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
|
|
40
|
+
|
|
39
41
|
</div>
|
|
40
42
|
|
|
41
43
|
<div align="center">
|
|
@@ -80,7 +82,7 @@ Run dev client server
|
|
|
80
82
|
npm run dev
|
|
81
83
|
```
|
|
82
84
|
<!-- -->
|
|
83
|
-
## underpost ci/cd cli v2.8.
|
|
85
|
+
## underpost ci/cd cli v2.8.839
|
|
84
86
|
|
|
85
87
|
### Usage: `underpost [options] [command]`
|
|
86
88
|
```
|
package/cli.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
## underpost ci/cd cli v2.8.
|
|
1
|
+
## underpost ci/cd cli v2.8.839
|
|
2
2
|
|
|
3
3
|
### Usage: `underpost [options] [command]`
|
|
4
4
|
```
|
|
@@ -554,28 +554,20 @@ Options:
|
|
|
554
554
|
Runs a script from the specified path.
|
|
555
555
|
|
|
556
556
|
Arguments:
|
|
557
|
-
runner-id
|
|
558
|
-
|
|
559
|
-
monitor, tf-vae-test, deploy-job.
|
|
560
|
-
path The absolute or relative directory path
|
|
561
|
-
where the script is located.
|
|
557
|
+
runner-id The runner ID to run. Options: spark-template, gpu-env, tf-gpu-test, ide, monitor, tf-vae-test, deploy-job.
|
|
558
|
+
path The absolute or relative directory path where the script is located.
|
|
562
559
|
|
|
563
560
|
Options:
|
|
564
|
-
--command <command-array>
|
|
565
|
-
--args <args-array>
|
|
566
|
-
--dev
|
|
567
|
-
|
|
568
|
-
--
|
|
569
|
-
|
|
570
|
-
--
|
|
571
|
-
|
|
572
|
-
--
|
|
573
|
-
|
|
574
|
-
--container-name <container-name> Optional: Specifies the container name for
|
|
575
|
-
test execution.
|
|
576
|
-
--namespace <namespace> Optional: Specifies the namespace for test
|
|
577
|
-
execution.
|
|
578
|
-
-h, --help display help for command
|
|
561
|
+
--command <command-array> Array of commands to run.
|
|
562
|
+
--args <args-array> Array of arguments to pass to the command.
|
|
563
|
+
--dev Sets the development context environment for the script.
|
|
564
|
+
--pod-name <pod-name> Optional: Specifies the pod name for test execution.
|
|
565
|
+
--volume-host-path <volume-host-path> Optional: Specifies the volume host path for test execution.
|
|
566
|
+
--volume-mount-path <volume-mount-path> Optional: Specifies the volume mount path for test execution.
|
|
567
|
+
--image-name <image-name> Optional: Specifies the image name for test execution.
|
|
568
|
+
--container-name <container-name> Optional: Specifies the container name for test execution.
|
|
569
|
+
--namespace <namespace> Optional: Specifies the namespace for test execution.
|
|
570
|
+
-h, --help display help for command
|
|
579
571
|
|
|
580
572
|
```
|
|
581
573
|
|
package/docker-compose.yml
CHANGED
|
@@ -17,7 +17,7 @@ spec:
|
|
|
17
17
|
spec:
|
|
18
18
|
containers:
|
|
19
19
|
- name: dd-template-development-blue
|
|
20
|
-
image: localhost/rockylinux9-underpost:v2.8.
|
|
20
|
+
image: localhost/rockylinux9-underpost:v2.8.839
|
|
21
21
|
# resources:
|
|
22
22
|
# requests:
|
|
23
23
|
# memory: "124Ki"
|
|
@@ -100,7 +100,7 @@ spec:
|
|
|
100
100
|
spec:
|
|
101
101
|
containers:
|
|
102
102
|
- name: dd-template-development-green
|
|
103
|
-
image: localhost/rockylinux9-underpost:v2.8.
|
|
103
|
+
image: localhost/rockylinux9-underpost:v2.8.839
|
|
104
104
|
# resources:
|
|
105
105
|
# requests:
|
|
106
106
|
# memory: "124Ki"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# GPUs and drivers in use
|
|
2
|
+
sudo lspci -nnk | egrep -i 'vga|3d' -A3
|
|
3
|
+
|
|
4
|
+
# modules loaded relevant
|
|
5
|
+
lsmod | egrep 'nvidia|nouveau|amdgpu' || true
|
|
6
|
+
|
|
7
|
+
# if exists nvidia tool
|
|
8
|
+
nvidia-smi 2>/dev/null || echo "nvidia-smi no disponible / driver no cargado"
|
|
9
|
+
|
|
10
|
+
# kernel related errors
|
|
11
|
+
sudo dmesg | egrep -i 'nvidia|nouveau|amdgpu' --color=auto
|
|
12
|
+
|
|
13
|
+
# recent system errors / gdm / mutter / X
|
|
14
|
+
sudo journalctl -b -p err --no-pager | head -n 200
|
|
15
|
+
journalctl -b _COMM=gdm --no-pager | tail -n 200
|
|
16
|
+
journalctl -b _COMM=Xorg --no-pager | tail -n 200
|
|
17
|
+
|
|
18
|
+
# X log (if exists)
|
|
19
|
+
sudo grep -E "(EE|WW|NVIDIA|nouveau|amdgpu)" /var/log/Xorg.0.log || true
|
package/package.json
CHANGED
package/src/cli/cluster.js
CHANGED
|
@@ -519,7 +519,7 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`);
|
|
|
519
519
|
|
|
520
520
|
try {
|
|
521
521
|
// Phase 0: Truncate large logs under /var/log to free up immediate space
|
|
522
|
-
logger.info('Phase 0/
|
|
522
|
+
logger.info('Phase 0/7: Truncating large log files under /var/log...');
|
|
523
523
|
try {
|
|
524
524
|
const cleanPath = `/var/log/`;
|
|
525
525
|
const largeLogsFiles = shellExec(
|
|
@@ -540,7 +540,7 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`);
|
|
|
540
540
|
|
|
541
541
|
// Phase 1: Clean up Persistent Volumes with hostPath
|
|
542
542
|
// This targets data created by Kubernetes Persistent Volumes that use hostPath.
|
|
543
|
-
logger.info('Phase 1/
|
|
543
|
+
logger.info('Phase 1/7: Cleaning Kubernetes hostPath volumes...');
|
|
544
544
|
try {
|
|
545
545
|
const pvListJson = shellExec(`kubectl get pv -o json || echo '{"items":[]}'`, { stdout: true, silent: true });
|
|
546
546
|
const pvList = JSON.parse(pvListJson);
|
|
@@ -563,7 +563,7 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`);
|
|
|
563
563
|
// Phase 2: Restore SELinux and stop services
|
|
564
564
|
// This is critical for fixing the 'permission denied' error you experienced.
|
|
565
565
|
// Enable SELinux permissive mode and restore file contexts.
|
|
566
|
-
logger.info('Phase 2/
|
|
566
|
+
logger.info('Phase 2/7: Stopping services and fixing SELinux...');
|
|
567
567
|
logger.info(' -> Ensuring SELinux is in permissive mode...');
|
|
568
568
|
shellExec(`sudo setenforce 0 || true`);
|
|
569
569
|
shellExec(`sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config || true`);
|
|
@@ -580,7 +580,7 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`);
|
|
|
580
580
|
shellExec('sudo umount -f /var/lib/kubelet/pods/*/* || true');
|
|
581
581
|
|
|
582
582
|
// Phase 3: Execute official uninstallation commands
|
|
583
|
-
logger.info('Phase 3/
|
|
583
|
+
logger.info('Phase 3/7: Executing official reset and uninstallation commands...');
|
|
584
584
|
logger.info(' -> Executing kubeadm reset...');
|
|
585
585
|
shellExec('sudo kubeadm reset --force || true');
|
|
586
586
|
logger.info(' -> Executing K3s uninstallation script if it exists...');
|
|
@@ -589,7 +589,7 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`);
|
|
|
589
589
|
shellExec('kind get clusters | xargs -r -t -n1 kind delete cluster || true');
|
|
590
590
|
|
|
591
591
|
// Phase 4: File system cleanup
|
|
592
|
-
logger.info('Phase 4/
|
|
592
|
+
logger.info('Phase 4/7: Cleaning up remaining file system artifacts...');
|
|
593
593
|
// Remove any leftover configurations and data.
|
|
594
594
|
shellExec('sudo rm -rf /etc/kubernetes/* || true');
|
|
595
595
|
shellExec('sudo rm -rf /etc/cni/net.d/* || true');
|
|
@@ -602,15 +602,21 @@ net.ipv4.ip_forward = 1' | sudo tee ${iptableConfPath}`);
|
|
|
602
602
|
shellExec('rm -rf $HOME/.kube || true');
|
|
603
603
|
|
|
604
604
|
// Phase 5: Host network cleanup
|
|
605
|
-
logger.info('Phase 5/
|
|
605
|
+
logger.info('Phase 5/7: Cleaning up host network configurations...');
|
|
606
606
|
// Remove iptables rules and CNI network interfaces.
|
|
607
607
|
shellExec('sudo iptables -F || true');
|
|
608
608
|
shellExec('sudo iptables -t nat -F || true');
|
|
609
|
+
// Restore iptables rules
|
|
610
|
+
shellExec(`chmod +x ${options.underpostRoot}/manifests/maas/nat-iptables.sh`);
|
|
611
|
+
shellExec(`${options.underpostRoot}/manifests/maas/nat-iptables.sh`, { silent: true });
|
|
609
612
|
shellExec('sudo ip link del cni0 || true');
|
|
610
613
|
shellExec('sudo ip link del flannel.1 || true');
|
|
611
614
|
|
|
615
|
+
logger.info('Phase 6/7: Clean up images');
|
|
616
|
+
shellExec(`podman rmi $(podman images -qa) --force`);
|
|
617
|
+
|
|
612
618
|
// Phase 6: Reload daemon and finalize
|
|
613
|
-
logger.info('Phase
|
|
619
|
+
logger.info('Phase 7/7: Reloading the system daemon and finalizing...');
|
|
614
620
|
// shellExec('sudo systemctl daemon-reload');
|
|
615
621
|
UnderpostCluster.API.config();
|
|
616
622
|
logger.info('Safe and complete reset finished. The system is ready for a new cluster initialization.');
|
package/src/cli/deploy.js
CHANGED
|
@@ -519,6 +519,15 @@ node bin/deploy build-full-client ${deployId}
|
|
|
519
519
|
logger.error(error, error.stack);
|
|
520
520
|
}
|
|
521
521
|
},
|
|
522
|
+
existsContainerFile({ podName, path }) {
|
|
523
|
+
return JSON.parse(
|
|
524
|
+
shellExec(`kubectl exec ${podName} -- test -f ${path} && echo "true" || echo "false"`, {
|
|
525
|
+
stdout: true,
|
|
526
|
+
disableLog: true,
|
|
527
|
+
silent: true,
|
|
528
|
+
}).trim(),
|
|
529
|
+
);
|
|
530
|
+
},
|
|
522
531
|
};
|
|
523
532
|
}
|
|
524
533
|
|
package/src/cli/index.js
CHANGED
|
@@ -324,7 +324,8 @@ program
|
|
|
324
324
|
.option('--args <args-array>', 'Array of arguments to pass to the command.')
|
|
325
325
|
.option('--dev', 'Sets the development context environment for the script.')
|
|
326
326
|
.option('--pod-name <pod-name>', 'Optional: Specifies the pod name for test execution.')
|
|
327
|
-
.option('--volume-
|
|
327
|
+
.option('--volume-host-path <volume-host-path>', 'Optional: Specifies the volume host path for test execution.')
|
|
328
|
+
.option('--volume-mount-path <volume-mount-path>', 'Optional: Specifies the volume mount path for test execution.')
|
|
328
329
|
.option('--image-name <image-name>', 'Optional: Specifies the image name for test execution.')
|
|
329
330
|
.option('--container-name <container-name>', 'Optional: Specifies the container name for test execution.')
|
|
330
331
|
.option('--namespace <namespace>', 'Optional: Specifies the namespace for test execution.')
|
package/src/cli/run.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { getTerminalPid, openTerminal, pbcopy, shellCd, shellExec } from '../server/process.js';
|
|
1
|
+
import { daemonProcess, getTerminalPid, openTerminal, pbcopy, shellCd, shellExec } from '../server/process.js';
|
|
2
2
|
import read from 'read';
|
|
3
3
|
import { getNpmRootPath } from '../server/conf.js';
|
|
4
4
|
import { loggerFactory } from '../server/logger.js';
|
|
5
5
|
import UnderpostTest from './test.js';
|
|
6
6
|
import fs from 'fs-extra';
|
|
7
|
-
import { timer } from '../client/components/core/CommonJs.js';
|
|
7
|
+
import { range, setPad, timer } from '../client/components/core/CommonJs.js';
|
|
8
|
+
import UnderpostDeploy from './deploy.js';
|
|
8
9
|
|
|
9
10
|
const logger = loggerFactory(import.meta);
|
|
10
11
|
|
|
@@ -12,7 +13,8 @@ class UnderpostRun {
|
|
|
12
13
|
static DEFAULT_OPTION = {
|
|
13
14
|
dev: false,
|
|
14
15
|
podName: '',
|
|
15
|
-
|
|
16
|
+
volumeHostPath: '',
|
|
17
|
+
volumeMountPath: '',
|
|
16
18
|
imageName: '',
|
|
17
19
|
containerName: '',
|
|
18
20
|
namespace: '',
|
|
@@ -58,13 +60,7 @@ class UnderpostRun {
|
|
|
58
60
|
logger.info('monitor pid', pid);
|
|
59
61
|
const checkPath = '/await';
|
|
60
62
|
const _monitor = async () => {
|
|
61
|
-
const result =
|
|
62
|
-
shellExec(`kubectl exec ${path} -- test -f ${checkPath} && echo "true" || echo "false"`, {
|
|
63
|
-
stdout: true,
|
|
64
|
-
disableLog: true,
|
|
65
|
-
silent: true,
|
|
66
|
-
}).trim(),
|
|
67
|
-
);
|
|
63
|
+
const result = UnderpostDeploy.API.existsContainerFile({ podName: path, path: checkPath });
|
|
68
64
|
logger.info('monitor', result);
|
|
69
65
|
if (result === true) {
|
|
70
66
|
switch (path) {
|
|
@@ -74,21 +70,21 @@ class UnderpostRun {
|
|
|
74
70
|
const podName = path;
|
|
75
71
|
const basePath = '/home/dd';
|
|
76
72
|
const scriptPath = '/site/en/tutorials/generative/cvae.py';
|
|
77
|
-
shellExec(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
);
|
|
82
|
-
const file = fs.readFileSync(`${basePath}/lab/src/${scriptPath.split('/').pop()}`, 'utf8');
|
|
83
|
-
fs.writeFileSync(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
print('=== SCRIPT UPDATE TEST ===')`,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
);
|
|
73
|
+
// shellExec(
|
|
74
|
+
// `sudo kubectl cp ${nameSpace}/${podName}:${basePath}/docs${scriptPath} ${basePath}/lab/src/${scriptPath
|
|
75
|
+
// .split('/')
|
|
76
|
+
// .pop()}`,
|
|
77
|
+
// );
|
|
78
|
+
// const file = fs.readFileSync(`${basePath}/lab/src/${scriptPath.split('/').pop()}`, 'utf8');
|
|
79
|
+
// fs.writeFileSync(
|
|
80
|
+
// `${basePath}/lab/src/${scriptPath.split('/').pop()}`,
|
|
81
|
+
// file.replace(
|
|
82
|
+
// `import time`,
|
|
83
|
+
// `import time
|
|
84
|
+
// print('=== SCRIPT UPDATE TEST ===')`,
|
|
85
|
+
// ),
|
|
86
|
+
// 'utf8',
|
|
87
|
+
// );
|
|
92
88
|
shellExec(
|
|
93
89
|
`sudo kubectl cp ${basePath}/lab/src/${scriptPath
|
|
94
90
|
.split('/')
|
|
@@ -96,6 +92,28 @@ print('=== SCRIPT UPDATE TEST ===')`,
|
|
|
96
92
|
);
|
|
97
93
|
// shellExec(`sudo kubectl exec -i ${podName} -- sh -c "ipython ${basePath}/docs${scriptPath}"`);
|
|
98
94
|
shellExec(`sudo kubectl exec -i ${podName} -- sh -c "rm -rf ${checkPath}"`);
|
|
95
|
+
|
|
96
|
+
{
|
|
97
|
+
const checkPath = `/latent_space_plot.png`;
|
|
98
|
+
const outsPaths = [];
|
|
99
|
+
logger.info('monitor', checkPath);
|
|
100
|
+
while (!UnderpostDeploy.API.existsContainerFile({ podName, path: `/home/dd/docs${checkPath}` }))
|
|
101
|
+
await timer(1000);
|
|
102
|
+
|
|
103
|
+
{
|
|
104
|
+
const toPath = `${basePath}/lab${checkPath}`;
|
|
105
|
+
outsPaths.push(toPath);
|
|
106
|
+
shellExec(`sudo kubectl cp ${nameSpace}/${podName}:${basePath}/docs${checkPath} ${toPath}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (let i of range(1, 10)) {
|
|
110
|
+
i = `/image_at_epoch_${setPad(i, '0', 4)}.png`;
|
|
111
|
+
const toPath = `${basePath}/lab/${i}`;
|
|
112
|
+
outsPaths.push(toPath);
|
|
113
|
+
shellExec(`sudo kubectl cp ${nameSpace}/${podName}:${basePath}/docs${i} ${toPath}`);
|
|
114
|
+
}
|
|
115
|
+
openTerminal(`firefox ${outsPaths.join(' ')}`, { single: true });
|
|
116
|
+
}
|
|
99
117
|
shellExec(`sudo kill -9 ${pid}`);
|
|
100
118
|
}
|
|
101
119
|
break;
|
|
@@ -115,6 +133,8 @@ print('=== SCRIPT UPDATE TEST ===')`,
|
|
|
115
133
|
const podName = 'tf-vae-test';
|
|
116
134
|
await UnderpostRun.RUNNERS['deploy-job']('', {
|
|
117
135
|
podName,
|
|
136
|
+
// volumeMountPath: '/custom_images',
|
|
137
|
+
// volumeHostPath: '/home/dd/engine/src/client/public/cyberia/assets/skin',
|
|
118
138
|
on: {
|
|
119
139
|
init: async () => {
|
|
120
140
|
openTerminal(`node bin run --dev monitor ${podName}`);
|
|
@@ -136,20 +156,23 @@ print('=== SCRIPT UPDATE TEST ===')`,
|
|
|
136
156
|
`echo '' > /await`,
|
|
137
157
|
`echo '=== WAITING SCRIPT LAUNCH ==='`,
|
|
138
158
|
`while [ -f /await ]; do sleep 1; done`,
|
|
139
|
-
`ipython site/en/tutorials/generative/cvae.py`,
|
|
140
159
|
`echo '=== FINISHED ==='`,
|
|
160
|
+
daemonProcess(`ipython site/en/tutorials/generative/cvae.py`),
|
|
141
161
|
],
|
|
142
162
|
});
|
|
143
163
|
},
|
|
144
164
|
'deploy-job': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
145
165
|
const podName = options.podName || 'deploy-job';
|
|
146
|
-
const volumeName =
|
|
166
|
+
const volumeName = `${podName}-volume`;
|
|
147
167
|
const args = (options.args ? options.args : path ? [`python ${path}`] : []).filter((c) => c.trim());
|
|
148
168
|
const imageName = options.imageName || 'nvcr.io/nvidia/tensorflow:24.04-tf2-py3';
|
|
149
169
|
const containerName = options.containerName || `${podName}-container`;
|
|
150
170
|
const gpuEnable = imageName.match('nvidia');
|
|
151
171
|
const runtimeClassName = gpuEnable ? 'nvidia' : '';
|
|
152
172
|
const namespace = options.namespace || 'default';
|
|
173
|
+
const volumeMountPath = options.volumeMountPath || path;
|
|
174
|
+
const volumeHostPath = options.volumeHostPath || path;
|
|
175
|
+
const enableVolumeMount = volumeHostPath && volumeMountPath;
|
|
153
176
|
|
|
154
177
|
const cmd = `kubectl apply -f - <<EOF
|
|
155
178
|
apiVersion: v1
|
|
@@ -185,16 +208,16 @@ ${
|
|
|
185
208
|
: ''
|
|
186
209
|
}
|
|
187
210
|
${
|
|
188
|
-
|
|
211
|
+
enableVolumeMount
|
|
189
212
|
? `
|
|
190
213
|
volumeMounts:
|
|
191
214
|
- name: ${volumeName}
|
|
192
|
-
mountPath: ${
|
|
215
|
+
mountPath: ${volumeMountPath}
|
|
193
216
|
volumes:
|
|
194
217
|
- name: ${volumeName}
|
|
195
218
|
hostPath:
|
|
196
|
-
path: ${
|
|
197
|
-
type: ${fs.statSync(
|
|
219
|
+
path: ${volumeHostPath}
|
|
220
|
+
type: ${fs.statSync(volumeHostPath).isDirectory() ? 'Directory' : 'File'}`
|
|
198
221
|
: ''
|
|
199
222
|
}
|
|
200
223
|
EOF`;
|
package/src/index.js
CHANGED
package/src/server/process.js
CHANGED
|
@@ -61,11 +61,18 @@ const shellCd = (cd, options = { disableLog: false }) => {
|
|
|
61
61
|
return shell.cd(cd);
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
-
const openTerminal = (cmd) =>
|
|
64
|
+
const openTerminal = (cmd, options = { single: false }) => {
|
|
65
|
+
if (options.single === true) {
|
|
66
|
+
shellExec(`setsid gnome-terminal -- bash -ic "${cmd}; exec bash" >/dev/null 2>&1 &`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
65
69
|
shellExec(`gnome-terminal -- bash -c "${cmd}; exec bash" & disown`, {
|
|
66
70
|
async: true,
|
|
67
71
|
stdout: true,
|
|
68
72
|
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const daemonProcess = (cmd) => `exec bash -c '${cmd}; exec tail -f /dev/null'`;
|
|
69
76
|
|
|
70
77
|
// list all terminals: pgrep gnome-terminal
|
|
71
78
|
// list last terminal: pgrep -n gnome-terminal
|
|
@@ -76,4 +83,4 @@ function pbcopy(data) {
|
|
|
76
83
|
logger.info(`copied to clipboard`, clipboard.readSync());
|
|
77
84
|
}
|
|
78
85
|
|
|
79
|
-
export { ProcessController, getRootDirectory, shellExec, shellCd, pbcopy, openTerminal, getTerminalPid };
|
|
86
|
+
export { ProcessController, getRootDirectory, shellExec, shellCd, pbcopy, openTerminal, getTerminalPid, daemonProcess };
|