I'd like to restart gulp on certain changes. That can be easily done by placing the following within the gulpfile
spawn('gulp', [], { stdio: 'inherit'});
However, once gulp restarts in this way, the process is no longer killed properly with Ctrl+C via the terminal. If I start gulp via terminal, I can capture a Ctrl+C signal, but can't if gulp was started via the spawn in gulpfile. How can I capture 'SIGINT' for the spawn?
Okay here's the full story to anyone who might encounter the issue. From what I have been reading, whenever you want to restart gulp from within gulp you simply use:
spawn('gulp', [], { stdio: 'inherit'});
process.exit();
I didn't mention process.exit() in my question as I didn't expect it to affect the usage of Ctrl+C. Indeed it was, as my server was an ExpressJS one, whenever I'd use Ctrl+C after gulp restarted from within itself, I would get the port still in use error (Error: listen EADDRINUSE). Obviously, all node processes wasn't being closed. Once I removed the line process.exit() from my code, I was able to use Ctrl+C and successfully close all processes. Below is the useful bit of code in the gulpfile and output in terminal that is related to this issue.
// gulpfile.js
gulp.task('restart', function() {
server.close();
spawn('gulp', [], { stdio: 'inherit'});
process.exit(); // this line needs to be removed
});
process.on('SIGINT', function() {
setTimeout(function() {
gutil.log(gutil.colors.red('Successfully closed ' + process.pid));
process.exit(1);
}, 500);
});
// Console results:
^C[20:12:12] Successfully closed 67160
[20:12:12] Successfully closed 67151
Related
I am trying to create some setup and teardown logic for an expressjs server. Here's my entry code:
import fs from "fs";
import express from "express";
import { setRoutes } from "./routes";
let app = express();
const server = app.listen(8080, function () {
console.log(`š§ Mock Server is now running on port : ${8080}`);
});
app = setRoutes(app);
function stop() {
fs.rmdirSync("./uploads", { recursive: true });
fs.mkdirSync("uploads");
console.log("\nš§¹ Uploads folder purged");
server.on("close", function () {
console.log("ā¬ Shutting down server");
process.exit();
});
server.close();
}
process.on("SIGINT", stop);
// Purge sw images on restart
process.once("SIGUSR2", function () {
fs.rmdirSync("./uploads/swimages", { recursive: true });
console.log("š§¹ Software Images folder purged");
process.kill(process.pid, "SIGUSR2");
});
The npm script to start this up is "start": "FORCE_COLOR=3 nodemon index.js --exec babel-node".
The setup and restart logic works as expected. I get š§ Mock Server is now running on port : 8080 logged to console on startup. When I save a file, nodemon restarts the server, and the code in process.once is executed. When I want to shut it all down, I ctrl + c in the terminal. The cleanup logic from within the stop function is run. However, the process bever fully exits. In the terminal, am still stuck in the process, and I have to hit ctrl + c again to fully exit the process. It looks like this:
As far as I know there are no open connections (other questions mentioned that if there is a keep-alive connection still open, the server will not close properly, but as far as I can tell, that is not the case). I have tried different variations of server.close(callback), server.on('close', callback), process.exit(), process.kill(process.pid), etc, but nothing seems to fully exit the process.
Note that if I simply run node index.js, I do not have this issue. The cleanup logic runs, and the process exits to completion without issue. It seems to be an issue when using nodemon only.
I don't want other developers to have to wait for cleanup logic to run and then hit ctrl + c again. What am I missing to run my cleanup logic and fully exit the process in the terminal?
There is an open connection for sure. Check this package that can tell you which one: https://www.npmjs.com/package/wtfnode
So I have some code which runs a command in a spawned child process. I do this using the execa module.
const childProcess = execa.command('yarn start');
const localhostStarted = await waitForLocalhost({ port: 8000 });
expect(localhostStarted.done).toBe(true);
childProcess.kill('SIGINT', { forceKillAfterTimeout: 2000 });
The yarn start command executes webpack-dev-server in another child process of its own. However when I kill the childProcess that I spawned, it does not automatically kill its spawned webpack-dev-server process. It is known to be an issue here https://github.com/webpack/webpack-dev-server/issues/2168.
To fix this I add manual listeners for SIGINT & SIGTERM inside my script which runs when yarn start is called
['SIGINT', 'SIGTERM'].forEach((signal) => {
console.log('registering events');
process.on(signal, () => {
console.log('received signal', signal);
devServer.close(() => {
console.log('exiting proces');
process.exit(0);
});
});
});
This fixes the issue on my local machine and when I kill child process I spawn, it kills all its descendents i.e the dev-server process too.
However, this fix still does not work on CI, and since the child process gets killed on CI but not the dev-server process, my tests dont exit and keeps hanging.
My local machine is OSX 10.15 but on CI we use ubuntu. If I change CI to use macos 10.15, then the fix works on CI too.
I am unable to find any docs on this issue which explains the different behaviour on why the signal of SIGTERM is not received by the dev-server process on ubuntu machines but receives fine on mac machines.
I've been browsing around but to no success. I've found some npm packages like nodemon and forever but documentation doesn't explain how to call a restart inside the script properly.
I've found this code snippet on another question but I'm not using Express or other frameworks since the script is using a pulling service not a response one.
This is code I've made so far using internal Node.js dependencies but no luck.
'use strict'
process.on('uncaughtException', (error) => {
console.error('Global uncaughtException error caught')
console.error(error.message)
console.log('Killing server with restart...')
process.exit(0)
})
process.on('exit', () => {
console.log('on exit detected')
const exec = require('child_process').exec
var command = 'node app.js'
exec(command, (error, stdout, stderr) => {
console.log(`error: ${error.message}`)
console.log(`stdout: ${stdout}`)
console.log(`stderr: ${stderr}`)
})
})
setTimeout(() => {
errorTriggerTimeBomb() // Dummy error for testing triggering uncaughtException
}, 3000)
Just to note I'm running the server on Termux, a Linux terminal app for android. I know it's better to run from desktop but I'm always at a WiFi or mobile data area and that I don't like leaving my PC on overnight.
A typical restart using something like nodemon or forever would be triggered by calling process.exit() in your script.
This would exit the current script and then the monitoring agent would see that it exited and would restart it for you. It's the same principal as if it crashed on its own, but if you're trying to orchestrate it shutting down, you just exit the process and then the monitoring agent will restart it.
I have a home automation server that is being monitored using forever. If it crashes forever will automatically restart it. I also have it set so that at 4am every morning, it will call process.exit() and then forever will automatically restart it. I do this to prevent any memory leak accumulation over a long period of time and 30 seconds of down time in the middle of the night for my application is no big deal.
I'm using mocha with Nodejs to test my restApi.
When I run mocha, I tell my test to create a child_process and run the API so I can make requests to it.
The problem is whenever the test exits (finishing or crashing), it seems that the API keeps running on background. I've seen some answers here that instructs to manually kill the child process whenever the main process exits. So I did it like this:
export function startProcess(done) {
const child = spawn('babel-node', ["app.js"]);
child.stdout.on("data", function(data) {
data = data.toString();
// console.log(data.toString());
if(data.indexOf("Server online") > -1) done();
});
child.stderr.on('data', function(err) {
console.log("ERROR: ", err.toString());
});
child.on('exit', function(code) {
console.log("PROPERLY EXITING");
console.log("Child process exited with code", code);
});
process.on('exit', function(code) {
console.log("Killing child process");
child.kill();
console.log("Main process exited with code", code);
});
}
When the main process exits it does log "Killing child process", meaning that child.kill() was indeed called. But if I try to run my test again, when the spawn command gets called, the API throws an error
Error: listen EADDRINUSE :::3300
, meaning that the API is still running and that port address is taken.
So I have to run sudo pkill node to really kill all node process and then npm test works again.
Am I missing something? Is this really the way to achieve what I'm expecting?
I thought about using child_process.exec to run sudo pkill node on my process.on('exit') listener, but that doesnt seem like a smart thing to do.
This is happening both in Mac and Ubuntu.
Any suggestions?
Thanks
"exit" is an event that gets triggered when node finishes it's event loop internally, it's not triggered when you terminate the process externally.
What you're looking for is executing something on a SIGINT.
Have a look at http://nodejs.org/api/process.html#process_signal_events
I'm trying to automate a simple gulp task to run/debug node, watch for file changes, and restart node if any files change. Most popular recipes I've seen for this use gulp-nodemon, but when a file change event occurs, (gulp-) nodemon crashes:
[nodemon] app crashed - waiting for file changes before starting...
The crashing happens inconsistently, so sometimes I have to manually send a SIGINT to stop the node process (which kind of defeats the purpose of nodemon).
I want to be able to run a gulp task that can watch files, run or debug node. How can this be accomplished without nodemon crashing?
It's not fancy, but the following should accomplish what you want.
'use strict'
const gulp = require('gulp');
const spawn = require('child_process').spawn;
gulp.task('debug', function() {
let child = spawn("node", ["debug", "./server.js"], { stdio: 'inherit' });
gulp.watch([__dirname + "/*.js", '!gulpfile.js'], function(event) {
console.log(`File %s was %s.`, event.path, event.type);
if (child) {
child.kill();
child = spawn("node", ["debug", "./server.js"], { stdio: 'inherit' });
}
});
});
This assumes you're watching for changes to any js files in __dirname except for your gulpfile.