如何管理pnpm monorepo多仓库模式下的版本与发包和给开源仓库提PR


theme: cyanosis

作者:易师傅github

声明:文章为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!

前言

大家好,我是易师傅,一个专门搞前端的搬(touch)砖(fish)师傅 ~

在上篇文章中《如何去搞 Vue/React Hooks 和 Utils 企业级开源工具库》中主要给大家介绍了如何从 0 到 1 搭建一个工具库;

正所谓,打铁要趁热 ~

这不,为了让各位大佬们都有参与感,同时也为了让大家了解熟悉开源项目的大致流程;

于是一个 A Crazy 开源工具库 vemjs 就此诞生了;

当然如果您对开源感兴趣,欢迎您加入我们的大家庭(已有五十多位大佬) ~

集思广益,因为身为万千优秀的贡献者的您,您的灵感是我们必不可少的一部分 ~

一、利用 changeset 规范 PR

通过 vmejs仓库,我们得知我们使用的 pnpm workspace 做的多仓库管理;

同时我们为了版本控制与 pnpm monorepo 更好的搭配,所以我们使用 changeset 来进行快速集成;

1. 安装 changeset

pnpm i @changesets/cli -Dw

2. 初始化 changesets

pnpm changeset init

就会生成一个 .changeset/conifg.json 文件,相关配置如下

{
  "$schema": "<https://unpkg.com/@changesets/config@2.2.0/schema.json>",
  "changelog": "@changesets/cli/changelog", //更新日志生成函数的加载地址
  "commit": true, //自动提交version的改动
  "fixed": [], //捆绑发布的包
  "linked": [],// 配置哪些包要共享版本
  "access": "public", //公开,如果你想阻止一个包被发布到npm,在包的package.json中设置private: true (可选值:restricted,不公开)
  "baseBranch": "main", //主分支名
  "updateInternalDependencies": "patch", //是否主动更新package的依赖,patch、minor
  "ignore": []  //指定不发布的包
}

3. 配置 package.json 的脚本

"scripts": {
    "change": "changeset add",
    "change:version": "changeset version",
    "release": "pnpm build && pnpm release:only",
    "release:only": "changeset publish"
}

4 运行 pnpm change

主要目的就是生成 changeset 文件(就是下图中的 md 文件);

如下图所示,其中每个 md 文档就代表了你执行的 pnpm changeset 后生成的;

image.png

需要注意的是执行 pnpm change 命令(上面changeset add 中的 add 可省略),会弹出一些询问窗口,主要分为三步:

  • 哪些包是需要生成 changeset 文件的?
  • 此次发布包的版本是什么?(遵循 semver 规范);
  • 输入此次变更的描述信息以便生成到 changeset 文件中;

5. 运行 pnpm change:version

主要目的就是消耗 pnpm change 生成的 changeset 文件,同时根据 changeset 文件中的内容生成对应子包的 CHANGELOG 信息;

image.png

到这里你的生成 changelog 就已经完成,但是如果你想让你的 log 信息更详细一点,可以引入 @changesets/changelog-github

6. 配置 @changesets/changelog-github

丰富生成的 CHANGELOG.md(建议开源中引入,企业中不需)

1. 安装

pnpm i @changesets/changelog-github -Dw

2. 修改 .changeset/conifg.json 配置文件

{
  ...
  "changelog": ["@changesets/changelog-github", { "repo": "vmejs/vmejs" }],
  ...
}

3. 配置 GITHUB_TOKEN

image.png

4. 手动生成对应的消息需要配置环境变量(本地配置测试):

# win
set GITHUB_TOKEN=Your Yoken
# mac 
export GITHUB_TOKEN=Your Yoken

如果你不想配置,可以直接运行以下命令:

GITHUB_TOKEN=xxx pnpm changeset version

注意:因为是本地环境配置测试,所以后续自动化发布会自动鉴权是不需要在本地设置的

5. 再重复上面的执行 pnpm changepnpm change:version,会多了许多信息

image.png

7. 运行 pnpm release 的最终发布

image.png

二、用 GitHub Action 做 CI/CD 流程

1. 添加发布脚本

编写对应的 .github/workflows/release.yml 执行脚本

# https://docs.github.com/en/actions/using-workflows/about-workflows
# 整个工作流的名称
name: Release

# 监听 main 分支的 push 事件(有更新时)
on:
  push:
    branches:
      - main

# 所有的 jobs
jobs:
  # 定义一个名为 release 的 job
  release:
    # 指定运行所需要的虚拟机环境(必填)
    runs-on: ubuntu-latest
    # 定义步骤
    steps:
      # https://github.com/actions/checkout
      # 拉取代码
      - name: Checkout Repo
        uses: actions/checkout@v2

      # https://github.com/actions/setup-node
      # 安装 node
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 16.x

      # https://github.com/pnpm/action-setup
      # 安装 pnpm
      - name: Setup Pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 7.x

      # https://pnpm.io/zh/cli/install#--frozen-lockfile
      # 如果缓存没有命中,安装依赖
      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile --ignore-scripts

      # https://github.com/changesets/action
      # 自动创建发布 PR 和 发布 npm
      - name: Create Release Pull Request or Publish to npm
        uses: changesets/action@v1
        # 参数配置
        with:
          publish: pnpm release
          version: pnpm change:version
          commit: 'chore: version packages'
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

