基于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 update或rush 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 -A 及 git 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流程
结尾
附上模板仓库地址