|
| 1 | +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file |
| 2 | +// for details. All rights reserved. Use of this source code is governed by a |
| 3 | + |
| 4 | +/// Tool for finding all tagged versions of the dart sdk, and in turn tag this |
| 5 | +/// repository with `SDK-$version` for each of these. |
| 6 | +library; |
| 7 | + |
| 8 | +import 'dart:io'; |
| 9 | + |
| 10 | +import 'package:args/args.dart'; |
| 11 | +import 'package:path/path.dart' as p; |
| 12 | +import 'package:pub_semver/pub_semver.dart'; |
| 13 | + |
| 14 | +Future<void> main(List<String> args) async { |
| 15 | + final argParser = |
| 16 | + ArgParser() |
| 17 | + ..addFlag( |
| 18 | + 'create', |
| 19 | + help: 'Create the missing tags, otherwise only list them', |
| 20 | + negatable: false, |
| 21 | + ) |
| 22 | + ..addFlag( |
| 23 | + 'push', |
| 24 | + help: 'Push missing sdk tags to remote', |
| 25 | + negatable: false, |
| 26 | + ) |
| 27 | + ..addOption('sdk-dir'); |
| 28 | + final ArgResults argResults; |
| 29 | + try { |
| 30 | + argResults = argParser.parse(args); |
| 31 | + } on FormatException catch (e) { |
| 32 | + stderr.writeln('${e.message}\n${argParser.usage}'); |
| 33 | + stderr.writeln(''' |
| 34 | +Will find all tagged sdk versions, and tag the corresponding revision in this |
| 35 | +repository with `--create`. |
| 36 | +And can push these tags to the remote with `--push`. |
| 37 | +
|
| 38 | +Usage: create_version_tags_from_sdk [--create] [--push] [--sdk-dir <path>] |
| 39 | +'''); |
| 40 | + exit(-1); |
| 41 | + } |
| 42 | + final create = argResults.flag('create'); |
| 43 | + final push = argResults.flag('push'); |
| 44 | + |
| 45 | + Directory? tempDir; |
| 46 | + String sdkDir; |
| 47 | + try { |
| 48 | + if (argResults.option('sdk-dir') == null) { |
| 49 | + tempDir = Directory.systemTemp.createTempSync(); |
| 50 | + final cloneResult = Process.runSync('git', [ |
| 51 | + 'clone', |
| 52 | + // Using a treeless clone is faster up-front, but slower for |
| 53 | + // showing a specific revision. |
| 54 | + // We assume we only miss a few tags. |
| 55 | + '--filter=tree:0', |
| 56 | + '-n', |
| 57 | + 'https://github.com/dart-lang/sdk', |
| 58 | + ], workingDirectory: tempDir.path); |
| 59 | + if (cloneResult.exitCode != 0) { |
| 60 | + throw Exception( |
| 61 | + 'Failed to clone sdk ${cloneResult.stderr} ${cloneResult.stdout}', |
| 62 | + ); |
| 63 | + } |
| 64 | + sdkDir = p.join(tempDir.path, 'sdk'); |
| 65 | + } else { |
| 66 | + sdkDir = argResults.option('sdk-dir')!; |
| 67 | + } |
| 68 | + |
| 69 | + final sdkTags = |
| 70 | + (Process.runSync('git', [ |
| 71 | + 'ls-remote', |
| 72 | + '--tags', |
| 73 | + '--refs', |
| 74 | + 'origin', |
| 75 | + ], workingDirectory: sdkDir).stdout |
| 76 | + as String) |
| 77 | + .split('\n') |
| 78 | + .where((line) => line.isNotEmpty) |
| 79 | + .map((line) => line.split('\t')[1]) |
| 80 | + .map((x) => x.substring('refs/tags/'.length)) |
| 81 | + .where((x) { |
| 82 | + try { |
| 83 | + Version.parse(x); |
| 84 | + } on FormatException { |
| 85 | + return false; |
| 86 | + } |
| 87 | + return true; |
| 88 | + }) |
| 89 | + .toSet(); |
| 90 | + final alreadyTagged = |
| 91 | + (Process.runSync('git', [ |
| 92 | + 'ls-remote', |
| 93 | + '--tags', |
| 94 | + '--refs', |
| 95 | + 'origin', |
| 96 | + ], workingDirectory: Directory.current.path).stdout |
| 97 | + as String) |
| 98 | + .split('\n') |
| 99 | + .where((line) => line.isNotEmpty) |
| 100 | + .map((line) => line.split('\t')[1]) |
| 101 | + .map((x) => x.substring('refs/tags/'.length)) |
| 102 | + .where((x) => x.startsWith('SDK-')) |
| 103 | + .map((x) => x.substring('SDK-'.length)) |
| 104 | + .toSet(); |
| 105 | + final missing = sdkTags.difference(alreadyTagged); |
| 106 | + var createdTagCount = 0; |
| 107 | + var pushedTagCount = 0; |
| 108 | + |
| 109 | + if (missing.isNotEmpty) { |
| 110 | + for (final sdkTag in missing) { |
| 111 | + final version = Version.parse(sdkTag); |
| 112 | + if ( |
| 113 | + // Old versions of the sdk had no pub or no DEPS file. |
| 114 | + version <= (Version.parse('1.11.3')) || |
| 115 | + // These version seems to have a broken DEPS file. |
| 116 | + version == Version.parse('1.12.0-dev.5.6') || |
| 117 | + version == Version.parse('1.12.0-dev.5.7')) { |
| 118 | + continue; |
| 119 | + } |
| 120 | + final depsResult = Process.runSync('git', [ |
| 121 | + 'show', |
| 122 | + '$sdkTag:DEPS', |
| 123 | + ], workingDirectory: sdkDir); |
| 124 | + if (depsResult.exitCode != 0) { |
| 125 | + stderr.writeln( |
| 126 | + 'Failed to get deps for $sdkTag ${depsResult.stderr} ' |
| 127 | + '${depsResult.stdout}', |
| 128 | + ); |
| 129 | + continue; |
| 130 | + } |
| 131 | + |
| 132 | + // Could use `gclient getdep -r sdk/third_party/pkg/pub` instead of a |
| 133 | + // regexp. But for some versions that seems to not work well. |
| 134 | + // The regexp |
| 135 | + var pubRev = RegExp( |
| 136 | + '"pub_rev": "([^"]*)"', |
| 137 | + ).firstMatch(depsResult.stdout as String)?.group(1); |
| 138 | + if (pubRev == null || pubRev.isEmpty) { |
| 139 | + stderr.writeln('Failed to get pub rev for $sdkTag '); |
| 140 | + continue; |
| 141 | + } |
| 142 | + if (pubRev.startsWith('@')) { |
| 143 | + pubRev = pubRev.substring(1); |
| 144 | + } |
| 145 | + |
| 146 | + stdout.writeln('$sdkTag uses pub: $pubRev'); |
| 147 | + if (create) { |
| 148 | + final tagResult = Process.runSync('git', [ |
| 149 | + 'tag', |
| 150 | + 'SDK-$sdkTag', |
| 151 | + pubRev, |
| 152 | + '-a', |
| 153 | + '-f', |
| 154 | + '-m', |
| 155 | + 'SDK $sdkTag', |
| 156 | + ], workingDirectory: Directory.current.path); |
| 157 | + if (tagResult.exitCode != 0) { |
| 158 | + stderr.writeln( |
| 159 | + 'Failed to tag sdk ${tagResult.stderr} ${tagResult.stdout}', |
| 160 | + ); |
| 161 | + continue; |
| 162 | + } |
| 163 | + createdTagCount++; |
| 164 | + } |
| 165 | + if (push) { |
| 166 | + final pushResult = Process.runSync('git', [ |
| 167 | + 'push', |
| 168 | + 'origin', |
| 169 | + // Don't run any hooks before pushing. |
| 170 | + '--no-verify', |
| 171 | + 'SDK-$sdkTag', |
| 172 | + ], workingDirectory: Directory.current.path); |
| 173 | + if (pushResult.exitCode != 0) { |
| 174 | + stderr.writeln( |
| 175 | + 'Failed to push sdk ${pushResult.stderr} ${pushResult.stdout}', |
| 176 | + ); |
| 177 | + continue; |
| 178 | + } |
| 179 | + pushedTagCount++; |
| 180 | + } |
| 181 | + } |
| 182 | + } |
| 183 | + if (!create) { |
| 184 | + stdout.writeln('Would have created $createdTagCount tags'); |
| 185 | + } else { |
| 186 | + stdout.writeln('Created $createdTagCount tags'); |
| 187 | + } |
| 188 | + if (push) { |
| 189 | + stdout.writeln('Pushed $pushedTagCount tags'); |
| 190 | + } |
| 191 | + } finally { |
| 192 | + tempDir?.deleteSync(recursive: true); |
| 193 | + } |
| 194 | +} |
0 commit comments