因为其中逻辑错综复杂;

所以其中具体详细逻辑不做细述,大家只需明白这是一个自动化合并 PR发布 npm 的脚本即可;

2. 添加文档打包脚本

编写对应的 .github/workflows/docs-deploy.yml 执行脚本

name: docs-deploy

on: # 触发条件
  # 每当 push 到 main 分支时触发部署
  push:
    branches: [main]

jobs:
  docs:
    runs-on: ubuntu-latest # 指定运行所需要的虚拟机环境(必填)

    steps:
      - uses: actions/checkout@v2
        with:
          # “最近更新时间” 等 git 日志相关信息,需要拉取全部提交记录
          fetch-depth: 0

      - name: Install pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 7

      - name: Setup Node.js
        uses: actions/setup-node@v2
        with:
          # 选择要使用的 node 版本
          node-version: '16'
          cache: 'pnpm'

      # 如果缓存没有命中,安装依赖
      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile --ignore-scripts

      # 运行构建脚本
      - name: Build vitepress site
        run: pnpm docs:build
        env:
          DOC_ENV: preview
          NODE_OPTIONS: --max-old-space-size=4096

      # 查看 workflow 的文档来获取更多信息
      # @see https://github.com/crazy-max/ghaction-github-pages
      - name: Deploy to GitHub Pages
        uses: crazy-max/ghaction-github-pages@v2
        # 环境变量
        env:
          GITHUB_TOKEN: ${{ secrets.ACTION_SECRET }}
        with:
          # 部署到 gh-pages 分支
          target_branch: gh-pages
          # 部署目录为 vitepress 的默认输出目录
          build_dir: packages/.vitepress/dist

三、贡献指南(提 PR)

  • Node.JS >= 16
  • PNPM v7

1. 本地环境安装试运行

```
# https
git clone https://github.com/vmejs/vmejs.git

# ssh
git clone git@github.com:vmejs/vmejs.git

# 进入 vmejs 目录
cd vemjs && pnpm install
```

2. 贡献流程

  • 先查阅文档是否有你所需要的函数方法
  • 如果没有你想要的函数方法,你有两种方式:
    • 发起一个 issues 讨论,评审人评论PR Welcome表示该idea可行,然后你就可以 fork 代码开发了;
    • 关闭当前页面,骂一顿这个作者:这是什么 laji 库;

