From a96b4f6dfe67d22565421cfc367ad4660c2c6538 Mon Sep 17 00:00:00 2001 From: Alexander Thomas Date: Fri, 27 Mar 2020 14:17:40 +0000 Subject: [PATCH] [homebrew] Add support for the beta channel formula This changes to the homebrew tooling to modify instead of replace the formulas. The formulas can now be changed by editing them in the dart-lang/homebrew-dart repo, rather than in the release tooling. Also adds a dry-run mode for local testing. https://github.com/dart-lang/sdk/issues/40990 Change-Id: Ibed90894800e4d84fac3c4558e6f08d629baed62 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/141244 Reviewed-by: William Hesse --- .../update_homebrew/bin/update_homebrew.dart | 12 +- tools/apps/update_homebrew/lib/src/impl.dart | 187 ++++-------------- .../update_homebrew/lib/update_homebrew.dart | 28 +-- 3 files changed, 56 insertions(+), 171 deletions(-) diff --git a/tools/apps/update_homebrew/bin/update_homebrew.dart b/tools/apps/update_homebrew/bin/update_homebrew.dart index 691f0a5b6a8..68dc9e0859d 100644 --- a/tools/apps/update_homebrew/bin/update_homebrew.dart +++ b/tools/apps/update_homebrew/bin/update_homebrew.dart @@ -10,14 +10,17 @@ import 'package:update_homebrew/update_homebrew.dart'; void main(List args) async { final parser = ArgParser() + ..addFlag('dry-run', abbr: 'n') ..addOption('revision', abbr: 'r') ..addOption('channel', abbr: 'c', allowed: supportedChannels) ..addOption('key', abbr: 'k'); final options = parser.parse(args); + final dryRun = options['dry-run'] as bool; final revision = options['revision'] as String; final channel = options['channel'] as String; if ([revision, channel].contains(null)) { - print("Usage: update_homebrew.dart -r revision -c channel [-k ssh_key]\n" + print( + "Usage: update_homebrew.dart -r version -c channel [-k ssh_key] [-n]\n" " ssh_key should allow pushes to $githubRepo on github"); exitCode = 1; return; @@ -46,8 +49,11 @@ void main(List args) async { '-m', 'Updated $channel branch to revision $revision' ], repository, gitEnvironment); - - await runGit(['push'], repository, gitEnvironment); + if (dryRun) { + await runGit(['diff', 'origin/master'], repository, gitEnvironment); + } else { + await runGit(['push'], repository, gitEnvironment); + } } finally { await tempDir.delete(recursive: true); } diff --git a/tools/apps/update_homebrew/lib/src/impl.dart b/tools/apps/update_homebrew/lib/src/impl.dart index 2f7b66def84..938108bfbd9 100644 --- a/tools/apps/update_homebrew/lib/src/impl.dart +++ b/tools/apps/update_homebrew/lib/src/impl.dart @@ -1,30 +1,23 @@ part of '../update_homebrew.dart'; -const _files = { - 'dev': [_x64Files, _ia32Files], - 'stable': [_x64Files, _ia32Files] -}; +const _files = [ + 'dartsdk-macos-x64-release.zip', + 'dartsdk-linux-x64-release.zip', + 'dartsdk-linux-arm64-release.zip', + 'dartsdk-linux-ia32-release.zip', + 'dartsdk-linux-arm-release.zip', +]; -const _urlBase = 'https://storage.googleapis.com/dart-archive/channels'; -const _x64Files = { - 'mac': 'sdk/dartsdk-macos-x64-release.zip', - 'linux': 'sdk/dartsdk-linux-x64-release.zip', - 'linux-arm': 'sdk/dartsdk-linux-arm64-release.zip', -}; -const _ia32Files = { - 'linux': 'sdk/dartsdk-linux-ia32-release.zip', - 'linux-arm': 'sdk/dartsdk-linux-arm-release.zip', -}; +const _host = 'https://storage.googleapis.com/dart-archive/channels'; Future _getHash256( - String channel, String revision, String download) async { + String channel, String version, String download) async { var client = http.Client(); try { var api = storage.StorageApi(client); - var media = await api.objects.get('dart-archive', - 'channels/$channel/release/$revision/$download.sha256sum', + var url = 'channels/$channel/release/$version/sdk/$download.sha256sum'; + var media = await api.objects.get('dart-archive', url, downloadOptions: DownloadOptions.FullMedia) as Media; - var hashLine = await ascii.decodeStream(media.stream); return RegExp('[0-9a-fA-F]*').stringMatch(hashLine); } finally { @@ -32,138 +25,32 @@ Future _getHash256( } } -Future _getVersion(String channel, String revision) async { - var client = http.Client(); - try { - var api = storage.StorageApi(client); - - var media = await api.objects.get( - 'dart-archive', 'channels/$channel/release/$revision/VERSION', - downloadOptions: DownloadOptions.FullMedia) as Media; - - var versionObject = - await json.fuse(ascii).decoder.bind(media.stream).first as Map; - return versionObject['version'] as String; - } finally { - client.close(); - } -} - -Future> _getCurrentRevisions(String repository) async { - var revisions = {}; - var lines = await (File(p.join(repository, dartRbFileName))).readAsLines(); - - for (var channel in supportedChannels) { - /// This RegExp between release/ and /sdk matches - /// * 1 digit followed by - /// * Any number of letters, numbers, dashes and dots - /// This covers both numeric- and version-formatted revisions - /// - /// Note: all of the regexp escape slashes `\` are double-escaped within the - /// Dart string - final regExp = RegExp('channels/$channel/release/(\\d[\\w\\d\\-\\.]*)/sdk'); - - revisions[channel] = - regExp.firstMatch(lines.firstWhere(regExp.hasMatch)).group(1); - } - return revisions; -} - -Future> _getHashes(Map revisions) async { - var hashes = {}; - for (var channel in supportedChannels) { - hashes[channel] = {}; - for (var files in _files[channel]) { - for (var file in files.values) { - var hash = await _getHash256(channel, revisions[channel], file); - hashes[channel][file] = hash; - } +Future _updateFormula(String channel, File file, String version, + Map hashes) async { + // Extract files and hashes that are stored in the formula in this format: + // url "//release//sdk/.zip" + // sha256 "" + var filesAndHashes = RegExp( + 'channels/$channel/release' + + r'/(\d[\w\d\-\.]*)/sdk/([\w\d\-\.]+)\"\n(\s+)sha256 \"[\da-f]+\"', + multiLine: true); + var contents = await file.readAsString(); + contents = contents.replaceAllMapped(filesAndHashes, (m) { + var currentVersion = m.group(1); + if (currentVersion == version) { + throw new ArgumentError( + "Channel $channel is already at version $version in homebrew."); } - } - return hashes; + var artifact = m.group(2); + var indent = m.group(3); + return 'channels/$channel/release/$version/sdk/$artifact"\n' + '${indent}sha256 "${hashes[artifact]}"'; + }); + await file.writeAsString(contents, flush: true); } -String _createDartFormula( - Map revisions, Map hashes, String devVersion, String stableVersion) => - ''' -class Dart < Formula - desc "The Dart SDK" - homepage "https://dart.dev" - - version "$stableVersion" - if OS.mac? - url "$_urlBase/stable/release/${revisions['stable']}/${_x64Files['mac']}" - sha256 "${hashes['stable'][_x64Files['mac']]}" - elsif OS.linux? && Hardware::CPU.intel? - if Hardware::CPU.is_64_bit? - url "$_urlBase/stable/release/${revisions['stable']}/${_x64Files['linux']}" - sha256 "${hashes['stable'][_x64Files['linux']]}" - else - url "$_urlBase/stable/release/${revisions['stable']}/${_ia32Files['linux']}" - sha256 "${hashes['stable'][_ia32Files['linux']]}" - end - elsif OS.linux? && Hardware::CPU.arm? - if Hardware::CPU.is_64_bit? - url "$_urlBase/stable/release/${revisions['stable']}/${_x64Files['linux-arm']}" - sha256 "${hashes['stable'][_x64Files['linux-arm']]}" - else - url "$_urlBase/stable/release/${revisions['stable']}/${_ia32Files['linux-arm']}" - sha256 "${hashes['stable'][_ia32Files['linux-arm']]}" - end - end - - devel do - version "$devVersion" - if OS.mac? - url "$_urlBase/dev/release/${revisions['dev']}/${_x64Files['mac']}" - sha256 "${hashes['dev'][_x64Files['mac']]}" - elsif OS.linux? && Hardware::CPU.intel? - if Hardware::CPU.is_64_bit? - url "$_urlBase/dev/release/${revisions['dev']}/${_x64Files['linux']}" - sha256 "${hashes['dev'][_x64Files['linux']]}" - else - url "$_urlBase/dev/release/${revisions['dev']}/${_ia32Files['linux']}" - sha256 "${hashes['dev'][_ia32Files['linux']]}" - end - elsif OS.linux? && Hardware::CPU.arm? - if Hardware::CPU.is_64_bit? - url "$_urlBase/dev/release/${revisions['dev']}/${_x64Files['linux-arm']}" - sha256 "${hashes['dev'][_x64Files['linux-arm']]}" - else - url "$_urlBase/dev/release/${revisions['dev']}/${_ia32Files['linux-arm']}" - sha256 "${hashes['dev'][_ia32Files['linux-arm']]}" - end - end - end - - def install - libexec.install Dir["*"] - bin.install_symlink "#{libexec}/bin/dart" - bin.write_exec_script Dir["#{libexec}/bin/{pub,dart?*}"] - end - - def shim_script(target) - <<~EOS - #!/usr/bin/env bash - exec "#{prefix}/#{target}" "\$@" - EOS - end - - def caveats - <<~EOS - Please note the path to the Dart SDK: - #{opt_libexec} - EOS - end - - test do - (testpath/"sample.dart").write <<~EOS - void main() { - print(r"test message"); - } - EOS - - assert_equal "test message\\n", shell_output("#{bin}/dart sample.dart") - end -end -'''; +Future> _getHashes(String channel, String version) async { + return { + for (var file in _files) file: await _getHash256(channel, version, file) + }; +} diff --git a/tools/apps/update_homebrew/lib/update_homebrew.dart b/tools/apps/update_homebrew/lib/update_homebrew.dart index c2018d7dad6..e38e51f5ee4 100644 --- a/tools/apps/update_homebrew/lib/update_homebrew.dart +++ b/tools/apps/update_homebrew/lib/update_homebrew.dart @@ -11,27 +11,19 @@ part 'src/impl.dart'; const githubRepo = 'dart-lang/homebrew-dart'; -const dartRbFileName = 'dart.rb'; +const formulaByChannel = { + 'beta': 'dart-beta.rb', + 'dev': 'dart.rb', + 'stable': 'dart.rb' +}; -Iterable get supportedChannels => _files.keys; +Iterable get supportedChannels => formulaByChannel.keys; Future writeHomebrewInfo( - String channel, String revision, String repository) async { - var revisions = await _getCurrentRevisions(repository); - - if (revisions[channel] == revision) { - print("Channel $channel is already at revision $revision in homebrew."); - exit(0); - } - revisions[channel] = revision; - var hashes = await _getHashes(revisions); - var devVersion = await _getVersion('dev', revisions['dev']); - - var stableVersion = await _getVersion('stable', revisions['stable']); - - await File(p.join(repository, dartRbFileName)).writeAsString( - _createDartFormula(revisions, hashes, devVersion, stableVersion), - flush: true); + String channel, String version, String repository) async { + var formula = File(p.join(repository, formulaByChannel[channel])); + var hashes = await _getHashes(channel, version); + await _updateFormula(channel, formula, version, hashes); } Future runGit(List args, String repository,