blob: 26539138a991fcb1433e7d02f8c2571552f315ce [file] [log] [blame]
// Copyright 2019 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.
/// @docImport '../config.dart';
/// @docImport 'dynamic_config_updater.dart';
library;
import 'dart:io' as io;
import 'package:json_annotation/json_annotation.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as p;
import 'package:yaml/yaml.dart';
import 'ci_yaml_flags.dart';
import 'content_aware_hashing_flags.dart';
import 'dynamic_config_updater.dart';
part 'dynamic_config.g.dart';
/// Flags for the service that can be updated dynamically with out a restart.
///
/// Normally, this object is from [Config.flags], and is updated in the
/// background automatically by [DynamicConfigUpdater], where the latest copy
/// of `//app_dart/config.yaml` is fetched periodically.
///
/// To get a one-time copy of the file, see also:
///
/// - [DynamicConfig.fromRemoteLatest]
/// - [DynamicConfig.fromLocalFileSystem]
@JsonSerializable(explicitToJson: true)
@immutable
final class DynamicConfig {
/// Default configuration for flags.
static const defaultInstance = DynamicConfig._(
backfillerCommitLimit: 50,
ciYaml: CiYamlFlags.defaultInstance,
contentAwareHashing: ContentAwareHashing.defaultInstance,
closeMqGuardAfterPresubmit: false,
);
/// Upper limit of commit rows to be backfilled in API call.
///
/// This limits the number of commits to be checked to backfill. When bots
/// are idle, we hope to scan as many commit rows as possible.
@JsonKey()
final int backfillerCommitLimit;
/// Flags associated with content-aware hashing.
@JsonKey()
final ContentAwareHashing contentAwareHashing;
/// Flags related to resolving `.ci.yaml`.
@JsonKey()
final CiYamlFlags ciYaml;
/// Whether to close the MQ guard right after LUCI presubmit compleated
/// instead of doing that as part of the `check_run` GitHub event handling.
@JsonKey()
final bool closeMqGuardAfterPresubmit;
const DynamicConfig._({
required this.backfillerCommitLimit,
required this.ciYaml,
required this.contentAwareHashing,
required this.closeMqGuardAfterPresubmit,
});
/// Creates [DynamicConfig] flags from a [json] object.
///
/// Any omitted fields default to the values in [defaultInstance].
factory DynamicConfig({
int? backfillerCommitLimit,
CiYamlFlags? ciYaml,
ContentAwareHashing? contentAwareHashing,
bool? closeMqGuardAfterPresubmit,
}) {
return DynamicConfig._(
backfillerCommitLimit:
backfillerCommitLimit ?? defaultInstance.backfillerCommitLimit,
ciYaml: ciYaml ?? defaultInstance.ciYaml,
contentAwareHashing:
contentAwareHashing ?? defaultInstance.contentAwareHashing,
closeMqGuardAfterPresubmit:
closeMqGuardAfterPresubmit ??
defaultInstance.closeMqGuardAfterPresubmit,
);
}
/// Creates [DynamicConfig] flags from a [json] object.
///
/// Any omitted fields default to the values in [defaultInstance].
factory DynamicConfig.fromJson(Map<String, Object?>? json) {
return _$DynamicConfigFromJson(json ?? {});
}
/// Creates [DynamicConfig] flags from a [yaml] object.
factory DynamicConfig.fromYaml(YamlMap? yaml) {
return DynamicConfig.fromJson(yaml?.asMap);
}
/// Returns the latest copy of [DynamicConfig] fetched from tip-of-tree.
///
/// Equivalent to a single call to [DynamicConfigUpdater.fetchDynamicConfig].
static Future<DynamicConfig> fromRemoteLatest() {
return DynamicConfigUpdater().fetchDynamicConfig();
}
/// Returns the latest copy of [DynamicConfig] fetched from the repository.
static Future<DynamicConfig> fromLocalFileSystem() async {
final execPath = io.Platform.resolvedExecutable;
// Walk backwards until the root of the Cocoon repository is found.
var dir = io.File(execPath).parent;
while (dir.path != dir.parent.path) {
final gitDir = io.Directory(p.join(dir.path, '.git'));
if (await gitDir.exists()) {
break;
}
dir = dir.parent;
}
final configPath = io.File(p.join(dir.path, 'app_dart', 'config.yaml'));
if (!await configPath.exists()) {
throw StateError('Could not find config.yaml at ${configPath.path}');
}
final yaml = loadYaml(await configPath.readAsString()) as YamlMap;
return DynamicConfig.fromYaml(yaml);
}
/// The inverse operation of [DynamicConfig.fromJson].
Map<String, Object?> toJson() => _$DynamicConfigToJson(this);
}
extension _YamlMapToMap on YamlMap {
Map<String, Object?> get asMap => <String, Object?>{
for (final MapEntry(:key, :value) in entries)
if (value is YamlMap)
'$key': value.asMap
else if (value is YamlList)
'$key': value.asList
else
'$key': value,
};
}
extension _YamlListToList on YamlList {
List<Object?> get asList => <Object?>[
for (final value in nodes)
if (value is YamlMap)
value.asMap
else if (value is YamlList)
value.asList
else
value,
];
}