使⽤pkg打包node.js项⽬(egg框架)为可执⾏包
问题:
公司有个⼯具型项⽬使⽤node.js 开发,需要部署到客户的服务器中,遇到的问题:
1、客户的服务器没有外⽹。环境配置,依赖安装等都⽐较⿇烦,只能⼿⼯上传,最好能⼀个⽂件直接搞定;
2、直接包源码部署到客户的机器中,存在源码泄露的风险。
⽅案:
使⽤pkg npm包可以很好的解决我们以上的问题。
先参考前⼈的经验:
pkg原理:
pkg打包⼯具主要会按平台(⽀持window、mac、linux)分别打包。
pkg中会包含node的可执⾏⽂件,还会包含你要打包进去的代码。代码通过⼀个虚拟的⽂件系统把所有的代码和资源⽂件都挂载到
/snapshot/${被打包项⽬的⽂件夹名} 下⾯(pkg hack了 fs 的很多⽅法,拦截⽂件操作,如果发现读的⽂件路径是在挂载⽬录下就特殊处理,返回打包进去的⽂件信息,如果不在挂载⽬录下,则按node默认逻辑进⾏)
pkg 会根据被打包项⽬的package.json 中的licence声明判断是否要对源码进⾏编译,npm上安装的依赖包基本都是开源的,会以源码的形式打包;⽽⽤户的源码如果没有声明开源的协议,则会把js⽂件编译为v8字节码进⾏保存(在项⽬中也就没法通过fs读取到源码,会给出抛错,即使是破解安装包也只能拿到v8字节码)
实现:
1、安装(也可以考虑只安装到项⽬内,通过npm script 调⽤打包)
npm install pkg -g
2、在egg配置⽂件中把涉及到写⽂件的路径都移到包外(pkg的虚拟⽂件系统只是⽤来应对读的⾏为,所有写相关都得移出包外)
// 通过process.cwd()获取当前执⾏⽂件执⾏的路径
config.rundir = process.cwd() + '/run';// 配置执⾏时临时⽂件的路径
config.logger = {
dir: path.join(process.cwd(), 'logs', logDir),//配置普通⽇志⽂件地址
};
config.customLogger = {
scheduleLogger: {
file: path.join(process.cwd(), 'logs', logDir, 'egg-schedule.log'),//配置定时任务⽇志的地址
},
};
config.static = { // 必须把public移出项⽬,否则在pkg的包中egg的static中间件会有对public操作(确保⽂件夹),会有抛错
prefix: '/',
dir: process.cwd() + '/public',//配置静态⽂件的地址
};
3、修改package.json⽂件
{
...
"bin": "pkg-entry.js", // 执⾏包的⼊⼝⽂件,可执⾏包启动的时候默认会调⽤该⽂件
js脚本开发"pkg": {// 以下主要是声明那些⽂件需要被打包(pkg会解析require中的静态路径,但在egg.js中很多⽂件都是通过框架引⽤的,⽆法依赖解析)
"scripts": [ // 这⾥是声明需要打包的js⽂件,这⾥的声明的js⽂件都会被编译为v8字节码(建议主动声明,不要依赖pkg⾃动引⼊)
"./app/**/*.js",
"./config/**/*.js",
"./normalJs/**/*.js", // 不只是egg的⽂件,只要要⽤到就要声明打包
"./app.js",
"./agent.js"
],
"assets": [ // 这⾥是声明需要打包的静态⽂件(即使有js⽂件也不进⾏编译)。
"./lib/**/*",// lib中是我打算开放的⼀些egg组件,所有不需要编译
"./app/public/**/*",// 如果要把前端静态⽂件打包进来,就直接声明(但是在egg中static中间件会有抛错,需要hack egg或者 hack pkg)
"./node_modules/**/*"// npm安装的所有依赖包全部打包进来,不要依赖⾃动引⼊,很容易导致部分⽂件没打包,出现各种意料外的错误
]
},
}
4、增加⼊⼝⽂件pkg-entry.js(名字保持和package.json中⼀致)
const fs = require('fs');
// 如果是egg的ts项⽬,由于egg-script会给ts项⽬通过-r引⼊sourcemap的注⼊⽂件,但是pkg的spawn不⽀持,所以把项⽬标识为飞ts
// 如果不是ts项⽬忽略⼀下两⾏
const pkgInfo = require('./package');
//我⾃⼰的⼯具包的配置⽂件是直接打包到安装包⾥⾯的,这样就不⽅便修改配置了。于是把提供给运维配置的配置都适⽤dotenv来配置,以下引⼊dotenv的预执⾏脚本//也可以考虑把配置⽂件放到包外,不过因为包内执⾏包外js,会增加被攻击的风险
require('dotenv/config');
// 由于egg-script是默认以当前执⾏proccess.cwd() 路径为默认项⽬的,打包后需要每次输⼊ /snapshot/${项⽬⽂件夹名} 作为指定⽬录
// 所以,以下为修改参数,⾃动嵌⼊“/snapshot/${项⽬⽂件夹名}”
const baseDir = '/snapshot/' + fs.readdirSync('/snapshot')[0];
console.log('baseDir:', baseDir);
// 当 start 的时候,⾃动嵌⼊bashDir为 /snapshot/${项⽬⽂件夹名}
const startIndex = process.argv.indexOf('start');
if (startIndex > -1) {
process.argv = [].concat(
process.argv.slice(0, startIndex + 1),
baseDir,
process.argv.slice(startIndex + 1),
);
}
// 然后直接调起egg-scripts执⾏
require('./node_modules/egg-scripts/bin/egg-scripts.js');
5、执⾏打包
## --targets ⽤于制定平台和node版本,不指定时默认为3个平台以package.json中的node版本配置为准
## --out-path 指定执⾏包输出⽂件夹,默认为当前⽂件夹
## --debug ⽤于调试,可了解哪些⽂件被打包
pkg . --targets node8-linux-x64 --out-path /usr/dist --debug
6、运⾏
> chmod a+x ./appName #给可执⾏包增加执⾏权限
> ./appName start # 启动项⽬,除项⽬路径其它参数都和egg-scripts⼀致可以⽤--title指定egg服务名
>./appName stop # 关闭项⽬(会关闭当前服务器的所有egg服务,如果有多个,最好⽤--title来指定要关闭的项⽬)
ps:
如果需要动态配置或者包中使⽤了c++编译的node依赖包,可参考该⽂章。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论