문제

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!

다른 팁

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

It doesn't use NPM to accomplish it but here's a working experiment I put together a while back because I wanted to be able to build a self-updating app using Node.js for installation on people's machines (think apps like SABnzbd+, Couch Potato, or the Plex Media Server).

The Github for the example code is here: https://github.com/JohnMunsch/selfhelp

I got the example to the point where it would automatically notify you when a new version was available and then download, update, and restart itself. Even if you don't end up using it as-is, you should be able to get some ideas from it.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top