| // Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:baseline/options.dart'; |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| import 'package:pool/pool.dart'; |
| |
| const _resultBase = 'gs://dart-test-results'; |
| |
| /// Baselines a builder with the [options] and copies the results to the |
| /// [resultBase]. [resultBase] can be a URL or a path supported by `gsutil cp`. |
| Future<void> baseline(BaselineOptions options, |
| [String resultBase = _resultBase]) async { |
| await Future.wait([ |
| for (final channel in options.channels) |
| if (channel == 'main') |
| // baseline a new builder on main |
| // builder,builder2 -> new-builder |
| baselineBuilder( |
| options.builders, |
| channel, |
| options.suites, |
| options.target, |
| options.configs, |
| options.dryRun, |
| options.mapping, |
| resultBase) |
| else if (options.builders.contains(options.target)) |
| // baseline a builder on a channel with main builder data |
| // builder,builder2 -> builder-dev |
| baselineBuilder( |
| options.builders, |
| channel, |
| options.suites, |
| '${options.target}-$channel', |
| options.configs, |
| options.dryRun, |
| options.mapping, |
| resultBase) |
| else |
| // baseline a builder on a channel with channel builder data |
| // builder-dev,builder2-dev -> new-builder-dev |
| baselineBuilder( |
| options.builders.map((b) => '$b-$channel').toList(), |
| channel, |
| options.suites, |
| '${options.target}-$channel', |
| options.configs, |
| options.dryRun, |
| options.mapping, |
| resultBase) |
| ]); |
| } |
| |
| Future<void> baselineBuilder( |
| List<String> builders, |
| String channel, |
| Set<String> suites, |
| String target, |
| Map<String, List<String>> configs, |
| bool dryRun, |
| ConfigurationMapping mapping, |
| String resultBase) async { |
| var resultsStream = Pool(4).forEach(builders, (builder) async { |
| var latest = await read('$resultBase/builders/$builder/latest'); |
| return await read('$resultBase/builders/$builder/$latest/results.json'); |
| }); |
| var modifiedResults = StringBuffer(); |
| var modifiedResultsPerConfig = <String, StringBuffer>{}; |
| await for (var results in resultsStream) { |
| for (var json in LineSplitter.split(results) |
| .map(jsonDecode) |
| .cast<Map<String, dynamic>>()) { |
| var configurations = mapping(json['configuration'], configs); |
| if (configurations == null) { |
| continue; |
| } |
| for (var configuration in configurations) { |
| if (suites.isNotEmpty && !suites.contains(json['suite'])) continue; |
| json['configuration'] = configuration; |
| json['build_number'] = '0'; |
| json['previous_build_number'] = '0'; |
| json['builder_name'] = target; |
| json['flaky'] = false; |
| json['previous_flaky'] = false; |
| |
| var encoded = jsonEncode(json); |
| modifiedResults.writeln(encoded); |
| modifiedResultsPerConfig |
| .putIfAbsent(configuration, () => StringBuffer()) |
| .writeln(encoded); |
| } |
| if (dryRun) break; |
| } |
| } |
| await write('$resultBase/builders/$target/0/results.json', |
| modifiedResults.toString(), dryRun); |
| for (var entry in modifiedResultsPerConfig.entries) { |
| await write( |
| '$resultBase/configuration/$channel/${entry.key}/0/results.json', |
| entry.value.toString(), |
| dryRun); |
| } |
| await write('$resultBase/builders/$target/latest', '0', dryRun); |
| } |
| |
| Future<String> read(String url) { |
| return run('gsutil.py', ['cp', url, '-']); |
| } |
| |
| Future<String> write(String url, String stdin, bool dryRun) { |
| return run('gsutil.py', ['cp', '-', url], stdin: stdin, dryRun: dryRun); |
| } |
| |
| Future<String> run(String command, List<String> arguments, |
| {String? stdin, bool dryRun = false}) async { |
| print('Running $command $arguments...'); |
| if (dryRun) { |
| if (stdin != null) { |
| print('stdin:\n$stdin'); |
| } |
| return ''; |
| } |
| var process = await Process.start(command, arguments); |
| process.stderr.transform(utf8.decoder).forEach(print); |
| if (stdin != null) { |
| process.stdin.write(stdin); |
| await process.stdin.flush(); |
| process.stdin.close(); |
| } |
| var stdout = process.stdout.transform(utf8.decoder).join(); |
| var result = await process.exitCode; |
| if (result != 0) { |
| throw Exception('Failed to run $command $arguments: $result'); |
| } |
| return await stdout; |
| } |