基于Rush管理Monorepo仓库

1,530 阅读5分钟

基于Rush管理Monorepo仓库

本文将引导你从无到有,基于Rush建立monorepo仓库,对多项目进行依赖管理,提交检查与项目部署

为什么采用Rush

为什么采用Monorepo

Rush的特点

安装依赖

$ npm install -g @microsoft/rush

如果是在CICD内安装rush,可通过 node common/scripts/install-run-rush.js install 安装,该执行文件文件会在后续步骤创建出来

初始化仓库

mkdir [repo name] 创建一个空的项目文件夹

cd [repo name] 进入项目文件夹内

rush init 初始化monorepo项目

此时的目录结构类似如下:

├── common
│   └── config
│       └── rush
│           ├── .npmrc
│           ├── .npmrc-publish
│           ├── .pnpmfile.cjs
│           ├── artifactory.json
│           ├── build-cache.json
│           ├── command-line.json
│           ├── common-versions.json
│           ├── experiments.json
│           ├── repo-state.json
│           ├── rush-plugins.json
│           └── version-policies.json
├── .travis.yml
├── .gitattributes
├── .gitignore
└── rush.json

添加多个子项目

分别在 libraries/project-a 及 examples/demo 初始化两个子项目, 此时类似如下结构:

├── common
│   └── config
│       └── rush
├── examples
│   └── demo
│       └── package.json
└── libraries
    └── project-a
        └── package.json

为项目设置基本配置

按需设置rush.json:

{
  "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json",
  "rushVersion": "5.93.1",
  "pnpmVersion": "7.27.1", /** 如果你不使用pnpm可以换成其他的.均可 */
  "nodeSupportedVersionRange": ">=12.13.0 <13.0.0 || >=14.15.0 <15.0.0 || >=16.13.0 <17.0.0 || >= 18.0.0",
  "projectFolderMinDepth": 2, /** 指定项目的最小深度 */
  "projectFolderMaxDepth": 2, /** 指定项目的最大深度 */
  "gitPolicy": {
    /** 可以防呆,避免私人邮件账号commit到业务库 */
    "allowedEmailRegExps": [
      "[^@]+@users\\.noreply\\.github\\.com",
      "rush-bot@example\\.org"
    ]
  },
  /** 配置此项可以方便后续比较版本差异来判断更新,主要影响rush change命令 */
  "repository": {
    "url": "repo_url",
    "defaultBranch": "master",
    "defaultRemote": "origin"
  },
  "projects": {
    {
      "packageName": "demo", /** 与package.json内的name完全匹配 */
      "projectFolder": "examples/demo", /** 项目路径 */
      // "decoupledLocalDependencies": [], /** 如果用的不是pnpm或者存在幻影依赖的问题,可以通过此项针对指定的包不使用软链接 */
      // "skipRushCheck": false, /** 如果被对等依赖的要求困扰的话,可以试着设置为true */
      "reviewCategory": "examples", // review 审查分类
      "versionPolicyName": "example" /** 声明对应的版本策略,后面会利用到它 */
    },
    {
      "packageName": "project-a",
      "projectFolder": "libraries/project-a",
      "reviewCategory": "libraries",
      "versionPolicyName": "components"
    },
  },
}

更新版本的更新策略,找到'common/config/rush/version-policies.json'文件,更新如下:

[
    /** 声明这个策略下的项目独立发布 */
    {
      "policyName": "components",
      "definitionName": "individualVersion"
    },
    /** 声明这个策略下的项目同步发布,版本一致,适用于一组相同领域内的业务包,领域内版本一致,总是共同发布更新 */
    {
      "policyName": "components",
      "definitionName": "lockStepVersion",
      "version": "1.0.0", /** 领域内均会使用此版本,忽略项目内版本,该值初始化后不需要手动维护 */
      "nextBump": "patch" /** prerelease", "release", "minor", "patch", "major" */
    },
]

恢复依赖

rush update 执行此命令安装依赖,新项目初次执行会在common内创建一批基础脚本文件.

为子项目添加依赖

cd examples/demo 进入子项目

rush add -p [package name] 安装到dependencies

