|
| 1 | +#!/usr/bin/env node |
| 2 | +/* eslint-disable no-plusplus */ |
| 3 | +/* eslint-disable no-console */ |
| 4 | +/* eslint-disable @typescript-eslint/no-var-requires */ |
| 5 | + |
| 6 | +// ************************************************************ |
| 7 | +// # Basic usage |
| 8 | +// node ./scripts/update-package-version.js |
| 9 | +// ************************************************************ |
| 10 | + |
| 11 | +// ************************************************************ |
| 12 | +// # With options |
| 13 | +// node ./scripts/update-package-version.js --prefix "v" --dry-run |
| 14 | +// ************************************************************ |
| 15 | + |
| 16 | +const { execSync } = require("child_process"); |
| 17 | +const fs = require("fs"); |
| 18 | +const path = require("path"); |
| 19 | + |
| 20 | +/** |
| 21 | + * Compare two semantic versions |
| 22 | + * Returns: 1 if v1 > v2, -1 if v1 < v2, 0 if equal |
| 23 | + */ |
| 24 | +function compareVersions(v1, v2) { |
| 25 | + const parts1 = v1.split(".").map(Number); |
| 26 | + const parts2 = v2.split(".").map(Number); |
| 27 | + |
| 28 | + // Ensure both arrays have same length by padding with 0s |
| 29 | + const maxLength = Math.max(parts1.length, parts2.length); |
| 30 | + while (parts1.length < maxLength) parts1.push(0); |
| 31 | + while (parts2.length < maxLength) parts2.push(0); |
| 32 | + |
| 33 | + for (let i = 0; i < maxLength; i++) { |
| 34 | + if (parts1[i] > parts2[i]) return 1; |
| 35 | + if (parts1[i] < parts2[i]) return -1; |
| 36 | + } |
| 37 | + |
| 38 | + return 0; |
| 39 | +} |
| 40 | + |
| 41 | +/** |
| 42 | + * Extract version from git tag |
| 43 | + * @param {string} tag - Git tag (e.g., "v1.2.3", "release-2.0.1") |
| 44 | + * @param {string} prefix - Prefix to remove (e.g., "v", "release-") |
| 45 | + * @returns {string|null} - Extracted version or null if invalid |
| 46 | + */ |
| 47 | +function extractVersion(tag, prefix) { |
| 48 | + if (!tag.startsWith(prefix)) return null; |
| 49 | + |
| 50 | + const version = tag.substring(prefix.length); |
| 51 | + |
| 52 | + // Basic semver validation (major.minor.patch with optional pre-release) |
| 53 | + const semverRegex = /^\d+\.\d+\.\d+(?:-[a-zA-Z0-9-.]+)?$/; |
| 54 | + |
| 55 | + if (!semverRegex.test(version)) return null; |
| 56 | + |
| 57 | + return version; |
| 58 | +} |
| 59 | + |
| 60 | +/** |
| 61 | + * Get all git tags with the specified prefix |
| 62 | + * @param {string} prefix - Tag prefix to filter by |
| 63 | + * @returns {string[]} - Array of version strings |
| 64 | + */ |
| 65 | +function getGitTags(prefix) { |
| 66 | + try { |
| 67 | + // Get all tags sorted by version (descending) |
| 68 | + const output = execSync("git tag --sort=-version:refname", { |
| 69 | + encoding: "utf8", |
| 70 | + stdio: ["pipe", "pipe", "pipe"], |
| 71 | + }); |
| 72 | + |
| 73 | + const tags = output |
| 74 | + .trim() |
| 75 | + .split("\n") |
| 76 | + .filter((tag) => tag.trim() !== ""); |
| 77 | + |
| 78 | + const versions = []; |
| 79 | + |
| 80 | + tags.forEach((tag) => { |
| 81 | + const version = extractVersion(tag, prefix); |
| 82 | + if (version) { |
| 83 | + versions.push(version); |
| 84 | + } |
| 85 | + }); |
| 86 | + |
| 87 | + return versions; |
| 88 | + } catch (error) { |
| 89 | + console.error("Error getting git tags:", error.message); |
| 90 | + return []; |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +/** |
| 95 | + * Find the latest version from an array of version strings |
| 96 | + * @param {string[]} versions - Array of version strings |
| 97 | + * @returns {string|null} - Latest version or null if no versions |
| 98 | + */ |
| 99 | +function getLatestVersion(versions) { |
| 100 | + if (versions.length === 0) return null; |
| 101 | + |
| 102 | + let latest = versions[0]; |
| 103 | + |
| 104 | + for (let i = 1; i < versions.length; i++) { |
| 105 | + if (compareVersions(versions[i], latest) > 0) { |
| 106 | + latest = versions[i]; |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + return latest; |
| 111 | +} |
| 112 | + |
| 113 | +/** |
| 114 | + * Update package.json version |
| 115 | + * @param {string} newVersion - New version to set |
| 116 | + * @param {string} packagePath - Path to package.json |
| 117 | + */ |
| 118 | +function updatePackageVersion(newVersion, packagePath) { |
| 119 | + try { |
| 120 | + const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8")); |
| 121 | + const oldVersion = packageJson.version; |
| 122 | + |
| 123 | + packageJson.version = newVersion; |
| 124 | + |
| 125 | + fs.writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`); |
| 126 | + |
| 127 | + console.log(`✅ Updated package.json version: ${oldVersion} → ${newVersion}`); |
| 128 | + } catch (error) { |
| 129 | + console.error("Error updating package.json:", error.message); |
| 130 | + process.exit(1); |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +/** |
| 135 | + * Main function |
| 136 | + */ |
| 137 | +function main() { |
| 138 | + const args = process.argv.slice(2); |
| 139 | + |
| 140 | + // Default configuration |
| 141 | + let prefix = "v"; |
| 142 | + let packagePath = path.join(process.cwd(), "package.json"); |
| 143 | + let dryRun = false; |
| 144 | + |
| 145 | + // Parse command line arguments |
| 146 | + for (let i = 0; i < args.length; i++) { |
| 147 | + const arg = args[i]; |
| 148 | + |
| 149 | + if (arg === "--prefix" && i + 1 < args.length) { |
| 150 | + prefix = args[i + 1]; |
| 151 | + i++; |
| 152 | + } else if (arg === "--package" && i + 1 < args.length) { |
| 153 | + packagePath = args[i + 1]; |
| 154 | + i++; |
| 155 | + } else if (arg === "--dry-run") { |
| 156 | + dryRun = true; |
| 157 | + } else if (arg === "--help" || arg === "-h") { |
| 158 | + console.log(` |
| 159 | +Usage: node update-package-version.js [options] |
| 160 | +
|
| 161 | +Options: |
| 162 | + --prefix <prefix> Tag prefix to filter by (default: "v") |
| 163 | + --package <path> Path to package.json (default: "./package.json") |
| 164 | + --dry-run Show what would be updated without making changes |
| 165 | + --help, -h Show this help message |
| 166 | +
|
| 167 | +Examples: |
| 168 | + node update-package-version.js |
| 169 | + node update-package-version.js --prefix "release-" |
| 170 | + node update-package-version.js --prefix "v" --package "./packages/core/package.json" |
| 171 | + node update-package-version.js --dry-run |
| 172 | + `); |
| 173 | + return; |
| 174 | + } |
| 175 | + } |
| 176 | + |
| 177 | + console.log(`🔍 Looking for git tags with prefix: "${prefix}"`); |
| 178 | + |
| 179 | + // Check if package.json exists |
| 180 | + if (!fs.existsSync(packagePath)) { |
| 181 | + console.error(`❌ Package.json not found at: ${packagePath}`); |
| 182 | + process.exit(1); |
| 183 | + } |
| 184 | + |
| 185 | + // Get git tags |
| 186 | + const versions = getGitTags(prefix); |
| 187 | + |
| 188 | + if (versions.length === 0) { |
| 189 | + console.log(`❌ No git tags found with prefix "${prefix}"`); |
| 190 | + process.exit(1); |
| 191 | + } |
| 192 | + |
| 193 | + console.log( |
| 194 | + `📋 Found ${versions.length} version tags:`, |
| 195 | + versions.slice(0, 10).join(", ") + (versions.length > 10 ? "..." : ""), |
| 196 | + ); |
| 197 | + |
| 198 | + // Get latest version |
| 199 | + const latestVersion = getLatestVersion(versions); |
| 200 | + |
| 201 | + if (!latestVersion) { |
| 202 | + console.log("❌ Could not determine latest version"); |
| 203 | + process.exit(1); |
| 204 | + } |
| 205 | + |
| 206 | + console.log(`🚀 Latest version found: ${latestVersion}`); |
| 207 | + |
| 208 | + // Read current package.json version |
| 209 | + try { |
| 210 | + const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8")); |
| 211 | + const currentVersion = packageJson.version; |
| 212 | + |
| 213 | + console.log(`📦 Current package.json version: ${currentVersion}`); |
| 214 | + |
| 215 | + if (currentVersion === latestVersion) { |
| 216 | + console.log("✅ Package.json is already up to date!"); |
| 217 | + return; |
| 218 | + } |
| 219 | + |
| 220 | + if (dryRun) { |
| 221 | + console.log(`🔍 [DRY RUN] Would update version: ${currentVersion} → ${latestVersion}`); |
| 222 | + return; |
| 223 | + } |
| 224 | + |
| 225 | + // Update package.json |
| 226 | + updatePackageVersion(latestVersion, packagePath); |
| 227 | + } catch (error) { |
| 228 | + console.error("Error reading package.json:", error.message); |
| 229 | + process.exit(1); |
| 230 | + } |
| 231 | +} |
| 232 | + |
| 233 | +// Run the script |
| 234 | +if (require.main === module) { |
| 235 | + main(); |
| 236 | +} |
0 commit comments