关于lerna

Lerna 是一个用git和npm来优化这个管理多包库工作流的工具。用于管理具有多个包的JavaScript项目的工具。 这个介绍可以说很清晰了,引入lerna后,上面提到的问题不仅迎刃而解,更为开发人员提供了一种管理多packages javascript项目的方式。 1. 自动解决packages之间的依赖关系。 2. 通过git 检测文件改动,自动发布。 3. 根据git 提交记录,自动生成CHANGELOG

命令介绍

api 作用
lerna bootstrap 安装依赖
lerna clean 删除各个包下的node_modules
lerna init 创建新的lerna库
lerna list 显示package列表
lerna changed 显示自上次relase tag以来有修改的包, 选项通 list
lerna diff 显示自上次relase tag以来有修改的包的差异, 执行 git diff
lerna exec 在每个包目录下执行任意命令
lerna run 执行每个包package.json中的脚本命令
lerna add 添加一个包的版本为各个包的依赖
lerna import 引入package
lerna link 链接互相引用的库
lerna create 新建package
lerna publish 发布

可以使用 lerna [command] -h 查找单个命令的使用方法和参数.

1、lerna bootstrap

执行该命令会做下面四件事情:
(1)、为每个包安装依赖
(2)、.链接相互依赖的库到具体的目录
(3)、.执行 npm run prepublish
(4)、执行 npm run prepare
默认是npm i,因为我们指定过yarn,so,run yarn install,会把所有包的依赖安装到根node_modules.

– —production —no-optional 指定npm client的参数
–hoist 把依赖安装到根目录的node_modules
–ignore 忽略的包 —ignore test-* 忽略名称以test开头的包
–scope 指定的包 参数的含义是指包的名称
–ignore-scripts 不执行声明周期脚本命令, 比如 prepare
–registry 指定registry
–npm-client 指定安装用的npm client lerna bootstrap —npm-client=yarn
–use-workspace 使用yarn workspace, 没用过
–no-ci 默认调用 npm ci 替换 npm install , 使用选项修改设置 npm ci 类似于 npm-install ,但它旨在用于自动化环境,如测试平台,持续集成和部署。
–skip-git 将不会创建git commit或tag
–skip-npm 将不会把包publish到npm上
–canary 可以用来独立发布每个commit,不打tag lerna publish —canary

2、lerna list 列举当前lerna 库包含的包

列出所有的包,如果与你文夹里面的不符,进入那个包运行yarn init -y解决

–json 显示为json格式
–all 显示包含private的包
–long 显示更多的扩展信息

3、lerna changed

列出下次发版lerna publish 要更新的包。选项通 list
原理:
需要先git add,git commit 提交。
然后内部会运行git diff —name-only v版本号 ,搜集改动的包,就是下次要发布的。并不是网上人说的所有包都是同一个版全发布。

  1. lerna-repo git:(master) lerna changed info cli using local version of lernalerna notice cli v3.14.1lerna info Looking for changed packages since v0.1.4daybyday #只改过这一个 那下次publish将只上传这一个lerna success found 1 package ready to publish

4、lerna diff

显示自上次relase tag以来有修改的包的差异, 执行 git diff

5、lerna exec

在每个包目录下执行任意命令

–concurrency 默认命令时并行执行的, 我们可以设置并发量为1(全局参数) lerna exec —concurrency 1 – ls -la
–scope 设置包含的package lerna exec —scope my-component – ls -la
–stream 交叉并行输出结果 lerna exec —stream – babel src -d lib
–parallel Execute command with unlimited concurrency, streaming prefixed output.指为项目的中需要一直的进程,打印所有子进程的输出。说得有点绕,理解为需要一直运行的就加上这个参数就可以了。
–no-bail Continue executing command despite non-zero exit in a given

$ lerna exec — < command > [..args] # runs the command in all packages$ lerna exec — rm -rf ./node_modules$ lerna exec — protractor conf.jslerna exec —scope my-component — ls -la

6、lerna run 选项同lerna exec

执行每个包package.json中的脚本命令
lerna run < script > — [..args] # 运行所有包里面的有这个script的命令$ lerna run —scope my-component test

7、lerna init