rush add -p [package name] --dev 安装到devDependencies

rush add -p [package name] --all 安装到所有子项目依赖内

rush add -m -p [package name] 安装依赖,并使其他子项目相同依赖的版本与此对齐,形成对等依赖

为demo项目添加workspace内的子项目,例如添加project-a作为依赖

rush add -p project-a 此处假设project-a的package.json里的name字段等于project-a

移除依赖项

新版rush直接执行 rush remove [package_name] 移除依赖

旧版的话:

  • 手动移除目标项目package.json中的依赖
  • 执行 rush update --recheck 检查并更新依赖

至于为什么没有类似 uninstall 或 remove 移除命令,参考Issue: #1457

执行子项目script命令

cd libraries/project-a 进入待开发的子项目

rushx [script] 执行子项目 package.json script内的命令

项目构建

rush build 增量构建所有项目

rush rebuild 构建所有项目

如果有项目不想参与构建,可将package.json build命令置空 如下所示:

{
  "scripts": {
    "build": ""
  }
}

为仓库创建 commitlint 进行提交检查

rush init-autoinstaller --name common-commands 创建一个自动安装的依赖库,会在rush install时自动安装.

cd common/autoinstallers/common-commands 进入这个路径内

pnpm add -D @commitlint/cli @commitlint/config-conventional 添加commitlint依赖

回到根目录创建 commitlint.config.js 文件,写入配置:

module.exports = {
    extends: ['./common/autoinstallers/common-commands/node_modules/@commitlint/config-conventional'],
};

修改 common/git-hooks/commit-msg 路径的文件如下:

#!/bin/sh
node common/scripts/install-run-rush.js commitlint --edit $1

调整 common/config/rush/command-line.json 路径的文件如下:

