Claude
Skills
Sign in
Back

monorepo-workflows

Included with Lifetime
$97 forever

Use when setting up CI/CD, implementing versioning, optimizing workflows, or managing releases with monorepo development workflows including version management, publishing, and team collaboration practices.

Cloud & DevOps

What this skill does


# Monorepo Workflows Skill

## Overview

This skill provides comprehensive guidance on development workflows, CI/CD
patterns, version management, publishing strategies, and collaboration
practices for monorepo environments.

## 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
# scripts/setup-dev.sh

echo "Setting up development environment..."

# 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

# Enable pnpm
corepack enable pnpm

# Install dependencies
pnpm install

# Build all packages
pnpm run build

# Setup git hooks
pnpm husky install

echo "Development environment ready!"
```

### Cross-Package Development

Work across multiple packages simultaneously.

**Using workspace linking**:

```bash
# All workspace packages automatically linked
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"
  }
}
```

### 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'
  }
});
```

### 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"
  }
}
```

### 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');
  });
});
```

## CI/CD Patterns

### Matrix Builds Per Package

Run builds in parallel across packages.

```yaml
# .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
```

### Affected-Only CI

Build and test only changed packages.

```yaml
# .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
```

### Distributed Task Execution

Spread tasks across multiple CI agents.

```yaml
# .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" >>

Related in Cloud & DevOps