有一点规模的团队,都需要逐渐构建自己的npm包。现在有一个比较好的解决方案Sinopia,可以快速搭建和部署私有的npm服务器。
本文详细记录了我在搭建私有npm包时,遇到的问题,以及个人的思考。
1. 安装Sinopia
安装
首先,你要自己配置nodejs及npm的环境,然后运行
npm install -g sinopia
启动
$ sinopia
warn --- config file - .....\AppData\Roaming\sinopia\config.yaml
warn --- http address - http://localhost:4873/
连接中的文档已经写的很详细了,我就不重复造轮子了。
深坑预警
后来我们把私有仓库改为了verdaccio,因为sinoipa不支持包命名空间,详情请见下文。
3、搭建好私有npm仓库后遇到什么坑?
我发现使用nrm切换到私有npm仓库对应的源后,下载带@ 符号的包都下载失败,比如下载 @angular/core,就会下载失败,这是为什么呢,查阅了一些资料,发现这其实是Sinopia自己的bug,bug产生的原因就是:sinopia在代理到npmjs.org公有库时将@符号转码为%40,致使在公有库中找不到对应的包,返回404 ,简单点说就是 @angular/core 代理请求的时候被转换成了 %40angular/core,所以我们需要在代理请求发出之前将其转回 @angular/core
4、如何解决?
修改sinopia源码:修改位于sinopia/lib/up-storage.js文件第10行:将var encode = encodeURIComponen;,更改为:var encode = function(thing) {return encodeURIComponent(thing).replace(/^%40/, ‘@’);}; ,这段代码的含义就是将%40转回@,于是就解决了不能下载带有@符号的npm包的bug
5、更好的解决方案?
由于sinopia的作者已于二年前停止对sinopia的维护和升级,所以出来了一个sinopia的fork,名字叫做Verdaccio,然后由Verdaccio继续对sinopia进行更新和维护,具体如何使用Verdaccio来构建私有npm服务器,请见Verdaccio的github介绍
2. 构建过程中遇到的问题:
2.1. 外网无法访问私有sinopia服务器
[root@webteam sysconfig]# systemctl stop firewalld.service
[root@webteam sysconfig]# systemctl start firewalld.service
[root@webteam sysconfig]# firewall-cmd --permanent --add-port=4873/tcp
success
[root@webteam sysconfig]# firewall-cmd --reload
success
[root@webteam sysconfig]#
2.2. 开机自启动
ascs@webteam sinopia]$ pm2 save
[PM2] Saving current process list...
[PM2] Successfully saved in /home/ascs/.pm2/dump.pm2
[ascs@webteam .pm2]$ pm2 startup centos
[PM2] Init System found: systemd
-----------------------------------------------------------
PM2 detected systemd but you precised centos
Please verify that your choice is indeed your init system
If you arent sure, just run : pm2 startup
-----------------------------------------------------------
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup centos -u ascs --hp /home/ascs
[ascs@webteam .pm2]$ sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup centos -u ascs --hp /home/ascs
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
[sudo] password for ascs:
2.3. 关于添加用户失败的问题
$ npm adduser
Username: webteam
Password:
Email: (this IS public) (yueshenghu@ascs.tech)
npm ERR! code E500
npm ERR! Internal Server Error
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\Administrator\AppData\Roaming\npm-cache\_logs\2018-05-08T13_17_14_767Z-debug.log
这个问题,在查看sinopia服务器之后,我发现是Linux目录权限问题。
[ascs@webteam sinopia]$ sinopia
warn --- config file - /usr/share/nginx/html/sinopia/config.yaml
warn --- http address - http://0.0.0.0:4873/
error --- unexpected error: EACCES: permission denied, open '/usr/share/nginx/html/sinopia/htpasswd'
Error: EACCES: permission denied, open '/usr/share/nginx/html/sinopia/htpasswd'
at Error (native)
http <-- 500, user: undefined, req: 'PUT /-/user/org.couchdb.user:webteam', error: internal server error
error --- unexpected error: EACCES: permission denied, open '/usr/share/nginx/html/sinopia/htpasswd'
Error: EACCES: permission denied, open '/usr/share/nginx/html/sinopia/htpasswd'
at Error (native)
http <-- 500, user: undefined, req: 'PUT /-/user/org.couchdb.user:webteam', error: internal server error
^C
那么怎么解决这个问题呢?
仔细查看Linux提示的错误,考虑远程向sinopia服务添加用户的时候,也是向linux目录下写文件的时候,这个时候的文件夹,如果没有其他用户的写权限,那么是没有办法写入的。所以就会报错。
解决方法:
进入sinopia的父级目录,然后将sinopia目录的权限提升。
[ascs@webteam sinopia]$ cd ..
[ascs@webteam html]$ su
密码:
[root@webteam html]# chmod 757 sinopia/
[root@webteam html]# ls -al
总用量 20
drwxrwxr-x+ 19 root root 4096 5月 8 14:17 .
drwxr-xr-x. 3 root root 18 4月 3 14:22 ..
-rw-rwxr--+ 1 root root 537 5月 3 16:40 50x.html
drwxr-xrwx 3 root root 40 5月 4 19:57 sinopia
2.4. 关于发布包失败的问题
解决了添加用户失败的问题,结果发布包又出了问题,为什么呢?
$ npm publish
npm ERR! Unexpected token u in JSON at position 0
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\Administrator\AppData\Roaming\npm-cache\_logs\2018-05-08T13_41_42_751Z-debug.log
查看sinopia的日志
/home/ascs/.pm2/logs/sinopia-error-1.log last 15 lines:
1|sinopia | Error: EACCES: permission denied, open '/usr/share/nginx/html/sinopia/storage/.sinopia-db.json'
1|sinopia | at Error (native)
1|sinopia | at Object.fs.openSync (fs.js:642:18)
1|sinopia | at Object.fs.writeFileSync (fs.js:1348:33)
1|sinopia | at LocalData.sync (/usr/lib/node_modules/sinopia/lib/local-data.js:42:6)
1|sinopia | at LocalData.add (/usr/lib/node_modules/sinopia/lib/local-data.js:20:10)
1|sinopia | at updater (/usr/lib/node_modules/sinopia/lib/local-storage.js:251:27)
1|sinopia | at /usr/lib/node_modules/sinopia/lib/local-storage.js:582:5
1|sinopia | at /usr/lib/node_modules/sinopia/lib/local-fs.js:248:8
1|sinopia | at callback (/usr/lib/node_modules/sinopia/lib/local-fs.js:197:19)
1|sinopia | at onRead (/usr/lib/node_modules/sinopia/lib/local-fs.js:214:9)
没错,又碰上了权限问题。老办法,给.sinopia-db.json
添加些权限。
切换到root权限,然后给文件授权。
简单粗暴的给了777,看官如果你要部署在公网上,请慎重哟。
[root@webteam storage]# chmod 777 .sinopia-db.json
[root@webteam storage]#
[root@webteam storage]# ls -al
总用量 4
drwxrwxrwx 3 root root 52 5月 8 21:55 .
drwxr-xrwx 3 root root 56 5月 8 21:37 ..
drwxrwxr-x 2 ascs ascs 90 5月 8 21:55 cityworks3d_cm
-rwxrwxrwx 1 root root 103 5月 8 21:55 .sinopia-db.json
再次发布,成功!
$ npm publish
+ cityworks3d_cm@1.0.0
3. 随之而来的问题:
3.1. npm的命名空间问题
如果因为你起的包名与现有的包名太相近而被阻止发布这个包,那么找到一个独一无二包名最简单方法就是使用你的作用域。你可以使用@+你的npm用户名加在包名前面将包划到你的npm账户作用域下。比如,我的npm用户名是ceejbot,所以我的作用域是@ceejbot。
在你自己的作用域下发布一个包是免费的!你可以这样去做:
我已经知道json-stream与现有包名太接近了,所以我需要找到一个新的名字。首先,我需要编辑我的package.json文件,添加我的npm账户名来划分一个作用域,所以:
{
"name": "json-stream"
}
修改为:
{
"name": "@ceejbot/json-stream"
}
然后我要发布这个包。被划了作用域的包默认是私有的,所以要通过—access=public 让它变为公有的包:
> npm publish --access=public
+ @ceejbot/json-stream@1.0.0
每个人都可以向npm注册表中发布公共的作用域包,但是你想发布私有的包就要付费订阅了。
3.2. 生产环境中的使用
3.2.1. 容器部署
最佳实践是将sinopia作为单独服务部署在docker容器中
在此我制作了一个可以直接使用的Docker Image启动容器则自动开启服务, pm2将会守护sinopia进程
3.2.2. 版本控制
将sinopia作为公司内部npm库还存在几个问题
使用npm publish上传的流程无法统一管理, 也就是无法持续集成/code review
每个版本的更新信息完全依靠文档, 无法通过code diff追溯
以gitlab为例, 我的做法是
将ci runner的服务器切换至私有源, 使用npm adduser注册一个root用户
在sinopia服务器上将sinopia/config.yaml中的max-users设置为-1, 禁止注册与私自更新版本
将node库源代码在gitlab中创建项目 提交代码走PR流程
ci.yml中, build环境处理lint, unit test, production build等流程,tag环境npm publish提交版本, 保持gitlab与npm版本一致
邮件通知各组成员 库版本更新信息
3.2.3. 团队使用
团队所有成员切换npm至私有库
推荐使用nrm库 可以方便的切换npm源
nrm add private http://localhost:4873 # 添加源管理
nrm ls # 查看源列表
nrm use private # 切换至该源
npm install @scope/package-name@version --save-dev #
其它操作与npm公用库相同
4. 补充说明
4.1. NRM常用命令
nrm最常有的命令有 :
1.nrm ls 查看已有的源
2.nrm add <源名称> <源地址> 新增源
3.nrm use <源名称>切换到现有的源
4.nrm test 测速
4.2. 使用npm安装包
使用 npm –save 标志下载一个包
常规: npm install pkg, 快捷方式: npm i pkg.
常规: npm i --global pkg, 快捷方式: npm i -g pkg.(全局安装一个包)
常规: npm i --save pkg, 快捷方式: npm i -S pkg`.(安装一个项目依赖包,写进dependence)
常规: npm i --save-dev pkg, 快捷方式: npm i -D pkg`.(全局安装一个项目开发依赖包,写进devDependence)
举个栗子:
npm install lodash --save
这将会在下载包文件之前修改 package.json 文件的依赖关系字段。现在打开刚才创建的 package.json 文件我们会发现多了个 dependencies 字段。