ソースを参照

dev: separate process management from DevWatcherService

KernelDeimos 4 ヶ月 前
コミット
4c72e9d285

+ 2 - 1
src/backend/exports.js

@@ -34,7 +34,7 @@ const { PuterFSModule } = require("./src/modules/puterfs/PuterFSModule.js");
 const { PerfMonModule } = require("./src/modules/perfmon/PerfMonModule.js");
 const { AppsModule } = require("./src/modules/apps/AppsModule.js");
 const { DevelopmentModule } = require("./src/modules/development/DevelopmentModule.js");
-
+const { HostOSModule } = require("./src/modules/hostos/HostOSModule.js");
 
 module.exports = {
     helloworld: () => {
@@ -52,6 +52,7 @@ module.exports = {
     EssentialModules: [
         Core2Module,
         PuterFSModule,
+        HostOSModule,
         CoreModule,
         WebModule,
         TemplateModule,

+ 14 - 0
src/backend/src/modules/hostos/HostOSModule.js

@@ -0,0 +1,14 @@
+const { AdvancedBase } = require("@heyputer/putility");
+
+class HostOSModule extends AdvancedBase {
+    async install (context) {
+        const services = context.get('services');
+
+        const ProcessService = require('./ProcessService');
+        services.registerService('process', ProcessService);
+    }
+}
+
+module.exports = {
+    HostOSModule,
+};

+ 97 - 0
src/backend/src/modules/hostos/ProcessService.js

@@ -0,0 +1,97 @@
+const BaseService = require("../../services/BaseService");
+
+class ProxyLogger {
+    constructor (log) {
+        this.log = log;
+    }
+    attach (stream) {
+        let buffer = '';
+        stream.on('data', (chunk) => {
+            buffer += chunk.toString();
+            let lineEndIndex = buffer.indexOf('\n');
+            while (lineEndIndex !== -1) {
+                const line = buffer.substring(0, lineEndIndex);
+                this.log(line);
+                buffer = buffer.substring(lineEndIndex + 1);
+                lineEndIndex = buffer.indexOf('\n');
+            }
+        });
+
+        stream.on('end', () => {
+            if (buffer.length) {
+                this.log(buffer);
+            }
+        });
+    }
+}
+
+class ProcessService extends BaseService {
+    static MODULES = {
+        path: require('path'),
+        spawn: require('child_process').spawn,
+    };
+
+    _construct () {
+        this.instances = [];
+    }
+
+    async _init (args) {
+        this.args = args;
+
+        process.on('exit', () => {
+            this.exit_all_();
+        })
+    }
+
+    log_ (name, isErr, line) {
+        let txt = `[${name}:`;
+        txt += isErr
+            ? `\x1B[34;1m2\x1B[0m`
+            : `\x1B[32;1m1\x1B[0m`;
+        txt += '] ' + line;
+        this.log.info(txt);
+    }
+
+    async exit_all_ () {
+        for ( const { proc } of this.instances ) {
+            proc.kill();
+        }
+    }
+
+    async start ({ name, fullpath, command, args, env }) {
+        this.log.info(`Starting ${name} in ${fullpath}`);
+        const env_processed = { ...(env ?? {}) };
+        for ( const k in env_processed ) {
+            if ( typeof env_processed[k] !== 'function' ) continue;
+            env_processed[k] = env_processed[k]({
+                global_config: this.global_config
+            });
+        }
+        console.log(
+            'command',
+            command,
+            ...args
+        )
+        const proc = this.modules.spawn(command, args, {
+            shell: true,
+            env: {
+                ...process.env,
+                ...env_processed,
+            },
+            cwd: fullpath,
+        });
+        this.instances.push({
+            name, proc,
+        });
+        const out = new ProxyLogger((line) => this.log_(name, false, line));
+        out.attach(proc.stdout);
+        const err = new ProxyLogger((line) => this.log_(name, true, line));
+        err.attach(proc.stderr);
+        proc.on('exit', () => {
+            this.log.info(`[${name}:exit] Process exited (${proc.exitCode})`);
+            this.instances = this.instances.filter((inst) => inst.proc !== proc);
+        })
+    }
+}
+
+module.exports = ProcessService;

+ 4 - 59
src/backend/src/modules/selfhosted/DevWatcherService.js

@@ -53,16 +53,8 @@ class DevWatcherService extends BaseService {
         spawn: require('child_process').spawn,
     };
 
-    _construct () {
-        this.instances = [];
-    }
-
     async _init (args) {
         this.args = args;
-
-        process.on('exit', () => {
-            this.exit_all_();
-        })
     }
     
     // Oh geez we need to wait for the web server to initialize
@@ -71,13 +63,16 @@ class DevWatcherService extends BaseService {
     // this was to debug the first time, like Ahhhhhh!!
     // but hey at least we have this convenient event listener.
     async ['__on_ready.webserver'] () {
+        const svc_process = this.services.get('process');
+
         const { root, commands } = this.args;
         let promises = [];
         for ( const entry of commands ) {
             const { directory } = entry;
             const fullpath = this.modules.path.join(
                 root, directory);
-            promises.push(this.start_({ ...entry, fullpath }));
+            // promises.push(this.start_({ ...entry, fullpath }));
+            promises.push(svc_process.start({ ...entry, fullpath }));
         }
         await Promise.all(promises);
 
@@ -85,56 +80,6 @@ class DevWatcherService extends BaseService {
         // run so we just wait a bit before we say we're ready.
         await new Promise((resolve) => setTimeout(resolve, 5000));
     }
-
-    log_ (name, isErr, line) {
-        let txt = `[${name}:`;
-        txt += isErr
-            ? `\x1B[34;1m2\x1B[0m`
-            : `\x1B[32;1m1\x1B[0m`;
-        txt += '] ' + line;
-        this.log.info(txt);
-    }
-
-    async start_ ({ name, fullpath, command, args, env }) {
-        this.log.info(`Starting ${name} in ${fullpath}`);
-        const env_processed = { ...(env ?? {}) };
-        for ( const k in env_processed ) {
-            if ( typeof env_processed[k] !== 'function' ) continue;
-            env_processed[k] = env_processed[k]({
-                global_config: this.global_config
-            });
-        }
-        console.log(
-            'command',
-            command,
-            ...args
-        )
-        const proc = this.modules.spawn(command, args, {
-            shell: true,
-            env: {
-                ...process.env,
-                ...env_processed,
-            },
-            cwd: fullpath,
-        });
-        this.instances.push({
-            name, proc,
-        });
-        const out = new ProxyLogger((line) => this.log_(name, false, line));
-        out.attach(proc.stdout);
-        const err = new ProxyLogger((line) => this.log_(name, true, line));
-        err.attach(proc.stderr);
-        proc.on('exit', () => {
-            this.log.info(`[${name}:exit] Process exited (${proc.exitCode})`);
-            this.instances = this.instances.filter((inst) => inst.proc !== proc);
-        })
-    }
-
-    async exit_all_ () {
-        for ( const { proc } of this.instances ) {
-            proc.kill();
-        }
-    }
 };
 
 module.exports = DevWatcherService;