Monorepo Support
bumpx provides comprehensive support for monorepo projects, allowing you to manage versions across multiple packages with flexible strategies.
Overview
Monorepos present unique challenges for version management:
- Multiple packages with different release cycles
- Interdependent packages requiring coordinated releases
- Maintaining version consistency across related packages
- Managing git operations across the entire repository
bumpx handles these scenarios with sophisticated detection and management capabilities.
Package Discovery
Automatic Detection
bumpx automatically finds all package.json files in your monorepo:
# Find and update all packages recursively
bumpx patch --recursive
# See what packages would be updated
bumpx patch --recursive --dry-run --verbose
Manual Package Selection
Target specific packages:
# Update specific packages
bumpx patch --files packages/core/package.json,packages/cli/package.json
# Use glob patterns
bumpx minor --files "packages/*/package.json"
bumpx patch --files "apps/*/package.json,libs/*/package.json"
Exclude Packages
Skip certain packages from updates:
# Update all except documentation packages
bumpx patch --files "packages/*/package.json" --exclude "**/docs/**"
# Custom selection logic
find packages -name "package.json" -not -path "*/test-*/*" | \
xargs bumpx patch --files
Versioning Strategies
Independent Versioning
Each package maintains its own version and release cycle:
# Bump each package from its current version
bumpx patch --recursive
# Shows current versions before bumping
bumpx minor --recursive --verbose
Example Output:
📦 packages/core/package.json: 1.2.3 → 1.3.0
📦 packages/cli/package.json: 2.1.0 → 2.2.0
📦 packages/utils/package.json: 0.5.2 → 0.6.0
Synchronized Versioning
All packages share the same version:
# Set all packages to the same version
bumpx patch --recursive --current-version 1.0.0
# Bump all packages to a specific version
bumpx 2.0.0 --recursive
Example Output:
📦 packages/core/package.json: 1.2.3 → 2.0.0
📦 packages/cli/package.json: 2.1.0 → 2.0.0
📦 packages/utils/package.json: 0.5.2 → 2.0.0
Hybrid Approach
Combine strategies for different package groups:
# Sync core packages
bumpx patch --files "packages/core*/package.json" --current-version 1.0.0
# Independent versioning for apps
bumpx patch --files "apps/*/package.json"
# Tools maintain separate versions
bumpx minor --files "tools/*/package.json"
Workspace Configurations
NPM Workspaces
bumpx works seamlessly with NPM workspaces:
{
"name": "my-monorepo",
"workspaces": [
"packages/*",
"apps/*"
]
}
# Update all workspace packages
bumpx patch --recursive
# Respect workspace configuration
npm run build --workspaces
bumpx minor --recursive --execute "npm run build --workspaces"
Yarn Workspaces
Compatible with Yarn workspace configurations:
{
"name": "my-monorepo",
"private": true,
"workspaces": {
"packages": ["packages/*", "tools/*"]
}
}
# Update and build all workspaces
bumpx patch --recursive --execute "yarn workspaces run build"
Pnpm Workspaces
Works with pnpm workspace setups:
# pnpm-workspace.yaml
packages:
- 'packages/*'
- 'apps/*'
- '!**/test/**'
# Update all packages
bumpx patch --recursive
# Run scripts across workspace
bumpx minor --recursive --execute "pnpm run build --recursive"
Lerna Integration
Integrate with Lerna-managed repositories:
# Use bumpx instead of lerna version
bumpx patch --recursive --commit --tag
# Combine with Lerna publishing
bumpx minor --recursive --commit --tag --execute "lerna publish from-package"
Dependency Management
Internal Dependencies
Handle packages that depend on each other:
{
"name": "@myorg/app",
"dependencies": {
"@myorg/core": "^1.2.0",
"@myorg/utils": "^0.5.0"
}
}
# Update all packages, preserving dependency relationships
bumpx patch --recursive
# Update dependencies to match new versions
bumpx patch --recursive --execute "npm update @myorg/*"
Version Range Updates
Automatically update internal dependency ranges:
# Custom script to update dependency versions
cat > scripts/update-deps.js << 'EOF'
const fs = require('fs');
const glob = require('glob');
// Find all package.json files
const packages = glob.sync('packages/*/package.json');
packages.forEach(pkgPath => {
const pkg = JSON.parse(fs.readFileSync(pkgPath));
// Update internal dependencies
if (pkg.dependencies) {
Object.keys(pkg.dependencies).forEach(dep => {
if (dep.startsWith('@myorg/')) {
pkg.dependencies[dep] = '^' + process.env.NEW_VERSION;
}
});
}
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
});
EOF
# Use in version bump workflow
NEW_VERSION=$(bumpx patch --dry-run | grep "→" | head -1 | awk '{print $3}')
bumpx patch --recursive --execute "NEW_VERSION=$NEW_VERSION node scripts/update-deps.js"
Git Operations in Monorepos
Unified Commits
Create single commits for monorepo releases:
# Single commit for all package updates
bumpx patch --recursive --commit --message "chore: release all packages to %s"
# Include package details in commit
bumpx minor --recursive --commit --verbose
Selective Commits
Commit packages separately:
# Commit each package separately
for package in packages/*/package.json; do
dir=$(dirname "$package")
name=$(basename "$dir")
bumpx patch --files "$package" --commit --message "chore($name): release v%s"
done
Tag Strategy
Different tagging approaches for monorepos:
# Single tag for entire monorepo
bumpx patch --recursive --current-version 1.0.0 --tag --tag-message "monorepo-v%s"
# Package-specific tags
bumpx patch --files packages/core/package.json --tag --tag-message "core-v%s"
bumpx patch --files packages/cli/package.json --tag --tag-message "cli-v%s"
Release Workflows
Coordinated Releases
Release all packages together:
#!/bin/bash
# scripts/release.sh
set -e
echo "🚀 Starting monorepo release..."
# Ensure clean state
git status --porcelain | wc -l | xargs test 0 -eq
# Run tests
npm run test --workspaces
# Update versions
bumpx patch --recursive --commit --tag
# Build all packages
npm run build --workspaces
# Push release
git push origin main --tags
echo "✅ Release complete!"
Selective Releases
Release only changed packages:
#!/bin/bash
# scripts/selective-release.sh
# Find packages with changes since last release
CHANGED_PACKAGES=$(git diff --name-only HEAD~1 | grep "packages/" | cut -d'/' -f1-2 | sort -u)
for package_dir in $CHANGED_PACKAGES; do
if [ -f "$package_dir/package.json" ]; then
echo "Releasing $package_dir..."
bumpx patch --files "$package_dir/package.json" --commit --tag
fi
done
git push origin main --tags
Canary Releases
Create prerelease versions for testing:
# Create canary releases for all packages
bumpx prerelease --preid canary --recursive --commit
# Create alpha releases with timestamp
TIMESTAMP=$(date +%Y%m%d%H%M%S)
bumpx prerelease --preid "alpha.$TIMESTAMP" --recursive --commit
Advanced Configurations
Conditional Updates
Update packages based on conditions:
#!/bin/bash
# Only update packages that have changes
git diff --name-only HEAD~1 | while read file; do
if [[ $file == packages/*/package.json ]]; then
package_dir=$(dirname "$file")
echo "Updating $package_dir due to changes"
bumpx patch --files "$file" --commit
elif [[ $file == packages/*/* ]]; then
package_dir=$(echo "$file" | cut -d'/' -f1-2)
package_json="$package_dir/package.json"
if [ -f "$package_json" ]; then
echo "Updating $package_dir due to file changes"
bumpx patch --files "$package_json" --commit
fi
fi
done
Version Constraints
Ensure version relationships:
#!/bin/bash
# Ensure core package version >= other packages
CORE_VERSION=$(jq -r '.version' packages/core/package.json)
for package in packages/*/package.json; do
if [[ $package != "packages/core/package.json" ]]; then
PACKAGE_VERSION=$(jq -r '.version' "$package")
# Compare versions (simplified)
if [[ "$PACKAGE_VERSION" > "$CORE_VERSION" ]]; then
echo "Warning: $package version $PACKAGE_VERSION > core version $CORE_VERSION"
# Optionally fix the constraint
bumpx "$CORE_VERSION" --files "$package"
fi
fi
done
Build Order Dependencies
Handle build order requirements:
#!/bin/bash
# Release packages in dependency order
# Define build order
PACKAGES=("packages/utils" "packages/core" "packages/cli" "apps/web")
for package_dir in "${PACKAGES[@]}"; do
if [ -f "$package_dir/package.json" ]; then
echo "Building and releasing $package_dir..."
# Build package
cd "$package_dir"
npm run build
cd - > /dev/null
# Update version
bumpx patch --files "$package_dir/package.json" --commit
fi
done
git push origin main --tags
Configuration Examples
Monorepo Configuration
Shared configuration for monorepo:
// bumpx.config.ts
export default {
recursive: true,
commit: true,
tag: false,
push: false,
message: 'chore: release packages',
execute: 'npm run build --workspaces && npm run test --workspaces',
files: [
'packages/*/package.json',
'apps/*/package.json'
]
}
Or in package.json:
{
"name": "my-monorepo",
"bumpx": {
"recursive": true,
"commit": true,
"tag": false,
"push": false,
"message": "chore: release packages",
"execute": "npm run build --workspaces && npm run test --workspaces",
"files": [
"packages/*/package.json",
"apps/*/package.json"
]
}
}
Package-Specific Configuration
Different configs for different package types:
// packages/core/bumpx.config.ts
export default {
tag: true,
tagMessage: 'core-v%s',
execute: 'npm run build && npm run test:integration'
}
// packages/cli/bumpx.config.ts
export default {
tag: true,
tagMessage: 'cli-v%s',
execute: 'npm run build && npm run test:e2e'
}
Or in each package's package.json:
// packages/core/package.json
{
"name": "@myorg/core",
"bumpx": {
"tag": true,
"tagMessage": "core-v%s",
"execute": "npm run build && npm run test:integration"
}
}
Best Practices
Version Strategy Guidelines
- Independent for different domains: Use independent versioning for packages serving different purposes
- Synchronized for related packages: Use synchronized versioning for tightly coupled packages
- Semantic versioning: Follow semver strictly for public packages
- Prerelease testing: Use prereleases for testing across the monorepo
Release Process
- Test thoroughly: Run comprehensive tests across all packages
- Update dependencies: Ensure internal dependencies are updated
- Document changes: Maintain changelogs for each package
- Automate where possible: Use scripts to reduce manual errors
Git Strategy
- Single repository: Keep all related packages in one repository
- Clear commit messages: Use conventional commits with package scopes
- Proper tagging: Use consistent tag naming conventions
- Branch protection: Protect main branch and require reviews
Troubleshooting
Common Issues
Version conflicts:
# Check for version mismatches
find packages -name "package.json" -exec jq '.version' {} \; | sort | uniq -c
# Force consistency
bumpx patch --recursive --current-version 1.0.0
Missing packages:
# Verify package discovery
bumpx patch --recursive --dry-run --verbose
# Check file patterns
ls packages/*/package.json
Build failures:
# Test build before release
npm run build --workspaces || exit 1
bumpx patch --recursive --commit
Dependency issues:
# Update all dependencies after version bump
npm update
npm audit fix
# Verify workspace consistency
npm ls --workspaces