{
  /** 以模板的默认值为准 */
  "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/command-line.schema.json",
  "commands": [
    /** 支持rush lint 触发所有项目的检查 */
    {
      "commandKind": "bulk",
      "name": "lint",
      "summary": "Execute the lint command within all projects",
      "enableParallelism": true,
      "ignoreMissingScript": true,
      "incremental": true
    },
    {
      "name": "commitlint",
      "commandKind": "global",
      "summary": "Used by the pre-commit Git hook. This command invokes commitlint to ensure that the commit messages meet the conventional commit format",
      "safeForSimultaneousRushProcesses": true,
      "autoinstallerName": "common-commands",
      "shellCommand": "commitlint"
    }
  ],
  "parameters": [
    {
      "parameterKind": "string",
      "argumentName": "MESSAGE",
      "longName": "--edit",
      "description": "",
      "associatedCommands": [
        "commitlint"
      ]
    }
}

在进行rush updaterush install后,进行commit提交就会执行提交校验

为项目配置NPM相关内容

在 common/config/rush/.npmrc 下进行基础的npm配置,内容请按需配置

registry=https://registry.npmmirror.com/
always-auth=false

在 common/config/rush/.npmrc-publish 内配置发布相关内容:

# 配置发布的地址及使用的环境变量,以便cicd使用
//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN}

为项目新增文件自动格式化

cd common/autoinstallers/common-commands 进入这个路径内

pnpm add prettier pretty-quick 添加prettier pretty-quick依赖

修改 common/config/rush/command-line.json

{
  "commands": [
    {
      "name": "prettier",
      "commandKind": "global",
      "summary": "Used by the pre-commit Git hook. This command invokes Prettier to reformat staged changes.",
      "safeForSimultaneousRushProcesses": true,
      "autoinstallerName": "common-commands",
      "shellCommand": "pretty-quick --staged"
    },
  ]
}

新增该文件 common/git-hooks/pre-commit, 内容如下:

#!/bin/sh
# 在 "git commit" 执行时,该钩子会被调用,并且没有参数。如果该钩子想要阻止提交,那么它应该以返回非零状态推出。

# Invoke the "rush prettier" custom command to reformat files whenever they
# are committed. The command is defined in common/config/rush/command-line.json
# and uses the "rush-prettier" autoinstaller.
# 当 commit 时调用自定义指令 "rush prettier" 来重新格式化文件。该指令定义在 common/config/rush/command-line.json, 并通过 "rush-prettier" 自动安装并使用。
node common/scripts/install-run-rush.js prettier || exit $?

根目录新增 .prettierrc.json 文件,内容按需配置

{
  "printWidth": 120,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true,
  "quoteProps": "as-needed",
  "jsxSingleQuote": false,
  "trailingComma": "all",
  "bracketSpacing": true,
  "bracketSameLine": false,
  "arrowParens": "always",
  "endOfLine": "lf"
}

项目发布

调整build命令

修改 common/config/rush/command-line.json文件:

rush在ci环境打包时会因为警告的信息导致构建失败,所以需要调整build命令,关键在于 allowWarningsInSuccessfulBuild 这个选项

{
  "commands": [
    {
      "name": "build",
      "commandKind": "bulk",
      "summary": "Build all projects that haven't been built, or have changed since they were last built",
      "incremental": true,
      "enableParallelism": true,
      "allowWarningsInSuccessfulBuild": true
    }
  ],
}

手动发布

rush check 检查依赖

rush change 生成变更日志,如果比较分支不是rush.json内配置的默认分支,则添加-b参数,类似rush change -b [branch_name]这个change的比较对象就是指定的分支

rush version --bump 根据版本策略变更版本

rush publish --publish --include-all 发布所有change标记的变更项目

git add -Agit commit -m "chore: published v1.0.0" 提交最后的文件变更

最后通过git push把内容推送到远程库进行PR.

⚠️ 如果是CICD发布就不要直接publish了,生成预发布文件让CICD执行就可以了.

Github自动发布

新建文件 .github/workflows/build-and-publish.yml文件,内容参考如下:

# 提交代码至master前 需要对pull_request进行检查: lint/test/
name: Build and publish

on:
  # Runs on pushes targeting the default branch
  push:
    branches: ['master']

  # Allows you to run this workflow manually from the Actions tab
#  workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  contents: write
  pages: write
  id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
  group: 'build-and-publish'
  cancel-in-progress: false

jobs:
  # Build job
  build-and-publish:
    runs-on: ubuntu-latest
    steps:
      # 签出分支
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 2

      # 设置rush cache
      - name: Setup cache
        uses: gigara/rush-cache@v2

      # 设置 node
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: 18

      # 设置pnpm
      - uses: pnpm/action-setup@v2
        with:
          version: 7.27.1

      # 设置 rush 缓存文件
      - name: Cache Rush
        uses: actions/cache@v2
        with:
          path: |
            common/temp/install-run
            ~/.rush
          key: Rush-cache-${{ hashFiles('rush.json') }}

      # 设置 pnpm 缓存文件
      - name: Cache pnpm
        uses: actions/cache@v3
        with:
          path: |
            common/temp/pnpm-store
          #            ~/.cache/Cypress
          key: pnpm-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}

      # 设置 git
      - name: Setup git
        run: |
          git config --local user.email example@users.noreply.github.com
          git config --local user.name exampleBot

      # rush 安装
      - name: Install rush cli
        env:
          HUSKY: 0
        run: |
          node common/scripts/install-run-rush.js install

      # 构建整个仓库项目
      - name: Run rush Build
        env:
          NODE_ENV: production
          NODE_OPTIONS: --max_old_space_size=8192
        run: node common/scripts/install-run-rush.js build --verbose

      # 变更检查
      - name: Check change
        run: node common/scripts/install-run-rush.js change --verify

      # 应用变更
      - name: Apply change
        env:
          HUSKY: 0
        run: node common/scripts/install-run-rush.js version --bump --target-branch master

      # 发布变更包
      - name: Publish
        env:
          NPM_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
        run: node common/scripts/install-run-rush.js publish --publish --include-all

      # 部署网站
      - name: Deploy GitHub Pages site
        uses: JamesIves/github-pages-deploy-action@v4
        with:
          folder: ./libraries/documents/src/.vuepress/dist # 调整为自己项目内的文档位置

之后想要发布只要执行了rush change命令并提交变更文件到master变更触发cicd流程

结尾

官方文档

附上模板仓库地址