创建一个新的lerna库或者是更新lerna版本
默认lerna有两种管理模式, 固定模式和独立模式
固定模式 —exact
固定模式,通过lerna.json的版本进行版本管理。当你执行lerna publish命令时, 如果距离上次发布只修改了一个模块,将会更新对应模块的版本到新的版本号,然后你可以只发布修改的库。
在publish的时候,会在lerna.json文件里面”version”: “0.1.5”,,依据这个号,进行增加,只选择一次,其他有改动的包自动更新版本号。
这种模式也是Babel使用的方式。如果你希望所有的版本一起变更, 可以更新minor版本号,这样会导致所有的模块都更新版本。
独立模式 —independent
独立模式,init的时候需要设置选项 —independent. 独立模式允许管理者对每个库单独改变版本号,每次发布的时候,你需要为每个改动的库指定版本号。这种情况下, lerna.json的版本号不会变化了, 默认为independent。
lerna.json文件里面”version”: “independent”,每次publish时,您都将得到一个提示符,提示每个已更改的包,以指定是补丁、次要更改、主要更改还是自定义更改。

8、lerna clean

删除各个包下的node_modules

9、lerna import

导入指定git仓库的包作为lerna管理的包

–flatten 如果有merge冲突, 用户可以使用这个选项所谓单独的commit lerna import ~/Product —flatten
–dest 可以指定导入的目录(lerna.json中设定的目录) lerna import ~/Product —dest=utilities

10、lerna add [@version] [—dev] [—exact]

增加本地或者远程 package做为当前项目 packages里面的依赖

  • —dev devDependencies 替代 dependencies
  • —exact 安装准确版本,就是安装的包版本前面不带^, Eg: “^2.20.0” ➜ “2.20.0” ```bash

    Adds the module-1 package to the packages in the ‘prefix-‘ prefixed folderslerna add module-1 packages/prefix-* # Install module-1 to module-2lerna add module-1 —scope=module-2 # Install module-1 to module-2 in devDependencieslerna add module-1 —scope=module-2 —dev # Install module-1 in all modules except module-1lerna add module-1 # Install babel-core in all moduleslerna add babel-core

  1. <a name="Y7e6w"></a>
  2. ### 11、lerna link
  3. 链接互相引用的库<br />链接依赖<br />有个强大的命令叫 `lerna link convert`,它可以将所有公共依赖提取到根目录并链接各个仓库。这样,可以确保所有仓库用的都是一个版本。<br />有种错误的链接方式是yarn workspace 一个lerna包 add 另一个lerna包@0.1.0,这种必须使用版本号安装,否则报错,如果用了lerna link convert,就不需要使用这种方式。
  4. <a name="FGZmr"></a>
  5. ### 12、lerna create <name> [loc]
  6. 新建包 name包名,loc 位置可选
  7. ```bash
  8. # 根目录的package.json
  9. "workspaces": [
  10. "packages/*",
  11. "packages/@gp0320/*"
  12. ],
  13. # 创建一个包gpnote默认放在 workspaces[0]所指位置
  14. lerna create gpnote
  15. # 创建一个包gpnote指定放在 packages/@gp0320文件夹下,注意必须在workspaces先写入packages/@gp0320,看上面
  16. lerna create gpnote packages/@gp0320

13、lerna version

这个命令 识别出修改的包 —> 创建新的版本号 —> 修改package.json —> 提交修改 打上版本的tag —> 推送到git上。
—allow-branch
设置git上的哪些分支允许执行 lerna version 命令, 也可以在lerna.json中设置

  1. {
  2. "command": {
  3. "publish": {
  4. "allowBranch": "master"
  5. }
  6. }
  7. }
  8. 多个
  9. {
  10. "command": {
  11. "publish": {
  12. "allowBranch": ["master", "feature/*"]
  13. }
  14. }
  15. }

具体其他参数:lerna的基础使用

14、lerna publish

lerna publish 就是将 monorepo 需要发布的包,发布到 npm registry 上面去。
会打tag,上传git,上传npm。
分为几种不同的场景去运行:
lerna publish 永远不会发布 package.json 中 private 设置为 true 的包

  1. lerna publish
  2. # lerna version + lerna publish from-git
  3. # 发布自上次发布来有更新的包(这里的上次发布也是基于上次执行lerna publish 而言)
  4. lerna publish from-git
  5. # 发布当前 commit 中打上 annoted tag version 的包(即 lerna publish from-git)
  6. lerna publish from-packages
  7. # 发布 package 中 package.json 上的 version 在 registry(高于 latest version)不存在的包
  8. # 发布在最近 commit 中修改了 package.json 中的 version (且该 version 在 registry 中没有发布过)的包(即 lerna publish from-package)
  • from-git 即根据 git commit 上的 annotaed tag 进行发包
  • from-package 即根据 lerna 下的 package 里面的 pkg.json 的 version 变动来发包
  • —canary 发测试版本的包
  • 剩下不带参数的情况就直接走一个 bump version(即执行 lerna version)

