blob: 69f2f039f4862882eadd4c09ee78065d5e9ee47d [file] [log] [blame]
// Copyright 2021 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:core';
import 'dart:io';
import 'package:cocoon_service/cocoon_service.dart';
import 'package:cocoon_service/src/model/ci_yaml/ci_yaml.dart';
import 'package:cocoon_service/src/model/proto/internal/scheduler.pbserver.dart' as pb;
import 'package:github/github.dart';
import 'package:http/http.dart' as http;
import 'package:process/process.dart';
import 'package:test/test.dart';
import 'package:yaml/yaml.dart';
import 'common.dart';
/// List of repositories that have supported .ci.yaml config files.
final List<SupportedConfig> configs = <SupportedConfig>[
SupportedConfig(RepositorySlug('flutter', 'cocoon'), 'main'),
SupportedConfig(RepositorySlug('flutter', 'engine'), 'main'),
SupportedConfig(RepositorySlug('flutter', 'flutter')),
SupportedConfig(RepositorySlug('flutter', 'packages'), 'main'),
SupportedConfig(RepositorySlug('flutter', 'plugins'), 'main'),
];
Future<void> main() async {
for (final SupportedConfig config in configs) {
test('validate config file of $config', () async {
final String configContent = await githubFileContent(
config.slug,
kCiYamlPath,
httpClientProvider: () => http.Client(),
ref: config.branch,
);
final YamlMap configYaml = loadYaml(configContent) as YamlMap;
final pb.SchedulerConfig currentSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(configYaml);
try {
CiYaml(
slug: config.slug,
branch: Config.defaultBranch(config.slug),
config: currentSchedulerConfig,
);
} on FormatException catch (e) {
fail(e.message);
}
});
test(
'validate enabled branches of $config',
() async {
final String configContent = await githubFileContent(
config.slug,
kCiYamlPath,
httpClientProvider: () => http.Client(),
ref: config.branch,
);
final YamlMap configYaml = loadYaml(configContent) as YamlMap;
final pb.SchedulerConfig schedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(configYaml);
final List<String> githubBranches = getBranchesForRepository(config.slug);
final Map<String, bool> validEnabledBranches = <String, bool>{};
// Add config wide enabled branches
for (String enabledBranch in schedulerConfig.enabledBranches) {
validEnabledBranches[enabledBranch] = false;
}
// Add all target specific enabled branches
for (pb.Target target in schedulerConfig.targets) {
for (String enabledBranch in target.enabledBranches) {
validEnabledBranches[enabledBranch] = false;
}
}
// N^2 scan to verify all enabled branch patterns match an exist branch on the repo.
for (String enabledBranch in validEnabledBranches.keys) {
for (String githubBranch in githubBranches) {
if (CiYaml.enabledBranchesMatchesCurrentBranch(<String>[enabledBranch], githubBranch)) {
validEnabledBranches[enabledBranch] = true;
}
}
}
if (config.slug.name == 'engine') {
print(githubBranches);
print(validEnabledBranches);
}
// Verify the enabled branches
for (String enabledBranch in validEnabledBranches.keys) {
expect(
validEnabledBranches[enabledBranch],
isTrue,
reason: '$enabledBranch does not match to a branch in ${config.slug.fullName}',
);
}
},
skip: config.slug.name == 'flutter',
);
}
}
/// Gets all branches for [slug].
///
/// Internally, uses the git on path to get the branches from the remote for [slug].
List<String> getBranchesForRepository(RepositorySlug slug) {
const ProcessManager processManager = LocalProcessManager();
final ProcessResult result =
processManager.runSync(<String>['git', 'ls-remote', '--head', 'https://github.com/${slug.fullName}']);
final List<String> lines = (result.stdout as String).split('\n');
final List<String> githubBranches = <String>[];
for (String line in lines) {
if (line.isEmpty) {
continue;
}
// Lines follow the format of `$sha\t$ref`
final String ref = line.split('\t')[1];
final String branch = ref.replaceAll('refs/heads/', '');
githubBranches.add(branch);
}
return githubBranches;
}