(我们活着不能与草木同腐,不能醉生梦死,枉度人生,要有所作为。——方志敏)
为什么需要使用NodeJs执行Linux脚本
- linux的sh脚本命令编写复杂,在不熟悉linux交互式命令的情况下,使用高级编程语言来执行是最明智的选择
- 高级语言的生态较为完善,可以使用各种现成的工具包来完成脚本执行,比如NodeJs的npm,Java的maven等
- linux命令较为复杂,通常需要专业的运维来执行各种复杂的命令操作,但使用高级语言则屏蔽了linux底层命令,直接使用高级语言封装好的工具包即可
NodeJs执行脚本命令
应用场景举例
比如以下场景,在传统k8s容器化服务的架构下,每次服务的发布都需要重新安装依赖,快则两三分钟,慢则十分钟甚至更久,在没有专业运维的情况下,我们可以使用NodeJs来执行命令脚本,在每次发布前,对比两个版本的package.json文件,如果不一致,则再安装依赖,否则直接跳过安装阶段,使用当前目录下的node_moodules进行发布,这样在最优的情况下,就能省去依赖安装的时间,极大的提升了服务发布速度
场景细则
上一个版本的node_modules/package.json哪里来的
- 在不使用容器化的情况下,我们在构建的目标目录下把上一个版本的node_modules/package.json复制来即可
- 使用容器化的情况下,我们可以直接使用docker镜像生成一个node_modules/package.json缓存,然后加入新版本的Dockerfile下,使用相同的工作目录就可以复用
代码示例
// 在本地环境有nodejs的情况下 直接使用node xx.js执行即可
// 使用nodejs内置破快的exec执行linux命令
const { exec } = require('child_process');
// 本地的新json
const newJson = require('./package.json');
// 从上一个版本中复制来的旧json
const oldJson = require('./old.package.json');
// 安装依赖方法
const installDependencies = async (isInstall) => {
return new Promise((resolve) =>{
// 如果不需要安装则打印信息并跳出
if(!isInstall){
process.stdout.write('no need to install dependencies \n');
// 结束promise
return resolve();
}
// 执行yarn安装依赖命令
const yarn = exec('yarn');
// 实时监听该命令输入的信息并打印
yarn.stdout.on('data', (data) => {
process.stdout.write(data);
});
// 实时监听该命令的结束指令并打印
yarn.stdout.on('end', () => {
process.stdout.write('dependencies install complete \n');
return resolve();
})
// 实时监听该命令的关闭指定并打印
yarn.stdout.on('close', () => {
process.stdout.write('dependencies install close \n');
return resolve();
});
});
};
// 编译打包命令
const buildDist = async ()=>{
return new Promise((resolve) =>{
// 执行打包命令
const yarn = exec('yarn run build');
yarn.stdout.on('data', (data) => {
process.stdout.write(data);
});
yarn.stdout.on('end', () => {
process.stdout.write('build complete \n');
return resolve();
})
yarn.stdout.on('close', () => {
process.stdout.write('build close \n');
return resolve();
});
});
};
// 立即执行函数,对比新旧两个依赖是否相同,然后执行依赖安装和打包编译操作
(async () => {
const oldPro = oldJson.dependencies;
const oldDev = oldJson.devDependencies;
const oldProKeys = Object.keys(oldPro);
const oldDevKeys = Object.keys(oldDev);
const newPro = newJson.dependencies;
const newdDev = newJson.devDependencies;
const newProKeys = Object.keys(newPro);
const newDevKeys = Object.keys(newdDev);
let isInstall = false;
// 对比新旧package.json的dependencies
const loopProDependencies = () => {
if (isInstall) {
return;
}
/**
* 对比新旧两个版本的安装包,出现以下情况的,需要重新安装依赖包
* 1. 新的依赖在旧依赖中不存在的
* 2. 新的依赖和旧依赖中版本号不同的
*/
for (let i = 0; i < newProKeys.length; i++) {
const prokey = newProKeys[i];
const proKeyVersion = newPro[prokey];
const oldKey = oldProKeys.find((v) => v === prokey);
if (!oldKey) {
process.stdout.write(`${prokey} no found, need install dependencies \n`);
isInstall = true;
break;
}
const oldKeyVersion = oldPro[oldKey];
const isSameVersion = oldKeyVersion === proKeyVersion;
if (!isSameVersion) {
process.stdout.write(`${prokey} version difference, old_version:${oldKeyVersion} new_version:${proKeyVersion} \n`);
isInstall = true;
break;
}
}
}
// 对比新旧package.json的devDependencies
const loopDevProDependencies = () => {
if (isInstall) {
return;
}
/**
* 对比新旧两个版本的开发环境安装包,出现以下情况的,需要重新安装依赖包
* 1. 新的依赖在旧依赖中不存在的
* 2. 新的依赖和旧依赖中版本号不同的
*/
for (let i = 0; i < newDevKeys.length; i++) {
const devKey = newDevKeys[i];
const devKeyVersion = newdDev[devKey];
const oldKey = oldDevKeys.find((v) => v === devKey);
if (!oldKey) {
process.stdout.write(`${devKey} no found, need install dependencies \n`);
isInstall = true;
break;
}
const oldKeyVersion = oldDev[oldKey];
const isSameVersion = oldKeyVersion === devKeyVersion;
if (!isSameVersion) {
process.stdout.write(`${devKey} version difference, old_version:${oldKeyVersion} new_version:${devKeyVersion} \n`);
isInstall = true;
break;
}
}
}
loopProDependencies();
loopDevProDependencies();
await installDependencies(isInstall);
await buildDist();
})();