|
| 1 | +// tool/set_version.dart |
| 2 | +// |
| 3 | +// Single source of truth for the SDK version. Keeps `insforge` and |
| 4 | +// `insforge_flutter` in lockstep and regenerates the runtime version constant |
| 5 | +// used for the `User-Agent` header. |
| 6 | +// |
| 7 | +// Usage (run from the repo root): |
| 8 | +// dart run tool/set_version.dart 0.2.0 # set the version everywhere |
| 9 | +// dart run tool/set_version.dart --check # verify everything is in sync (CI) |
| 10 | +// |
| 11 | +// "Set" updates, all to the same version: |
| 12 | +// * packages/insforge/pubspec.yaml version: |
| 13 | +// * packages/insforge_flutter/pubspec.yaml version: and insforge: ^<v> |
| 14 | +// * packages/insforge/lib/src/core/version.dart insforgeSdkVersion |
| 15 | +import 'dart:io'; |
| 16 | + |
| 17 | +const String _insforgePubspec = 'packages/insforge/pubspec.yaml'; |
| 18 | +const String _flutterPubspec = 'packages/insforge_flutter/pubspec.yaml'; |
| 19 | +const String _versionDart = 'packages/insforge/lib/src/core/version.dart'; |
| 20 | + |
| 21 | +final RegExp _semver = RegExp(r'^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$'); |
| 22 | +final RegExp _versionLine = RegExp(r'^version:.*$', multiLine: true); |
| 23 | +final RegExp _depLine = |
| 24 | + RegExp(r'^(\s+insforge:\s*\^).*$', multiLine: true); |
| 25 | +final RegExp _dartConst = |
| 26 | + RegExp(r"const String insforgeSdkVersion = '.*';"); |
| 27 | + |
| 28 | +void main(List<String> args) { |
| 29 | + _ensureRepoRoot(); |
| 30 | + if (args.isEmpty) { |
| 31 | + _usageAndExit(); |
| 32 | + } |
| 33 | + if (args.first == '--check') { |
| 34 | + exit(_check() ? 0 : 1); |
| 35 | + } |
| 36 | + final version = args.first; |
| 37 | + if (!_semver.hasMatch(version)) { |
| 38 | + stderr.writeln('Invalid version "$version" (expected X.Y.Z[-pre][+build]).'); |
| 39 | + exit(64); |
| 40 | + } |
| 41 | + _set(version); |
| 42 | +} |
| 43 | + |
| 44 | +void _set(String version) { |
| 45 | + _replaceFirst(_insforgePubspec, _versionLine, 'version: $version'); |
| 46 | + _replaceFirst(_flutterPubspec, _versionLine, 'version: $version'); |
| 47 | + _replaceFirstMapped( |
| 48 | + _flutterPubspec, |
| 49 | + _depLine, |
| 50 | + (Match m) => '${m.group(1)}$version', |
| 51 | + ); |
| 52 | + _replaceFirst( |
| 53 | + _versionDart, |
| 54 | + _dartConst, |
| 55 | + "const String insforgeSdkVersion = '$version';", |
| 56 | + ); |
| 57 | + stdout.writeln('Set insforge + insforge_flutter to $version ' |
| 58 | + '(and insforge_flutter -> insforge: ^$version).'); |
| 59 | + stdout.writeln('Regenerated $_versionDart.'); |
| 60 | + stdout.writeln('Next: update each package CHANGELOG.md, commit, tag ' |
| 61 | + 'v$version, then publish insforge before insforge_flutter.'); |
| 62 | +} |
| 63 | + |
| 64 | +bool _check() { |
| 65 | + final core = _pubspecVersion(_insforgePubspec); |
| 66 | + final flutter = _pubspecVersion(_flutterPubspec); |
| 67 | + final dart = _dartVersion(); |
| 68 | + final dep = _depConstraint(); |
| 69 | + |
| 70 | + final problems = <String>[ |
| 71 | + if (flutter != core) |
| 72 | + 'insforge_flutter version ($flutter) != insforge version ($core)', |
| 73 | + if (dart != core) |
| 74 | + 'version.dart insforgeSdkVersion ($dart) != insforge version ($core)', |
| 75 | + if (dep != core) |
| 76 | + 'insforge_flutter dep "insforge: ^$dep" != ^$core', |
| 77 | + ]; |
| 78 | + |
| 79 | + if (problems.isEmpty) { |
| 80 | + stdout.writeln('Version $core is in sync across both packages + version.dart.'); |
| 81 | + return true; |
| 82 | + } |
| 83 | + stderr.writeln('Version drift detected:'); |
| 84 | + for (final p in problems) { |
| 85 | + stderr.writeln(' - $p'); |
| 86 | + } |
| 87 | + stderr.writeln('Fix with: dart run tool/set_version.dart $core'); |
| 88 | + return false; |
| 89 | +} |
| 90 | + |
| 91 | +// --- file helpers --- |
| 92 | + |
| 93 | +void _replaceFirst(String path, RegExp re, String replacement) { |
| 94 | + final file = File(path); |
| 95 | + final content = file.readAsStringSync(); |
| 96 | + if (!re.hasMatch(content)) { |
| 97 | + stderr.writeln('Pattern ${re.pattern} not found in $path'); |
| 98 | + exit(70); |
| 99 | + } |
| 100 | + file.writeAsStringSync(content.replaceFirst(re, replacement)); |
| 101 | +} |
| 102 | + |
| 103 | +void _replaceFirstMapped( |
| 104 | + String path, |
| 105 | + RegExp re, |
| 106 | + String Function(Match) replace, |
| 107 | +) { |
| 108 | + final file = File(path); |
| 109 | + final content = file.readAsStringSync(); |
| 110 | + if (!re.hasMatch(content)) { |
| 111 | + stderr.writeln('Pattern ${re.pattern} not found in $path'); |
| 112 | + exit(70); |
| 113 | + } |
| 114 | + file.writeAsStringSync(content.replaceFirstMapped(re, replace)); |
| 115 | +} |
| 116 | + |
| 117 | +String _pubspecVersion(String path) => |
| 118 | + _firstGroup(path, RegExp(r'^version:\s*(.+?)\s*$', multiLine: true)); |
| 119 | + |
| 120 | +String _dartVersion() => |
| 121 | + _firstGroup(_versionDart, RegExp(r"insforgeSdkVersion = '(.+?)'")); |
| 122 | + |
| 123 | +String _depConstraint() => _firstGroup( |
| 124 | + _flutterPubspec, |
| 125 | + RegExp(r'^\s+insforge:\s*\^(.+?)\s*$', multiLine: true), |
| 126 | + ); |
| 127 | + |
| 128 | +String _firstGroup(String path, RegExp re) { |
| 129 | + final m = re.firstMatch(File(path).readAsStringSync()); |
| 130 | + if (m == null) { |
| 131 | + stderr.writeln('Could not read ${re.pattern} from $path'); |
| 132 | + exit(70); |
| 133 | + } |
| 134 | + return m.group(1)!; |
| 135 | +} |
| 136 | + |
| 137 | +void _ensureRepoRoot() { |
| 138 | + if (!File(_insforgePubspec).existsSync()) { |
| 139 | + stderr.writeln('Run this from the repository root ' |
| 140 | + '(could not find $_insforgePubspec).'); |
| 141 | + exit(66); |
| 142 | + } |
| 143 | +} |
| 144 | + |
| 145 | +void _usageAndExit() { |
| 146 | + stdout.writeln('Usage:'); |
| 147 | + stdout.writeln(' dart run tool/set_version.dart <X.Y.Z> set the version'); |
| 148 | + stdout.writeln(' dart run tool/set_version.dart --check verify sync (CI)'); |
| 149 | + exit(64); |
| 150 | +} |
0 commit comments