3. 功能开发流程

  1. 请先 fork 一份到自己的项目下,然后新建一个分支用于变更

    image.png

  2. 基于 fork 后的项目新建分支,新建功能分支(例如 feature-getDevice)

    # https
    git clone 你的fork项目 https 地址
    
    # ssh
    git clone 你的fork项目 ssh 地址
    
    # 进入 vmejs 目录
    cd vemjs && pnpm install
    
    # 新建功能分支
    git checkout -b feature-getDevice
    
  3. 完成对应函数方法(例如:https://github.com/vmejs/vmejs/blob/main/packages/core/getDevice/index.ts)

  4. 完成对应的测试用例(例如:https://github.com/vmejs/vmejs/blob/main/packages/core/getDevice/index.test.ts)

  5. 完成对应的使用文档(例如:https://github.com/vmejs/vmejs/blob/main/docs/packages/core/getDevice/index.md)

  6. 你可以本地执行一些命令:

    "scripts": {
        // 本地打包
        "dev": "tsup --watch",
        "build": "tsup",
        // 本地打包文档
        "docs:dev": "pnpm -C docs dev",
        "docs:build": "pnpm run -C docs build",
        // 本地执行eslint prettier检查
        "lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx,.json --max-warnings 0 --cache",
        "lint:fix": "pnpm run lint --fix",
        "format": "prettier --write --cache .",
        // 执行测试用例
        "test": "vitest test",
        // 测试用例覆盖率
        "coverage": "vitest run --coverage"
      },
    
    
  7. 研发完成后需进行相关规则的 commit 检测

    # 1. 检查代码格式
    pnpm eslint && pnpm format
    
    # 2. 执行测试用例
    pnpm test
    
    # 3. 测试用例覆盖率
    pnpm coverage
    
  8. 以上全部 ok 后,一定要在本地执行 pnpm change 生成 changeset 文件

    pnpm change
    
  9. Git 上传:严格遵守 Git Commit 规范

    • 统一格式:
    <type>(<scope>): <subject>
    // 注意冒号 : 后有空格
    // 如 feat(user): 增加用户中心的 xx 功能
    复制代码
    
    • scope 表示 commit 的作用范围,如用户中心、购物车中心,也可以是目录名称,一般可以限定几种;

    • subject 用于对 commit 进行简短的描述;

    • type 必填,表示提交类型,值一般有以下几种:

      • feat:新功能 feature
      • bug:测试反馈 bug 列表中的 bug 号
      • fix: 修复 bug
      • ui:更新UI;
      • docs: 文档注释变更
      • style: 代码格式(不影响代码运行的变动);
      • refactor: 重构、优化(既不增加新功能,也不是修复bug);
      • perf: 性能优化;
      • release:发布;
      • deploy:部署;
      • test: 增加测试
      • chore: 构建过程或辅助工具的变动
      • revert: 回退
      • build: 打包

4. Code Review

  • 你 fork 的功能分支提交 PR 合并至main分支
  • 代码审核与优化
  • 审核人Approved后合入main分支

四、目录结构

项目采用 Monorepo 进行代码管理,下面展示了主要目录结构

root
├── docs                     # 文档
│   ├── guide                # 指引文档
│   │   └── index.md
│   └── packages
│            └── core        # 函数展示文档
│                └── getDevice
                       └── index.md # 函数方法文档
├── packages
│   ├──  core                # 主工具函数 Utils 集合
│   │   ├── getDevice
│   │   ├── getxxxx
│   │   └── 其它
│   └── shared               # 主工具函数公用方法集合
│       ├── is               # 方法集合
│       ├── xx               
│       └── 其它              # 其它
└── playground               # 本地开发调试操场

工具函数目录结构 (以getDevice为例):

# packages/core/getDevice
core
└──getDevice
   ├── index.test.ts         # 单元测试
   └── index.ts              # 函数方法入口

五、开源的意义

这里借用阴明的一句话:开源即责任

一个好的软件项目是为了服务好用户,选择开源模式,本身就是选择了一个为解决复杂问题提供通用(高市占率)解决方案的途径;

选择开源模式,即提高了普及项目的速度,也就提高了你的软件被更多人依赖的可能;

开源更多的无非是时间与精力上的损耗,但是一旦你的软件被更多人认可,那即变成了对你的一种束缚,亦可称之为责任;

这也就是为什么,开源即责任。

开源的目的:

  • 开源是为了更好地协作开发出解决用户需求的软件;
  • 开源是为了解决技术和架构的停滞不前而有的新思维;
  • 开源是为了从万千优秀的贡献者中汲取灵感,加速整个行业的创新;
  • 开源不仅是为自由软件做市场营销,更多应该是造福整个社区;

总结

如果您对开源感兴趣,欢迎您加入我们 ~

如果您对工具库感兴趣,欢迎加入我们 ~

如果您对 Vue Hooks 感兴趣,想写自己的 Hooks,欢迎加入我们 ~

如果您对 React Hooks 感兴趣,想写自己的 Hooks,欢迎加入我们 ~

如果您有更好的工具库 Idea,欢迎加入我们 ~

集思广益,因为身为万千优秀的贡献者的您,您的灵感是我们必不可少的一部分;

一个完善的工具库离不开万千函数的支撑、也离不开广大掘友们的支持;

您还在等什么,赶紧加入我们吧!联系 vx:JeddyGong

写得好了,您的点赞关注是我最大的鼓励 🤝🤝🤝

写的差了,您的评论批评是我最大的动力 💪💪💪


只是其中有许多是需要我们优化的,这篇文章就不做详细的介绍了,因为每个优化都是一副长篇概论;

当然,既然做我们就要做好了,所以下面有几个自动化的优化是需要持续跟进的:

  • 文档自动化打包,不需手动添加对应的路径;
  • 文档抽离成一个单独的 docs 目录(可忽略);
  • changeset 的更多功能使用,让开源更加轻松;[已完成]
  • Github 自动部署发布配置,自动 Tag/release 等[已完成]
  • 开源贡献指导文档;[已完成]
  • 更多 cli 脚本;

工具库模板地址:链接

Github 开源地址:链接

最后

其实大家都明白做开源是一个很费劲的事,先不说出发点在哪;

究竟是单纯的开源,还是另有目的我想每个人的想法都不一样;

笔者的开源很简单,就是简简单单的爱好罢了,无欲无求就是最真实的写照;


该系列会是一个持续更新系列,关于 前端基建,笔者主要会从如下图几个方面讲解,如果您想第一时间看到我的更新文章,可以关注我和我的《前端要搞基建》专栏

前端基建之路.png

如果你对 Vite 感兴趣,可以看看我的专栏:《Vite 从入门到精通》

如果你对微前端感兴趣,可以看看我的专栏:《微前端从入门到精通》

如果想跟我一起讨论技术吹水聊球, 欢迎加入前端学习群聊(群人数太多,只能加vx,望谅解) vx: JeddyGong

感谢大家的支持,码字实在不易,其中如若有错误,望指出,如果您觉得文章不错,记得 点赞关注加收藏 哦 ~

关注我,带您一起搞基建 ~

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容