Javascript 让NodeJS应用程序使用NPM进行自我更新

Javascript 让NodeJS应用程序使用NPM进行自我更新,javascript,node.js,npm,spawn,Javascript,Node.js,Npm,Spawn,嘿,好了 我正在尝试向我的NodeJS应用程序添加一些非常规功能,但遇到了一些问题。 我想做的是: 我想从客户端更新服务器代码。(如果愿意,可以使用自动更新功能。) 我的第一次尝试是利用NPM API并运行: npm.commands.install([package], function(err, data) 当然,这会导致一个错误,告诉我在服务器运行时NPM无法安装 我的第二次尝试是使用以下代码生成NPM更新: spawnProcess('npm', ['update'], { cw

嘿,好了

我正在尝试向我的NodeJS应用程序添加一些非常规功能,但遇到了一些问题。 我想做的是:

我想从客户端更新服务器代码。(如果愿意,可以使用自动更新功能。)

我的第一次尝试是利用NPM API并运行:

 npm.commands.install([package], function(err, data)
当然,这会导致一个错误,告诉我在服务器运行时NPM无法安装

我的第二次尝试是使用以下代码生成NPM更新:

  spawnProcess('npm', ['update'], { cwd: projectPath }, done);
spawnProcess函数是一个通用的spawn函数:

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();
            }
        });
    }
};
但这给了我一个stderr,后跟一个“CreateProcessW:cannotfindfile”错误。 我不知道我做错了什么

如果所有这些都失败了,我认为可以编写一个shellscript终止节点,更新应用程序,然后重新启动它。比如:

kill -9 45728
npm update
node server
但我不知道这是否是一个合理的解决方案,以及如何从节点服务器执行它。 当然,我宁愿让spawn函数工作

欢迎任何帮助。 提前谢谢

未经测试

你试过“开始前”吗?当然,这意味着您将使用npm来运行服务器:npm start

在package.json中(如果有):

NPM start命令隐式默认为“start”:“node server.js”。如果您的服务器脚本名为server.js,则无需包含此脚本。您可以在customScript.js上安装npm,也可以直接调用npm安装,尽管我还没有测试过


您还可以使用环境变量process.env.npm\u package\u scripts\u prestart来分配/读取处理程序。它不使用npm来完成,但这里有一个我不久前做的工作实验,因为我希望能够使用Node.js构建一个自我更新的应用程序,以便安装在人们的机器上(想想SABnzbd+、沙发土豆或Plex媒体服务器之类的应用程序)

示例代码的Github如下所示:


我把这个例子理解为,当新版本可用时,它会自动通知您,然后下载、更新并重新启动。即使您最终没有按原样使用它,您也应该能够从中获得一些想法。

因此,我终于解决了这个问题。如果有人对我是如何做到的感兴趣,下面是:

我使用NPM api构建了一个函数来检查当前版本是否是最新的:

 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;
};
如果版本不是最新的,我将使用节点wget启动下载(在我的示例中是github主repo url):

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);
});
回调启动了基于@John Munsch'Self-help'脚本的解压函数,但我添加了一个检查,以查看是否有以前的解压尝试,如果有,我将删除文件夹:

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');
openSync函数启动基于“NodeMon”的文件(),该文件会终止服务器,因为它正在列出对该特定文件的更改。最后,它会重新启动并启动以下函数:

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();
    }
}

感谢所有帮助我的人!

试试
data.toString()
。如果您的代码更新模块成功,旧版本仍将加载到内存中:重新加载依赖项的唯一方法是重新启动应用程序。太好了!我真傻,竟然没有想到这一点!这会给我一个CeatProcessW错误。所以我仍然不太确定问题出在哪里。不过,谢谢!@Paul Mougel:我知道因此,我将在spawn进程的回调中重新加载服务器(该功能已经就绪):@Vpml,您用来启动服务器的命令是什么?谢谢分享。这似乎非常符合我的想法。我绝对可以从中吸取一些好的想法。谢谢嘿,我添加了脚本,效果很好!非常感谢你。但我还不明白如何使用“npm启动”而不是当前的“nodemon服务器”解决方案触发重启。我是否需要以编程方式终止服务器并生成npm启动?再次感谢。@Vprnl还有一个重启脚本句柄(预重启和后重启)。我会试一试的。一旦配置了npm重启,您当然可以运行npm重启。如果需要,您还可以额外停止(预停止、后停止)。有很多地方可以执行自定义代码或命令。Hej cbayram,谢谢你对我如此耐心,但如果可以的话,我还有一个问题:我现在使用:
if(that.update==true){npm.load([],function(err,npm){npm.commands.stop();npm.commands.restart();});}
停止并启动服务器。这工作得很好,但我仍然得到一个错误,它不能作为一个独立的依赖安装。但是,我在预启动时没有发现这个错误。@Vprnl您的npm更新代码到底是什么?哪种依赖性会产生这种eror?有没有代码/github repo你可以指给我看?谢谢你帮我。当然有。此文件将终止服务器并使用npm重新启动:此文件实际上更新了应用程序:再次感谢!关于错误:我正在尝试自动更新应用程序,因此在本例中,mediacenterjs正在尝试在自身上执行npm安装,这显然是不可能的,即使我关闭服务器并使用npm重新启动它。很好的解决方案。你能用“永远”运行基于“NodeMon”的server.js吗?嘿@FranklinDattein,我写了我自己的版本if NodeMon,它基本上和前面提到的一样。它与上述解决方案完美配合。这是我的keep alive服务器:希望有帮助
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();
    }
}