From b70cf563a867efc4c9fa376aa02e991ce4360867 Mon Sep 17 00:00:00 2001 From: Jerome Date: Sat, 14 Jun 2025 19:52:36 +0100 Subject: [PATCH] feat: add version management scripts and CI check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added update-version.js script to automate version bumps across all packages - Added check-version-consistency.js script to verify version consistency - Integrated version check into CI workflow - Scripts check package.json versions AND package-lock.json sync - Added npm scripts: `npm run update-version` and `npm run check-version` - Added documentation in scripts/README.md This ensures version consistency across the monorepo and prevents accidental version mismatches or outdated lock files. šŸ¤– Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .github/workflows/main.yml | 3 + package.json | 4 +- scripts/README.md | 66 +++++++++++ scripts/check-version-consistency.js | 169 +++++++++++++++++++++++++++ scripts/update-version.js | 96 +++++++++++++++ 5 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 scripts/README.md create mode 100755 scripts/check-version-consistency.js create mode 100755 scripts/update-version.js diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8efb6773..fa430b79 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,6 +26,9 @@ jobs: # - run: npm ci - run: npm install --no-package-lock + - name: Check version consistency + run: npm run check-version + - name: Check linting working-directory: ./client run: npm run lint diff --git a/package.json b/package.json index 69bbfaaf..656a511f 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,9 @@ "prettier-check": "prettier --check .", "lint": "prettier --check . && cd client && npm run lint", "prepare": "npm run build", - "publish-all": "npm publish --workspaces --access public && npm publish --access public" + "publish-all": "npm publish --workspaces --access public && npm publish --access public", + "update-version": "node scripts/update-version.js", + "check-version": "node scripts/check-version-consistency.js" }, "dependencies": { "@modelcontextprotocol/inspector-cli": "^0.14.1", diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..5f4cf6f5 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,66 @@ +# Version Management Scripts + +This directory contains scripts for managing version consistency across the monorepo. + +## Scripts + +### update-version.js + +Updates the version across all package.json files in the monorepo and updates package-lock.json. + +**Usage:** +```bash +npm run update-version +# Example: +npm run update-version 0.14.3 +``` + +This script will: +1. Update the version in all package.json files (root, client, server, cli) +2. Update workspace dependencies in the root package.json +3. Run `npm install` to update package-lock.json +4. Provide next steps for committing and tagging + +### check-version-consistency.js + +Checks that all packages have consistent versions and that package-lock.json is up to date. + +**Usage:** +```bash +npm run check-version +``` + +This script checks: +1. All package.json files have the same version +2. Workspace dependencies in root package.json match the current version +3. package-lock.json version matches package.json +4. Workspace packages in package-lock.json have the correct versions + +This check runs automatically in CI on every PR and push to main. + +## CI Integration + +The version consistency check is integrated into the GitHub Actions workflow (`.github/workflows/main.yml`) and will fail the build if: +- Package versions are inconsistent +- package-lock.json is out of sync + +## Common Workflows + +### Bumping version for a release: +```bash +# Update to new version +npm run update-version 0.15.0 + +# Verify everything is correct +npm run check-version + +# Commit the changes +git add -A +git commit -m "chore: bump version to 0.15.0" + +# Create a tag +git tag 0.15.0 + +# Push changes and tag +git push && git push --tags +``` \ No newline at end of file diff --git a/scripts/check-version-consistency.js b/scripts/check-version-consistency.js new file mode 100755 index 00000000..72fa15f2 --- /dev/null +++ b/scripts/check-version-consistency.js @@ -0,0 +1,169 @@ +#!/usr/bin/env node + +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** + * Checks version consistency across all package.json files in the monorepo + * Exits with code 1 if versions are inconsistent + * Usage: node scripts/check-version-consistency.js + */ + +console.log('šŸ” Checking version consistency across packages...\n'); + +// List of package.json files to check +const packagePaths = [ + 'package.json', + 'client/package.json', + 'server/package.json', + 'cli/package.json' +]; + +const versions = new Map(); +const errors = []; + +// Read version from each package.json +packagePaths.forEach(packagePath => { + const fullPath = path.join(__dirname, '..', packagePath); + + if (!fs.existsSync(fullPath)) { + console.warn(`āš ļø Skipping ${packagePath} - file not found`); + return; + } + + try { + const packageJson = JSON.parse(fs.readFileSync(fullPath, 'utf8')); + const version = packageJson.version; + const packageName = packageJson.name || packagePath; + + versions.set(packagePath, { + name: packageName, + version: version, + dependencies: packageJson.dependencies || {} + }); + + console.log(`šŸ“¦ ${packagePath}:`); + console.log(` Name: ${packageName}`); + console.log(` Version: ${version}`); + } catch (error) { + errors.push(`Failed to read ${packagePath}: ${error.message}`); + } +}); + +if (errors.length > 0) { + console.error('\nāŒ Errors occurred while reading package files:'); + errors.forEach(error => console.error(` - ${error}`)); + process.exit(1); +} + +// Check if all versions match +const allVersions = Array.from(versions.values()).map(v => v.version); +const uniqueVersions = [...new Set(allVersions)]; + +console.log('\nšŸ“Š Version Summary:'); +console.log(` Total packages: ${versions.size}`); +console.log(` Unique versions: ${uniqueVersions.length}`); + +if (uniqueVersions.length > 1) { + console.error('\nāŒ Version mismatch detected!'); + console.error(' Found versions: ' + uniqueVersions.join(', ')); + + console.error('\n Package versions:'); + versions.forEach((info, path) => { + console.error(` - ${path}: ${info.version}`); + }); +} else { + console.log(` āœ… All packages are at version: ${uniqueVersions[0]}`); +} + +// Check workspace dependencies in root package.json +const rootPackage = versions.get('package.json'); +if (rootPackage) { + console.log('\nšŸ”— Checking workspace dependencies...'); + const expectedVersion = rootPackage.version; + let dependencyErrors = false; + + Object.entries(rootPackage.dependencies).forEach(([dep, version]) => { + if (dep.startsWith('@modelcontextprotocol/inspector-')) { + const expectedDepVersion = `^${expectedVersion}`; + if (version !== expectedDepVersion) { + console.error(` āŒ ${dep}: ${version} (expected ${expectedDepVersion})`); + dependencyErrors = true; + } else { + console.log(` āœ… ${dep}: ${version}`); + } + } + }); + + if (dependencyErrors) { + errors.push('Workspace dependency versions do not match package versions'); + } +} + +// Check if package-lock.json is up to date +console.log('\nšŸ”’ Checking package-lock.json...'); +const lockPath = path.join(__dirname, '..', 'package-lock.json'); +let lockFileError = false; + +if (!fs.existsSync(lockPath)) { + console.error(' āŒ package-lock.json not found'); + lockFileError = true; +} else { + try { + const lockFile = JSON.parse(fs.readFileSync(lockPath, 'utf8')); + const lockVersion = lockFile.version; + const expectedVersion = rootPackage?.version || uniqueVersions[0]; + + if (lockVersion !== expectedVersion) { + console.error(` āŒ package-lock.json version (${lockVersion}) does not match package.json version (${expectedVersion})`); + lockFileError = true; + } else { + console.log(` āœ… package-lock.json version matches: ${lockVersion}`); + } + + // Check workspace package versions in lock file + if (lockFile.packages) { + const workspacePackages = [ + { path: 'client', name: '@modelcontextprotocol/inspector-client' }, + { path: 'server', name: '@modelcontextprotocol/inspector-server' }, + { path: 'cli', name: '@modelcontextprotocol/inspector-cli' } + ]; + + workspacePackages.forEach(({ path, name }) => { + const lockPkgPath = lockFile.packages[path]; + if (lockPkgPath && lockPkgPath.version !== expectedVersion) { + console.error(` āŒ ${name} in lock file: ${lockPkgPath.version} (expected ${expectedVersion})`); + lockFileError = true; + } + }); + } + } catch (error) { + console.error(` āŒ Failed to parse package-lock.json: ${error.message}`); + lockFileError = true; + } +} + +// Final result +console.log('\nšŸŽÆ Result:'); +if (uniqueVersions.length === 1 && errors.length === 0 && !lockFileError) { + console.log(' āœ… Version consistency check passed!'); + process.exit(0); +} else { + console.error(' āŒ Version consistency check failed!'); + if (uniqueVersions.length > 1) { + console.error(' - Package versions are not consistent'); + } + if (errors.length > 0) { + console.error(' - ' + errors.join('\n - ')); + } + if (lockFileError) { + console.error(' - package-lock.json is out of sync'); + } + console.error('\nšŸ’” Run "npm run update-version " to fix version inconsistencies'); + console.error(' or run "npm install" to update package-lock.json'); + process.exit(1); +} \ No newline at end of file diff --git a/scripts/update-version.js b/scripts/update-version.js new file mode 100755 index 00000000..7f82b936 --- /dev/null +++ b/scripts/update-version.js @@ -0,0 +1,96 @@ +#!/usr/bin/env node + +import fs from 'fs'; +import path from 'path'; +import { execSync } from 'child_process'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** + * Updates version across all package.json files in the monorepo + * Usage: node scripts/update-version.js + * Example: node scripts/update-version.js 0.14.2 + */ + +const newVersion = process.argv[2]; + +if (!newVersion) { + console.error('āŒ Please provide a version number'); + console.error('Usage: node scripts/update-version.js '); + console.error('Example: node scripts/update-version.js 0.14.2'); + process.exit(1); +} + +// Validate version format +const versionRegex = /^\d+\.\d+\.\d+(-[\w.]+)?$/; +if (!versionRegex.test(newVersion)) { + console.error('āŒ Invalid version format. Please use semantic versioning (e.g., 1.2.3 or 1.2.3-beta.1)'); + process.exit(1); +} + +console.log(`šŸ”„ Updating all packages to version ${newVersion}...`); + +// List of package.json files to update +const packagePaths = [ + 'package.json', + 'client/package.json', + 'server/package.json', + 'cli/package.json' +]; + +const updatedFiles = []; + +// Update version in each package.json +packagePaths.forEach(packagePath => { + const fullPath = path.join(__dirname, '..', packagePath); + + if (!fs.existsSync(fullPath)) { + console.warn(`āš ļø Skipping ${packagePath} - file not found`); + return; + } + + try { + const packageJson = JSON.parse(fs.readFileSync(fullPath, 'utf8')); + const oldVersion = packageJson.version; + packageJson.version = newVersion; + + // Update workspace dependencies in root package.json + if (packagePath === 'package.json' && packageJson.dependencies) { + Object.keys(packageJson.dependencies).forEach(dep => { + if (dep.startsWith('@modelcontextprotocol/inspector-')) { + packageJson.dependencies[dep] = `^${newVersion}`; + } + }); + } + + fs.writeFileSync(fullPath, JSON.stringify(packageJson, null, 2) + '\n'); + updatedFiles.push(packagePath); + console.log(`āœ… Updated ${packagePath} from ${oldVersion} to ${newVersion}`); + } catch (error) { + console.error(`āŒ Failed to update ${packagePath}:`, error.message); + process.exit(1); + } +}); + +console.log('\nšŸ“ Summary:'); +console.log(`Updated ${updatedFiles.length} files to version ${newVersion}`); + +// Update package-lock.json +console.log('\nšŸ”’ Updating package-lock.json...'); +try { + execSync('npm install', { stdio: 'inherit' }); + console.log('āœ… package-lock.json updated successfully'); +} catch (error) { + console.error('āŒ Failed to update package-lock.json:', error.message); + console.error('Please run "npm install" manually'); + process.exit(1); +} + +console.log('\n✨ Version update complete!'); +console.log('\nNext steps:'); +console.log('1. Review the changes: git diff'); +console.log('2. Commit the changes: git add -A && git commit -m "chore: bump version to ' + newVersion + '"'); +console.log('3. Create a git tag: git tag v' + newVersion); +console.log('4. Push changes and tag: git push && git push --tags'); \ No newline at end of file