在执行一些比较复杂的命令时,需要用到子进程,以避免阻塞主进程。
在使用hexo-admin插件的时候,发现作者也是用了子进程来处理deploy,并且,通过一定的设置,还能将执行进程的结果都返回给前端。
1.问题
由于仓库年久失修,发布的命令用不了,决定查看源码一看究竟。
2.定位
打开hexo-admin的模块,找到deploy.js文件,就是此文件暴露了发布方法,我们看一下大概的结构:
var spawn = require('child_process').spawn
function once(fn) {
var called = false
return function () {
if (!called) fn.apply(this, arguments)
called = true
}
}
module.exports = function (command, message, done) {
done = once(done);
var proc = spawn(command, [message], {detached: true});
var stdout = '';
var stderr = '';
proc.stdout.on('data', function(data){stdout += data.toString()})
proc.stderr.on('data', function(data){stderr += data.toString()})
proc.on('error', function(err) {
done(err, {stdout: stdout, stderr: stderr});
});
proc.on('close', function () {
done(null, {stdout: stdout, stderr: stderr});
});
}
module导出的就是主方法,这里有三个参数,我们关注的是第一个command和第二个message参数,从下面调用的方法—— var proc = spawn(command, [message], {detached: true});
可以看出command是需要执行的命令,在后面会提到这个命令作者是直接放在配置文件中,灵活性很强,第二个message参数在这里其实是附加给command的参数,比如command是cmd.exe ,那么参数可以是’/dsf/some.bat’,具体的用法参考node文档:https://nodejs.org/dist/latest-v16.x/docs/api/child_process.html,
查看此方法的调用,找到api.js,里面有一段使用deploy的代码,如下:
use('deploy', function(req, res, next) {
if (req.method !== 'POST') return next()
if (!hexo.config.admin || !hexo.config.admin.deployCommand) {
return res.done({error: 'Config value "admin.deployCommand" not found'});
}
try {
deploy(hexo.config.admin.deployCommand, req.body.message, function(err, result) {
console.log('res', err, result);
if (err) {
return res.done({error: err.message || err})
}
res.done(result);
});
} catch (e) {
console.log('EEE', e);
res.done({error: e.message})
}
});
从这个方法中就能明确看到,这是写的一个http接口,用的post方法,在执行deploy方法的时候,第一个参数是hexo.config.admin.deployCommand 。也就是说,需要改执行的命令方法,只要修改配置文件就可以,那这里我就填了 ‘bash’,因为需要在linux下运行。
再看第二个参数,很明显,req.body.message
指向的就是post方法传的参数,这样一想就明白了,难怪会运行不起来,这个message参数传的不对,命令就执行不下去,但是我看了nodejs文档之后发现,需要执行的脚本文件名可以固定下来,这个message参数可以作为额外的参数传入。
3.解决
我想用这个deploy执行一下几个命令
#!/bin/bash
hexo clean
hexo g
hexo d
git add .
git commit -m "generated files on `date +'%Y-%m-%d %H:%M:%S'`"
git push -u origin master
由于commit -m中需要一个清楚的描述,我觉得这里可以把这个message参数利用起来,稍加修改,改成
#!/bin/bash
message=${1}
echo 提交内容:${message}
hexo clean
hexo g
hexo d
git add .
git commit -m "${message} generated files on `date +'%Y-%m-%d %H:%M:%S'`"
git push -u origin master
这样,这个bash文件就可以接受一个message参数了。那么,上面的deploy.js也需要做相应的修改,
// var proc = spawn(command, [message], {detached: true});
var proc = spawn(command, ['deploy.sh', message], {detached: true});
仅此而已,执行的对象就固定成了deploy.sh啦,并且前端传入的message可以作为提交的摘要写入commit。
经测试,可以运行。