monorepo-workflows

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Monorepo 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/bash

scripts/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
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

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!"
undefined
pnpm husky install
echo "Development environment ready!"
undefined

Cross-Package Development

跨包开发

Work across multiple packages simultaneously.
Using workspace linking:
bash
undefined
同时在多个包中进行开发工作。
使用工作区链接:
bash
undefined

All 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.tsx
Shared 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
undefined
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
undefined

Affected-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=3
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]

**使用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=3

Distributed 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 }}
undefined
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 }}
undefined

Parallel 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
undefined
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
undefined

Selective 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 here
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
      # Deploy commands here
undefined
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-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
      # 此处添加部署命令
undefined

Version Management

版本管理

Changesets Workflow

Changesets工作流

Automated versioning and changelog generation.
Installation and setup:
bash
pnpm add -DW @changesets/cli
pnpm changeset init
Configuration:
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
undefined

Interactive 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 documentation
Version bumping:
bash
undefined

```markdown
---
"@myorg/ui": minor
"@myorg/web": patch
---

添加新的Button变体并更新文档
版本升级:
bash
undefined

Consume 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

**发布**:

```bash

Build and publish changed packages

构建并发布变更的包

pnpm changeset publish
pnpm changeset publish

Push tags

推送标签

git push --follow-tags

**GitHub Action integration**:

```yaml
git 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 }}
undefined
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 }}
undefined

Conventional 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 $1

Automated Changelogs

自动化变更日志

Generate changelogs from commit history.
Using conventional-changelog:
bash
pnpm add -DW conventional-changelog-cli
json
{
  "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-cli
json
{
  "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
undefined

Enter 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.0
With NPM dist-tags:
bash
undefined
pnpm 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
undefined

Publish 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
undefined
pnpm view @myorg/ui dist-tags
undefined

Publishing 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/bash

scripts/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**:

```bash
git push --follow-tags

**NPM自动化令牌**:

```bash

Generate 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
undefined
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc pnpm publish --no-git-checks
undefined

GitHub 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/bash

scripts/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
undefined

Public 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}

**环境特定认证**:

```bash

Development

开发环境

cp .npmrc.dev .npmrc
cp .npmrc.dev .npmrc

CI

CI环境

cp .npmrc.ci .npmrc
undefined
cp .npmrc.ci .npmrc
undefined

Code 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.ts
Generator 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/bash

scripts/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"
undefined
cat > package.json << EOF { "name": "my-monorepo", "private": true, "workspaces": ["packages/*"] } EOF
git add package.json git commit -m "Add workspace configuration"
undefined

Monorepo Splitting

单体仓库拆分

Extract packages from monorepo to separate repos.
Split script:
bash
#!/bin/bash
从单体仓库中提取包到独立仓库。
拆分脚本:
bash
#!/bin/bash

scripts/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)"
undefined
cd ../monorepo rm -rf packages/$PACKAGE git add . git commit -m "Remove $PACKAGE (moved to separate repo)"
undefined

Incremental Adoption

渐进式采用

Gradually adopt monorepo practices.
Phase approach:
  1. Move packages: Migrate repositories one at a time
  2. Add tooling: Implement Turborepo/Nx incrementally
  3. Optimize builds: Enable caching and affected analysis
  4. Automate workflows: Set up CI/CD and publishing
Gradual migration:
json
{
  "workspaces": [
    "packages/migrated/*",
    "legacy/*"
  ]
}
逐步采用单体仓库实践。
分阶段方案:
  1. 迁移包: 逐个迁移仓库
  2. 添加工具: 逐步实现Turborepo/Nx
  3. 优化构建: 启用缓存和变更分析
  4. 自动化工作流: 搭建CI/CD和发布流程
渐进式迁移:
json
{
  "workspaces": [
    "packages/migrated/*",
    "legacy/*"
  ]
}

Tooling Migration

工具迁移

Switch between monorepo tools.
Lerna to Turborepo:
bash
undefined
在不同单体仓库工具间切换。
从Lerna到Turborepo:
bash
undefined

1. 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**:

```bash

1. 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
undefined
pnpm install
undefined

Git Workflows

Git工作流

Branch Strategies

分支策略

Effective branching for monorepo development.
Feature branches:
bash
undefined
适用于单体仓库开发的高效分支策略。
功能分支:
bash
undefined

Create 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**:

```bash
git push -u origin feature/add-button-component

**发布分支**:

```bash

Create 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
undefined
git checkout main git merge release/v2.0.0
undefined

Pull Request Structure

拉取请求结构

Organize PRs for efficient reviews.
PR template:
markdown
undefined
组织PR以提高评审效率。
PR模板:
markdown
undefined

Description

描述

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**:

```text

Global 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
undefined

Code 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
undefined

Merge Strategies

合并策略

Effective merging for monorepo.
Squash and merge (recommended):
bash
undefined
适用于单体仓库的高效合并策略。
** squash合并(推荐)**:
bash
undefined

PR 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**:

```bash
git commit -m "feat(ui): add Button component (#123)"

**变基合并**:

```bash

Rebase feature branch

对功能分支进行变基

git rebase main
git rebase main

Fast-forward merge

快进合并

git merge --ff-only feature/add-button
undefined
git merge --ff-only feature/add-button
undefined

Protected 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**:

```text
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

**包特定保护**:

```text

CODEOWNERS

CODEOWNERS

/packages/core/ @myorg/core-maintainers /packages/security/ @myorg/security-team
undefined
/packages/core/ @myorg/core-maintainers /packages/security/ @myorg/security-team
undefined

Documentation 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
undefined
MIT
undefined

Architecture Decision Records

架构决策记录

Document significant architectural decisions.
ADR template:
markdown
undefined
记录重要的架构决策。
ADR模板:
markdown
undefined

ADR-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
undefined
2024-01-15
undefined

API 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.json

Onboarding Guides

入职指南

Help new developers get started.
ONBOARDING.md:
markdown
undefined
帮助新开发者快速上手。
ONBOARDING.md:
markdown
undefined

Developer Onboarding

开发者入职

Prerequisites

前置要求

  • Node.js 18+
  • PNPM 8+
  • Node.js 18+
  • PNPM 8+

Setup

搭建环境

Clone repository

克隆仓库

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
undefined

Runbooks

运行手册

Operational procedures and troubleshooting.
RUNBOOK.md:
markdown
undefined
操作流程和故障排除指南。
RUNBOOK.md:
markdown
undefined

Operations 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
undefined
pnpm syncpack fix-mismatches pnpm install
undefined

Best 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

参考资源