Hej There,
I'm trying to add some non-conventional functionality to my NodeJS application but I'm having some trouble. What I'm trying to do is the following:
I want to update my server code from the client. (An auto-update functionality if you will.)
My first attempt was to utilize the NPM API and run:
npm.commands.install([package], function(err, data)
But of course this results in an error telling me NPM can not install while the server is running.
My second attempt was spawning NPM update using the following code:
spawnProcess('npm', ['update'], { cwd: projectPath }, done);
The spawnProcess function is a generic spawn function:
var projectPath = path.resolve(process.cwd());
var spawnProcess = function(command, args, options, callback) {
var spawn = require('child_process').spawn;
var process = spawn(command, args, options);
var err = false;
process.stdout.on('data', function(data) {
console.log('stdout', data.toString());
});
process.stderr.on('data', function(data) {
err = true;
console.log('stderr', data.toString());
});
if (typeof callback === 'function') {
process.on('exit', function() {
if (!err) {
return callback();
}
});
}
};
But this gives me a stderr followed by a 'CreateProcessW: can not find file' error. I don't quite know what I'm doing wrong.
If all else fails I thought it might be possible to write a shellscript killing Node, updating the application and then rebooting it. Something like:
kill -9 45728
npm update
node server
But I don't know if this is a plausible solution and how I would go about executing it from my node server. I'd rather have the spawn function working of course.
Any help is welcome. Thanks in advance!
So I finally fixed this issue. If someone is interested how I did it, this is how:
I built a function using the NPM api to check if the current version is up to date:
exports.checkVersion = function(req, res) {
npm.load([], function (err, npm) {
npm.commands.search(["mediacenterjs"], function(err, data){
if (err){
console.log('NPM search error ' + err);
return;
} else{
var currentInfo = checkCurrentVersion();
for (var key in data) {
var obj = data[key];
if(obj.name === 'mediacenterjs' && obj.version > currentInfo.version){
var message = 'New version '+obj.version+' Available';
res.json(message);
}
}
}
});
});
}
var checkCurrentVersion = function(){
var info = {};
var data = fs.readFileSync('./package.json' , 'utf8');
try{
info = JSON.parse(data);
}catch(e){
console.log('JSON Parse Error', e);
}
return info;
};
If the version is not up to date I initiate a download (in my case the github master repo url) using node-wget:
var wget = require('wget');
var download = wget.download(src, output, options);
download.on('error', function(err) {
console.log('Error', err);
callback(output);
});
download.on('end', function(output) {
console.log(output);
callback(output);
});
download.on('progress', function(progress) {
console.log(progress * 100);
});
The callback kicks off the unzip function bases on @John Munsch 'Self help' script but I added a check to see if there was a previous unzip attempt and if so, I delete the folder:
if(fs.existsSync(dir) === false){
fs.mkdirSync(dir);
} else {
rimraf(dir, function (err) {
if(err) {
console.log('Error removing temp folder', err);
} else {
fileHandler.downloadFile(src, output, options, function(output){
console.log('Done', output);
unzip(req, res, output, dir);
});
}
});
}
console.log("Unzipping New Version...");
var AdmZip = require("adm-zip");
var zip = new AdmZip(output);
zip.extractAllTo(dir, true);
fs.openSync('./configuration/update.js', 'w');
The openSync function kicks off my 'NodeMon' based file (https://github.com/jansmolders86/mediacenterjs/blob/master/server.js) which kills the server because it is listing for changes to that specific file. Finally it restart and starts the following functions:
function installUpdate(output, dir){
console.log('Installing update...');
var fsExtra = require("fs.extra");
fsExtra.copy(dir+'/mediacenterjs-master', './', function (err) {
if (err) {
console.error('Error', err);
} else {
console.log("success!");
cleanUp(output, dir);
}
});
}
function cleanUp(output, dir) {
console.log('Cleanup...');
var rimraf = require('rimraf');
rimraf(dir, function (e) {
if(e) {
console.log('Error removing module', e .red);
}
});
if(fs.existsSync(output) === true){
fs.unlinkSync(output);
console.log('Done, restarting server...')
server.start();
}
}
Thanks to everyone that helped me out!