Skip to content

Commit

Permalink
chore(aft): Custom GitHub actions config (#3628)
Browse files Browse the repository at this point in the history
Currently, `aft generate workflows` checks for the presence of a `workflow.yaml` file next to a `pubspec.yaml` to know if we should generate a GitHub action workflow or copy over the custom one. This leads to drift, though, since dependabot does not search these directories by default.

We could update Dependabot to search these directories, but instead we add a setting to the aft config to say that the workflow which exists is custom and should not be overridden so that the information is not copied in two places.
  • Loading branch information
dnys1 authored Aug 31, 2023
1 parent ee4cdf0 commit fca9d4d
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,9 @@ ${dependentPackages.map((dep) => ' - dependency-name: "${dep.name}"').join(
required String repoRelativePath,
required List<PackageInfo> dependentPackages,
}) async {
// Packages without a `lib/` folder generally contain only native code,
// e.g. `amplify_auth_cognito_android`. These packages are tested via their
// parent package, e.g. `amplify_flutter`, and do not require a workflow
// of their own.
// Packages without a `lib/` folder generally contain only native code.
// These packages are tested via their parent package and do not require
// a workflow of their own.
final libDir = Directory(p.join(package.path, 'lib'));
if (!libDir.existsSync()) {
return;
Expand All @@ -134,9 +133,13 @@ ${dependentPackages.map((dep) => ' - dependency-name: "${dep.name}"').join(
'${package.name}.yaml',
);
final workflowFile = File(workflowFilepath);
final customWorkflow = File(p.join(package.path, 'workflow.yaml'));
if (customWorkflow.existsSync()) {
customWorkflow.copySync(workflowFilepath);
if (package.pubspecInfo.config.aft?.github?.custom ?? false) {
if (!workflowFile.existsSync()) {
throw Exception(
'Package "${package.name}" sets `custom: true` but no workflow '
'file was found at: $workflowFilepath',
);
}
return;
}
final isDartPackage = package.flavor == PackageFlavor.dart;
Expand Down
6 changes: 6 additions & 0 deletions packages/aft/lib/src/config/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,12 @@ class PubspecInfo with AWSSerializable<Map<String, Object?>>, AWSDebuggable {
/// The package's pubspec as a YAML map.
final YamlMap pubspecMap;

/// Package-specific configuration.
@JsonKey(includeToJson: false, includeFromJson: false)
late final RawPubspecConfig config = RawPubspecConfig.fromJson(
pubspecMap.cast(),
);

/// The pubspec as a YAML editor, used to alter dependencies or other info.
@JsonKey(includeFromJson: false, includeToJson: false)
late final YamlEditor pubspecYamlEditor = YamlEditor(pubspecYaml);
Expand Down
45 changes: 22 additions & 23 deletions packages/aft/lib/src/config/config_loader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,29 +55,6 @@ class AftConfigLoader {
return reload();
}

Map<String, PackageInfo> _collectPackages({
required Directory rootDirectory,
required List<String> ignore,
}) {
final allDirs = rootDirectory
.listSync(recursive: true, followLinks: false)
.whereType<Directory>();
final allPackages = <PackageInfo>[];
for (final dir in allDirs) {
final package = PackageInfo.fromDirectory(dir);
if (package == null) {
continue;
}
if (ignore.contains(package.name)) {
continue;
}
allPackages.add(package);
}
return UnmodifiableMapView({
for (final package in allPackages..sort()) package.name: package,
});
}

AftConfig _processPubspecs({
required Queue<(YamlMap, Pubspec)> pubspecQueue,
required Directory rootDirectory,
Expand Down Expand Up @@ -180,4 +157,26 @@ class AftConfigLoader {

return aftConfig.build();
}

/// Collects all packages in [rootDirectory] by recursively searching for directories
/// with a `pubspec.yaml`.
Map<String, PackageInfo> _collectPackages({
required Directory rootDirectory,
required List<String> ignore,
}) {
final allDirs = rootDirectory
.listSync(recursive: true, followLinks: false)
.whereType<Directory>();
final allPackages = <PackageInfo>[];
for (final dir in allDirs) {
final package = PackageInfo.fromDirectory(dir);
if (package == null || ignore.contains(package.name)) {
continue;
}
allPackages.add(package);
}
return UnmodifiableMapView({
for (final package in allPackages..sort()) package.name: package,
});
}
}
45 changes: 45 additions & 0 deletions packages/aft/lib/src/config/raw_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class RawAftConfig with AWSSerializable<Map<String, Object?>>, AWSDebuggable {
this.ignore = const [],
this.components = const [],
this.scripts = const {},
this.github,
});

factory RawAftConfig.fromJson(Map<String, Object?> json) =>
Expand All @@ -89,8 +90,12 @@ class RawAftConfig with AWSSerializable<Map<String, Object?>>, AWSDebuggable {
/// {@macro aft.models.aft_component}
final List<RawAftComponent> components;

/// Package- or repo-specific [AftScript]s.
final Map<String, AftScript> scripts;

/// Package-specific GitHub configuration.
final GitHubPackageConfig? github;

/// Retrieves the component for [packageName], if any.
String componentForPackage(String packageName) {
return components
Expand Down Expand Up @@ -268,6 +273,46 @@ abstract class MacOSEnvironment
_$macOSEnvironmentSerializer;
}

abstract class GitHubPackageConfig
with AWSSerializable<Map<String, Object?>>, AWSDebuggable
implements Built<GitHubPackageConfig, GitHubPackageConfigBuilder> {
factory GitHubPackageConfig({
bool custom = false,
}) =>
_$GitHubPackageConfig._(custom: custom);

factory GitHubPackageConfig.build(
void Function(GitHubPackageConfigBuilder) updates,
) = _$GitHubPackageConfig;

factory GitHubPackageConfig.fromJson(Map<String, Object?> json) =>
aftSerializers.deserializeWith(serializer, json)!;

const GitHubPackageConfig._();

@BuiltValueHook(initializeBuilder: true)
static void _initialize(GitHubPackageConfigBuilder b) {
b.custom = false;
}

/// Whether a custom workflow is being used.
///
/// If this is `false`, one will be generated during `aft generate workflows`.
/// Otherwise, an assertion will be made that one is present for `package_name.yaml`
/// in the `.github/workflows` directory.
bool get custom;

@override
String get runtimeTypeName => 'GitHubPackageConfig';

@override
Map<String, Object?> toJson() =>
aftSerializers.serializeWith(serializer, this) as Map<String, Object?>;

static Serializer<GitHubPackageConfig> get serializer =>
_$gitHubPackageConfigSerializer;
}

/// Specifies how to propagate version changes within a component.
enum VersionPropagation {
/// Propagates only major version changes.
Expand Down
144 changes: 143 additions & 1 deletion packages/aft/lib/src/config/raw_config.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/aft/lib/src/config/serializers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ part 'serializers.g.dart';
AndroidEnvironment,
IosEnvironment,
MacOSEnvironment,
GitHubPackageConfig,
])
final Serializers aftSerializers = (_$aftSerializers.toBuilder()
..addPlugin(StandardJsonPlugin())
Expand Down
1 change: 1 addition & 0 deletions packages/aft/lib/src/config/serializers.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/aft/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,7 @@ dev_dependencies:

executables:
aft:

aft:
github:
custom: true
Loading

0 comments on commit fca9d4d

Please sign in to comment.