Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Let NodeJS application update itself using NPM

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!

like image 472
jansmolders86 Avatar asked Dec 16 '13 17:12

jansmolders86


People also ask

Can you use npm to update node?

Use NPM to Update Your Node Version To update Node with NPM, you will install the n package, which will be used to interactively manage node versions on your device.

Which package is used to refresh the node js code automatically as we save it?

Basically, Nodemon is a utility that monitor for any changes in your source and automatically restart your server. After installing the nodemon utility we will use the following command to run the code.


2 Answers

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!

like image 51
jansmolders86 Avatar answered Sep 18 '22 11:09

jansmolders86


Untested!

Have you tried the "prestart" npm script handle? Of course this would mean that you'd use npm to run your server: npm start

In your package.json (if you have) :

{ "scripts" :
  { 
    "start" : "node server.js",
    "prestart" : "scripts/customScript.js"
  }
}

NPM start command is implicitly defaulted to "start": "node server.js". If your server script is called server.js, there is no need to include this. You can do your npm install on the customScript.js or might be able to call npm install directly though I haven't tested this.

You can also assign/read your handler using the environment variable process.env.npm_package_scripts_prestart

like image 37
cbayram Avatar answered Sep 19 '22 11:09

cbayram