monorepo-workflows
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMonorepo Workflows Skill
Monorepo工作流技能指南
Overview
概述
This skill provides comprehensive guidance on development workflows, CI/CD
patterns, version management, publishing strategies, and collaboration
practices for monorepo environments.
本技能指南为monorepo环境下的开发工作流、CI/CD模式、版本管理、发布策略及协作实践提供全面指导。
Development Workflows
开发工作流
Local Development Setup
本地开发环境搭建
Configure efficient local development environment.
Package.json scripts:
json
{
"scripts": {
"dev": "turbo run dev --parallel",
"dev:web": "turbo run dev --filter=@myorg/web...",
"build": "turbo run build",
"test": "turbo run test",
"lint": "turbo run lint",
"clean": "turbo run clean && rm -rf node_modules",
"reset": "pnpm clean && pnpm install"
}
}Environment setup script:
bash
#!/bin/bash配置高效的本地开发环境。
Package.json脚本:
json
{
"scripts": {
"dev": "turbo run dev --parallel",
"dev:web": "turbo run dev --filter=@myorg/web...",
"build": "turbo run build",
"test": "turbo run test",
"lint": "turbo run lint",
"clean": "turbo run clean && rm -rf node_modules",
"reset": "pnpm clean && pnpm install"
}
}环境搭建脚本:
bash
#!/bin/bashscripts/setup-dev.sh
scripts/setup-dev.sh
echo "Setting up development environment..."
echo "Setting up development environment..."
Check Node version
Check Node version
required_node_version="18.0.0"
current_node_version=$(node -v | cut -d'v' -f2)
if [ "$(printf '%s\n' "$required_node_version"
"$current_node_version" | sort -V | head -n1)" !=
"$required_node_version" ]; then echo "Error: Node.js $required_node_version or higher required" exit 1 fi
"$current_node_version" | sort -V | head -n1)" !=
"$required_node_version" ]; then echo "Error: Node.js $required_node_version or higher required" exit 1 fi
required_node_version="18.0.0"
current_node_version=$(node -v | cut -d'v' -f2)
if [ "$(printf '%s\n' "$required_node_version"
"$current_node_version" | sort -V | head -n1)" !=
"$required_node_version" ]; then echo "Error: Node.js $required_node_version or higher required" exit 1 fi
"$current_node_version" | sort -V | head -n1)" !=
"$required_node_version" ]; then echo "Error: Node.js $required_node_version or higher required" exit 1 fi
Enable pnpm
Enable pnpm
corepack enable pnpm
corepack enable pnpm
Install dependencies
Install dependencies
pnpm install
pnpm install
Build all packages
Build all packages
pnpm run build
pnpm run build
Setup git hooks
Setup git hooks
pnpm husky install
echo "Development environment ready!"
undefinedpnpm husky install
echo "Development environment ready!"
undefinedCross-Package Development
跨包开发
Work across multiple packages simultaneously.
Using workspace linking:
bash
undefined同时在多个包中进行开发工作。
使用工作区链接:
bash
undefinedAll workspace packages automatically linked
所有工作区包自动链接
pnpm install
pnpm install
Verify links
验证链接
pnpm list --depth 1
**Development with watch mode**:
```json
{
"scripts": {
"dev:packages": "turbo run dev --filter='./packages/*'",
"dev:apps": "turbo run dev --filter='./apps/*'"
}
}Concurrent development:
json
{
"scripts": {
"dev:all": "concurrently \"pnpm:dev:*\"",
"dev:ui": "pnpm --filter @myorg/ui run dev",
"dev:web": "pnpm --filter @myorg/web run dev",
"dev:api": "pnpm --filter @myorg/api run dev"
},
"devDependencies": {
"concurrently": "^8.2.2"
}
}pnpm list --depth 1
**监听模式下开发**:
```json
{
"scripts": {
"dev:packages": "turbo run dev --filter='./packages/*'",
"dev:apps": "turbo run dev --filter='./apps/*'"
}
}并发开发:
json
{
"scripts": {
"dev:all": "concurrently \"pnpm:dev:*\"",
"dev:ui": "pnpm --filter @myorg/ui run dev",
"dev:web": "pnpm --filter @myorg/web run dev",
"dev:api": "pnpm --filter @myorg/api run dev"
},
"devDependencies": {
"concurrently": "^8.2.2"
}
}Hot Module Reloading
热模块重载
Enable fast refresh across package boundaries.
Vite configuration:
typescript
// apps/web/vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
watch: {
// Watch workspace packages
ignored: ['!**/node_modules/@myorg/**']
}
},
optimizeDeps: {
// Force optimize workspace packages
include: ['@myorg/ui', '@myorg/utils']
}
});Next.js configuration:
javascript
// apps/web/next.config.js
const withTM = require('next-transpile-modules')([
'@myorg/ui',
'@myorg/utils'
]);
module.exports = withTM({
reactStrictMode: true,
experimental: {
esmExternals: 'loose'
}
});跨包边界启用快速刷新功能。
Vite配置:
typescript
// apps/web/vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
watch: {
// 监听工作区包
ignored: ['!**/node_modules/@myorg/**']
}
},
optimizeDeps: {
// 强制优化工作区包
include: ['@myorg/ui', '@myorg/utils']
}
});Next.js配置:
javascript
// apps/web/next.config.js
const withTM = require('next-transpile-modules')([
'@myorg/ui',
'@myorg/utils'
]);
module.exports = withTM({
reactStrictMode: true,
experimental: {
esmExternals: 'loose'
}
});Debugging Across Packages
跨包调试
Set up debugging for monorepo projects.
VS Code launch configuration:
json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Web App",
"type": "node",
"request": "launch",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["--filter", "@myorg/web", "run", "dev"],
"skipFiles": ["<node_internals>/**"],
"console": "integratedTerminal"
},
{
"name": "Debug API",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/apps/api/src/index.ts",
"preLaunchTask": "build-dependencies",
"outFiles": ["${workspaceFolder}/apps/api/dist/**/*.js"],
"sourceMaps": true
},
{
"name": "Debug Tests",
"type": "node",
"request": "launch",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["test", "--", "--inspect-brk"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}Chrome DevTools debugging:
json
{
"scripts": {
"debug:web": "NODE_OPTIONS='--inspect' pnpm --filter @myorg/web run dev",
"debug:api": "NODE_OPTIONS='--inspect-brk' pnpm --filter @myorg/api run dev"
}
}为monorepo项目配置调试环境。
VS Code启动配置:
json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Web App",
"type": "node",
"request": "launch",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["--filter", "@myorg/web", "run", "dev"],
"skipFiles": ["<node_internals>/**"],
"console": "integratedTerminal"
},
{
"name": "Debug API",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/apps/api/src/index.ts",
"preLaunchTask": "build-dependencies",
"outFiles": ["${workspaceFolder}/apps/api/dist/**/*.js"],
"sourceMaps": true
},
{
"name": "Debug Tests",
"type": "node",
"request": "launch",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["test", "--", "--inspect-brk"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}Chrome DevTools调试:
json
{
"scripts": {
"debug:web": "NODE_OPTIONS='--inspect' pnpm --filter @myorg/web run dev",
"debug:api": "NODE_OPTIONS='--inspect-brk' pnpm --filter @myorg/api run dev"
}
}Testing Strategies
测试策略
Comprehensive testing across monorepo packages.
Test organization:
text
packages/ui/
├── src/
│ ├── components/
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ └── Button.test.tsx
│ │ └── Input/
│ │ ├── Input.tsx
│ │ └── Input.test.tsx
└── __tests__/
└── integration/
└── form.test.tsxShared test configuration:
typescript
// packages/test-config/jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
moduleNameMapper: {
'^@myorg/(.*)$': '<rootDir>/../../packages/$1/src'
},
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/**/*.stories.tsx'
]
};Package test scripts:
json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:ci": "jest --ci --coverage --maxWorkers=2"
}
}Integration testing:
typescript
// __tests__/integration/package-interaction.test.ts
import { Button } from '@myorg/ui';
import { formatDate } from '@myorg/utils';
describe('Package Integration', () => {
it('uses utility in component', () => {
const date = new Date('2024-01-01');
const formatted = formatDate(date);
expect(formatted).toBe('2024-01-01');
});
});针对monorepo包的全面测试方案。
测试组织结构:
text
packages/ui/
├── src/
│ ├── components/
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ └── Button.test.tsx
│ │ └── Input/
│ │ ├── Input.tsx
│ │ └── Input.test.tsx
└── __tests__/
└── integration/
└── form.test.tsx共享测试配置:
typescript
// packages/test-config/jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
moduleNameMapper: {
'^@myorg/(.*)$': '<rootDir>/../../packages/$1/src'
},
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/**/*.stories.tsx'
]
};包测试脚本:
json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:ci": "jest --ci --coverage --maxWorkers=2"
}
}集成测试:
typescript
// __tests__/integration/package-interaction.test.ts
import { Button } from '@myorg/ui';
import { formatDate } from '@myorg/utils';
describe('Package Integration', () => {
it('uses utility in component', () => {
const date = new Date('2024-01-01');
const formatted = formatDate(date);
expect(formatted).toBe('2024-01-01');
});
});CI/CD Patterns
CI/CD模式
Matrix Builds Per Package
按包矩阵构建
Run builds in parallel across packages.
yaml
undefined跨包并行运行构建任务。
yaml
undefined.github/workflows/ci.yml
.github/workflows/ci.yml
name: CI
user-invocable: false
on:
pull_request:
push:
branches: [main]
jobs:
setup:
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.packages.outputs.packages }}
steps:
- uses: actions/checkout@v4
- name: Get changed packages
id: packages
run: |
packages=$(pnpm -r list --json | jq -r '.[].name' | jq -R -s -c 'split("\n")[:-1]')
echo "packages=$packages" >> $GITHUB_OUTPUT
build:
needs: setup
runs-on: ubuntu-latest
strategy:
matrix:
package: ${{ fromJson(needs.setup.outputs.packages) }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm --filter ${{ matrix.package }} run build
- run: pnpm --filter ${{ matrix.package }} run test
undefinedname: CI
user-invocable: false
on:
pull_request:
push:
branches: [main]
jobs:
setup:
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.packages.outputs.packages }}
steps:
- uses: actions/checkout@v4
- name: Get changed packages
id: packages
run: |
packages=$(pnpm -r list --json | jq -r '.[].name' | jq -R -s -c 'split("\n")[:-1]')
echo "packages=$packages" >> $GITHUB_OUTPUT
build:
needs: setup
runs-on: ubuntu-latest
strategy:
matrix:
package: ${{ fromJson(needs.setup.outputs.packages) }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm --filter ${{ matrix.package }} run build
- run: pnpm --filter ${{ matrix.package }} run test
undefinedAffected-Only CI
仅变更包CI
Build and test only changed packages.
yaml
undefined仅对变更的包进行构建和测试。
yaml
undefined.github/workflows/ci.yml
.github/workflows/ci.yml
name: CI
user-invocable: false
on:
pull_request:
push:
branches: [main]
jobs:
affected:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build affected
run: pnpm turbo run build --filter=[origin/main...HEAD]
- name: Test affected
run: pnpm turbo run test --filter=[origin/main...HEAD]
- name: Lint affected
run: pnpm turbo run lint --filter=[origin/main...HEAD]
**With Nx affected**:
```yaml
- name: Build affected
run: npx nx affected --target=build --base=origin/main --head=HEAD
- name: Test affected
run: npx nx affected --target=test --base=origin/main --head=HEAD --parallel=3name: CI
user-invocable: false
on:
pull_request:
push:
branches: [main]
jobs:
affected:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build affected
run: pnpm turbo run build --filter=[origin/main...HEAD]
- name: Test affected
run: pnpm turbo run test --filter=[origin/main...HEAD]
- name: Lint affected
run: pnpm turbo run lint --filter=[origin/main...HEAD]
**使用Nx变更检测**:
```yaml
- name: Build affected
run: npx nx affected --target=build --base=origin/main --head=HEAD
- name: Test affected
run: npx nx affected --target=test --base=origin/main --head=HEAD --parallel=3Distributed Task Execution
分布式任务执行
Spread tasks across multiple CI agents.
yaml
undefined将任务分散到多个CI代理上执行。
yaml
undefined.github/workflows/ci.yml
.github/workflows/ci.yml
name: CI with Distribution
user-invocable: false
on: [pull_request, push]
jobs:
setup:
runs-on: ubuntu-latest
outputs:
tasks: ${{ steps.tasks.outputs.tasks }}
steps:
- uses: actions/checkout@v4
- name: Generate task list
id: tasks
run: |
tasks=$(pnpm turbo run build test --dry-run=json | jq -c '.tasks')
echo "tasks=$tasks" >> $GITHUB_OUTPUT
execute:
needs: setup
runs-on: ubuntu-latest
strategy:
matrix:
task: ${{ fromJson(needs.setup.outputs.tasks) }}
max-parallel: 10
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- run: pnpm install --frozen-lockfile
- run: ${{ matrix.task.command }}
undefinedname: CI with Distribution
user-invocable: false
on: [pull_request, push]
jobs:
setup:
runs-on: ubuntu-latest
outputs:
tasks: ${{ steps.tasks.outputs.tasks }}
steps:
- uses: actions/checkout@v4
- name: Generate task list
id: tasks
run: |
tasks=$(pnpm turbo run build test --dry-run=json | jq -c '.tasks')
echo "tasks=$tasks" >> $GITHUB_OUTPUT
execute:
needs: setup
runs-on: ubuntu-latest
strategy:
matrix:
task: ${{ fromJson(needs.setup.outputs.tasks) }}
max-parallel: 10
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- run: pnpm install --frozen-lockfile
- run: ${{ matrix.task.command }}
undefinedParallel Pipeline Jobs
并行流水线任务
Execute independent jobs concurrently.
yaml
undefined并发执行独立任务。
yaml
undefined.github/workflows/ci.yml
.github/workflows/ci.yml
name: Parallel CI
user-invocable: false
on: [pull_request, push]
jobs:
install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('pnpm-lock.yaml') }}
lint:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('pnpm-lock.yaml') }}
- run: pnpm turbo run lint
test:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('pnpm-lock.yaml') }}
- run: pnpm turbo run test --coverage
build:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('pnpm-lock.yaml') }}
- run: pnpm turbo run build
undefinedname: Parallel CI
user-invocable: false
on: [pull_request, push]
jobs:
install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('pnpm-lock.yaml') }}
lint:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('pnpm-lock.yaml') }}
- run: pnpm turbo run lint
test:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('pnpm-lock.yaml') }}
- run: pnpm turbo run test --coverage
build:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('pnpm-lock.yaml') }}
- run: pnpm turbo run build
undefinedSelective Deployments
选择性部署
Deploy only changed applications.
yaml
undefined仅部署变更的应用。
yaml
undefined.github/workflows/deploy.yml
.github/workflows/deploy.yml
name: Deploy
user-invocable: false
on:
push:
branches: [main]
jobs:
deploy-web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check if web changed
id: changed
run: |
if git diff --name-only HEAD^ HEAD | grep -q "^apps/web/"; then
echo "changed=true" >> $GITHUB_OUTPUT
fi
- name: Deploy web
if: steps.changed.outputs.changed == 'true'
run: |
pnpm turbo run build --filter=@myorg/web
# Deploy commands heredeploy-api:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check if API changed
id: changed
run: |
if git diff --name-only HEAD^ HEAD | grep -q "^apps/api/"; then
echo "changed=true" >> $GITHUB_OUTPUT
fi
- name: Deploy API
if: steps.changed.outputs.changed == 'true'
run: |
pnpm turbo run build --filter=@myorg/api
# Deploy commands hereundefinedname: Deploy
user-invocable: false
on:
push:
branches: [main]
jobs:
deploy-web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check if web changed
id: changed
run: |
if git diff --name-only HEAD^ HEAD | grep -q "^apps/web/"; then
echo "changed=true" >> $GITHUB_OUTPUT
fi
- name: Deploy web
if: steps.changed.outputs.changed == 'true'
run: |
pnpm turbo run build --filter=@myorg/web
# 此处添加部署命令deploy-api:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check if API changed
id: changed
run: |
if git diff --name-only HEAD^ HEAD | grep -q "^apps/api/"; then
echo "changed=true" >> $GITHUB_OUTPUT
fi
- name: Deploy API
if: steps.changed.outputs.changed == 'true'
run: |
pnpm turbo run build --filter=@myorg/api
# 此处添加部署命令undefinedVersion Management
版本管理
Changesets Workflow
Changesets工作流
Automated versioning and changelog generation.
Installation and setup:
bash
pnpm add -DW @changesets/cli
pnpm changeset initConfiguration:
json
{
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": [
"@myorg/private-package"
]
}Creating changesets:
bash
undefined自动化版本控制和变更日志生成。
安装与配置:
bash
pnpm add -DW @changesets/cli
pnpm changeset init配置文件:
json
{
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": [
"@myorg/private-package"
]
}创建变更集:
bash
undefinedInteractive changeset creation
交互式创建变更集
pnpm changeset
pnpm changeset
Example changeset file generated:
生成的示例变更集文件:
.changeset/cool-feature.md
.changeset/cool-feature.md
```markdown
---
"@myorg/ui": minor
"@myorg/web": patch
---
Add new Button variant and update documentationVersion bumping:
bash
undefined
```markdown
---
"@myorg/ui": minor
"@myorg/web": patch
---
添加新的Button变体并更新文档版本升级:
bash
undefinedConsume changesets and update versions
应用变更集并更新版本
pnpm changeset version
pnpm changeset version
Updates package.json versions
更新package.json版本
Updates CHANGELOG.md files
更新CHANGELOG.md文件
Removes consumed changeset files
删除已应用的变更集文件
**Publishing**:
```bash
**发布**:
```bashBuild and publish changed packages
构建并发布变更的包
pnpm changeset publish
pnpm changeset publish
Push tags
推送标签
git push --follow-tags
**GitHub Action integration**:
```yamlgit push --follow-tags
**GitHub Action集成**:
```yaml.github/workflows/release.yml
.github/workflows/release.yml
name: Release
user-invocable: false
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm turbo run build
- name: Create Release Pull Request or Publish
uses: changesets/action@v1
with:
publish: pnpm changeset publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}undefinedname: Release
user-invocable: false
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm turbo run build
- name: Create Release Pull Request or Publish
uses: changesets/action@v1
with:
publish: pnpm changeset publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}undefinedConventional Commits
约定式提交
Standardized commit message format for automated versioning.
Commit message format:
text
<type>(<scope>): <subject>
<body>
<footer>Examples:
bash
git commit -m "feat(ui): add Button component variant"
git commit -m "fix(api): resolve authentication bug"
git commit -m "docs(readme): update installation instructions"
git commit -m "chore(deps): update dependencies"Commitlint configuration:
javascript
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'scope-enum': [
2,
'always',
['ui', 'api', 'web', 'mobile', 'utils', 'config', 'ci']
],
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'style',
'refactor',
'perf',
'test',
'chore',
'revert'
]
]
}
};Husky pre-commit hook:
bash
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
pnpm commitlint --edit $1标准化的提交消息格式,用于自动化版本控制。
提交消息格式:
text
<type>(<scope>): <subject>
<body>
<footer>示例:
bash
git commit -m "feat(ui): add Button component variant"
git commit -m "fix(api): resolve authentication bug"
git commit -m "docs(readme): update installation instructions"
git commit -m "chore(deps): update dependencies"Commitlint配置:
javascript
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'scope-enum': [
2,
'always',
['ui', 'api', 'web', 'mobile', 'utils', 'config', 'ci']
],
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'style',
'refactor',
'perf',
'test',
'chore',
'revert'
]
]
}
};Husky提交前钩子:
bash
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
pnpm commitlint --edit $1Automated Changelogs
自动化变更日志
Generate changelogs from commit history.
Using conventional-changelog:
bash
pnpm add -DW conventional-changelog-clijson
{
"scripts": {
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"version": "pnpm run changelog && git add CHANGELOG.md"
}
}Using semantic-release:
json
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/github",
"@semantic-release/git"
]
}从提交历史生成变更日志。
使用conventional-changelog:
bash
pnpm add -DW conventional-changelog-clijson
{
"scripts": {
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"version": "pnpm run changelog && git add CHANGELOG.md"
}
}使用semantic-release:
json
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/github",
"@semantic-release/git"
]
}Version Bumping Strategies
版本升级策略
Different approaches to version management.
Independent versioning:
json
{
"version": "independent",
"packages": [
"packages/*"
]
}Each package has its own version:
@myorg/ui@2.1.0@myorg/utils@1.5.3@myorg/api@3.0.1
Fixed versioning:
json
{
"version": "1.2.3",
"packages": [
"packages/*"
]
}All packages share same version:
@myorg/ui@1.2.3@myorg/utils@1.2.3@myorg/api@1.2.3
不同的版本管理方法。
独立版本控制:
json
{
"version": "independent",
"packages": [
"packages/*"
]
}每个包拥有独立版本:
@myorg/ui@2.1.0@myorg/utils@1.5.3@myorg/api@3.0.1
统一版本控制:
json
{
"version": "1.2.3",
"packages": [
"packages/*"
]
}所有包共享同一版本:
@myorg/ui@1.2.3@myorg/utils@1.2.3@myorg/api@1.2.3
Pre-Release Versions
预发布版本
Manage alpha, beta, and release candidate versions.
With changesets:
bash
undefined管理alpha、beta和候选发布版本。
使用changesets:
bash
undefinedEnter pre-release mode
进入预发布模式
pnpm changeset pre enter next
pnpm changeset pre enter next
Create changeset
创建变更集
pnpm changeset
pnpm changeset
Version packages (creates -next.0 versions)
升级版本(创建-next.0版本)
pnpm changeset version
pnpm changeset version
Publish pre-release
发布预发布版本
pnpm changeset publish --tag next
pnpm changeset publish --tag next
Exit pre-release mode
退出预发布模式
pnpm changeset pre exit
**Result**:
```text
@myorg/ui@2.1.0-next.0
@myorg/ui@2.1.0-next.1
@myorg/ui@2.1.0With NPM dist-tags:
bash
undefinedpnpm changeset pre exit
**结果**:
```text
@myorg/ui@2.1.0-next.0
@myorg/ui@2.1.0-next.1
@myorg/ui@2.1.0使用NPM dist-tags:
bash
undefinedPublish to specific tag
发布到指定标签
pnpm publish --tag beta
pnpm publish --tag beta
Install from tag
从标签安装
pnpm add @myorg/ui@beta
pnpm add @myorg/ui@beta
List tags
列出标签
pnpm view @myorg/ui dist-tags
undefinedpnpm view @myorg/ui dist-tags
undefinedPublishing Workflows
发布工作流
NPM Publishing
NPM发布
Publish packages to NPM registry.
Configuration:
json
{
"name": "@myorg/ui",
"version": "1.0.0",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"files": [
"dist",
"README.md",
"LICENSE"
]
}Publishing script:
bash
#!/bin/bash将包发布到NPM注册表。
配置:
json
{
"name": "@myorg/ui",
"version": "1.0.0",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"files": [
"dist",
"README.md",
"LICENSE"
]
}发布脚本:
bash
#!/bin/bashscripts/publish.sh
scripts/publish.sh
Build all packages
构建所有包
pnpm turbo run build
pnpm turbo run build
Run tests
运行测试
pnpm turbo run test
pnpm turbo run test
Version packages
升级包版本
pnpm changeset version
pnpm changeset version
Publish
发布
pnpm changeset publish
pnpm changeset publish
Push tags
推送标签
git push --follow-tags
**NPM automation token**:
```bashgit push --follow-tags
**NPM自动化令牌**:
```bashGenerate automation token on npmjs.com
在npmjs.com生成自动化令牌
Add to GitHub secrets as NPM_TOKEN
添加到GitHub secrets中命名为NPM_TOKEN
Use in CI
在CI中使用
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
pnpm publish --no-git-checks
undefinedecho "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
pnpm publish --no-git-checks
undefinedGitHub Packages
GitHub Packages
Publish to GitHub Package Registry.
Configuration:
json
{
"name": "@myorg/ui",
"publishConfig": {
"registry": "https://npm.pkg.github.com/"
}
}.npmrc for publishing:
ini
@myorg:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}GitHub Action:
yaml
- name: Publish to GitHub Packages
run: pnpm changeset publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}发布到GitHub包注册表。
配置:
json
{
"name": "@myorg/ui",
"publishConfig": {
"registry": "https://npm.pkg.github.com/"
}
}用于发布的.npmrc:
ini
@myorg:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}GitHub Action:
yaml
- name: Publish to GitHub Packages
run: pnpm changeset publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}Canary Releases
金丝雀发布
Publish test versions from PRs or branches.
Canary script:
bash
#!/bin/bash从PR或分支发布测试版本。
金丝雀发布脚本:
bash
#!/bin/bashscripts/canary-release.sh
scripts/canary-release.sh
Get short commit hash
获取简短提交哈希
COMMIT_HASH=$(git rev-parse --short HEAD)
COMMIT_HASH=$(git rev-parse --short HEAD)
Update versions with canary identifier
使用canary标识符更新版本
pnpm changeset version --snapshot canary-$COMMIT_HASH
pnpm changeset version --snapshot canary-$COMMIT_HASH
Publish with canary tag
发布到canary标签
pnpm changeset publish --tag canary
echo "Published canary release: canary-$COMMIT_HASH"
**GitHub Action for canary**:
```yaml
name: Canary Release
user-invocable: false
on:
pull_request:
types: [labeled]
jobs:
canary:
if: contains(github.event.pull_request.labels.*.name, 'canary')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- run: pnpm install --frozen-lockfile
- run: pnpm turbo run build
- run: bash scripts/canary-release.sh
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}pnpm changeset publish --tag canary
echo "Published canary release: canary-$COMMIT_HASH"
**金丝雀发布GitHub Action**:
```yaml
name: Canary Release
user-invocable: false
on:
pull_request:
types: [labeled]
jobs:
canary:
if: contains(github.event.pull_request.labels.*.name, 'canary')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- run: pnpm install --frozen-lockfile
- run: pnpm turbo run build
- run: bash scripts/canary-release.sh
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}Package Provenance
包溯源
Enable package provenance for supply chain security.
NPM provenance:
yaml
- name: Publish with provenance
run: pnpm publish --provenance --access public
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}Package.json metadata:
json
{
"repository": {
"type": "git",
"url": "https://github.com/myorg/monorepo.git",
"directory": "packages/ui"
}
}启用包溯源以保障供应链安全。
NPM溯源:
yaml
- name: Publish with provenance
run: pnpm publish --provenance --access public
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}Package.json元数据:
json
{
"repository": {
"type": "git",
"url": "https://github.com/myorg/monorepo.git",
"directory": "packages/ui"
}
}Registry Authentication
注册表认证
Manage authentication for multiple registries.
.npmrc configuration:
ini
undefined管理多个注册表的认证。
.npmrc配置:
ini
undefinedPublic packages
公共包
@myorg:registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
@myorg:registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
Private packages
私有包
@private:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
@private:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
Corporate registry
企业注册表
@corp:registry=https://npm.corp.example.com/
//npm.corp.example.com/:_authToken=${CORP_TOKEN}
**Environment-specific auth**:
```bash@corp:registry=https://npm.corp.example.com/
//npm.corp.example.com/:_authToken=${CORP_TOKEN}
**环境特定认证**:
```bashDevelopment
开发环境
cp .npmrc.dev .npmrc
cp .npmrc.dev .npmrc
CI
CI环境
cp .npmrc.ci .npmrc
undefinedcp .npmrc.ci .npmrc
undefinedCode Sharing
代码共享
Shared ESLint Configs
共享ESLint配置
Maintain consistent linting across packages.
Create config package:
javascript
// packages/eslint-config/index.js
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended'
],
rules: {
'no-console': 'warn',
'@typescript-eslint/no-unused-vars': 'error',
'react/react-in-jsx-scope': 'off'
},
settings: {
react: {
version: 'detect'
}
}
};Package.json:
json
{
"name": "@myorg/eslint-config",
"version": "1.0.0",
"main": "index.js",
"peerDependencies": {
"eslint": "^8.0.0",
"typescript": "^5.0.0"
}
}Usage in packages:
json
{
"extends": "@myorg/eslint-config"
}在所有包中保持一致的代码检查规则。
创建配置包:
javascript
// packages/eslint-config/index.js
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended'
],
rules: {
'no-console': 'warn',
'@typescript-eslint/no-unused-vars': 'error',
'react/react-in-jsx-scope': 'off'
},
settings: {
react: {
version: 'detect'
}
}
};Package.json:
json
{
"name": "@myorg/eslint-config",
"version": "1.0.0",
"main": "index.js",
"peerDependencies": {
"eslint": "^8.0.0",
"typescript": "^5.0.0"
}
}在包中使用:
json
{
"extends": "@myorg/eslint-config"
}Shared TypeScript Configs
共享TypeScript配置
Share TypeScript configuration across packages.
Base configuration:
json
// packages/tsconfig/base.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}React configuration:
json
// packages/tsconfig/react.json
{
"extends": "./base.json",
"compilerOptions": {
"jsx": "react-jsx",
"lib": ["ES2022", "DOM", "DOM.Iterable"]
}
}Node configuration:
json
// packages/tsconfig/node.json
{
"extends": "./base.json",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"]
}
}Package usage:
json
// apps/web/tsconfig.json
{
"extends": "@myorg/tsconfig/react.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"exclude": ["node_modules"]
}在所有包中共享TypeScript配置。
基础配置:
json
// packages/tsconfig/base.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}React配置:
json
// packages/tsconfig/react.json
{
"extends": "./base.json",
"compilerOptions": {
"jsx": "react-jsx",
"lib": ["ES2022", "DOM", "DOM.Iterable"]
}
}Node配置:
json
// packages/tsconfig/node.json
{
"extends": "./base.json",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"]
}
}包中使用:
json
// apps/web/tsconfig.json
{
"extends": "@myorg/tsconfig/react.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"],
"exclude": ["node_modules"]
}Shared Testing Setup
共享测试设置
Common test configuration and utilities.
Jest preset:
javascript
// packages/test-config/jest-preset.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
moduleNameMapper: {
'^@myorg/(.*)$': '<rootDir>/../../packages/$1/src',
'\\.(css|less|scss|sass)$': 'identity-obj-proxy'
},
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/**/*.stories.tsx',
'!src/**/index.ts'
],
coverageThresholds: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};Test utilities:
typescript
// packages/test-utils/src/index.ts
import { render, RenderOptions } from '@testing-library/react';
import { ReactElement } from 'react';
export function customRender(
ui: ReactElement,
options?: RenderOptions
) {
return render(ui, {
wrapper: ({ children }) => children,
...options
});
}
export * from '@testing-library/react';
export { customRender as render };Package usage:
json
// packages/ui/jest.config.js
{
"preset": "@myorg/test-config"
}typescript
// packages/ui/src/Button.test.tsx
import { render, screen } from '@myorg/test-utils';
import { Button } from './Button';
describe('Button', () => {
it('renders correctly', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
});通用的测试配置和工具。
Jest预设:
javascript
// packages/test-config/jest-preset.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
moduleNameMapper: {
'^@myorg/(.*)$': '<rootDir>/../../packages/$1/src',
'\\.(css|less|scss|sass)$': 'identity-obj-proxy'
},
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/**/*.stories.tsx',
'!src/**/index.ts'
],
coverageThresholds: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};测试工具:
typescript
// packages/test-utils/src/index.ts
import { render, RenderOptions } from '@testing-library/react';
import { ReactElement } from 'react';
export function customRender(
ui: ReactElement,
options?: RenderOptions
) {
return render(ui, {
wrapper: ({ children }) => children,
...options
});
}
export * from '@testing-library/react';
export { customRender as render };包中使用:
json
// packages/ui/jest.config.js
{
"preset": "@myorg/test-config"
}typescript
// packages/ui/src/Button.test.tsx
import { render, screen } from '@myorg/test-utils';
import { Button } from './Button';
describe('Button', () => {
it('renders correctly', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
});Shared Build Configs
共享构建配置
Common build tool configurations.
Vite config:
typescript
// packages/build-config/vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import dts from 'vite-plugin-dts';
export function createConfig() {
return defineConfig({
plugins: [
react(),
dts({
insertTypesEntry: true
})
],
build: {
lib: {
entry: 'src/index.ts',
formats: ['es', 'cjs'],
fileName: (format) => `index.${format}.js`
},
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
}
}
}
}
});
}Package usage:
typescript
// packages/ui/vite.config.ts
import { createConfig } from '@myorg/build-config';
export default createConfig();通用的构建工具配置。
Vite配置:
typescript
// packages/build-config/vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import dts from 'vite-plugin-dts';
export function createConfig() {
return defineConfig({
plugins: [
react(),
dts({
insertTypesEntry: true
})
],
build: {
lib: {
entry: 'src/index.ts',
formats: ['es', 'cjs'],
fileName: (format) => `index.${format}.js`
},
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
}
}
}
}
});
}包中使用:
typescript
// packages/ui/vite.config.ts
import { createConfig } from '@myorg/build-config';
export default createConfig();Package Templates
包模板
Standardized templates for new packages.
Template structure:
text
templates/package/
├── package.json.template
├── tsconfig.json
├── README.md.template
├── src/
│ └── index.ts
└── __tests__/
└── index.test.tsGenerator script:
typescript
// scripts/create-package.ts
import fs from 'fs';
import path from 'path';
interface PackageOptions {
name: string;
type: 'library' | 'app';
description: string;
}
function createPackage({ name, type, description }: PackageOptions) {
const dir = path.join('packages', name);
const template = path.join('templates', type);
// Copy template
fs.cpSync(template, dir, { recursive: true });
// Update package.json
const pkgPath = path.join(dir, 'package.json');
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
pkg.name = `@myorg/${name}`;
pkg.description = description;
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
console.log(`Created package: @myorg/${name}`);
}标准化的新包模板。
模板结构:
text
templates/package/
├── package.json.template
├── tsconfig.json
├── README.md.template
├── src/
│ └── index.ts
└── __tests__/
└── index.test.ts生成器脚本:
typescript
// scripts/create-package.ts
import fs from 'fs';
import path from 'path';
interface PackageOptions {
name: string;
type: 'library' | 'app';
description: string;
}
function createPackage({ name, type, description }: PackageOptions) {
const dir = path.join('packages', name);
const template = path.join('templates', type);
// 复制模板
fs.cpSync(template, dir, { recursive: true });
// 更新package.json
const pkgPath = path.join(dir, 'package.json');
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
pkg.name = `@myorg/${name}`;
pkg.description = description;
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
console.log(`Created package: @myorg/${name}`);
}Migration Strategies
迁移策略
Polyrepo to Monorepo
多仓库到单体仓库
Migrate multiple repositories into monorepo.
Migration steps:
bash
#!/bin/bash将多个仓库迁移到单体仓库。
迁移步骤:
bash
#!/bin/bashscripts/migrate-to-monorepo.sh
scripts/migrate-to-monorepo.sh
1. Create monorepo structure
1. 创建单体仓库结构
mkdir my-monorepo
cd my-monorepo
mkdir my-monorepo
cd my-monorepo
2. Initialize git with clean history
2. 初始化git并创建干净历史
git init
git commit --allow-empty -m "Initial commit"
git init
git commit --allow-empty -m "Initial commit"
3. Add first repo preserving history
3. 添加第一个仓库并保留历史
git remote add -f repo1 ../old-repo1
git merge repo1/main --allow-unrelated-histories
mkdir -p packages/package1
git mv * packages/package1/
git commit -m "Move repo1 to packages/package1"
git remote add -f repo1 ../old-repo1
git merge repo1/main --allow-unrelated-histories
mkdir -p packages/package1
git mv * packages/package1/
git commit -m "Move repo1 to packages/package1"
4. Add second repo preserving history
4. 添加第二个仓库并保留历史
git remote add -f repo2 ../old-repo2
git merge repo2/main --allow-unrelated-histories
mkdir -p packages/package2
git mv * packages/package2/
git commit -m "Move repo2 to packages/package2"
git remote add -f repo2 ../old-repo2
git merge repo2/main --allow-unrelated-histories
mkdir -p packages/package2
git mv * packages/package2/
git commit -m "Move repo2 to packages/package2"
5. Set up workspace
5. 设置工作区
cat > package.json << EOF
{
"name": "my-monorepo",
"private": true,
"workspaces": ["packages/*"]
}
EOF
git add package.json
git commit -m "Add workspace configuration"
undefinedcat > package.json << EOF
{
"name": "my-monorepo",
"private": true,
"workspaces": ["packages/*"]
}
EOF
git add package.json
git commit -m "Add workspace configuration"
undefinedMonorepo Splitting
单体仓库拆分
Extract packages from monorepo to separate repos.
Split script:
bash
#!/bin/bash从单体仓库中提取包到独立仓库。
拆分脚本:
bash
#!/bin/bashscripts/split-package.sh
scripts/split-package.sh
PACKAGE=$1
TARGET_REPO=$2
PACKAGE=$1
TARGET_REPO=$2
Filter git history for package
过滤包的git历史
git filter-branch --prune-empty --subdirectory-filter packages/$PACKAGE -- --all
git filter-branch --prune-empty --subdirectory-filter packages/$PACKAGE -- --all
Push to new repo
推送到新仓库
git remote add origin $TARGET_REPO
git push -u origin main
git remote add origin $TARGET_REPO
git push -u origin main
Cleanup original monorepo
清理原单体仓库
cd ../monorepo
rm -rf packages/$PACKAGE
git add .
git commit -m "Remove $PACKAGE (moved to separate repo)"
undefinedcd ../monorepo
rm -rf packages/$PACKAGE
git add .
git commit -m "Remove $PACKAGE (moved to separate repo)"
undefinedIncremental Adoption
渐进式采用
Gradually adopt monorepo practices.
Phase approach:
- Move packages: Migrate repositories one at a time
- Add tooling: Implement Turborepo/Nx incrementally
- Optimize builds: Enable caching and affected analysis
- Automate workflows: Set up CI/CD and publishing
Gradual migration:
json
{
"workspaces": [
"packages/migrated/*",
"legacy/*"
]
}逐步采用单体仓库实践。
分阶段方案:
- 迁移包: 逐个迁移仓库
- 添加工具: 逐步实现Turborepo/Nx
- 优化构建: 启用缓存和变更分析
- 自动化工作流: 搭建CI/CD和发布流程
渐进式迁移:
json
{
"workspaces": [
"packages/migrated/*",
"legacy/*"
]
}Tooling Migration
工具迁移
Switch between monorepo tools.
Lerna to Turborepo:
bash
undefined在不同单体仓库工具间切换。
从Lerna到Turborepo:
bash
undefined1. Install Turborepo
1. 安装Turborepo
pnpm add -DW turbo
pnpm add -DW turbo
2. Create turbo.json
2. 创建turbo.json
cat > turbo.json << EOF
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
EOF
cat > turbo.json << EOF
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
EOF
3. Remove Lerna
3. 移除Lerna
pnpm remove -DW lerna
rm lerna.json
pnpm remove -DW lerna
rm lerna.json
4. Update scripts
4. 更新脚本
Replace "lerna run build" with "turbo run build"
将"lerna run build"替换为"turbo run build"
**NPM to PNPM**:
```bash
**从NPM到PNPM**:
```bash1. Install PNPM
1. 安装PNPM
corepack enable pnpm
corepack enable pnpm
2. Create workspace file
2. 创建工作区文件
cat > pnpm-workspace.yaml << EOF
packages:
- 'packages/*'
- 'apps/*' EOF
cat > pnpm-workspace.yaml << EOF
packages:
- 'packages/*'
- 'apps/*' EOF
3. Remove old files
3. 移除旧文件
rm -rf node_modules package-lock.json
rm -rf node_modules package-lock.json
4. Install with PNPM
4. 使用PNPM安装依赖
pnpm install
undefinedpnpm install
undefinedGit Workflows
Git工作流
Branch Strategies
分支策略
Effective branching for monorepo development.
Feature branches:
bash
undefined适用于单体仓库开发的高效分支策略。
功能分支:
bash
undefinedCreate feature branch
创建功能分支
git checkout -b feature/add-button-component
git checkout -b feature/add-button-component
Work on specific package
针对特定包工作
cd packages/ui
cd packages/ui
Make changes
进行修改
Commit with conventional format
按约定格式提交
git commit -m "feat(ui): add Button component"
git commit -m "feat(ui): add Button component"
Push and create PR
推送并创建PR
git push -u origin feature/add-button-component
**Release branches**:
```bashgit push -u origin feature/add-button-component
**发布分支**:
```bashCreate release branch
创建发布分支
git checkout -b release/v2.0.0
git checkout -b release/v2.0.0
Version packages
升级包版本
pnpm changeset version
pnpm changeset version
Commit and tag
提交并打标签
git commit -m "chore: version packages for v2.0.0"
git tag v2.0.0
git commit -m "chore: version packages for v2.0.0"
git tag v2.0.0
Merge to main
合并到主分支
git checkout main
git merge release/v2.0.0
undefinedgit checkout main
git merge release/v2.0.0
undefinedPull Request Structure
拉取请求结构
Organize PRs for efficient reviews.
PR template:
markdown
undefined组织PR以提高评审效率。
PR模板:
markdown
undefinedDescription
描述
Brief description of changes
变更内容的简要说明
Packages Changed
变更的包
- @myorg/ui
- @myorg/web
- @myorg/ui
- @myorg/web
Type of Change
变更类型
- Bug fix
- New feature
- Breaking change
- Documentation
- Bug修复
- 新功能
- 破坏性变更
- 文档更新
Checklist
检查清单
- Tests added/updated
- Documentation updated
- Changeset added
- No breaking changes (or documented)
- All CI checks passing
**CODEOWNERS**:
```text- 添加/更新测试
- 更新文档
- 添加变更集
- 无破坏性变更(或已文档化)
- 所有CI检查通过
**CODEOWNERS**:
```textGlobal owners
全局所有者
- @myorg/core-team
- @myorg/core-team
Package-specific owners
包特定所有者
/packages/ui/ @myorg/frontend-team
/packages/api/ @myorg/backend-team
/apps/web/ @myorg/web-team
/apps/mobile/ @myorg/mobile-team
undefined/packages/ui/ @myorg/frontend-team
/packages/api/ @myorg/backend-team
/apps/web/ @myorg/web-team
/apps/mobile/ @myorg/mobile-team
undefinedCode Review Practices
代码评审实践
Effective code review in monorepo context.
Review checklist:
- Changes limited to necessary packages
- No unnecessary dependency additions
- Tests cover new/changed code
- Documentation updated
- Breaking changes documented
- Changesets added
- No circular dependencies introduced
Automated checks:
yaml
undefined单体仓库环境下的高效代码评审。
评审检查清单:
- 变更仅涉及必要的包
- 无不必要的依赖添加
- 测试覆盖新增/变更代码
- 文档已更新
- 破坏性变更已文档化
- 添加了变更集
- 未引入循环依赖
自动化检查:
yaml
undefined.github/workflows/pr-checks.yml
.github/workflows/pr-checks.yml
-
name: Check for circular dependencies run: pnpm check:circular
-
name: Check for missing changesets run: pnpm changeset status
-
name: Verify package boundaries run: pnpm lint:boundaries
undefined-
name: Check for circular dependencies run: pnpm check:circular
-
name: Check for missing changesets run: pnpm changeset status
-
name: Verify package boundaries run: pnpm lint:boundaries
undefinedMerge Strategies
合并策略
Effective merging for monorepo.
Squash and merge (recommended):
bash
undefined适用于单体仓库的高效合并策略。
** squash合并(推荐)**:
bash
undefinedPR merged as single commit
PR合并为单个提交
git merge --squash feature/add-button
git merge --squash feature/add-button
Commit with conventional format
按约定格式提交
git commit -m "feat(ui): add Button component (#123)"
**Rebase and merge**:
```bashgit commit -m "feat(ui): add Button component (#123)"
**变基合并**:
```bashRebase feature branch
对功能分支进行变基
git rebase main
git rebase main
Fast-forward merge
快进合并
git merge --ff-only feature/add-button
undefinedgit merge --ff-only feature/add-button
undefinedProtected Packages
受保护的包
Restrict changes to critical packages.
GitHub branch protection:
yaml
undefined限制对关键包的变更。
GitHub分支保护:
yaml
undefined.github/settings.yml
.github/settings.yml
branches:
- name: main protection: required_pull_request_reviews: required_approving_review_count: 2 dismiss_stale_reviews: true required_status_checks: strict: true contexts: - build - test - lint
**Package-specific protection**:
```textbranches:
- name: main protection: required_pull_request_reviews: required_approving_review_count: 2 dismiss_stale_reviews: true required_status_checks: strict: true contexts: - build - test - lint
**包特定保护**:
```textCODEOWNERS
CODEOWNERS
/packages/core/ @myorg/core-maintainers
/packages/security/ @myorg/security-team
undefined/packages/core/ @myorg/core-maintainers
/packages/security/ @myorg/security-team
undefinedDocumentation Practices
文档实践
Package READMEs
包README
Comprehensive documentation for each package.
README template:
markdown
undefined每个包的全面文档。
README模板:
markdown
undefined@myorg/ui
@myorg/ui
React component library for MyOrg applications.
MyOrg应用的React组件库。
Installation
安装
pnpm add @myorg/ui
pnpm add @myorg/ui
Usage
使用
import { Button } from '@myorg/ui';
function App() {
return <Button onClick={() => alert('Clicked!')}>Click me</Button>;
}
import { Button } from '@myorg/ui';
function App() {
return <Button onClick={() => alert('Clicked!')}>Click me</Button>;
}
Components
组件
- Button
- Input
- Modal
- Button
- Input
- Modal
API Reference
API参考
See API.md for detailed documentation.
详见API.md获取详细文档。
Development
开发
pnpm run dev
pnpm run build
pnpm run test
pnpm run dev
pnpm run build
pnpm run test
Contributing
贡献
See CONTRIBUTING.md.
详见CONTRIBUTING.md。
License
许可证
MIT
undefinedMIT
undefinedArchitecture Decision Records
架构决策记录
Document significant architectural decisions.
ADR template:
markdown
undefined记录重要的架构决策。
ADR模板:
markdown
undefinedADR-001: Use Turborepo for Build Orchestration
ADR-001: 使用Turborepo进行构建编排
Status
状态
Accepted
已接受
Context
背景
Need efficient build system for monorepo with 20+ packages.
需要为包含20+包的单体仓库提供高效的构建系统。
Decision
决策
Use Turborepo for task orchestration and caching.
使用Turborepo进行任务编排和缓存。
Consequences
影响
Positive:
- Fast builds with intelligent caching
- Simple configuration
- Remote cache support
Negative:
- Additional dependency
- Learning curve for team
正面:
- 智能缓存实现快速构建
- 配置简单
- 支持远程缓存
负面:
- 新增依赖
- 团队需要学习成本
Alternatives Considered
备选方案
- Nx: More features but steeper learning curve
- Lerna: Simpler but lacks caching
- Nx: 功能更丰富但学习曲线更陡
- Lerna: 更简单但缺乏缓存功能
Date
日期
2024-01-15
undefined2024-01-15
undefinedAPI Documentation
API文档
Generate and maintain API documentation.
TypeDoc configuration:
json
{
"entryPoints": ["packages/*/src/index.ts"],
"out": "docs/api",
"excludePrivate": true,
"excludeProtected": true,
"categorizeByGroup": true,
"categoryOrder": ["Components", "Hooks", "Utilities", "*"]
}Generate docs:
bash
pnpm typedoc --options typedoc.json生成并维护API文档。
TypeDoc配置:
json
{
"entryPoints": ["packages/*/src/index.ts"],
"out": "docs/api",
"excludePrivate": true,
"excludeProtected": true,
"categorizeByGroup": true,
"categoryOrder": ["Components", "Hooks", "Utilities", "*"]
}生成文档:
bash
pnpm typedoc --options typedoc.jsonOnboarding Guides
入职指南
Help new developers get started.
ONBOARDING.md:
markdown
undefined帮助新开发者快速上手。
ONBOARDING.md:
markdown
undefinedDeveloper Onboarding
开发者入职
Prerequisites
前置要求
- Node.js 18+
- PNPM 8+
- Node.js 18+
- PNPM 8+
Setup
搭建环境
Clone repository
克隆仓库
git clone https://github.com/myorg/monorepo.git
git clone https://github.com/myorg/monorepo.git
Install dependencies
安装依赖
pnpm install
pnpm install
Build all packages
构建所有包
pnpm run build
pnpm run build
Development
开发
Start development servers
启动开发服务器
pnpm run dev
pnpm run dev
Run tests
运行测试
pnpm run test
pnpm run test
Project Structure
项目结构
/apps - Applications
/packages - Shared packages
/docs - Documentation
/apps - 应用程序
/packages - 共享包
/docs - 文档
Common Tasks
常见任务
See COMMON_TASKS.md.
undefined详见COMMON_TASKS.md。
undefinedRunbooks
运行手册
Operational procedures and troubleshooting.
RUNBOOK.md:
markdown
undefined操作流程和故障排除指南。
RUNBOOK.md:
markdown
undefinedOperations Runbook
操作运行手册
Build Failures
构建失败
Symptom
症状
Build fails with "Cannot find module '@myorg/ui'"
构建失败并提示"Cannot find module '@myorg/ui'"
Solution
解决方案
pnpm install
pnpm run build --filter=@myorg/ui...
pnpm install
pnpm run build --filter=@myorg/ui...
Cache Issues
缓存问题
Symptom
症状
Stale builds despite changes
即使有变更仍出现陈旧构建结果
Solution
解决方案
turbo run build --force
turbo run build --force
Version Conflicts
版本冲突
Symptom
症状
Peer dependency warnings
出现对等依赖警告
Solution
解决方案
pnpm syncpack fix-mismatches
pnpm install
undefinedpnpm syncpack fix-mismatches
pnpm install
undefinedBest Practices
最佳实践
1. Use Changesets for Versioning
1. 使用Changesets进行版本管理
Automate version management with changesets.
使用changesets自动化版本管理。
2. Implement Affected-Based CI
2. 实现基于变更的CI
Only build and test changed packages in CI.
在CI中仅构建和测试变更的包。
3. Automate Package Publishing
3. 自动化包发布
Use CI/CD for consistent, reliable publishing.
使用CI/CD实现一致、可靠的发布。
4. Document Workflows Clearly
4. 清晰记录工作流
Maintain comprehensive workflow documentation.
维护全面的工作流文档。
5. Use Consistent Git Practices
5. 使用一致的Git实践
Enforce conventional commits and branch naming.
强制执行约定式提交和分支命名规范。
6. Implement Code Owners
6. 实现代码所有者
Assign package ownership for better reviews.
分配包所有权以提升评审质量。
7. Set Up Pre-Commit Hooks
7. 设置提交前钩子
Catch issues before they reach CI.
在代码进入CI前捕获问题。
8. Use Semantic Versioning
8. 使用语义化版本
Follow semver for all package versions.
所有包版本遵循语义化版本规范。
9. Test in CI Before Publish
9. 发布前在CI中测试
Never publish untested packages.
绝不发布未测试的包。
10. Monitor Deployment Health
10. 监控部署健康状况
Track deployment success and rollback capability.
跟踪部署成功率和回滚能力。
Common Pitfalls
常见陷阱
1. Manual Version Management
1. 手动版本管理
Leads to errors and inconsistencies.
导致错误和不一致。
2. Running Full CI for All Changes
2. 所有变更都运行完整CI
Wastes time and resources.
浪费时间和资源。
3. Inconsistent Publishing Process
3. 不一致的发布流程
Causes confusion and potential errors.
导致混乱和潜在错误。
4. Poor Documentation
4. 文档不完善
Makes onboarding and collaboration difficult.
使入职和协作变得困难。
5. Complex Git Workflows
5. 复杂的Git工作流
Slows development and causes confusion.
拖慢开发速度并导致混乱。
6. No Deployment Automation
6. 无部署自动化
Manual deployments are error-prone.
手动部署容易出错。
7. Breaking Changes Without Warning
7. 无预警的破坏性变更
Breaks dependent packages and applications.
破坏依赖包和应用。
8. Missing Changelogs
8. 缺少变更日志
Users don't know what changed.
用户不知道有哪些变更。
9. Untested Packages Published
9. 发布未测试的包
Breaks production for consumers.
破坏生产环境。
10. No Rollback Strategy
10. 无回滚策略
Can't recover from bad deployments.
无法从不良部署中恢复。
When to Use This Skill
何时使用本技能
Apply monorepo workflow practices when:
- Setting up CI/CD - Configuring automated builds and deployments
- Implementing versioning - Establishing version management
- Optimizing workflows - Improving development efficiency
- Managing releases - Publishing and deploying packages
- Team collaboration - Establishing team practices
- Automating processes - Creating automated workflows
- Troubleshooting issues - Solving workflow problems
- Onboarding developers - Teaching monorepo workflows
- Migrating workflows - Updating or changing processes
- Scaling operations - Growing team and processes
在以下场景应用monorepo工作流实践:
- 搭建CI/CD - 配置自动化构建和部署
- 实现版本控制 - 建立版本管理体系
- 优化工作流程 - 提升开发效率
- 管理发布 - 包的发布和部署
- 团队协作 - 建立团队协作实践
- 自动化流程 - 创建自动化工作流
- 排查问题 - 解决工作流相关问题
- 开发者入职 - 教授monorepo工作流
- 工作流迁移 - 更新或变更现有流程
- 规模化运营 - 团队和流程扩张
Resources
参考资源
- Changesets Documentation - Version management and changelog generation
- Conventional Commits - Commit message specification
- Semantic Versioning - Version numbering scheme
- GitHub Actions - CI/CD automation
- Turborepo CI/CD - Turborepo in CI/CD environments
- Nx CI/CD - Nx continuous integration patterns
- PNPM Publishing - Publishing with PNPM
- NPM Provenance
- Package supply chain security
- Changesets文档 - 版本管理和变更日志生成
- 约定式提交 - 提交 消息规范
- 语义化版本 - 版本编号方案
- GitHub Actions - CI/CD自动化
- Turborepo CI/CD - Turborepo在CI/CD 环境中的使用
- Nx CI/CD - Nx持续 集成模式
- PNPM发布 - 使用PNPM发布包
- NPM溯源
- 包供应链安全