[devicelab] Remove legacy devicelab manifest logic (#101554)
This commit is contained in:
parent
2979ff6f13
commit
3109073fd9
@ -7,7 +7,6 @@ import 'dart:io';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:flutter_devicelab/framework/ab.dart';
|
||||
import 'package:flutter_devicelab/framework/manifest.dart';
|
||||
import 'package:flutter_devicelab/framework/runner.dart';
|
||||
import 'package:flutter_devicelab/framework/task_result.dart';
|
||||
import 'package:flutter_devicelab/framework/utils.dart';
|
||||
@ -63,16 +62,6 @@ Future<void> main(List<String> rawArgs) async {
|
||||
/// Path to write test results to.
|
||||
final String? resultsPath = args['results-file'] as String?;
|
||||
|
||||
if (!args.wasParsed('task')) {
|
||||
if (args.wasParsed('stage') || args.wasParsed('all')) {
|
||||
addTasks(
|
||||
tasks: loadTaskManifest().tasks,
|
||||
args: args,
|
||||
taskNames: taskNames,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (args.wasParsed('list')) {
|
||||
for (int i = 0; i < taskNames.length; i++) {
|
||||
print('${(i + 1).toString().padLeft(3)} - ${taskNames[i]}');
|
||||
@ -209,29 +198,6 @@ File _uniqueFile(String filenameTemplate) {
|
||||
return file;
|
||||
}
|
||||
|
||||
void addTasks({
|
||||
required List<ManifestTask> tasks,
|
||||
required ArgResults args,
|
||||
required List<String> taskNames,
|
||||
}) {
|
||||
if (args.wasParsed('continue-from')) {
|
||||
final int index = tasks.indexWhere((ManifestTask task) => task.name == args['continue-from']);
|
||||
if (index == -1) {
|
||||
throw Exception('Invalid task name "${args['continue-from']}"');
|
||||
}
|
||||
tasks.removeRange(0, index);
|
||||
}
|
||||
// Only start skipping if user specified a task to continue from
|
||||
final String stage = args['stage'] as String;
|
||||
for (final ManifestTask task in tasks) {
|
||||
final bool isQualifyingStage = stage == null || task.stage == stage;
|
||||
final bool isQualifyingHost = !(args['match-host-platform'] as bool) || task.isSupportedByHost();
|
||||
if (isQualifyingHost && isQualifyingStage) {
|
||||
taskNames.add(task.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArgParser createArgParser(List<String> taskNames) {
|
||||
return ArgParser()
|
||||
..addMultiOption(
|
||||
@ -293,16 +259,6 @@ ArgParser createArgParser(List<String> taskNames) {
|
||||
'The filename may contain a single # character to be replaced by a sequence\n'
|
||||
'number if the name already exists.',
|
||||
)
|
||||
..addFlag(
|
||||
'all',
|
||||
abbr: 'a',
|
||||
help: 'Runs all tasks defined in manifest.yaml in alphabetical order.',
|
||||
)
|
||||
..addOption(
|
||||
'continue-from',
|
||||
abbr: 'c',
|
||||
help: 'With --all or --stage, continue from the given test.',
|
||||
)
|
||||
..addFlag(
|
||||
'exit',
|
||||
defaultsTo: true,
|
||||
@ -352,12 +308,6 @@ ArgParser createArgParser(List<String> taskNames) {
|
||||
'service-account-token-file',
|
||||
help: '[Flutter infrastructure] Authentication for uploading results.',
|
||||
)
|
||||
..addOption(
|
||||
'stage',
|
||||
abbr: 's',
|
||||
help: 'Name of the stage. Runs all tasks for that stage. The tasks and\n'
|
||||
'their stages are read from manifest.yaml.',
|
||||
)
|
||||
..addFlag(
|
||||
'silent',
|
||||
help: 'Reduce verbosity slightly.',
|
||||
|
@ -1,185 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
import 'utils.dart';
|
||||
|
||||
/// Loads manifest data from `manifest.yaml` file or from [yaml], if present.
|
||||
Manifest loadTaskManifest([ String? yaml ]) {
|
||||
final dynamic manifestYaml = yaml == null
|
||||
? loadYaml(file('manifest.yaml').readAsStringSync())
|
||||
: loadYamlNode(yaml);
|
||||
|
||||
_checkType(manifestYaml is YamlMap, manifestYaml, 'Manifest', 'dictionary');
|
||||
return _validateAndParseManifest(manifestYaml as YamlMap);
|
||||
}
|
||||
|
||||
/// Contains CI task information.
|
||||
class Manifest {
|
||||
Manifest._(this.tasks);
|
||||
|
||||
/// CI tasks.
|
||||
final List<ManifestTask> tasks;
|
||||
}
|
||||
|
||||
/// A CI task.
|
||||
class ManifestTask {
|
||||
ManifestTask._({
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.stage,
|
||||
required this.requiredAgentCapabilities,
|
||||
required this.isFlaky,
|
||||
required this.timeoutInMinutes,
|
||||
required this.onLuci,
|
||||
}) {
|
||||
final String taskName = 'task "$name"';
|
||||
_checkIsNotBlank(name, 'Task name', taskName);
|
||||
_checkIsNotBlank(description, 'Task description', taskName);
|
||||
_checkIsNotBlank(stage, 'Task stage', taskName);
|
||||
_checkIsNotBlank(requiredAgentCapabilities, 'requiredAgentCapabilities', taskName);
|
||||
}
|
||||
|
||||
/// Task name as it appears on the dashboard.
|
||||
final String name;
|
||||
|
||||
/// A human-readable description of the task.
|
||||
final String description;
|
||||
|
||||
/// The stage this task should run in.
|
||||
final String stage;
|
||||
|
||||
/// Capabilities required of the build agent to be able to perform this task.
|
||||
final List<String> requiredAgentCapabilities;
|
||||
|
||||
/// Whether this test is flaky.
|
||||
///
|
||||
/// Flaky tests are not considered when deciding if the build is broken.
|
||||
final bool isFlaky;
|
||||
|
||||
/// An optional custom timeout specified in minutes.
|
||||
final int timeoutInMinutes;
|
||||
|
||||
/// (Optional) Whether this test runs on LUCI.
|
||||
final bool onLuci;
|
||||
|
||||
/// Whether the task is supported by the current host platform
|
||||
bool isSupportedByHost() {
|
||||
final Set<String> supportedHosts = Set<String>.from(
|
||||
requiredAgentCapabilities.map<String>(
|
||||
(String str) => str.split('/')[0]
|
||||
)
|
||||
);
|
||||
String hostPlatform = Platform.operatingSystem;
|
||||
if (hostPlatform == 'macos') {
|
||||
hostPlatform = 'mac'; // package:platform uses 'macos' while manifest.yaml uses 'mac'
|
||||
}
|
||||
return supportedHosts.contains(hostPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
/// Thrown when the manifest YAML is not valid.
|
||||
class ManifestError extends Error {
|
||||
ManifestError(this.message);
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
String toString() => '$ManifestError: $message';
|
||||
}
|
||||
|
||||
// There's no good YAML validator, at least not for Dart, so we validate
|
||||
// manually. It's not too much code and produces good error messages.
|
||||
Manifest _validateAndParseManifest(YamlMap manifestYaml) {
|
||||
_checkKeys(manifestYaml, 'manifest', const <String>['tasks']);
|
||||
return Manifest._(_validateAndParseTasks(manifestYaml['tasks']));
|
||||
}
|
||||
|
||||
List<ManifestTask> _validateAndParseTasks(dynamic tasksYaml) {
|
||||
_checkType(tasksYaml is YamlMap, tasksYaml, 'Value of "tasks"', 'dictionary');
|
||||
final List<String> sortedKeys = (tasksYaml as YamlMap).keys.toList().cast<String>()..sort();
|
||||
// ignore: avoid_dynamic_calls
|
||||
return sortedKeys.map<ManifestTask>((String taskName) => _validateAndParseTask(taskName, tasksYaml[taskName])).toList();
|
||||
}
|
||||
|
||||
ManifestTask _validateAndParseTask(String taskName, dynamic taskYaml) {
|
||||
_checkType(taskYaml is YamlMap, taskYaml, 'Value of task "$taskName"', 'dictionary');
|
||||
_checkKeys(taskYaml as YamlMap, 'Value of task "$taskName"', const <String>[
|
||||
'description',
|
||||
'stage',
|
||||
'required_agent_capabilities',
|
||||
'flaky',
|
||||
'timeout_in_minutes',
|
||||
'on_luci',
|
||||
]);
|
||||
// ignore: avoid_dynamic_calls
|
||||
final dynamic isFlaky = taskYaml['flaky'];
|
||||
if (isFlaky != null) {
|
||||
_checkType(isFlaky is bool, isFlaky, 'flaky', 'boolean');
|
||||
}
|
||||
|
||||
// ignore: avoid_dynamic_calls
|
||||
final dynamic timeoutInMinutes = taskYaml['timeout_in_minutes'];
|
||||
if (timeoutInMinutes != null) {
|
||||
_checkType(timeoutInMinutes is int, timeoutInMinutes, 'timeout_in_minutes', 'integer');
|
||||
}
|
||||
|
||||
// ignore: avoid_dynamic_calls
|
||||
final List<dynamic> capabilities = _validateAndParseCapabilities(taskName, taskYaml['required_agent_capabilities']);
|
||||
|
||||
// ignore: avoid_dynamic_calls
|
||||
final dynamic onLuci = taskYaml['on_luci'];
|
||||
if (onLuci != null) {
|
||||
_checkType(onLuci is bool, onLuci, 'on_luci', 'boolean');
|
||||
}
|
||||
|
||||
return ManifestTask._(
|
||||
name: taskName,
|
||||
// ignore: avoid_dynamic_calls
|
||||
description: taskYaml['description'] as String,
|
||||
// ignore: avoid_dynamic_calls
|
||||
stage: taskYaml['stage'] as String,
|
||||
requiredAgentCapabilities: capabilities as List<String>,
|
||||
isFlaky: isFlaky as bool,
|
||||
timeoutInMinutes: timeoutInMinutes as int,
|
||||
onLuci: onLuci as bool,
|
||||
);
|
||||
}
|
||||
|
||||
List<String> _validateAndParseCapabilities(String taskName, dynamic capabilitiesYaml) {
|
||||
_checkType(capabilitiesYaml is List, capabilitiesYaml, 'required_agent_capabilities', 'list');
|
||||
final List<dynamic> capabilities = capabilitiesYaml as List<dynamic>;
|
||||
for (int i = 0; i < capabilities.length; i++) {
|
||||
final dynamic capability = capabilities[i];
|
||||
_checkType(capability is String, capability, 'required_agent_capabilities[$i]', 'string');
|
||||
}
|
||||
return capabilitiesYaml.cast<String>();
|
||||
}
|
||||
|
||||
void _checkType(bool isValid, dynamic value, String variableName, String typeName) {
|
||||
if (!isValid) {
|
||||
throw ManifestError(
|
||||
'$variableName must be a $typeName but was ${value.runtimeType}: $value',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _checkIsNotBlank(dynamic value, String variableName, String ownerName) {
|
||||
if (value == null || value is String && value.isEmpty || value is List<dynamic> && value.isEmpty) {
|
||||
throw ManifestError('$variableName must not be empty in $ownerName.');
|
||||
}
|
||||
}
|
||||
|
||||
void _checkKeys(Map<dynamic, dynamic> map, String variableName, List<String> allowedKeys) {
|
||||
for (final String key in map.keys.cast<String>()) {
|
||||
if (!allowedKeys.contains(key)) {
|
||||
throw ManifestError(
|
||||
'Unrecognized property "$key" in $variableName. '
|
||||
'Allowed properties: ${allowedKeys.join(', ')}');
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user