|
| 1 | +#!/usr/bin/env dart |
| 2 | +// ignore_for_file: avoid_print |
| 3 | + |
| 4 | +import 'dart:io'; |
| 5 | + |
| 6 | +class VersionManager { |
| 7 | + static const String pubspecPath = 'pubspec.yaml'; |
| 8 | + |
| 9 | + void run() async { |
| 10 | + try { |
| 11 | + print('🚀 AzyX Version Manager\n'); |
| 12 | + |
| 13 | + final currentVersion = await getCurrentVersion(); |
| 14 | + final currentBuildNumber = await getCurrentBuildNumber(); |
| 15 | + |
| 16 | + print('📋 Current Status:'); |
| 17 | + print(' Version: $currentVersion'); |
| 18 | + print(' Build Number: $currentBuildNumber\n'); |
| 19 | + |
| 20 | + print('🔧 What would you like to do?'); |
| 21 | + print( |
| 22 | + ' 1. Patch increment ($currentVersion → ${incrementVersion(currentVersion, 'patch')})'); |
| 23 | + print( |
| 24 | + ' 2. Minor increment ($currentVersion → ${incrementVersion(currentVersion, 'minor')})'); |
| 25 | + print( |
| 26 | + ' 3. Major increment ($currentVersion → ${incrementVersion(currentVersion, 'major')})'); |
| 27 | + print( |
| 28 | + ' 4. Release hotfix ($currentVersion → ${addHotfix(currentVersion)})'); |
| 29 | + print( |
| 30 | + ' 5. Release pre-release ($currentVersion → $currentVersion-[alpha/beta/rc])'); |
| 31 | + print(' 6. Custom version'); |
| 32 | + print(' 7. Exit\n'); |
| 33 | + |
| 34 | + stdout.write('Enter your choice (1-7): '); |
| 35 | + final choice = stdin.readLineSync()?.trim(); |
| 36 | + |
| 37 | + String? newVersion; |
| 38 | + String versionType = ''; |
| 39 | + |
| 40 | + switch (choice) { |
| 41 | + case '1': |
| 42 | + newVersion = incrementVersion(currentVersion, 'patch'); |
| 43 | + versionType = 'patch'; |
| 44 | + break; |
| 45 | + case '2': |
| 46 | + newVersion = incrementVersion(currentVersion, 'minor'); |
| 47 | + versionType = 'minor'; |
| 48 | + break; |
| 49 | + case '3': |
| 50 | + newVersion = incrementVersion(currentVersion, 'major'); |
| 51 | + versionType = 'major'; |
| 52 | + break; |
| 53 | + case '4': |
| 54 | + newVersion = addHotfix(currentVersion); |
| 55 | + versionType = 'hotfix'; |
| 56 | + break; |
| 57 | + case '5': |
| 58 | + newVersion = await handlePreRelease(currentVersion); |
| 59 | + versionType = 'pre-release'; |
| 60 | + break; |
| 61 | + case '6': |
| 62 | + newVersion = await getCustomVersion(); |
| 63 | + versionType = 'custom'; |
| 64 | + break; |
| 65 | + case '7': |
| 66 | + print('👋 Goodbye!'); |
| 67 | + return; |
| 68 | + default: |
| 69 | + print('❌ Invalid choice'); |
| 70 | + return; |
| 71 | + } |
| 72 | + |
| 73 | + if (newVersion == null) return; |
| 74 | + |
| 75 | + // Confirm the change |
| 76 | + print('\n📝 Proposed Changes:'); |
| 77 | + print(' Current: $currentVersion+$currentBuildNumber'); |
| 78 | + print(' New: $newVersion+${currentBuildNumber + 1}'); |
| 79 | + |
| 80 | + stdout.write('\n✅ Proceed with this update? (y/N): '); |
| 81 | + final confirm = stdin.readLineSync()?.trim().toLowerCase(); |
| 82 | + |
| 83 | + if (confirm != 'y' && confirm != 'yes') { |
| 84 | + print('❌ Update cancelled'); |
| 85 | + return; |
| 86 | + } |
| 87 | + |
| 88 | + await updateVersions(newVersion, currentBuildNumber + 1); |
| 89 | + |
| 90 | + await handleGitOperations(newVersion, currentVersion, versionType); |
| 91 | + |
| 92 | + print('\n🎉 Version updated successfully!'); |
| 93 | + } catch (e) { |
| 94 | + print('❌ Error: $e'); |
| 95 | + } |
| 96 | + } |
| 97 | + |
| 98 | + Future<String> getCurrentVersion() async { |
| 99 | + final pubspecContent = await File(pubspecPath).readAsString(); |
| 100 | + final versionMatch = |
| 101 | + RegExp(r'version:\s*([^\+\s]+)').firstMatch(pubspecContent); |
| 102 | + |
| 103 | + if (versionMatch == null) { |
| 104 | + throw Exception('Could not find version in pubspec.yaml'); |
| 105 | + } |
| 106 | + |
| 107 | + return versionMatch.group(1)!; |
| 108 | + } |
| 109 | + |
| 110 | + Future<int> getCurrentBuildNumber() async { |
| 111 | + final pubspecContent = await File(pubspecPath).readAsString(); |
| 112 | + final versionMatch = |
| 113 | + RegExp(r'version:\s*[^\+\s]+\+(\d+)').firstMatch(pubspecContent); |
| 114 | + |
| 115 | + if (versionMatch == null) { |
| 116 | + throw Exception('Could not find build number in pubspec.yaml'); |
| 117 | + } |
| 118 | + |
| 119 | + return int.parse(versionMatch.group(1)!); |
| 120 | + } |
| 121 | + |
| 122 | + String incrementVersion(String version, String type) { |
| 123 | + final cleanVersion = version.split('-')[0]; |
| 124 | + final parts = cleanVersion.split('.').map(int.parse).toList(); |
| 125 | + |
| 126 | + while (parts.length < 3) { |
| 127 | + parts.add(0); |
| 128 | + } |
| 129 | + |
| 130 | + switch (type) { |
| 131 | + case 'major': |
| 132 | + parts[0]++; |
| 133 | + parts[1] = 0; |
| 134 | + parts[2] = 0; |
| 135 | + break; |
| 136 | + case 'minor': |
| 137 | + parts[1]++; |
| 138 | + parts[2] = 0; |
| 139 | + break; |
| 140 | + case 'patch': |
| 141 | + parts[2]++; |
| 142 | + break; |
| 143 | + } |
| 144 | + |
| 145 | + return parts.join('.'); |
| 146 | + } |
| 147 | + |
| 148 | + String addHotfix(String version) { |
| 149 | + final cleanVersion = version.split('-')[0]; |
| 150 | + return '$cleanVersion-hotfix'; |
| 151 | + } |
| 152 | + |
| 153 | + Future<String?> handlePreRelease(String currentVersion) async { |
| 154 | + print('\n🔖 Pre-release options:'); |
| 155 | + print(' 1. Alpha'); |
| 156 | + print(' 2. Beta'); |
| 157 | + print(' 3. Release Candidate (RC)'); |
| 158 | + |
| 159 | + stdout.write('Choose pre-release type (1-3): '); |
| 160 | + final choice = stdin.readLineSync()?.trim(); |
| 161 | + |
| 162 | + String tag; |
| 163 | + switch (choice) { |
| 164 | + case '1': |
| 165 | + tag = 'alpha'; |
| 166 | + break; |
| 167 | + case '2': |
| 168 | + tag = 'beta'; |
| 169 | + break; |
| 170 | + case '3': |
| 171 | + tag = 'rc'; |
| 172 | + break; |
| 173 | + default: |
| 174 | + print('❌ Invalid choice'); |
| 175 | + return null; |
| 176 | + } |
| 177 | + |
| 178 | + final cleanVersion = currentVersion.split('-')[0]; |
| 179 | + return '$cleanVersion-$tag'; |
| 180 | + } |
| 181 | + |
| 182 | + Future<String?> getCustomVersion() async { |
| 183 | + stdout.write('\n📝 Enter custom version (e.g., 3.0.0, 2.9.9-beta): '); |
| 184 | + final customVersion = stdin.readLineSync()?.trim(); |
| 185 | + |
| 186 | + if (customVersion == null || customVersion.isEmpty) { |
| 187 | + print('❌ Invalid version'); |
| 188 | + return null; |
| 189 | + } |
| 190 | + |
| 191 | + if (!RegExp(r'^\d+\.\d+\.\d+(-\w+)?$').hasMatch(customVersion)) { |
| 192 | + print('❌ Invalid version format. Use: major.minor.patch[-tag]'); |
| 193 | + return null; |
| 194 | + } |
| 195 | + |
| 196 | + return customVersion; |
| 197 | + } |
| 198 | + |
| 199 | + Future<void> updateVersions(String newVersion, int newBuildNumber) async { |
| 200 | + await updatePubspec(newVersion, newBuildNumber); |
| 201 | + } |
| 202 | + |
| 203 | + Future<void> updatePubspec(String newVersion, int newBuildNumber) async { |
| 204 | + final content = await File(pubspecPath).readAsString(); |
| 205 | + |
| 206 | + var updatedContent = content.replaceAll( |
| 207 | + RegExp(r'version:\s*[^\n]+'), 'version: $newVersion+$newBuildNumber'); |
| 208 | + |
| 209 | + if (updatedContent.contains('inno_bundle:')) { |
| 210 | + updatedContent = updatedContent.replaceAllMapped( |
| 211 | + RegExp(r'(inno_bundle:.*?)(\n\s*version:\s*)([^\n]+)', dotAll: true), |
| 212 | + (match) { |
| 213 | + final before = match.group(1); |
| 214 | + final versionKey = match.group(2); |
| 215 | + return '$before$versionKey$newVersion'; |
| 216 | + }, |
| 217 | + ); |
| 218 | + } |
| 219 | + |
| 220 | + await File(pubspecPath).writeAsString(updatedContent); |
| 221 | + print('✅ Updated pubspec.yaml (main version and inno_bundle)'); |
| 222 | + } |
| 223 | + |
| 224 | + String generateCommitMessage( |
| 225 | + String newVersion, String oldVersion, String versionType) { |
| 226 | + final messages = { |
| 227 | + 'major': 'build: bump version to v$newVersion (major release)', |
| 228 | + 'minor': 'build: bump version to v$newVersion (minor release)', |
| 229 | + 'patch': 'build: bump version to v$newVersion (patch release)', |
| 230 | + 'hotfix': 'build: bump version to v$newVersion (hotfix)', |
| 231 | + 'pre-release': 'build: bump version to v$newVersion (pre-release)', |
| 232 | + 'custom': 'build: bump version to v$newVersion', |
| 233 | + }; |
| 234 | + |
| 235 | + return messages[versionType] ?? 'build: bump version to v$newVersion'; |
| 236 | + } |
| 237 | + |
| 238 | + Future<void> handleGitOperations( |
| 239 | + String newVersion, String oldVersion, String versionType) async { |
| 240 | + print('\n🔄 Git Operations:'); |
| 241 | + print(' 1. Commit changes, create tag, and push'); |
| 242 | + print(' 2. Commit changes and create tag only'); |
| 243 | + print(' 3. Commit changes only'); |
| 244 | + print(' 4. Create tag only (no commit)'); |
| 245 | + print(' 5. Skip all git operations'); |
| 246 | + |
| 247 | + stdout.write('Choose option (1-5): '); |
| 248 | + final choice = stdin.readLineSync()?.trim(); |
| 249 | + |
| 250 | + switch (choice) { |
| 251 | + case '1': |
| 252 | + await commitChanges(newVersion, oldVersion, versionType); |
| 253 | + await createTag(newVersion); |
| 254 | + await pushChanges(); |
| 255 | + await pushTag(newVersion); |
| 256 | + break; |
| 257 | + case '2': |
| 258 | + await commitChanges(newVersion, oldVersion, versionType); |
| 259 | + await createTag(newVersion); |
| 260 | + break; |
| 261 | + case '3': |
| 262 | + await commitChanges(newVersion, oldVersion, versionType); |
| 263 | + break; |
| 264 | + case '4': |
| 265 | + await createTag(newVersion); |
| 266 | + break; |
| 267 | + case '5': |
| 268 | + print('⏭️ Skipped all git operations'); |
| 269 | + break; |
| 270 | + default: |
| 271 | + print('❌ Invalid choice, skipping git operations'); |
| 272 | + } |
| 273 | + } |
| 274 | + |
| 275 | + Future<void> commitChanges( |
| 276 | + String newVersion, String oldVersion, String versionType) async { |
| 277 | + try { |
| 278 | + final addResult = await Process.run('git', ['add', pubspecPath]); |
| 279 | + if (addResult.exitCode != 0) { |
| 280 | + print('❌ Failed to stage pubspec.yaml: ${addResult.stderr}'); |
| 281 | + return; |
| 282 | + } |
| 283 | + |
| 284 | + final commitMessage = |
| 285 | + generateCommitMessage(newVersion, oldVersion, versionType); |
| 286 | + |
| 287 | + final commitResult = |
| 288 | + await Process.run('git', ['commit', '-m', commitMessage]); |
| 289 | + if (commitResult.exitCode == 0) { |
| 290 | + print('✅ Committed changes: $commitMessage'); |
| 291 | + } else { |
| 292 | + print('❌ Failed to commit changes: ${commitResult.stderr}'); |
| 293 | + } |
| 294 | + } catch (e) { |
| 295 | + print('❌ Git commit failed: $e'); |
| 296 | + } |
| 297 | + } |
| 298 | + |
| 299 | + Future<void> createTag(String version) async { |
| 300 | + try { |
| 301 | + final result = await Process.run('git', ['tag', 'v$version']); |
| 302 | + if (result.exitCode == 0) { |
| 303 | + print('✅ Created git tag: v$version'); |
| 304 | + } else { |
| 305 | + print('❌ Failed to create git tag: ${result.stderr}'); |
| 306 | + } |
| 307 | + } catch (e) { |
| 308 | + print('❌ Git tag creation failed: $e'); |
| 309 | + } |
| 310 | + } |
| 311 | + |
| 312 | + Future<void> pushChanges() async { |
| 313 | + try { |
| 314 | + final result = await Process.run('git', ['push']); |
| 315 | + if (result.exitCode == 0) { |
| 316 | + print('✅ Pushed commits to remote'); |
| 317 | + } else { |
| 318 | + print('❌ Failed to push commits: ${result.stderr}'); |
| 319 | + } |
| 320 | + } catch (e) { |
| 321 | + print('❌ Git push failed: $e'); |
| 322 | + } |
| 323 | + } |
| 324 | + |
| 325 | + Future<void> pushTag(String version) async { |
| 326 | + try { |
| 327 | + final result = await Process.run('git', ['push', 'origin', 'v$version']); |
| 328 | + if (result.exitCode == 0) { |
| 329 | + print('✅ Pushed git tag: v$version'); |
| 330 | + } else { |
| 331 | + print('❌ Failed to push git tag: ${result.stderr}'); |
| 332 | + } |
| 333 | + } catch (e) { |
| 334 | + print('❌ Git tag push failed: $e'); |
| 335 | + } |
| 336 | + } |
| 337 | +} |
| 338 | + |
| 339 | +void main() { |
| 340 | + final manager = VersionManager(); |
| 341 | + manager.run(); |
| 342 | +} |
0 commit comments