--- name: monorepo-workflows user-invocable: false description: 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. allowed-tools: - Read - Write - Edit - Bash - Glob - Grep --- # 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": ["/**"], "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: ['/jest.setup.ts'], moduleNameMapper: { '^@myorg/(.*)$': '/../../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" >> $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 }} ``` ### Parallel Pipeline Jobs Execute independent jobs concurrently. ```yaml # .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 ``` ### Selective Deployments Deploy only changed applications. ```yaml # .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 ``` ## Version Management ### Changesets Workflow 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 # Interactive changeset creation pnpm changeset # Example changeset file generated: # .changeset/cool-feature.md ``` ```markdown --- "@myorg/ui": minor "@myorg/web": patch --- Add new Button variant and update documentation ``` **Version bumping**: ```bash # Consume changesets and update versions pnpm changeset version # Updates package.json versions # Updates CHANGELOG.md files # Removes consumed changeset files ``` **Publishing**: ```bash # Build and publish changed packages pnpm changeset publish # Push tags git push --follow-tags ``` **GitHub Action integration**: ```yaml # .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 }} ``` ### Conventional Commits Standardized commit message format for automated versioning. **Commit message format**: ```text ():