Monorepo

Monorepo 是管理项目代码的一个方式,指在一个项目仓库 (repo) 中管理多个模块/包 (package),不同于常见的每个模块建一个 repo。
monorepo 最主要的好处是统一的工作流和Code Sharing。

2021.4.5 星期一 15:32

Google 宁愿把所有的代码都放在一个 Monorepo 工程下面,
Vue 3、Yarn、Npm7,vue-router, babel, 等等知名开源项目的源码也是采用 Monorepo 的方式来进行管理的。

优缺点

Visibility
Simpler dependency management
Single source of truth
Consistency
Shared timeline
Atomic commits
Implicit CI
Unified CI/CD
Unified build process

Bad performance
Broken main/master
Learning curve
Large volumes of data
Ownership
Code reviews

Monorepo 优点:
单个的lint,build,test和release流程。
统一的地方处理issue。
不用到处找项目的repo。
方便管理版本和依赖管理。
跨项目的操作和修改很容易。
方便统一生成 ChangeLog。

Monorepo 缺点:
repo 体积变大。
需要额外的工具实现项目间的联合调试(例如:Jest 的 watch.js,监听子项目中文件的变化,动态编译)。
由于项目间的依赖通过符号链接(快捷方式)实现,对打包工具有比较高的要求。
多个项目集中到一起后,常用的 IDE 可能会遇到麻烦

解决方案

目前最常见的 monorepo 解决方案是 Lerna 和 yarn 的 workspaces 特性。其中,lerna 是一个独立的包,其官网的介绍是:

a tool that optimizes the workflow around managing multi-package repositories with git and npm.

yarn workspace VS lerna

对比了 lerna 和 yarn workspace 之后,最后放弃了 lerna 的方式,改用 yarn workspace。主要的原因是 lerna 默认情况下重复安装的依赖包太多,而 yarn workspace 则避免了这个问题。

对于node_modules包重复安装的问题,lerna提供了–hoist选项,相同的依赖,会「提升」到 repo 根目录下安装,但……太鸡肋了,lerna 直接以字符串对比 dependency 的版本号,完全相同才提升,semver 约定在这并不起作用。
yarn作为包管理器很好的解决了这个问题,只需要在根package.json中以 workspaces 字段声明 packages目录和”private”: true,yarn 就会以 monorepo 的方式管理 packages。

yarn workspace + lerna

目前业界最佳实践是采用yarn workspace + lerna 来实现,vue3.0也是采用两者结合的方式来实现。
yarn workspace可以实现在一个项目中实现多个模块的依赖新增和共用,而lerna的功能则更完善,不仅可以管理多个模块,还有清除模块node_modules,发布模块到npm,自动更新模块间版本依赖,并支持全量发布和根据改动单独发布等功能。
yarn官方推荐用yarn来处理依赖安装,用lerna来处理依赖更新和发布问题。

multirepo VS monorepo

代码分散在不同的参考中,容易导致大量重复的内容,这个问题通过 ​monorepo​ 容易避免
Dev environments
Build configurations
Dependencies
Test configuration
Pull request templates
ESLint
Prettier
CI/CD

对于我们的项目而言,由于项目处在快速的迭代周期内,multirepo 的问题是致命的,所以选择了更复杂的 monorepo 的管理模式,也随之设计了一套复杂的版本管理方案。但是猜测在项目较为稳定的阶段,是会迁移到 multirepo 的模式的(已离职,做个猜想哈)。

monorepo 需要开发人员对代码熟悉,对 git 熟悉,否则几十人的团队去维护同一个 monorepo 仓库是玩不转的。著名开源项目采用这种模式,并且玩的有声有色,也许是因为他们都是非常优秀的程序员吧。对于上面提到的一系列问题,业务未来会找到好的解决方案,在两种模式间采用一种更为平衡的方式吧。到时再重写一篇文章做思考。

monorepo 的问题在于,各个模块都在同一仓库,不好进行模块的版本管理
出于代码安全层面考虑,monorepo 不能很好的控制各个模块的代码权限
出于代码的分支管理考虑,monorepo 不得不采用一套复杂的分支管理机制

实践

主要以 Monorepo 的概念、MultiRepo的弊端、Monorepo 的收益以及Monorepo 的落地



3.3.2 Eslint
3.3.3 Babel
3.4 统一命令脚本:scripty
3.5 统一包管理:Lerna
3.5.2 npm 包本地发布:Verdaccio
3.6 格式化 commit 信息
\4. ? 如何从 multirepo 迁移至使用 monorepo 策略?

others

有的公司对一些核心代码需要有读权限的限制,开源的代码仓库管理软件比如 Git 和 Mercurial 对此是不支持的。这时候把那部分代码移到一个 git-submodule 或者 subrepository 里面也是可以的解决方案。submodule / subrepository 大家捏着鼻子也是可以用的。

因为所有人的 commit 在 monorepo 里都在一个线性历史里面,所以很嘈杂。比如要二分找一个导致网页出错的 bug 需要搜索的版本可能就不必要的多。这种时候用 Buck / Bazel 的优势就体现出来了,可以很方便的确定某个 commit 对于某个功能是不是真的有影响,不是的话可以直接跳过。

What is monorepo? (and should you use it?)
npm 官宣:未来将支持 monorepo 特性,带来源码管理新姿势!

knowledge is no pay,reward is kindness
0%