diff --git a/packages/aft/lib/src/commands/generate/generate_workflows_command.dart b/packages/aft/lib/src/commands/generate/generate_workflows_command.dart index 1ad1484b2c..aabceda218 100644 --- a/packages/aft/lib/src/commands/generate/generate_workflows_command.dart +++ b/packages/aft/lib/src/commands/generate/generate_workflows_command.dart @@ -119,10 +119,9 @@ ${dependentPackages.map((dep) => ' - dependency-name: "${dep.name}"').join( required String repoRelativePath, required List 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; @@ -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; diff --git a/packages/aft/lib/src/config/config.dart b/packages/aft/lib/src/config/config.dart index d009b167a8..e2fb5c3d8e 100644 --- a/packages/aft/lib/src/config/config.dart +++ b/packages/aft/lib/src/config/config.dart @@ -396,6 +396,12 @@ class PubspecInfo with AWSSerializable>, 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); diff --git a/packages/aft/lib/src/config/config_loader.dart b/packages/aft/lib/src/config/config_loader.dart index 8357b8f32a..cdb5f318be 100644 --- a/packages/aft/lib/src/config/config_loader.dart +++ b/packages/aft/lib/src/config/config_loader.dart @@ -55,29 +55,6 @@ class AftConfigLoader { return reload(); } - Map _collectPackages({ - required Directory rootDirectory, - required List ignore, - }) { - final allDirs = rootDirectory - .listSync(recursive: true, followLinks: false) - .whereType(); - final allPackages = []; - 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, @@ -180,4 +157,26 @@ class AftConfigLoader { return aftConfig.build(); } + + /// Collects all packages in [rootDirectory] by recursively searching for directories + /// with a `pubspec.yaml`. + Map _collectPackages({ + required Directory rootDirectory, + required List ignore, + }) { + final allDirs = rootDirectory + .listSync(recursive: true, followLinks: false) + .whereType(); + final allPackages = []; + 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, + }); + } } diff --git a/packages/aft/lib/src/config/raw_config.dart b/packages/aft/lib/src/config/raw_config.dart index bb021ce42b..040b41d102 100644 --- a/packages/aft/lib/src/config/raw_config.dart +++ b/packages/aft/lib/src/config/raw_config.dart @@ -75,6 +75,7 @@ class RawAftConfig with AWSSerializable>, AWSDebuggable { this.ignore = const [], this.components = const [], this.scripts = const {}, + this.github, }); factory RawAftConfig.fromJson(Map json) => @@ -89,8 +90,12 @@ class RawAftConfig with AWSSerializable>, AWSDebuggable { /// {@macro aft.models.aft_component} final List components; + /// Package- or repo-specific [AftScript]s. final Map scripts; + /// Package-specific GitHub configuration. + final GitHubPackageConfig? github; + /// Retrieves the component for [packageName], if any. String componentForPackage(String packageName) { return components @@ -268,6 +273,46 @@ abstract class MacOSEnvironment _$macOSEnvironmentSerializer; } +abstract class GitHubPackageConfig + with AWSSerializable>, AWSDebuggable + implements Built { + factory GitHubPackageConfig({ + bool custom = false, + }) => + _$GitHubPackageConfig._(custom: custom); + + factory GitHubPackageConfig.build( + void Function(GitHubPackageConfigBuilder) updates, + ) = _$GitHubPackageConfig; + + factory GitHubPackageConfig.fromJson(Map 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 toJson() => + aftSerializers.serializeWith(serializer, this) as Map; + + static Serializer get serializer => + _$gitHubPackageConfigSerializer; +} + /// Specifies how to propagate version changes within a component. enum VersionPropagation { /// Propagates only major version changes. diff --git a/packages/aft/lib/src/config/raw_config.g.dart b/packages/aft/lib/src/config/raw_config.g.dart index 48dab668b5..6f68dfb9f4 100644 --- a/packages/aft/lib/src/config/raw_config.g.dart +++ b/packages/aft/lib/src/config/raw_config.g.dart @@ -15,6 +15,8 @@ Serializer _$iosEnvironmentSerializer = new _$IosEnvironmentSerializer(); Serializer _$macOSEnvironmentSerializer = new _$MacOSEnvironmentSerializer(); +Serializer _$gitHubPackageConfigSerializer = + new _$GitHubPackageConfigSerializer(); class _$EnvironmentSerializer implements StructuredSerializer { @override @@ -255,6 +257,51 @@ class _$MacOSEnvironmentSerializer } } +class _$GitHubPackageConfigSerializer + implements StructuredSerializer { + @override + final Iterable types = const [ + GitHubPackageConfig, + _$GitHubPackageConfig + ]; + @override + final String wireName = 'GitHubPackageConfig'; + + @override + Iterable serialize( + Serializers serializers, GitHubPackageConfig object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'custom', + serializers.serialize(object.custom, specifiedType: const FullType(bool)), + ]; + + return result; + } + + @override + GitHubPackageConfig deserialize( + Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = new GitHubPackageConfigBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'custom': + result.custom = serializers.deserialize(value, + specifiedType: const FullType(bool))! as bool; + break; + } + } + + return result.build(); + } +} + class _$Environment extends Environment { @override final VersionConstraint sdk; @@ -711,6 +758,88 @@ class MacOSEnvironmentBuilder } } +class _$GitHubPackageConfig extends GitHubPackageConfig { + @override + final bool custom; + + factory _$GitHubPackageConfig( + [void Function(GitHubPackageConfigBuilder)? updates]) => + (new GitHubPackageConfigBuilder()..update(updates))._build(); + + _$GitHubPackageConfig._({required this.custom}) : super._() { + BuiltValueNullFieldError.checkNotNull( + custom, r'GitHubPackageConfig', 'custom'); + } + + @override + GitHubPackageConfig rebuild( + void Function(GitHubPackageConfigBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + GitHubPackageConfigBuilder toBuilder() => + new GitHubPackageConfigBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is GitHubPackageConfig && custom == other.custom; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, custom.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } +} + +class GitHubPackageConfigBuilder + implements Builder { + _$GitHubPackageConfig? _$v; + + bool? _custom; + bool? get custom => _$this._custom; + set custom(bool? custom) => _$this._custom = custom; + + GitHubPackageConfigBuilder() { + GitHubPackageConfig._initialize(this); + } + + GitHubPackageConfigBuilder get _$this { + final $v = _$v; + if ($v != null) { + _custom = $v.custom; + _$v = null; + } + return this; + } + + @override + void replace(GitHubPackageConfig other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$GitHubPackageConfig; + } + + @override + void update(void Function(GitHubPackageConfigBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + GitHubPackageConfig build() => _build(); + + _$GitHubPackageConfig _build() { + final _$result = _$v ?? + new _$GitHubPackageConfig._( + custom: BuiltValueNullFieldError.checkNotNull( + custom, r'GitHubPackageConfig', 'custom')); + replace(_$result); + return _$result; + } +} + // ignore_for_file: deprecated_member_use_from_same_package,type=lint // ************************************************************************** @@ -742,7 +871,13 @@ RawAftConfig _$RawAftConfigFromJson(Map json) => $checkedCreate( ($checkedConvert) { $checkKeys( json, - allowedKeys: const ['platforms', 'ignore', 'components', 'scripts'], + allowedKeys: const [ + 'platforms', + 'ignore', + 'components', + 'scripts', + 'github' + ], ); final val = RawAftConfig( platforms: $checkedConvert( @@ -774,6 +909,12 @@ RawAftConfig _$RawAftConfigFromJson(Map json) => $checkedCreate( Map.from(e as Map))), ) ?? const {}), + github: $checkedConvert( + 'github', + (v) => v == null + ? null + : GitHubPackageConfig.fromJson( + Map.from(v as Map))), ); return val; }, @@ -785,6 +926,7 @@ Map _$RawAftConfigToJson(RawAftConfig instance) => 'ignore': instance.ignore, 'components': instance.components.map((e) => e.toJson()).toList(), 'scripts': instance.scripts.map((k, e) => MapEntry(k, e.toJson())), + 'github': instance.github?.toJson(), }; RawAftComponent _$RawAftComponentFromJson(Map json) => $checkedCreate( diff --git a/packages/aft/lib/src/config/serializers.dart b/packages/aft/lib/src/config/serializers.dart index 054956830e..a5e4748740 100644 --- a/packages/aft/lib/src/config/serializers.dart +++ b/packages/aft/lib/src/config/serializers.dart @@ -17,6 +17,7 @@ part 'serializers.g.dart'; AndroidEnvironment, IosEnvironment, MacOSEnvironment, + GitHubPackageConfig, ]) final Serializers aftSerializers = (_$aftSerializers.toBuilder() ..addPlugin(StandardJsonPlugin()) diff --git a/packages/aft/lib/src/config/serializers.g.dart b/packages/aft/lib/src/config/serializers.g.dart index 452e457beb..f71d24f927 100644 --- a/packages/aft/lib/src/config/serializers.g.dart +++ b/packages/aft/lib/src/config/serializers.g.dart @@ -10,6 +10,7 @@ Serializers _$aftSerializers = (new Serializers().toBuilder() ..add(AftConfig.serializer) ..add(AndroidEnvironment.serializer) ..add(Environment.serializer) + ..add(GitHubPackageConfig.serializer) ..add(IosEnvironment.serializer) ..add(MacOSEnvironment.serializer) ..add(PlatformEnvironment.serializer) diff --git a/packages/aft/pubspec.yaml b/packages/aft/pubspec.yaml index 8b1f27046b..f5aa941441 100644 --- a/packages/aft/pubspec.yaml +++ b/packages/aft/pubspec.yaml @@ -73,3 +73,7 @@ dev_dependencies: executables: aft: + +aft: + github: + custom: true diff --git a/packages/aft/workflow.yaml b/packages/aft/workflow.yaml deleted file mode 100644 index 0aefd79d5e..0000000000 --- a/packages/aft/workflow.yaml +++ /dev/null @@ -1,53 +0,0 @@ -name: aft -on: - push: - branches: - - main - - stable - - next - paths: - - "packages/aft/**/*.dart" - pull_request: - paths: - - "packages/aft/**/*.dart" - schedule: - - cron: "0 0 * * 0" # Every Sunday at 00:00 -defaults: - run: - shell: bash -permissions: read-all - -jobs: - test: - name: Test - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # 3.6.0 - with: - submodules: true - - # Needed for `git` but only ever used locally. - - name: Git Config - run: | - git config --global user.email "amplify-flutter@amazon.com" - git config --global user.name "Amplify Flutter" - - - name: Setup Dart - uses: dart-lang/setup-dart@e58aeb62aef51dcc4d0ba8eada7c08092aad5314 # main - with: - sdk: stable - - - name: Get Packages - working-directory: packages/aft - run: dart pub upgrade - - - name: Save Repo State - working-directory: packages/aft - run: | - dart pub get - dart bin/aft.dart save-repo-state - - - name: Run Tests - working-directory: packages/aft - run: dart test diff --git a/packages/aws_sdk/smoke_test/pubspec.yaml b/packages/aws_sdk/smoke_test/pubspec.yaml index 0dbc44c8aa..4f6f4d4132 100644 --- a/packages/aws_sdk/smoke_test/pubspec.yaml +++ b/packages/aws_sdk/smoke_test/pubspec.yaml @@ -22,3 +22,7 @@ dev_dependencies: built_value_generator: 8.6.1 lints: ^2.0.0 test: ^1.22.1 + +aft: + github: + custom: true diff --git a/packages/aws_sdk/smoke_test/workflow.yaml b/packages/aws_sdk/smoke_test/workflow.yaml deleted file mode 100644 index ed1c150963..0000000000 --- a/packages/aws_sdk/smoke_test/workflow.yaml +++ /dev/null @@ -1,65 +0,0 @@ -name: AWS SDK Smoke Tests -on: - push: - branches: - - main - - stable - - next - paths: - - "packages/aws_sdk/**/*.dart" - - "packages/aws_sdk/smoke_test/workflow.yaml" - - "packages/smithy/**/*.dart" - pull_request: - paths: - - "packages/aws_sdk/**/*.dart" - - "packages/aws_sdk/smoke_test/workflow.yaml" - - "packages/smithy/**/*.dart" - schedule: - - cron: "0 0 * * 0" # Every Sunday at 00:00 -defaults: - run: - shell: bash -permissions: read-all - -jobs: - smoke_test: - name: Smoke Test - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # 3.6.0 - - - name: Git Submodules - run: git submodule update --init - - - name: Setup Dart - uses: dart-lang/setup-dart@e58aeb62aef51dcc4d0ba8eada7c08092aad5314 # main - with: - sdk: stable - - - name: Link Packages - run: | - dart pub global activate -spath packages/aft - aft link - - - name: Get Packages - run: dart pub upgrade - working-directory: packages/aws_sdk/smoke_test - - - name: Install Localstack - run: | - # Workaround for dependency issue: - # https://github.com/localstack/localstack/pull/6831 - pip install pyOpenSSL --upgrade - pip install cryptography --upgrade - pip install localstack - - docker pull localstack/localstack:latest - localstack start --detached - echo "Waiting for localstack startup..." - localstack wait -t 30 - echo "Startup complete" - - - name: Run Tests - run: dart test - working-directory: packages/aws_sdk/smoke_test