configureProperties -> initialize -> execute
在 lerna 中,几乎所有子命令源码的执行顺序都是按照这样一个结构在进行,lerna 本身作为一个 monorepo,主要是使用 core 核心中的执行机制来去分发命令给各个子项目去执行
lerna使用文档 - 图1
另外:如果你的包名是带scope的例如:”name”: “@gp0320/gpwebpack”,
那需要在packages.json添加

  1. "publishConfig": {
  2. "access": "public"
  3. },
  1. lerna publish
  2. lerna info current version 0.1.4
  3. #这句意思是查找从v0.1.4到现在改动过的包
  4. lerna info Looking for changed packages since v0.1.4
  5. ? Select a new version (currently 0.1.4) Patch (0.1.5)
  6. Changes:
  7. - daybyday: 0.1.3 => 0.1.5 #只改动过一个
  8. ...
  9. Successfully published:
  10. - daybyday@0.1.5
  11. lerna success published 1 package

发布过程
image.png
查找变更逻辑
image.png
从上面这个图可以看到其中会有一些坑:
坑1:分支3的情况,因为开发者自己打的一些标签会影响lerna查找变更,可能会造成一些变更的包没有发布
解决办法:
1. 尽量避免自己打Tag
2. 或者只在一个专门的分支上,例如master,专门运行lerna publish进行发布,这个分支不能自己打其他Tag
坑2:几条分支同时进行的情况,可能生成了相同的版本号,从而发生版本冲突
解决办法:
1. 分支开发者之间约定好各自版本号
2. 或者只在一个专门的分支上,例如master,专门运行lerna publish进行发布
坑3:运行lerna publish如果中途有包发布失败,再运行lerna publish的时候,因为Tag已经打上去了,所以不会再重新发布包到NPM
解决办法:
1. 运行lerna publish from-git,会把当前标签中涉及的NPM包再发布一次,PS:不会再更新package.json,只是执行npm publish
2. 运行lerna publish from-package,会把当前所有本地包中的package.json和远端NPM比对,如果是NPM上不存在的包版本,都执行一次npm publish
版本号的变更
分为independent模式和fix模式,上面第一张图其实是independent模式,就是每个包有自己的版本号,再附上一张fix模式的图,主要的差异部分用红色标出来了
image.png
其他问题
lerna3.x只支持npm,内部的可以用@ali/lerna,但是比较头疼的是如果packages里面既有要发外部的包,又有要发内部的包
目前的解法是将所有要发内部的包,package.json里面加上private:true,发布的步骤变成了:
1. 运行lerna version更新package.json和打Tag,这一步也会更新private:true的包
2. 运行lerna publish from-git,将刚刚更新的包发布到npm,这一步会忽略private:true的包
3. 写个自动化脚本,先把private:true的包改为private:false,代用tnpm publish发布,然后再把private改回来

DEMO

初始化项目

  1. $ npm install lerna -g
  2. $ mkdir lerna-gp && cd $_
  3. $ npm lerna init # 用的默认的固定模式,vue babel等都是这个
  4. # Add packages
  5. $ cd packages
  6. $ mkdir daybyday gpnode gpwebpack
  7. ...
  8. #分别进入三个目录初始化成包
  9. $ cd daybyday
  10. $ npm init -y
  11. $ cd ../gpnode
  12. $ npm init -y
  13. $ cd ../gpwebpack
  14. $ npm init -y

完成后的目录结构:

  1. lerna-gp git:(master) tree
  2. .
  3. ├── lerna.json
  4. ├── package.json
  5. └── packages
  6. ├── daybyday
  7. └── package.json
  8. ├── gpnode
  9. └── package.json
  10. └── gpwebpack
  11. └── package.json
  12. 4 directories, 5 files

修改顶层 package.json & lerna.json 配置

  1. # package.json 文件加入
  2. "private": true,
  3. "workspaces": [
  4. "packages/*"
  5. ],
  6. # lerna.json 文件加入
  7. "useWorkspaces": true,
  8. "npmClient": "yarn",

这样使得nodemodule会装在根目录并让所有包链接根目录node_modules。

参考: