From b775e2b9ab867f5d3f5b89f04da4f75465b5c251 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Wed, 16 Nov 2022 12:25:21 -0700 Subject: [PATCH 01/40] chore(aft): Make base commands sync There isn't any value with these being async since it's okay to block in this context. And sync makes everything easier to work with. commit-id:6743d9d4 --- .../aft/lib/src/commands/amplify_command.dart | 112 ++++++++---------- .../lib/src/commands/bootstrap_command.dart | 1 - .../aft/lib/src/commands/clean_command.dart | 3 +- .../aft/lib/src/commands/deps_command.dart | 10 +- .../aft/lib/src/commands/link_command.dart | 1 - .../src/commands/list_packages_command.dart | 2 +- .../aft/lib/src/commands/pub_command.dart | 1 - .../aft/lib/src/commands/publish_command.dart | 2 - packages/aft/test/amplify_command_test.dart | 6 +- 9 files changed, 61 insertions(+), 77 deletions(-) diff --git a/packages/aft/lib/src/commands/amplify_command.dart b/packages/aft/lib/src/commands/amplify_command.dart index 09145b65a4..93aa67caba 100644 --- a/packages/aft/lib/src/commands/amplify_command.dart +++ b/packages/aft/lib/src/commands/amplify_command.dart @@ -16,7 +16,6 @@ import 'dart:io'; import 'package:aft/aft.dart'; import 'package:args/command_runner.dart'; -import 'package:async/async.dart'; import 'package:aws_common/aws_common.dart'; import 'package:checked_yaml/checked_yaml.dart'; import 'package:cli_util/cli_logging.dart'; @@ -25,7 +24,6 @@ import 'package:http/http.dart' as http; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:pub/pub.dart'; -import 'package:stream_transform/stream_transform.dart'; /// Base class for all commands in this package providing common functionality. abstract class AmplifyCommand extends Command implements Closeable { @@ -49,77 +47,69 @@ abstract class AmplifyCommand extends Command implements Closeable { /// HTTP client for remote operations. http.Client get httpClient => _httpClient ??= _PubHttpClient(); - final _rootDirMemo = AsyncMemoizer(); - /// The root directory of the Amplify Flutter repo. - Future get rootDir => _rootDirMemo.runOnce(() async { - var dir = workingDirectory; - while (dir.parent != dir) { - final files = dir.list(followLinks: false).whereType(); - await for (final file in files) { - if (p.basename(file.path) == 'mono_repo.yaml') { - return dir; - } - } - dir = dir.parent; - } - throw StateError( - 'Root directory not found. Make sure to run this command ' - 'from within the Amplify Flutter repo', - ); - }); - - final _allPackagesMemo = AsyncMemoizer>(); - - /// All packages in the Amplify Flutter repo. - Future> get allPackages => - _allPackagesMemo.runOnce(() async { - final allDirs = (await rootDir) - .list(recursive: true, followLinks: false) - .whereType(); - final aftConfig = await this.aftConfig; - - final allPackages = []; - await for (final dir in allDirs) { - final pubspecInfo = dir.pubspec; - if (pubspecInfo == null) { - continue; - } - final pubspec = pubspecInfo.pubspec; - if (aftConfig.ignore.contains(pubspec.name)) { - continue; - } - allPackages.add( - PackageInfo( - name: pubspec.name, - path: dir.path, - usesMonoRepo: dir.usesMonoRepo, - pubspecInfo: pubspecInfo, - flavor: pubspec.flavor, - ), - ); + late final Directory rootDir = () { + var dir = workingDirectory; + while (dir.parent != dir) { + final files = dir.listSync(followLinks: false).whereType(); + for (final file in files) { + if (p.basename(file.path) == 'mono_repo.yaml') { + return dir; } - return UnmodifiableMapView({ - for (final package in allPackages..sort()) package.name: package, - }); - }); + } + dir = dir.parent; + } + throw StateError( + 'Root directory not found. Make sure to run this command ' + 'from within the Amplify Flutter repo', + ); + }(); + + late final Map allPackages = () { + final allDirs = rootDir + .listSync(recursive: true, followLinks: false) + .whereType(); + final allPackages = []; + for (final dir in allDirs) { + final pubspecInfo = dir.pubspec; + if (pubspecInfo == null) { + continue; + } + final pubspec = pubspecInfo.pubspec; + if (aftConfig.ignore.contains(pubspec.name)) { + continue; + } + allPackages.add( + PackageInfo( + name: pubspec.name, + path: dir.path, + usesMonoRepo: dir.usesMonoRepo, + pubspecInfo: pubspecInfo, + flavor: pubspec.flavor, + ), + ); + } + return UnmodifiableMapView({ + for (final package in allPackages..sort()) package.name: package, + }); + }(); /// The absolute path to the `aft.yaml` document. - Future get aftConfigPath async { - final rootDir = await this.rootDir; + late final String aftConfigPath = () { + final rootDir = this.rootDir; return p.join(rootDir.path, 'aft.yaml'); - } + }(); /// The `aft.yaml` document. - Future get aftConfigYaml async { - final configFile = File(await aftConfigPath); + String get aftConfigYaml { + final configFile = File(aftConfigPath); assert(configFile.existsSync(), 'Could not find aft.yaml'); return configFile.readAsStringSync(); } /// The global `aft` configuration for the repo. - Future get aftConfig async { - final configYaml = await aftConfigYaml; + AftConfig get aftConfig { + final configYaml = aftConfigYaml; return checkedYamlDecode(configYaml, AftConfig.fromJson); } diff --git a/packages/aft/lib/src/commands/bootstrap_command.dart b/packages/aft/lib/src/commands/bootstrap_command.dart index af5da64592..89382e8060 100644 --- a/packages/aft/lib/src/commands/bootstrap_command.dart +++ b/packages/aft/lib/src/commands/bootstrap_command.dart @@ -78,7 +78,6 @@ const amplifyEnvironments = {}; @override Future run() async { - final allPackages = await this.allPackages; await linkPackages(allPackages); await pubAction( action: upgrade ? PubAction.upgrade : PubAction.get, diff --git a/packages/aft/lib/src/commands/clean_command.dart b/packages/aft/lib/src/commands/clean_command.dart index 6182cfad60..5b10500d3a 100644 --- a/packages/aft/lib/src/commands/clean_command.dart +++ b/packages/aft/lib/src/commands/clean_command.dart @@ -56,8 +56,7 @@ class CleanCommand extends AmplifyCommand { @override Future run() async { await Future.wait([ - for (final package in (await allPackages).values) - _cleanBuildFolder(package) + for (final package in allPackages.values) _cleanBuildFolder(package), ]); stdout.writeln('Project successfully cleaned'); diff --git a/packages/aft/lib/src/commands/deps_command.dart b/packages/aft/lib/src/commands/deps_command.dart index 497475fc31..490c5f2029 100644 --- a/packages/aft/lib/src/commands/deps_command.dart +++ b/packages/aft/lib/src/commands/deps_command.dart @@ -117,8 +117,8 @@ class _DepsSubcommand extends AmplifyCommand { } Future _run(_DepsAction action) async { - final globalDependencyConfig = (await aftConfig).dependencies; - for (final package in (await allPackages).values) { + final globalDependencyConfig = aftConfig.dependencies; + for (final package in allPackages.values) { for (final globalDep in globalDependencyConfig.entries) { _checkDependency( package, @@ -166,9 +166,9 @@ class _DepsUpdateCommand extends _DepsSubcommand { @override Future run() async { - final globalDependencyConfig = (await aftConfig).dependencies; + final globalDependencyConfig = aftConfig.dependencies; - final aftEditor = YamlEditor(await aftConfigYaml); + final aftEditor = YamlEditor(aftConfigYaml); final failedUpdates = []; for (final entry in globalDependencyConfig.entries) { final package = entry.key; @@ -245,7 +245,7 @@ class _DepsUpdateCommand extends _DepsSubcommand { } if (aftEditor.edits.isNotEmpty) { - File(await aftConfigPath).writeAsStringSync( + File(aftConfigPath).writeAsStringSync( aftEditor.toString(), flush: true, ); diff --git a/packages/aft/lib/src/commands/link_command.dart b/packages/aft/lib/src/commands/link_command.dart index 4491264949..80a66ca671 100644 --- a/packages/aft/lib/src/commands/link_command.dart +++ b/packages/aft/lib/src/commands/link_command.dart @@ -32,7 +32,6 @@ class LinkCommand extends AmplifyCommand { @override Future run() async { - final allPackages = await this.allPackages; await linkPackages(allPackages); stdout.writeln('Packages successfully linked!'); } diff --git a/packages/aft/lib/src/commands/list_packages_command.dart b/packages/aft/lib/src/commands/list_packages_command.dart index 798969cf5f..1451326d14 100644 --- a/packages/aft/lib/src/commands/list_packages_command.dart +++ b/packages/aft/lib/src/commands/list_packages_command.dart @@ -24,7 +24,7 @@ class ListPackagesCommand extends AmplifyCommand { @override Future run() async { - for (final package in (await allPackages).keys) { + for (final package in allPackages.keys) { logger.stdout(package); } } diff --git a/packages/aft/lib/src/commands/pub_command.dart b/packages/aft/lib/src/commands/pub_command.dart index 168c330ad7..8ec48ce251 100644 --- a/packages/aft/lib/src/commands/pub_command.dart +++ b/packages/aft/lib/src/commands/pub_command.dart @@ -75,7 +75,6 @@ class PubSubcommand extends AmplifyCommand { @override Future run() async { - final allPackages = await this.allPackages; await pubAction( action: action, allPackages: allPackages, diff --git a/packages/aft/lib/src/commands/publish_command.dart b/packages/aft/lib/src/commands/publish_command.dart index a446f814c9..4e89f04c33 100644 --- a/packages/aft/lib/src/commands/publish_command.dart +++ b/packages/aft/lib/src/commands/publish_command.dart @@ -171,8 +171,6 @@ class PublishCommand extends AmplifyCommand { @override Future run() async { - final allPackages = await this.allPackages; - // Gather packages which can be published. final publishablePackages = (await Future.wait([ for (final package in allPackages.values) _checkPublishable(package), diff --git a/packages/aft/test/amplify_command_test.dart b/packages/aft/test/amplify_command_test.dart index eaa5a0fdf5..66dcf63218 100644 --- a/packages/aft/test/amplify_command_test.dart +++ b/packages/aft/test/amplify_command_test.dart @@ -28,11 +28,11 @@ void main() { final command = MockCommand(); test('rootDir', () { - expect(command.rootDir, completes); + expect(() => command.rootDir, returnsNormally); }); test('allPackages', () async { - final allPackages = await command.allPackages; + final allPackages = command.allPackages; expect( allPackages, contains('amplify_flutter'), @@ -40,7 +40,7 @@ void main() { }); test('globalDependencyConfig', () async { - final config = await command.aftConfig; + final config = command.aftConfig; expect(config.dependencies, contains('uuid')); }); }); From de1ab7b01bedd114dca923d3b192fb9f5b3b7009 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Fri, 26 Aug 2022 13:23:37 -0700 Subject: [PATCH 02/40] feat(aft): Changelog/version commands commit-id:0c17379d --- aft.yaml | 37 +- packages/aft/bin/aft.dart | 4 +- packages/aft/lib/aft.dart | 2 + packages/aft/lib/src/changelog/changelog.dart | 165 +++++++++ .../aft/lib/src/changelog/changelog.g.dart | 109 ++++++ .../aft/lib/src/changelog/commit_message.dart | 255 ++++++++++++++ packages/aft/lib/src/changelog/parser.dart | 88 +++++ packages/aft/lib/src/changelog/printer.dart | 60 ++++ .../aft/lib/src/commands/amplify_command.dart | 100 +++--- .../lib/src/commands/changelog_command.dart | 91 +++++ .../aft/lib/src/commands/clean_command.dart | 4 +- .../aft/lib/src/commands/deps_command.dart | 4 +- .../src/commands/generate_sdk_command.dart | 18 +- .../src/commands/list_packages_command.dart | 2 +- .../aft/lib/src/commands/pub_command.dart | 19 +- .../aft/lib/src/commands/publish_command.dart | 82 ++--- .../aft/lib/src/commands/version_command.dart | 227 ++++++++++++ packages/aft/lib/src/models.dart | 133 +++++++- packages/aft/lib/src/models.g.dart | 31 +- .../aft/lib/src/options/git_ref_options.dart | 56 +++ .../aft/lib/src/options/glob_options.dart | 57 ++++ packages/aft/lib/src/pub/pub_runner.dart | 7 +- packages/aft/lib/src/repo.dart | 202 +++++++++++ packages/aft/pubspec.yaml | 9 + .../parser_test.dart} | 44 +-- packages/aft/test/e2e_test.dart | 323 ++++++++++++++++++ packages/aft/test/model_test.dart | 67 ++++ 27 files changed, 2038 insertions(+), 158 deletions(-) create mode 100644 packages/aft/lib/src/changelog/changelog.dart create mode 100644 packages/aft/lib/src/changelog/changelog.g.dart create mode 100644 packages/aft/lib/src/changelog/commit_message.dart create mode 100644 packages/aft/lib/src/changelog/parser.dart create mode 100644 packages/aft/lib/src/changelog/printer.dart create mode 100644 packages/aft/lib/src/commands/changelog_command.dart create mode 100644 packages/aft/lib/src/commands/version_command.dart create mode 100644 packages/aft/lib/src/options/git_ref_options.dart create mode 100644 packages/aft/lib/src/options/glob_options.dart create mode 100644 packages/aft/lib/src/repo.dart rename packages/aft/test/{amplify_command_test.dart => changelog/parser_test.dart} (50%) create mode 100644 packages/aft/test/e2e_test.dart create mode 100644 packages/aft/test/model_test.dart diff --git a/aft.yaml b/aft.yaml index 4346e7aeed..fcbd96d0cc 100644 --- a/aft.yaml +++ b/aft.yaml @@ -14,6 +14,41 @@ dependencies: json_serializable: 6.5.4 uuid: ">=3.0.6 <=3.0.7" -# Packages to ignore in all repo operations +# Packages to ignore in all repo operations. ignore: - synthetic_package + +# Branch names which map to pub.dev stable and prerelease tracks. +branches: + stable: stable + prerelease: next + +# Strongly connected components which should have minor/major version bumps happen +# in unison, i.e. a version bump to one package cascades to all. +components: + amplify: + - amplify_flutter + - amplify_flutter_ios + - amplify_flutter_android + - amplify_core + - amplify_datastore + - amplify_datastore_plugin_interface + - amplify_analytics_pinpoint + - amplify_analytics_pinpoint_android + - amplify_analytics_pinpoint_ios + - amplify_api + - amplify_api_android + - amplify_api_ios + - amplify_auth_cognito + - amplify_auth_cognito_android + - amplify_auth_cognito_ios + - amplify_storage_s3 + - amplify_storage_s3_android + - amplify_storage_s3_ios + smithy: + - smithy + - smithy_aws + - smithy_codegen + worker_bee: + - worker_bee + - worker_bee_builder diff --git a/packages/aft/bin/aft.dart b/packages/aft/bin/aft.dart index e6764c77ec..f8774241c7 100644 --- a/packages/aft/bin/aft.dart +++ b/packages/aft/bin/aft.dart @@ -36,7 +36,9 @@ Future main(List args) async { ..addCommand(LinkCommand()) ..addCommand(CleanCommand()) ..addCommand(PubCommand()) - ..addCommand(BootstrapCommand()); + ..addCommand(BootstrapCommand()) + ..addCommand(ChangelogCommand()) + ..addCommand(VersionCommand()); try { await runner.run(args); } on UsageException catch (e) { diff --git a/packages/aft/lib/aft.dart b/packages/aft/lib/aft.dart index 26ece76e85..8dc889a1f2 100644 --- a/packages/aft/lib/aft.dart +++ b/packages/aft/lib/aft.dart @@ -16,6 +16,7 @@ library aft; export 'src/commands/amplify_command.dart'; export 'src/commands/bootstrap_command.dart'; +export 'src/commands/changelog_command.dart'; export 'src/commands/clean_command.dart'; export 'src/commands/deps_command.dart'; export 'src/commands/generate_sdk_command.dart'; @@ -23,5 +24,6 @@ export 'src/commands/link_command.dart'; export 'src/commands/list_packages_command.dart'; export 'src/commands/pub_command.dart'; export 'src/commands/publish_command.dart'; +export 'src/commands/version_command.dart'; export 'src/models.dart'; export 'src/pub/pub_runner.dart'; diff --git a/packages/aft/lib/src/changelog/changelog.dart b/packages/aft/lib/src/changelog/changelog.dart new file mode 100644 index 0000000000..de151407f8 --- /dev/null +++ b/packages/aft/lib/src/changelog/changelog.dart @@ -0,0 +1,165 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; + +import 'package:aft/src/changelog/commit_message.dart'; +import 'package:aft/src/changelog/printer.dart'; +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:cli_util/cli_logging.dart'; +import 'package:collection/collection.dart'; +import 'package:markdown/markdown.dart'; +import 'package:pub_semver/pub_semver.dart'; + +part 'parser.dart'; +part 'changelog.g.dart'; + +/// Marker version for the `NEXT` (unreleased) version. +final Version nextVersion = Version(0, 0, 0, pre: nextVersionTag); + +/// Tag for the `NEXT` version. +const String nextVersionTag = 'NEXT'; + +/// {@template aft.changelog.changelog} +/// A Dart representation of a `CHANGELOG.md` file. +/// {@endtemplate} +abstract class Changelog implements Built { + /// {@macro aft.changelog.changelog} + factory Changelog([void Function(ChangelogBuilder) updates]) = _$Changelog; + Changelog._(); + + /// Parses [changelogMd] for a list of the versions. + /// + /// Throws a [ChangelogParseException] if there are issues processing the + /// changelog. + factory Changelog.parse( + String changelogMd, { + Logger? logger, + }) { + final parser = Document(); + final lines = LineSplitter.split(changelogMd).toList(); + final ast = parser.parseLines(lines); + final visitor = _ChangelogParser(logger); + for (final node in ast) { + node.accept(visitor); + } + return (visitor.builder..originalText = changelogMd).build(); + } + + /// The original CHANGELOG.md text. + String get originalText; + + /// A map of semantic versions to their nodes. + BuiltListMultimap get versions; + + /// The latest version in the changelog, or `null` if the changelog is empty. + Version? get latestVersion => maxBy(versions.keys, (v) => v); + + /// Whether there's a `NEXT` entry in the changelog. + bool get hasNextEntry => versions.keys.any((v) => v == nextVersion); + + /// Creates a version entry which can be rendered as markdown for the given + /// list of [commits]. + /// + /// If [version] is not specified, it defaults to [nextVersion]. If [version] + /// already exists in the changelog, it is updated with the new list of + /// commits. + List makeVersionEntry({ + required Iterable commits, + Version? version, + }) { + version ??= nextVersion; + commits = commits.where((commit) => commit.includeInChangelog); + final commitsByType = + commits.groupListsBy((element) => element.group); + + final versionText = + version == nextVersion ? nextVersionTag : version.toString(); + final header = Element.text('h2', versionText); + final nodes = [header]; + + if (commits.isEmpty) { + // If there are no commits worth including, add a generic message about + // bug fixes/improvements. + nodes.add(Element.text('li', 'Minor bug fixes and improvements')); + } else { + for (final typedCommits in commitsByType.entries) { + nodes.add(Element.text('h3', typedCommits.key.header)); + final list = Element('ul', [ + for (final commit + in typedCommits.value.sortedBy((commit) => commit.summary)) + Element.text('li', commit.summary), + ]); + nodes.add(list); + } + } + + return nodes; + } + + /// Updates the changelog with relevant entries from [commits]. + /// + /// If [version] is not specified, the default `NEXT` tag is used. + ChangelogUpdate update({ + required Iterable commits, + Version? version, + }) { + if (commits.isEmpty) { + return ChangelogUpdate(originalText, commits: commits); + } + final nodes = makeVersionEntry( + commits: commits, + version: version, + ); + // Replace the text in changelogMd so that the latest version matches + // `version`, if given, else `NEXT`. + final String keepText; + if (hasNextEntry || (version != null && latestVersion == version)) { + // Update latest entry, either to `version` or as a new `NEXT` entry. + keepText = LineSplitter.split(originalText) + // Skip latest version entry + .skip(1) + // Find previous version header + .skipWhile((line) => !line.startsWith('## ')) + .join('\n'); + } else { + // No `NEXT` or `version` entry exists yet. + keepText = originalText; + } + return ChangelogUpdate(keepText, commits: commits, newText: render(nodes)); + } + + @override + String toString() { + return render(versions.values); + } +} + +class ChangelogUpdate { + const ChangelogUpdate( + this.keepText, { + required this.commits, + this.newText, + }); + + final String keepText; + final Iterable commits; + final String? newText; + + bool get hasUpdate => newText != null; + + @override + String toString() => newText == null ? keepText : '$newText\n\n$keepText'; +} diff --git a/packages/aft/lib/src/changelog/changelog.g.dart b/packages/aft/lib/src/changelog/changelog.g.dart new file mode 100644 index 0000000000..b3e67817c5 --- /dev/null +++ b/packages/aft/lib/src/changelog/changelog.g.dart @@ -0,0 +1,109 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'changelog.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +class _$Changelog extends Changelog { + @override + final String originalText; + @override + final BuiltListMultimap versions; + + factory _$Changelog([void Function(ChangelogBuilder)? updates]) => + (new ChangelogBuilder()..update(updates))._build(); + + _$Changelog._({required this.originalText, required this.versions}) + : super._() { + BuiltValueNullFieldError.checkNotNull( + originalText, r'Changelog', 'originalText'); + BuiltValueNullFieldError.checkNotNull(versions, r'Changelog', 'versions'); + } + + @override + Changelog rebuild(void Function(ChangelogBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + ChangelogBuilder toBuilder() => new ChangelogBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is Changelog && + originalText == other.originalText && + versions == other.versions; + } + + @override + int get hashCode { + return $jf($jc($jc(0, originalText.hashCode), versions.hashCode)); + } +} + +class ChangelogBuilder implements Builder { + _$Changelog? _$v; + + String? _originalText; + String? get originalText => _$this._originalText; + set originalText(String? originalText) => _$this._originalText = originalText; + + ListMultimapBuilder? _versions; + ListMultimapBuilder get versions => + _$this._versions ??= new ListMultimapBuilder(); + set versions(ListMultimapBuilder? versions) => + _$this._versions = versions; + + ChangelogBuilder(); + + ChangelogBuilder get _$this { + final $v = _$v; + if ($v != null) { + _originalText = $v.originalText; + _versions = $v.versions.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(Changelog other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$Changelog; + } + + @override + void update(void Function(ChangelogBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + Changelog build() => _build(); + + _$Changelog _build() { + _$Changelog _$result; + try { + _$result = _$v ?? + new _$Changelog._( + originalText: BuiltValueNullFieldError.checkNotNull( + originalText, r'Changelog', 'originalText'), + versions: versions.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'versions'; + versions.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'Changelog', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,deprecated_member_use_from_same_package,lines_longer_than_80_chars,no_leading_underscores_for_local_identifiers,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new,unnecessary_lambdas diff --git a/packages/aft/lib/src/changelog/commit_message.dart b/packages/aft/lib/src/changelog/commit_message.dart new file mode 100644 index 0000000000..e9bb4b1afe --- /dev/null +++ b/packages/aft/lib/src/changelog/commit_message.dart @@ -0,0 +1,255 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:aws_common/aws_common.dart'; +import 'package:collection/collection.dart'; + +final RegExp _commitRegex = RegExp( + r'(?build|chore|ci|docs|feat|fix|bug|perf|refactor|revert|style|test)?' + r'(?\([a-zA-Z0-9_,\s\*]+\)?((?=:\s?)|(?=!:\s?)))?' + r'(?!)?' + r'(?:\s?.*)?|^(?Merge .+)', +); + +enum CommitTypeGroup { + breaking('Breaking Changes'), + fixes('Fixes'), + features('Features'), + other('Other Changes'); + + final String header; + + const CommitTypeGroup(this.header); +} + +enum CommitType { + unconventional.other(), + merge.other(), + build.other(), + chore.other(), + ci.other(), + docs.other(), + feat.features(), + fix.fixes(), + bug.fixes(), + perf.fixes(), + refactor.other(), + revert.other(), + style.other(), + test.other(); + + final CommitTypeGroup group; + + const CommitType.fixes() : group = CommitTypeGroup.fixes; + const CommitType.features() : group = CommitTypeGroup.features; + const CommitType.other() : group = CommitTypeGroup.other; +} + +/// {@template aft.changelog.commit_message} +/// A parsed git commit message. +/// {@endtemplate} +abstract class CommitMessage with AWSEquatable { + /// {@macro aft.changelog.commit_message} + const CommitMessage( + this.sha, + this.summary, { + required this.dateTime, + }); + + /// Parses a commit message [summary]. + factory CommitMessage.parse( + String sha, + String summary, { + required DateTime dateTime, + }) { + final commitMessage = _commitRegex.firstMatch(summary); + if (commitMessage == null) { + throw ArgumentError.value( + summary, + 'summary', + 'Not a valid commit message', + ); + } + + final mergeCommit = commitMessage.namedGroup('merge'); + if (mergeCommit != null) { + return MergeCommitMessage(sha, mergeCommit, dateTime: dateTime); + } + + final typeStr = commitMessage.namedGroup('type'); + if (typeStr == null) { + return UnconventionalCommitMessage(sha, summary, dateTime: dateTime); + } + + final type = CommitType.values.byName(typeStr); + final isBreakingChange = commitMessage.namedGroup('breaking') != null; + final scopes = commitMessage + .namedGroup('scope') + ?.split(',') + .map((scope) => scope.trim()) + .toList() ?? + const []; + final description = commitMessage + .namedGroup('description')! + .replaceAll(RegExp(r'^:\s'), '') + .trim(); + + return ConventionalCommitMessage( + sha, + summary, + description: description, + type: type, + isBreakingChange: isBreakingChange, + scopes: scopes, + dateTime: dateTime, + ); + } + + /// The commit's OID SHA. + final String sha; + + /// The full, unmodified, commit summary. + final String summary; + + /// The date/time the commit was made. + final DateTime dateTime; + + /// The parsed commit description. + String get description => summary; + + /// The type of commit message. + CommitType get type; + + /// The group for the commit type. + CommitTypeGroup get group { + if (isBreakingChange) { + return CommitTypeGroup.breaking; + } + return type.group; + } + + /// Whether this commit message is for a version bump. + bool get isVersionBump => false; + + /// Whether a commit of this type should be included in a CHANGELOG by + /// default. + bool get includeInChangelog => false; + + /// Whether this is a breaking change, denoted by a `!` after the scope, e.g. + /// `fix(auth)!`. + bool get isBreakingChange => false; + + /// The PR tagged in this commit, e.g. `(#2012)`. + int? get taggedPr { + final match = RegExp(r'#(\d+)').firstMatch(summary)?.group(1); + if (match == null) { + return null; + } + return int.parse(match); + } + + @override + List get props => [sha]; + + @override + String toString() => summary; +} + +/// {@template aft.changelog.merge_commit_message} +/// A commit message representing a merge commit. +/// {@endtemplate} +class MergeCommitMessage extends CommitMessage { + /// {@macro aft.changelog.merge_commit_message} + const MergeCommitMessage( + super.sha, + super.summary, { + required super.dateTime, + }); + + @override + CommitType get type => CommitType.merge; +} + +/// {@template aft.changelog.conventional_commit_message} +/// A commit message representing a [conventional commit](https://www.conventionalcommits.org/). +/// {@endtemplate} +class ConventionalCommitMessage extends CommitMessage { + /// {@macro aft.changelog.conventional_commit_message} + const ConventionalCommitMessage( + super.sha, + super.summary, { + required this.description, + required this.type, + required this.isBreakingChange, + required this.scopes, + required super.dateTime, + }); + + @override + final String description; + + @override + final CommitType type; + + @override + final bool isBreakingChange; + + /// The list of scopes, or tags, which this commit covers. + final List scopes; + + @override + bool get isVersionBump => + type == CommitType.chore && scopes.singleOrNull == 'version'; + + @override + bool get includeInChangelog { + if (isBreakingChange) { + return true; + } + switch (type) { + case CommitType.unconventional: + case CommitType.merge: + case CommitType.build: + case CommitType.chore: + case CommitType.ci: + case CommitType.docs: + case CommitType.refactor: + case CommitType.style: + case CommitType.test: + return false; + case CommitType.feat: + case CommitType.fix: + case CommitType.bug: + case CommitType.perf: + case CommitType.revert: + return true; + } + } +} + +/// {@template aft.changelog.unconventional_commit_message} +/// A commit message which is not a [ConventionalCommitMessage], i.e. a regular +/// commit message with no special formatting or meaning. +/// {@endtemplate} +class UnconventionalCommitMessage extends CommitMessage { + /// {@macro aft.changelog.unconventional_commit_message} + const UnconventionalCommitMessage( + super.sha, + super.summary, { + required super.dateTime, + }); + + @override + CommitType get type => CommitType.unconventional; +} diff --git a/packages/aft/lib/src/changelog/parser.dart b/packages/aft/lib/src/changelog/parser.dart new file mode 100644 index 0000000000..245a8465a6 --- /dev/null +++ b/packages/aft/lib/src/changelog/parser.dart @@ -0,0 +1,88 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +part of 'changelog.dart'; + +/// Matcher for a semantic version. +final RegExp semverRegex = RegExp(r'\d+\.\d+\.\d+[\d\w\.\+\-]*'); + +class _ChangelogParser implements NodeVisitor { + _ChangelogParser(this.logger); + + final Logger? logger; + + final builder = ChangelogBuilder(); + + late Version _currentVersion; + + @override + void visitElementAfter(Element element) {} + + @override + bool visitElementBefore(Element element) { + switch (element.type) { + case ElementType.h2: + final versionText = element.textContent; + if (versionText.toLowerCase() == 'next') { + _currentVersion = nextVersion; + break; + } + final versionMatch = semverRegex.firstMatch(versionText)?.group(0); + if (versionMatch == null) { + logger?.trace('Could not parse version: $versionText'); + break; + } + _currentVersion = Version.parse(versionMatch); + break; + default: + break; + } + builder.versions.add(_currentVersion, element); + return false; + } + + @override + void visitText(Text text) {} +} + +/// {@template aft.changelog.changelog_parse_exception} +/// Exception thrown while parsing a changelog. +/// {@endtemplate} +class ChangelogParseException implements Exception { + /// {@macro aft.changelog.changelog_parse_exception} + const ChangelogParseException(this.message); + + final String message; + + @override + String toString() => 'ChangelogParseException: $message'; +} + +/// The type of [Element] tag. +enum ElementType { h1, h2, h3, h4, h5, h6, ul, li, unknown } + +extension ElementX on Element { + /// The type of [Element] tag. + ElementType get type { + if (tag == 'h1') return ElementType.h1; + if (tag == 'h2') return ElementType.h2; + if (tag == 'h3') return ElementType.h3; + if (tag == 'h4') return ElementType.h4; + if (tag == 'h5') return ElementType.h5; + if (tag == 'h6') return ElementType.h6; + if (tag == 'ul') return ElementType.ul; + if (tag == 'li') return ElementType.li; + return ElementType.unknown; + } +} diff --git a/packages/aft/lib/src/changelog/printer.dart b/packages/aft/lib/src/changelog/printer.dart new file mode 100644 index 0000000000..f0db6dac81 --- /dev/null +++ b/packages/aft/lib/src/changelog/printer.dart @@ -0,0 +1,60 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:aft/src/changelog/changelog.dart'; +import 'package:markdown/markdown.dart'; + +String render(Iterable markdown) { + final renderer = _MarkdownRenderer(); + for (final node in markdown) { + node.accept(renderer); + } + return renderer.output; +} + +class _MarkdownRenderer implements NodeVisitor { + final StringBuffer _builder = StringBuffer(); + String get output => _builder.toString(); + + @override + void visitElementAfter(Element element) {} + + @override + bool visitElementBefore(Element element) { + switch (element.type) { + case ElementType.h1: + case ElementType.h2: + case ElementType.h3: + case ElementType.h4: + case ElementType.h5: + case ElementType.h6: + final headerNum = int.parse(element.type.name.substring(1)); + _builder.writeln('${'#' * headerNum} ${element.textContent}'); + if (headerNum < 3) { + _builder.writeln(); + } + break; + case ElementType.li: + _builder.writeln('- ${element.textContent}'); + break; + case ElementType.ul: + case ElementType.unknown: + break; + } + return true; + } + + @override + void visitText(Text text) {} +} diff --git a/packages/aft/lib/src/commands/amplify_command.dart b/packages/aft/lib/src/commands/amplify_command.dart index 93aa67caba..6f0b907f82 100644 --- a/packages/aft/lib/src/commands/amplify_command.dart +++ b/packages/aft/lib/src/commands/amplify_command.dart @@ -15,23 +15,63 @@ import 'dart:io'; import 'package:aft/aft.dart'; +import 'package:aft/src/repo.dart'; import 'package:args/command_runner.dart'; import 'package:aws_common/aws_common.dart'; -import 'package:checked_yaml/checked_yaml.dart'; -import 'package:cli_util/cli_logging.dart'; -import 'package:collection/collection.dart'; import 'package:http/http.dart' as http; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:pub/pub.dart'; /// Base class for all commands in this package providing common functionality. -abstract class AmplifyCommand extends Command implements Closeable { - /// Whether verbose logging is enabled. - late final bool verbose = globalResults!['verbose'] as bool; +abstract class AmplifyCommand extends Command + implements AWSLoggerPlugin, Closeable { + AmplifyCommand() { + init(); + } + + /// Initializer which runs when this command is instantiated. + /// + /// This can be overridden for setting additional flags or subcommands + /// via mixins or direct overrides. + @mustCallSuper + void init() { + AWSLogger() + ..unregisterAllPlugins() + ..registerPlugin(this); + } + + late final AWSLogger logger = () { + final allCommands = []; + for (Command? cmd = this; cmd != null; cmd = cmd.parent) { + allCommands.add(cmd.name); + } + return AWSLogger().createChild(allCommands.reversed.join('.')) + ..logLevel = verbose ? LogLevel.verbose : LogLevel.info; + }(); + + @override + void handleLogEntry(LogEntry logEntry) { + final message = verbose + ? '${logEntry.loggerName} | ${logEntry.message}' + : logEntry.message; + switch (logEntry.level) { + case LogLevel.verbose: + case LogLevel.debug: + case LogLevel.info: + stdout.writeln(message); + break; + case LogLevel.warn: + case LogLevel.error: + stderr.writeln(message); + break; + case LogLevel.none: + break; + } + } - /// The configured logger for the command. - late final Logger logger = verbose ? Logger.verbose() : Logger.standard(); + /// Whether verbose logging is enabled. + bool get verbose => globalResults?['verbose'] as bool? ?? false; /// The current working directory. late final Directory workingDirectory = () { @@ -65,40 +105,12 @@ abstract class AmplifyCommand extends Command implements Closeable { ); }(); - late final Map allPackages = () { - final allDirs = rootDir - .listSync(recursive: true, followLinks: false) - .whereType(); - final allPackages = []; - for (final dir in allDirs) { - final pubspecInfo = dir.pubspec; - if (pubspecInfo == null) { - continue; - } - final pubspec = pubspecInfo.pubspec; - if (aftConfig.ignore.contains(pubspec.name)) { - continue; - } - allPackages.add( - PackageInfo( - name: pubspec.name, - path: dir.path, - usesMonoRepo: dir.usesMonoRepo, - pubspecInfo: pubspecInfo, - flavor: pubspec.flavor, - ), - ); - } - return UnmodifiableMapView({ - for (final package in allPackages..sort()) package.name: package, - }); - }(); + late final Repo repo = Repo(rootDir, logger: logger); - /// The absolute path to the `aft.yaml` document. - late final String aftConfigPath = () { - final rootDir = this.rootDir; - return p.join(rootDir.path, 'aft.yaml'); - }(); + Map get allPackages => repo.allPackages; + + String get aftConfigPath => repo.aftConfigPath; + AftConfig get aftConfig => repo.aftConfig; /// The `aft.yaml` document. String get aftConfigYaml { @@ -107,12 +119,6 @@ abstract class AmplifyCommand extends Command implements Closeable { return configFile.readAsStringSync(); } - /// The global `aft` configuration for the repo. - AftConfig get aftConfig { - final configYaml = aftConfigYaml; - return checkedYamlDecode(configYaml, AftConfig.fromJson); - } - /// A command runner for `pub`. PubCommandRunner createPubRunner() => PubCommandRunner( pubCommand(isVerbose: () => verbose), diff --git a/packages/aft/lib/src/commands/changelog_command.dart b/packages/aft/lib/src/commands/changelog_command.dart new file mode 100644 index 0000000000..fe7b73c669 --- /dev/null +++ b/packages/aft/lib/src/commands/changelog_command.dart @@ -0,0 +1,91 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:io'; + +import 'package:aft/aft.dart'; +import 'package:aft/src/options/git_ref_options.dart'; +import 'package:aft/src/options/glob_options.dart'; +import 'package:path/path.dart' as p; + +/// Command for manipulating changelogs. +class ChangelogCommand extends AmplifyCommand { + ChangelogCommand() { + addSubcommand(_ChangelogUpdateCommand()); + addSubcommand(_ChangelogPreviewCommand()); + } + + @override + String get description => 'Update changelog entries automatically'; + + @override + String get name => 'changelog'; + + @override + List get aliases => ['cl']; +} + +abstract class _ChangelogBaseCommand extends AmplifyCommand + with GitRefOptions, GlobOptions { + @override + String get baseRef => super.baseRef ?? repo.latestTag('amplify_flutter')!; + + late final changes = repo.changes(baseRef, headRef); + + Future _updateChangelogs({required bool preview}) async { + final publishablePackages = + allPackages.values.where((pkg) => !pkg.isExample); + for (final package in publishablePackages) { + final commits = + changes.commitsByPackage[package.name]?.toSet() ?? const {}; + final changelogUpdate = package.changelog.update(commits: commits); + if (preview) { + if (changelogUpdate.hasUpdate) { + logger + ..info('${package.name}\n') + ..info(changelogUpdate.newText!); + } + } else { + await File(p.join(package.path, 'CHANGELOG.md')) + .writeAsString(changelogUpdate.toString()); + } + } + } +} + +class _ChangelogPreviewCommand extends _ChangelogBaseCommand { + @override + String get description => 'Previews changelogs with the latest commit info'; + + @override + String get name => 'preview'; + + @override + Future run() async { + return _updateChangelogs(preview: true); + } +} + +class _ChangelogUpdateCommand extends _ChangelogBaseCommand { + @override + String get description => 'Updates changelogs with the latest commit info'; + + @override + String get name => 'update'; + + @override + Future run() async { + return _updateChangelogs(preview: false); + } +} diff --git a/packages/aft/lib/src/commands/clean_command.dart b/packages/aft/lib/src/commands/clean_command.dart index 5b10500d3a..05bb3ce195 100644 --- a/packages/aft/lib/src/commands/clean_command.dart +++ b/packages/aft/lib/src/commands/clean_command.dart @@ -43,8 +43,8 @@ class CleanCommand extends AmplifyCommand { cleanCmd.captureStderr(sink: errors.writeln); if (await cleanCmd.exitCode != 0) { logger - ..stderr('Could not clean ${package.path}: ') - ..stderr(errors.toString()); + ..error('Could not clean ${package.path}: ') + ..error(errors.toString()); } break; case PackageFlavor.dart: diff --git a/packages/aft/lib/src/commands/deps_command.dart b/packages/aft/lib/src/commands/deps_command.dart index 490c5f2029..88cba2a69c 100644 --- a/packages/aft/lib/src/commands/deps_command.dart +++ b/packages/aft/lib/src/commands/deps_command.dart @@ -148,11 +148,11 @@ class _DepsSubcommand extends AmplifyCommand { } if (_mismatchedDependencies.isNotEmpty) { for (final mismatched in _mismatchedDependencies) { - logger.stderr(mismatched); + logger.error(mismatched); } exit(1); } - logger.stdout(action.successMessage); + logger.info(action.successMessage); } @override diff --git a/packages/aft/lib/src/commands/generate_sdk_command.dart b/packages/aft/lib/src/commands/generate_sdk_command.dart index 15689dd28f..f6f4bf85c4 100644 --- a/packages/aft/lib/src/commands/generate_sdk_command.dart +++ b/packages/aft/lib/src/commands/generate_sdk_command.dart @@ -65,7 +65,9 @@ class GenerateSdkCommand extends AmplifyCommand { /// Downloads AWS models from GitHub into a temporary directory. Future _downloadModels(String ref) async { final cloneDir = await Directory.systemTemp.createTemp('models'); - logger.trace('Cloning models to ${cloneDir.path}'); + logger + ..info('Downloading models...') + ..verbose('Cloning models to ${cloneDir.path}'); await runGit([ 'clone', 'https://github.com/aws/aws-models.git', @@ -75,9 +77,9 @@ class GenerateSdkCommand extends AmplifyCommand { ['checkout', ref], processWorkingDir: cloneDir.path, ); - logger.trace('Successfully cloned models'); + logger.verbose('Successfully cloned models'); final modelsDir = await Directory.systemTemp.createTemp('models'); - logger.trace('Organizing models in ${modelsDir.path}'); + logger.verbose('Organizing models in ${modelsDir.path}'); final services = cloneDir.list(followLinks: false).whereType(); await for (final serviceDir in services) { final serviceName = p.basename(serviceDir.path); @@ -90,7 +92,7 @@ class GenerateSdkCommand extends AmplifyCommand { } final smithyModel = File(p.join(smithyDir.path, 'model.json')); final copyToPath = p.join(modelsDir.path, '$serviceName.json'); - logger.trace('Copying $serviceName.json to $copyToPath'); + logger.verbose('Copying $serviceName.json to $copyToPath'); await smithyModel.copy(copyToPath); } return modelsDir; @@ -107,7 +109,7 @@ class GenerateSdkCommand extends AmplifyCommand { final configYaml = await configFile.readAsString(); final config = checkedYamlDecode(configYaml, SdkConfig.fromJson); - logger.stdout('Got config: $config'); + logger.verbose('Got config: $config'); final modelsPath = args['models'] as String?; final Directory modelsDir; @@ -211,10 +213,10 @@ class GenerateSdkCommand extends AmplifyCommand { } logger - ..stdout('Successfully generated SDK') - ..trace('Make sure to add the following dependencies:'); + ..info('Successfully generated SDK') + ..verbose('Make sure to add the following dependencies:'); for (final dep in dependencies.toList()..sort()) { - logger.trace('- $dep'); + logger.verbose('- $dep'); } } } diff --git a/packages/aft/lib/src/commands/list_packages_command.dart b/packages/aft/lib/src/commands/list_packages_command.dart index 1451326d14..e62165d6af 100644 --- a/packages/aft/lib/src/commands/list_packages_command.dart +++ b/packages/aft/lib/src/commands/list_packages_command.dart @@ -25,7 +25,7 @@ class ListPackagesCommand extends AmplifyCommand { @override Future run() async { for (final package in allPackages.keys) { - logger.stdout(package); + logger.info(package); } } } diff --git a/packages/aft/lib/src/commands/pub_command.dart b/packages/aft/lib/src/commands/pub_command.dart index 8ec48ce251..4ae623d8ad 100644 --- a/packages/aft/lib/src/commands/pub_command.dart +++ b/packages/aft/lib/src/commands/pub_command.dart @@ -17,6 +17,7 @@ import 'dart:io'; import 'package:aft/aft.dart'; import 'package:async/async.dart'; +import 'package:aws_common/aws_common.dart'; import 'package:cli_util/cli_logging.dart'; import 'package:http/http.dart' as http; import 'package:pub/src/http.dart' as pub_http; @@ -97,16 +98,16 @@ Future pubAction({ required bool verbose, required PubCommandRunner Function() createPubRunner, required http.Client httpClient, - Logger? logger, + AWSLogger? logger, }) async { // Set the internal HTTP client to one that can be reused multiple times. pub_http.innerHttpClient = httpClient; + logger ??= AWSLogger('pubAction'); - final progress = - logger?.progress('Running `pub ${action.name}` in all packages'); + logger.info('Running `pub ${action.name}` in all packages...'); final results = >{}; for (final package in allPackages.values) { - final packageProgress = logger?.progress(package.name); + logger.info('${package.name}...'); switch (package.flavor) { case PackageFlavor.flutter: results[package.name] = await Result.capture( @@ -129,19 +130,17 @@ Future pubAction({ ); break; } - packageProgress?.finish(); } - progress?.finish(message: action.successMessage, showTiming: true); final failed = results.entries.where((entry) => entry.value.isError); if (failed.isNotEmpty) { - logger?.stderr('The following packages failed: '); + logger.error('The following packages failed: '); for (final failedPackage in failed) { final error = failedPackage.value.asError!; logger - ?..stderr(failedPackage.key) - ..stderr(error.error.toString()) - ..stderr(error.stackTrace.toString()); + ..error(failedPackage.key) + ..error(error.error.toString()) + ..error(error.stackTrace.toString()); } exitCode = 1; } diff --git a/packages/aft/lib/src/commands/publish_command.dart b/packages/aft/lib/src/commands/publish_command.dart index 4e89f04c33..65105441f0 100644 --- a/packages/aft/lib/src/commands/publish_command.dart +++ b/packages/aft/lib/src/commands/publish_command.dart @@ -12,14 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:convert'; import 'dart:io'; import 'package:aft/aft.dart'; import 'package:aft/src/util.dart'; +import 'package:aws_common/aws_common.dart'; import 'package:cli_util/cli_logging.dart'; import 'package:graphs/graphs.dart'; -import 'package:pub_semver/pub_semver.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; /// Command to publish all Dart/Flutter packages in the repo. @@ -54,64 +53,42 @@ class PublishCommand extends AmplifyCommand { /// Checks if [package] can be published based on whether the local version /// is newer than the one published to `pub.dev`. Future _checkPublishable(PackageInfo package) async { - final publishTo = package.pubspecInfo.pubspec.publishTo; - if (publishTo == 'none') { - return null; - } - Never fail(String error) { logger - ..stderr('Could not retrieve package info for ${package.name}: ') - ..stderr(error) - ..stderr('Retry with `--force` to ignore this error'); + ..error('Could not retrieve package info for ${package.name}: ') + ..error(error) + ..error('Retry with `--force` to ignore this error'); exit(1); } - // Get the currently published version of the package. - final uri = Uri.parse(publishTo ?? 'https://pub.dev') - .replace(path: '/api/packages/${package.name}'); - logger.trace('GET $uri'); - final resp = await httpClient.get( - uri, - headers: {'Accept': 'application/vnd.pub.v2+json'}, - ); - - // Package is unpublished - if (resp.statusCode == 404) { - return package; - } - if (resp.statusCode != 200) { - if (force) { - return package; - } else { - fail(resp.body); + try { + final versionInfo = await package.resolveVersionInfo(httpClient); + final latestVersion = versionInfo?.latestVersion; + if (latestVersion == null) { + if (force) { + return package; + } else { + fail('Could not determine latest version'); + } } - } - final respJson = jsonDecode(resp.body) as Map; - final latestVersionStr = - (respJson['latest'] as Map?)?['version'] as String?; - if (latestVersionStr == null) { + return latestVersion < package.pubspecInfo.pubspec.version! + ? package + : null; + } on Exception catch (e) { if (force) { - return package; + return null; } else { - fail('Could not determine latest version'); + fail(e.toString()); } } - - final latestVersion = Version.parse(latestVersionStr); - return latestVersion < package.pubspecInfo.pubspec.version! - ? package - : null; } /// Runs pre-publish operations for [package], most importantly any necessary /// `build_runner` tasks. Future _prePublish(PackageInfo package) async { - final progress = - logger.progress('Running pre-publish checks for ${package.name}...'); + logger.info('Running pre-publish checks for ${package.name}...'); await runBuildRunner(package, logger: logger, verbose: verbose); - progress.finish(message: 'Success!'); } static final _validationStartRegex = RegExp( @@ -124,8 +101,7 @@ class PublishCommand extends AmplifyCommand { /// Publishes the package using `pub`. Future _publish(PackageInfo package) async { - final progress = logger - .progress('Publishing ${package.name}${dryRun ? ' (dry run)' : ''}...'); + logger.info('Publishing ${package.name}${dryRun ? ' (dry run)' : ''}...'); final publishCmd = await Process.start( package.flavor.entrypoint, [ @@ -156,17 +132,15 @@ class PublishCommand extends AmplifyCommand { .where(_validationErrorRegex.hasMatch) .where((line) => !_validationConstraintRegex.hasMatch(line)); if (failures.isNotEmpty) { - progress.cancel(); logger - ..stderr( + ..error( 'Failed to publish package ${package.name} ' 'due to the following errors: ', ) - ..stderr(failures.join('\n')); + ..error(failures.join('\n')); exit(exitCode); } } - progress.finish(message: 'Success!'); } @override @@ -186,7 +160,7 @@ class PublishCommand extends AmplifyCommand { ); if (publishablePackages.isEmpty) { - logger.stdout('No packages need publishing!'); + logger.info('No packages need publishing!'); return; } @@ -244,13 +218,13 @@ class PublishCommand extends AmplifyCommand { /// Runs `build_runner` for [package]. Future runBuildRunner( PackageInfo package, { - required Logger logger, + required AWSLogger logger, required bool verbose, }) async { if (!package.needsBuildRunner) { return; } - logger.stdout('Running build_runner for ${package.name}...'); + logger.info('Running build_runner for ${package.name}...'); final buildRunnerCmd = await Process.start( package.flavor.entrypoint, [ @@ -273,9 +247,9 @@ Future runBuildRunner( ..captureStderr(); } if (await buildRunnerCmd.exitCode != 0) { - logger.stderr('Failed to run `build_runner` for ${package.name}: '); + logger.error('Failed to run `build_runner` for ${package.name}: '); if (!verbose) { - logger.stderr(output.toString()); + logger.error(output.toString()); } exit(1); } diff --git a/packages/aft/lib/src/commands/version_command.dart b/packages/aft/lib/src/commands/version_command.dart new file mode 100644 index 0000000000..1a5dccc997 --- /dev/null +++ b/packages/aft/lib/src/commands/version_command.dart @@ -0,0 +1,227 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:io'; + +import 'package:aft/aft.dart'; +import 'package:aft/src/changelog/changelog.dart'; +import 'package:aft/src/options/git_ref_options.dart'; +import 'package:aft/src/options/glob_options.dart'; +import 'package:aft/src/repo.dart'; +import 'package:aws_common/aws_common.dart'; +import 'package:collection/collection.dart'; +import 'package:path/path.dart' as p; +import 'package:pub_semver/pub_semver.dart'; + +/// Command for bumping package versions across the repo. +class VersionCommand extends AmplifyCommand { + VersionCommand() { + addSubcommand(_VersionUpdateCommand()); + addSubcommand(_VersionPreviewCommand()); + } + + @override + String get description => + 'Bump package versions automatically using git magic'; + + @override + String get name => 'version'; +} + +abstract class _VersionBaseCommand extends AmplifyCommand + with GitRefOptions, GlobOptions { + @override + Map get allPackages { + return Map.fromEntries( + super.allPackages.entries.where((entry) => !entry.value.isExample), + ); + } + + GitChanges _changesForPackage(PackageInfo package) { + final baseRef = this.baseRef ?? repo.latestTag(package.name); + if (baseRef == null) { + exitError( + 'No tag exists for package. ' + 'Supply a base ref manually using --base-ref', + ); + } + return repo.changes(baseRef, headRef); + } + + Future _updateVersions({required bool preview}) async { + final updates = await bumpVersions( + repo: repo, + changesForPackage: _changesForPackage, + ); + final changelogUpdates = updates.updatedChangelogs; + + for (final package in allPackages.values) { + final edits = package.pubspecInfo.pubspecYamlEditor.edits; + if (edits.isNotEmpty) { + if (preview) { + logger.info('pubspec.yaml'); + for (final edit in edits) { + final originalText = package.pubspecInfo.pubspecYaml + .substring(edit.offset, edit.offset + edit.length); + logger.info('$originalText --> ${edit.replacement}'); + } + } else { + await File(p.join(package.path, 'pubspec.yaml')) + .writeAsString(package.pubspecInfo.pubspecYamlEditor.toString()); + } + } + final changelogUpdate = changelogUpdates[package]; + if (changelogUpdate != null && changelogUpdate.hasUpdate) { + if (preview) { + logger + ..info('CHANGELOG.md') + ..info(changelogUpdate.newText!); + } else { + await File(p.join(package.path, 'CHANGELOG.md')) + .writeAsString(changelogUpdate.toString()); + } + } + } + } +} + +class _VersionPreviewCommand extends _VersionBaseCommand { + @override + String get description => 'Previews changes to package versions'; + + @override + String get name => 'preview'; + + @override + Future run() async { + return _updateVersions(preview: true); + } +} + +class _VersionUpdateCommand extends _VersionBaseCommand { + @override + String get description => 'Updates package versions'; + + @override + String get name => 'update'; + + @override + Future run() async { + return _updateVersions(preview: false); + } +} + +Future bumpVersions({ + required Repo repo, + required GitChanges Function(PackageInfo) changesForPackage, +}) async { + final logger = AWSLogger('updateVersions'); + + // Version updates, by component. + final versionUpdates = {}; + + // Changelog updates. by package. + final changelogUpdates = {}; + + Version? bumpVersion(PackageInfo package, {required bool isBreakingChange}) { + final component = + repo.aftConfig.componentForPackage(package) ?? package.name; + final currentVersion = package.version; + final currentProposedVersion = versionUpdates[component] ?? currentVersion; + final newProposedVersion = currentVersion.nextAmplifyVersion( + isBreakingChange: isBreakingChange, + ); + final newVersion = versionUpdates[component] = maxBy( + [currentProposedVersion, newProposedVersion], + (version) => version, + )!; + if (newVersion > currentProposedVersion) { + final currentChangelogUpdate = changelogUpdates[package]; + changelogUpdates[package] = package.changelog.update( + commits: currentChangelogUpdate?.commits ?? const Iterable.empty(), + version: newVersion, + ); + return newVersion; + } + return null; + } + + for (final package in repo.publishablePackages) { + final changes = changesForPackage(package); + final commits = changes.commitsByPackage[package.name]?.toSet() ?? const {}; + + var isBreakingChange = false; + final affectedPackages = {}; + for (final commit in commits) { + if (commit.isBreakingChange) { + isBreakingChange = true; + } + // Packages affected by the same commit. + affectedPackages.addAll( + changes.packagesByCommit[commit]?.where((pkg) => pkg != package.name) ?? + const {}, + ); + } + final newVersion = bumpVersion(package, isBreakingChange: isBreakingChange); + + final allAffectedPackages = + affectedPackages.map((packageName) => repo.allPackages[packageName]!); + for (final affectedPackage in allAffectedPackages) { + if (!affectedPackage.pubspecInfo.pubspec.dependencies + .containsKey(package.name)) { + continue; + } + affectedPackage.pubspecInfo.pubspecYamlEditor.update( + ['dependencies', package.name], + '^$newVersion', + ); + // Do a patch bump of the affected package and update its changelog. + bumpVersion(affectedPackage, isBreakingChange: false); + } + changelogUpdates[package] = package.changelog.update( + commits: commits, + version: newVersion, + ); + } + + // Update pubspecs + for (final package in repo.publishablePackages) { + logger.info(package.name); + final newVersion = versionUpdates[package]; + if (newVersion != null) { + package.pubspecInfo.pubspecYamlEditor.update( + ['version'], + newVersion.toString(), + ); + logger + ..info('Version') + ..info('${package.version} --> $newVersion'); + } + } + + return VersionChanges( + updatedChangelogs: changelogUpdates, + updatedVersions: versionUpdates, + ); +} + +class VersionChanges { + const VersionChanges({ + required this.updatedChangelogs, + required this.updatedVersions, + }); + + final Map updatedVersions; + final Map updatedChangelogs; +} diff --git a/packages/aft/lib/src/models.dart b/packages/aft/lib/src/models.dart index 2b1e7dfc27..ad79413ad8 100644 --- a/packages/aft/lib/src/models.dart +++ b/packages/aft/lib/src/models.dart @@ -12,9 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'dart:convert'; import 'dart:io'; +import 'package:aft/src/changelog/changelog.dart'; import 'package:aws_common/aws_common.dart'; +import 'package:collection/collection.dart'; +import 'package:http/http.dart' as http; import 'package:json_annotation/json_annotation.dart'; import 'package:path/path.dart' as p; import 'package:pub_semver/pub_semver.dart'; @@ -39,6 +43,18 @@ enum PackageFlavor { final String entrypoint; } +class PubVersionInfo { + const PubVersionInfo(this.allVersions); + + final List allVersions; + + Version? get latestVersion => + allVersions.where((version) => !version.isPreRelease).lastOrNull; + + Version? get latestPrerelease => + allVersions.where((version) => version.isPreRelease).lastOrNull; +} + /// {@template amplify_tools.package_info} /// Information about a Dart/Flutter package in the repo. /// {@endtemplate} @@ -76,8 +92,60 @@ class PackageInfo bool get needsBuildRunner { return pubspecInfo.pubspec.devDependencies.containsKey('build_runner') && // aft should not be used to run `build_runner` in example projects - (pubspecInfo.pubspec.publishTo != 'none' || - falsePositiveExamples.contains(name)); + !isExample; + } + + /// Whether the package is an example package. + bool get isExample { + return pubspecInfo.pubspec.publishTo == 'none' || + falsePositiveExamples.contains(name); + } + + /// The parsed `CHANGELOG.md`. + Changelog get changelog { + final changelogMd = File(p.join(path, 'CHANGELOG.md')).readAsStringSync(); + return Changelog.parse(changelogMd); + } + + /// The current version in `pubspec.yaml`. + Version get version => pubspecInfo.pubspec.version!; + + /// Resolves the latest version information from `pub.dev`. + Future resolveVersionInfo(http.Client client) async { + final publishTo = pubspecInfo.pubspec.publishTo; + if (publishTo == 'none') { + return null; + } + + // Get the currently published version of the package. + final uri = Uri.parse(publishTo ?? 'https://pub.dev') + .replace(path: '/api/packages/$name'); + final resp = await client.get( + uri, + headers: {AWSHeaders.accept: 'application/vnd.pub.v2+json'}, + ); + + // Package is unpublished + if (resp.statusCode == 404) { + return null; + } + if (resp.statusCode != 200) { + throw http.ClientException(resp.body, uri); + } + + final respJson = jsonDecode(resp.body) as Map; + final versions = (respJson['versions'] as List?) ?? []; + final semvers = []; + for (final version in versions) { + final map = (version as Map).cast(); + final semver = map['version'] as String?; + if (semver == null) { + continue; + } + semvers.add(Version.parse(semver)); + } + + return PubVersionInfo(semvers..sort()); } @override @@ -103,6 +171,15 @@ class PubspecInfo { required this.uri, }); + factory PubspecInfo.fromUri(Uri uri) { + final yaml = File.fromUri(uri).readAsStringSync(); + return PubspecInfo( + pubspec: Pubspec.parse(yaml), + pubspecYaml: yaml, + uri: uri, + ); + } + /// The package's parsed pubspec. final Pubspec pubspec; @@ -116,6 +193,35 @@ class PubspecInfo { late final YamlEditor pubspecYamlEditor = YamlEditor(pubspecYaml); } +extension AmplifyVersion on Version { + /// The next version according to Amplify rules for incrementing. + Version nextAmplifyVersion({bool isBreakingChange = false}) { + if (preRelease.isEmpty) { + return isBreakingChange ? nextMinor : nextPatch; + } + if (isBreakingChange) { + final newPrelease = preRelease.map((el) { + if (el is! int) return el; + return el + 1; + }).join('.'); + return Version( + major, + minor, + patch, + pre: newPrelease, + ); + } + final newBuild = (build.singleOrNull as int? ?? 0) + 1; + return Version( + major, + minor, + patch, + pre: preRelease.join('.'), + build: '$newBuild', + ); + } +} + enum DependencyType { dependency('dependencies', 'dependency'), devDependency('dev_dependencies', 'dev dependency'), @@ -170,6 +276,8 @@ const yamlSerializable = JsonSerializable( disallowUnrecognizedKeys: true, ); +enum PubTrack { stable, prerelease } + /// The typed representation of the `aft.yaml` file. @yamlSerializable @_VersionConstraintConverter() @@ -177,14 +285,35 @@ class AftConfig { const AftConfig({ this.dependencies = const {}, this.ignore = const [], + this.branches = const {PubTrack.stable: 'main'}, + this.components = const {}, }); factory AftConfig.fromJson(Map? json) => _$AftConfigFromJson(json ?? const {}); + /// Global dependency versions for third-party dependencies representing the + /// values which have been vetted by manual review and/or those should be used + /// consistently across all packages. final Map dependencies; + + /// Packages to ignore in all repo operations. final List ignore; + /// Branch names which map to pub.dev stable and prerelease tracks. + final Map branches; + + /// Strongly connected components which should have minor/major version bumps + /// happen in unison, i.e. a version bump to one package cascades to all. + final Map> components; + + /// Retrieves the component for [package], if any. + String? componentForPackage(PackageInfo package) { + return components.entries.firstWhereOrNull((component) { + return component.value.contains(package.name); + })?.key; + } + Map toJson() => _$AftConfigToJson(this); } diff --git a/packages/aft/lib/src/models.g.dart b/packages/aft/lib/src/models.g.dart index 18a213e266..2f0d573fa1 100644 --- a/packages/aft/lib/src/models.g.dart +++ b/packages/aft/lib/src/models.g.dart @@ -12,7 +12,12 @@ AftConfig _$AftConfigFromJson(Map json) => $checkedCreate( ($checkedConvert) { $checkKeys( json, - allowedKeys: const ['dependencies', 'ignore'], + allowedKeys: const [ + 'dependencies', + 'ignore', + 'branches', + 'components' + ], ); final val = AftConfig( dependencies: $checkedConvert( @@ -30,6 +35,22 @@ AftConfig _$AftConfigFromJson(Map json) => $checkedCreate( (v) => (v as List?)?.map((e) => e as String).toList() ?? const []), + branches: $checkedConvert( + 'branches', + (v) => + (v as Map?)?.map( + (k, e) => MapEntry( + $enumDecode(_$PubTrackEnumMap, k), e as String), + ) ?? + const {PubTrack.stable: 'main'}), + components: $checkedConvert( + 'components', + (v) => + (v as Map?)?.map( + (k, e) => MapEntry(k as String, + (e as List).map((e) => e as String).toList()), + ) ?? + const {}), ); return val; }, @@ -39,8 +60,16 @@ Map _$AftConfigToJson(AftConfig instance) => { 'dependencies': instance.dependencies.map( (k, e) => MapEntry(k, const _VersionConstraintConverter().toJson(e))), 'ignore': instance.ignore, + 'branches': + instance.branches.map((k, e) => MapEntry(_$PubTrackEnumMap[k]!, e)), + 'components': instance.components, }; +const _$PubTrackEnumMap = { + PubTrack.stable: 'stable', + PubTrack.prerelease: 'prerelease', +}; + SdkConfig _$SdkConfigFromJson(Map json) => $checkedCreate( 'SdkConfig', json, diff --git a/packages/aft/lib/src/options/git_ref_options.dart b/packages/aft/lib/src/options/git_ref_options.dart new file mode 100644 index 0000000000..c7a8c3b665 --- /dev/null +++ b/packages/aft/lib/src/options/git_ref_options.dart @@ -0,0 +1,56 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:convert'; +import 'dart:io'; + +import 'package:aft/aft.dart'; +import 'package:aft/src/changelog/changelog.dart'; +import 'package:aft/src/changelog/commit_message.dart'; +import 'package:aft/src/changelog/printer.dart'; +import 'package:pub_semver/pub_semver.dart'; + +/// Adds git ref options and functionality to a command. +mixin GitRefOptions on AmplifyCommand { + @override + void init() { + super.init(); + argParser + ..addOption( + 'base-ref', + help: 'The base ref to update against', + ) + ..addOption( + 'head-ref', + help: 'The head ref to update against', + ); + } + + /// The base reference git operations should be based on. + /// + /// By default, this is the latest release tag. + String? get baseRef { + return Platform.environment['GITHUB_BASE_REF'] ?? + argResults!['base-ref'] as String?; + } + + /// The head reference git operations should be based on. + /// + /// By default, this is the current `HEAD`. + String get headRef { + return Platform.environment['GITHUB_HEAD_REF'] ?? + argResults!['head-ref'] as String? ?? + 'HEAD'; + } +} diff --git a/packages/aft/lib/src/options/glob_options.dart b/packages/aft/lib/src/options/glob_options.dart new file mode 100644 index 0000000000..09386ae5bb --- /dev/null +++ b/packages/aft/lib/src/options/glob_options.dart @@ -0,0 +1,57 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:aft/aft.dart'; +import 'package:glob/glob.dart'; + +/// Adds globbing options to a command. +mixin GlobOptions on AmplifyCommand { + @override + void init() { + super.init(); + argParser + ..addMultiOption('include', help: 'Glob of packages to include') + ..addMultiOption('exclude', help: 'Glob of packages to exclude'); + } + + /// Globs of packages which should be included in versioning. + late final include = (argResults?['include'] as List? ?? const []) + .map(Glob.new) + .toList(); + + /// Globs of packages which should be excluded from versioning. + late final exclude = (argResults?['exclude'] as List? ?? const []) + .map(Glob.new) + .toList(); + + @override + Map get allPackages { + return Map.fromEntries( + super.allPackages.entries.where((entry) { + final package = entry.value; + for (final glob in include) { + if (glob.matches(package.path)) { + return true; + } + } + for (final glob in exclude) { + if (glob.matches(package.path)) { + return false; + } + } + return true; + }), + ); + } +} diff --git a/packages/aft/lib/src/pub/pub_runner.dart b/packages/aft/lib/src/pub/pub_runner.dart index 31bf52d3a9..43e93b27c6 100644 --- a/packages/aft/lib/src/pub/pub_runner.dart +++ b/packages/aft/lib/src/pub/pub_runner.dart @@ -14,7 +14,7 @@ import 'package:aft/aft.dart'; import 'package:args/command_runner.dart'; -import 'package:cli_util/cli_logging.dart'; +import 'package:aws_common/aws_common.dart'; import 'package:cli_util/cli_util.dart'; import 'package:flutter_tools/src/base/template.dart'; import 'package:flutter_tools/src/cache.dart'; @@ -74,13 +74,14 @@ Future runDartPub( Future runFlutterPub( PubAction action, PackageInfo package, { - Logger? logger, + AWSLogger? logger, }) async { + logger ??= AWSLogger('runFlutterPub'); // Assumes using Dart SDK from Flutter Cache.flutterRoot = path.normalize( path.absolute(path.dirname(path.dirname(path.dirname(getSdkPath())))), ); - logger?.trace('Resolved flutter root: ${Cache.flutterRoot}'); + logger.verbose('Resolved flutter root: ${Cache.flutterRoot}'); await flutter.runInContext( () async { final runner = FlutterCommandRunner() diff --git a/packages/aft/lib/src/repo.dart b/packages/aft/lib/src/repo.dart new file mode 100644 index 0000000000..301a115ced --- /dev/null +++ b/packages/aft/lib/src/repo.dart @@ -0,0 +1,202 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:io'; + +import 'package:aft/aft.dart'; +import 'package:aft/src/changelog/changelog.dart'; +import 'package:aft/src/changelog/commit_message.dart'; +import 'package:aws_common/aws_common.dart'; +import 'package:built_collection/built_collection.dart'; +import 'package:checked_yaml/checked_yaml.dart'; +import 'package:collection/collection.dart'; +import 'package:libgit2dart/libgit2dart.dart'; +import 'package:path/path.dart' as p; +import 'package:pub_semver/pub_semver.dart'; + +/// Encapsulates all repository functionality including package and Git +/// management. +class Repo { + Repo(this.rootDir, {AWSLogger? logger}) + : logger = logger ?? AWSLogger('Repo'); + + /// The root directory of the repository. + final Directory rootDir; + + final AWSLogger logger; + + /// All packages in the Amplify Flutter repo. + late final Map allPackages = () { + final allDirs = rootDir + .listSync(recursive: true, followLinks: false) + .whereType(); + final allPackages = []; + for (final dir in allDirs) { + final pubspecInfo = dir.pubspec; + if (pubspecInfo == null) { + continue; + } + final pubspec = pubspecInfo.pubspec; + if (aftConfig.ignore.contains(pubspec.name)) { + continue; + } + allPackages.add( + PackageInfo( + name: pubspec.name, + path: dir.path, + usesMonoRepo: dir.usesMonoRepo, + pubspecInfo: pubspecInfo, + flavor: pubspec.flavor, + ), + ); + } + return UnmodifiableMapView({ + for (final package in allPackages..sort()) package.name: package, + }); + }(); + + late final List publishablePackages = + allPackages.values.where((pkg) => !pkg.isExample).toList(); + + /// The absolute path to the `aft.yaml` document. + late final String aftConfigPath = () { + final rootDir = this.rootDir; + return p.join(rootDir.path, 'aft.yaml'); + }(); + + /// The global `aft` configuration for the repo. + late final AftConfig aftConfig = () { + final configFile = File(p.join(rootDir.path, 'aft.yaml')); + assert(configFile.existsSync(), 'Could not find aft.yaml'); + final configYaml = configFile.readAsStringSync(); + return checkedYamlDecode(configYaml, AftConfig.fromJson); + }(); + + /// The libgit repository. + late final Repository repo = Repository.open(rootDir.path); + + /// The latest tag in the [repo], i.e. the last checkpoint for versioning. + String? latestTag([String? packageName]) => maxBy( + repo.tags.where((tag) { + if (!semverRegex.hasMatch(tag)) { + return false; + } + if (packageName == null) { + return true; + } + final packageRegex = RegExp('${packageName}_v${semverRegex.pattern}'); + if (packageRegex.hasMatch(tag)) { + return true; + } + final componentForPackage = aftConfig.components.entries + .singleWhereOrNull((entry) => entry.value.contains(packageName)) + ?.key; + if (componentForPackage != null) { + final componentRegex = + RegExp('${componentForPackage}_v${semverRegex.pattern}'); + return componentRegex.hasMatch(tag); + } + return false; + }), + (t) { + final version = semverRegex.firstMatch(t)!.group(0)!; + return Version.parse(version); + }, + ); + + /// The git diff between [baseRef] and [headRef]. + Diff diffRefs(String baseRef, String headRef) => diffTrees( + RevParse.single(repo: repo, spec: '$baseRef^{tree}') as Tree, + RevParse.single(repo: repo, spec: '$headRef^{tree}') as Tree, + ); + + /// The git diff between [oldTree] and [newTree]. + Diff diffTrees(Tree oldTree, Tree newTree) => Diff.treeToTree( + repo: repo, + oldTree: oldTree, + newTree: newTree, + ); + + /// Collect all the packages which have changed between [baseRef]..[headRef] + /// and the commits which changed them. + GitChanges changes(String baseRef, String headRef) { + final diff = diffRefs(baseRef, headRef); + final changedPaths = diff.deltas.expand( + (delta) => [delta.oldFile.path, delta.newFile.path], + ); + final changedPackages = {}; + for (final changedPath in changedPaths) { + final changedPackage = allPackages.values.firstWhereOrNull( + (pkg) { + final relativePkgPath = p.relative(pkg.path, from: rootDir.path); + return changedPath.contains('$relativePkgPath/'); + }, + ); + if (changedPackage != null && + // Do not track example packages for git ops + !changedPackage.isExample) { + logger.verbose( + 'Package ${changedPackage.name} changed by $changedPath', + ); + changedPackages.add(changedPackage); + } + } + + // For each package, gather all the commits between baseRef..headRef which + // affected the package. + final commitsByPackage = SetMultimapBuilder(); + final packagesByCommit = SetMultimapBuilder(); + for (final package in changedPackages) { + final walker = RevWalk(repo)..pushRange('$baseRef..$headRef'); + for (final commit in walker.walk()) { + for (var i = 0; i < commit.parents.length; i++) { + final parent = commit.parent(i); + final commitDiff = diffTrees(parent.tree, commit.tree); + final commitPaths = commitDiff.deltas.expand( + (delta) => [delta.oldFile.path, delta.newFile.path], + ); + final relativePath = p.relative(package.path, from: rootDir.path); + if (commitPaths.any( + (path) => path.contains('$relativePath/'), + )) { + final commitMessage = CommitMessage.parse( + commit.oid.sha, + commit.summary, + dateTime: DateTime.fromMillisecondsSinceEpoch( + commit.time * 1000, + ).toUtc(), + ); + commitsByPackage.add(package.name, commitMessage); + packagesByCommit.add(commitMessage, package.name); + } + } + } + } + + return GitChanges( + commitsByPackage: commitsByPackage.build(), + packagesByCommit: packagesByCommit.build(), + ); + } +} + +class GitChanges { + const GitChanges({ + required this.commitsByPackage, + required this.packagesByCommit, + }); + + final BuiltSetMultimap commitsByPackage; + final BuiltSetMultimap packagesByCommit; +} diff --git a/packages/aft/pubspec.yaml b/packages/aft/pubspec.yaml index b2169d465d..af979cd6b9 100644 --- a/packages/aft/pubspec.yaml +++ b/packages/aft/pubspec.yaml @@ -10,6 +10,8 @@ dependencies: args: ^2.3.0 async: ^2.8.0 aws_common: ">=0.3.0 <0.4.0" + built_collection: ^5.0.0 + built_value: ^8.0.0 checked_yaml: ^2.0.0 cli_util: ^0.3.5 collection: ^1.16.0 @@ -19,9 +21,15 @@ dependencies: ref: 0df0e2ea1f7fe5021c3593d61903e70bdbda8107 path: packages/flutter_tools git: ^2.0.0 + glob: ^2.1.0 graphs: ^2.1.0 http: ^0.13.0 json_annotation: ^4.7.0 + libgit2dart: + git: + url: https://github.com/SkinnyMind/libgit2dart + ref: d55742d6d7725fa190df0caf3c1cbe60b7b481aa + markdown: ^5.0.0 meta: ^1.7.0 path: any pub: @@ -45,6 +53,7 @@ dev_dependencies: amplify_lints: path: ../amplify_lints build_runner: ^2.0.0 + built_value_generator: ^8.0.0 json_serializable: 6.5.4 test: ^1.16.0 diff --git a/packages/aft/test/amplify_command_test.dart b/packages/aft/test/changelog/parser_test.dart similarity index 50% rename from packages/aft/test/amplify_command_test.dart rename to packages/aft/test/changelog/parser_test.dart index 66dcf63218..97aece707c 100644 --- a/packages/aft/test/amplify_command_test.dart +++ b/packages/aft/test/changelog/parser_test.dart @@ -12,36 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:aft/aft.dart'; +import 'package:aft/src/changelog/changelog.dart'; +import 'package:pub_semver/pub_semver.dart'; import 'package:test/test.dart'; -class MockCommand extends AmplifyCommand { - @override - String get description => throw UnimplementedError(); - - @override - String get name => throw UnimplementedError(); -} - void main() { - group('AmplifyCommand', () { - final command = MockCommand(); - - test('rootDir', () { - expect(() => command.rootDir, returnsNormally); - }); - - test('allPackages', () async { - final allPackages = command.allPackages; - expect( - allPackages, - contains('amplify_flutter'), - ); - }); + group('Changelog', () { + group('parses semver', () { + final semverStrings = { + '1.0.0': Version(1, 0, 0), + 'v1.0.0': Version(1, 0, 0), + 'v1.0.0 (08-02-2022)': Version(1, 0, 0), + '1.0.0-tag.1': Version(1, 0, 0, pre: 'tag.1'), + '1.0.0+1': Version(1, 0, 0, build: '1'), + 'NEXT': nextVersion, + }; - test('globalDependencyConfig', () async { - final config = command.aftConfig; - expect(config.dependencies, contains('uuid')); + for (final semver in semverStrings.entries) { + test(semver.key, () { + final changlog = Changelog.parse('## ${semver.key}'); + expect(changlog.versions.keys.single, semver.value); + }); + } }); }); } diff --git a/packages/aft/test/e2e_test.dart b/packages/aft/test/e2e_test.dart new file mode 100644 index 0000000000..be83402e4a --- /dev/null +++ b/packages/aft/test/e2e_test.dart @@ -0,0 +1,323 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ignore_for_file: overridden_fields + +import 'dart:io'; + +import 'package:aft/aft.dart'; +import 'package:aft/src/repo.dart'; +import 'package:git/git.dart' as git; +import 'package:libgit2dart/libgit2dart.dart'; +import 'package:path/path.dart' as p; +import 'package:pub_semver/pub_semver.dart'; +import 'package:test/test.dart'; + +class MockRepo extends Repo { + MockRepo(super.rootDir, {required this.repo, super.logger}); + + @override + final Repository repo; + + @override + final AftConfig aftConfig = const AftConfig( + components: { + 'amplify': [ + 'amplify_auth_cognito', + 'amplify_auth_cognito_ios', + ], + }, + ); + + @override + final Map allPackages = {}; +} + +void main() { + group('Repo', () { + late Repo repo; + + Future runGit(List args) => git.runGit( + args, + processWorkingDir: repo.rootDir.path, + throwOnError: true, + ); + + PackageInfo createPackage( + String packageName, { + Map? dependencies, + Version? version, + }) { + version ??= Version(0, 1, 0); + final packagePath = p.join(repo.rootDir.path, 'packages', packageName); + final pubspec = StringBuffer( + ''' +name: $packageName +version: $version + +environment: + sdk: '>=2.17.0 <3.0.0' +''', + ); + if (dependencies != null && dependencies.isNotEmpty) { + pubspec.writeln('dependencies:'); + for (final dependency in dependencies.entries) { + pubspec.writeln('${dependency.key}: ${dependency.value}'); + } + } + final changelog = ''' +## $version + +Initial version. +'''; + final pubspecUri = Uri.file(p.join(packagePath, 'pubspec.yaml')); + File.fromUri(pubspecUri) + ..createSync(recursive: true) + ..writeAsStringSync(pubspec.toString()); + File(p.join(packagePath, 'CHANGELOG.md')) + ..createSync(recursive: true) + ..writeAsStringSync(changelog); + + final package = PackageInfo( + name: packageName, + path: packagePath, + usesMonoRepo: false, + pubspecInfo: PubspecInfo.fromUri(pubspecUri), + flavor: PackageFlavor.dart, + ); + + repo.allPackages[packageName] = package; + return package; + } + + Future makeChange( + String commitTitle, + List packages, + ) async { + for (final package in packages) { + final newDir = Directory(p.join(repo.rootDir.path, 'packages', package)) + .createTempSync(); + File(p.join(newDir.path, 'file.txt')).createSync(); + } + + await runGit(['add', '.']); + await runGit(['commit', '-m', commitTitle]); + } + + setUp(() async { + final gitDir = Directory.systemTemp.createTempSync('aft'); + repo = MockRepo( + gitDir, + repo: Repository.init(path: gitDir.path), + ); + await runGit(['commit', '--allow-empty', '-m', 'Initial commit']); + }); + + test('latestTag', () async { + // Create tags for packages + components + await runGit(['tag', 'amplify_v1.0.0-next.0+2']); + await runGit(['tag', 'amplify_v1.0.0-next.0+1']); + await runGit(['tag', 'amplify_auth_cognito_v0.6.6']); + await runGit(['tag', 'amplify_auth_cognito_dart_v0.1.0']); + await runGit(['tag', 'amplify_auth_cognito_dart_v0.1.1']); + await runGit(['tag', 'amplify_auth_cognito_dart_v0.2.0']); + + expect( + repo.latestTag('amplify_auth_cognito'), + 'amplify_v1.0.0-next.0+2', + reason: 'Package should follow components', + ); + expect( + repo.latestTag('amplify'), + 'amplify_v1.0.0-next.0+2', + reason: 'latestTag should allow component lookups directly', + ); + expect( + repo.latestTag('amplify_auth_cognito_dart'), + 'amplify_auth_cognito_dart_v0.2.0', + ); + expect(repo.latestTag('smithy'), isNull); + }); + + group('E2E', () { + final nextVersion = Version(1, 0, 0, pre: 'next.0'); + final coreVersion = Version(0, 1, 0); + final nextConstraint = nextVersion; + final coreConstraint = VersionConstraint.compatibleWith(coreVersion); + + setUp(() async { + createPackage( + 'amplify_auth_cognito', + version: nextVersion, + dependencies: { + 'amplify_auth_cognito_ios': nextConstraint, + 'amplify_auth_cognito_dart': coreConstraint, + 'amplify_core': nextConstraint, + 'aws_common': coreConstraint, + }, + ); + createPackage('amplify_auth_cognito_ios', version: nextVersion); + createPackage( + 'amplify_auth_cognito_dart', + version: coreVersion, + dependencies: { + 'amplify_core': coreConstraint, + 'aws_common': coreConstraint, + }, + ); + createPackage( + 'amplify_core', + version: nextVersion, + dependencies: { + 'aws_common': coreConstraint, + }, + ); + createPackage('aws_common', version: coreVersion); + + await runGit(['add', '.']); + await runGit(['commit', '-m', 'Add packages']); + await runGit(['tag', 'amplify_v$nextVersion']); + await runGit(['tag', 'aws_common_v$coreVersion']); + await runGit(['tag', 'amplify_core_v$coreVersion']); + await runGit(['tag', 'amplify_auth_cognito_dart_v$coreVersion']); + + // Make changes that affect: + // - Only a leaf package + // - Only one package of a component + // - Only a root package + await makeChange('fix(aws_common): Fix type', ['aws_common']); + await makeChange( + 'chore(amplify_auth_cognito_ios): Update iOS dependency', + ['amplify_auth_cognito_ios'], + ); + await makeChange( + 'fix(amplify_auth_cognito_ios)!: Change iOS dependency', + ['amplify_auth_cognito_ios'], + ); + await makeChange( + 'feat(amplify_core): New hub events', + ['amplify_core'], + ); + await makeChange( + 'feat(auth): New feature', + [ + 'amplify_core', + 'amplify_auth_cognito', + 'amplify_auth_cognito_dart', + ], + ); + }); + + GitChanges changesForPackage(PackageInfo package) { + final latestTag = repo.latestTag(package.name)!; + return repo.changes(latestTag, 'HEAD'); + } + + group('calculates changes', () { + final numCommits = { + 'aws_common': 1, + 'amplify_core': 2, + 'amplify_auth_cognito_dart': 1, + 'amplify_auth_cognito': 1, + 'amplify_auth_cognito_ios': 2, + }; + final changelogs = { + 'aws_common': ''' +## NEXT + +### Fixes +- fix(aws_common): Fix type +''', + 'amplify_core': ''' +## NEXT + +### Features +- feat(amplify_core): New hub events +- feat(auth): New feature +''', + 'amplify_auth_cognito_dart': ''' +## NEXT + +### Features +- feat(auth): New feature +''', + 'amplify_auth_cognito': ''' +## NEXT + +### Features +- feat(auth): New feature +''', + 'amplify_auth_cognito_ios': ''' +## NEXT + +### Breaking Changes +- fix(amplify_auth_cognito_ios)!: Change iOS dependency +''', + }; + + for (final check in numCommits.entries) { + final packageName = check.key; + + test(packageName, () { + final package = repo.allPackages[packageName]!; + final changes = changesForPackage(package); + final commits = changes.commitsByPackage[packageName]!; + expect(commits, hasLength(check.value)); + + // Bump changelogs to NEXT + final updateChangelog = package.changelog.update( + commits: commits, + ); + expect(updateChangelog.hasUpdate, true); + expect(updateChangelog.newText, changelogs[packageName]); + }); + } + }); + + group('bumps versions', () { + final finalVersions = { + 'aws_common': '0.1.1', + 'amplify_core': '1.0.0-next.0+1', + 'amplify_auth_cognito_dart': '0.1.1', + 'amplify_auth_cognito': '1.0.0-next.1', + 'amplify_auth_cognito_ios': '1.0.0-next.1', + }; + + late VersionChanges updates; + setUp(() async { + updates = await bumpVersions( + repo: repo, + changesForPackage: changesForPackage, + ); + }); + + for (final check in finalVersions.entries) { + final packageName = check.key; + test(packageName, () { + final package = repo.allPackages[packageName]!; + final component = repo.aftConfig.componentForPackage(package); + if (component != null) { + expect(updates.updatedVersions[package.name], isNull); + final newVersion = updates.updatedVersions[component]!; + expect(newVersion.toString(), finalVersions[packageName]); + } else { + final newVersion = updates.updatedVersions[package.name]!; + expect(newVersion.toString(), finalVersions[packageName]); + } + }); + } + }); + }); + }); +} diff --git a/packages/aft/test/model_test.dart b/packages/aft/test/model_test.dart new file mode 100644 index 0000000000..6e62789bc4 --- /dev/null +++ b/packages/aft/test/model_test.dart @@ -0,0 +1,67 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:aft/src/models.dart'; +import 'package:pub_semver/pub_semver.dart'; +import 'package:test/test.dart'; + +void main() { + group('AmplifyVersion', () { + test('0-version', () { + final version = Version(0, 1, 0); + expect( + version.nextAmplifyVersion(isBreakingChange: false), + Version(0, 1, 1), + ); + expect( + version.nextAmplifyVersion(isBreakingChange: true), + Version(0, 2, 0), + ); + }); + + test('pre-release', () { + var version = Version(1, 0, 0, pre: 'next.0'); + expect( + version.nextAmplifyVersion(isBreakingChange: false), + Version(1, 0, 0, pre: 'next.0', build: '1'), + ); + expect( + version.nextAmplifyVersion(isBreakingChange: true), + Version(1, 0, 0, pre: 'next.1'), + ); + + version = Version(1, 0, 0, pre: 'next.0', build: '1'); + expect( + version.nextAmplifyVersion(isBreakingChange: false), + Version(1, 0, 0, pre: 'next.0', build: '2'), + ); + expect( + version.nextAmplifyVersion(isBreakingChange: true), + Version(1, 0, 0, pre: 'next.1'), + ); + }); + + test('release', () { + final version = Version(1, 0, 0); + expect( + version.nextAmplifyVersion(isBreakingChange: false), + Version(1, 0, 1), + ); + expect( + version.nextAmplifyVersion(isBreakingChange: true), + Version(1, 1, 0), + ); + }); + }); +} From e44b881a20bb3bd01d5b0d4e324194eed10c9252 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Fri, 26 Aug 2022 13:24:13 -0700 Subject: [PATCH 03/40] fix(aws_common): Logger initialization Fixes an issue in logger initialization where the initial plugin is not registered to the root plugin. commit-id:063fbfe0 --- packages/aws_common/lib/src/logging/aws_logger.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/aws_common/lib/src/logging/aws_logger.dart b/packages/aws_common/lib/src/logging/aws_logger.dart index 90d8efe175..30de268bc5 100644 --- a/packages/aws_common/lib/src/logging/aws_logger.dart +++ b/packages/aws_common/lib/src/logging/aws_logger.dart @@ -50,15 +50,15 @@ class AWSLogger implements Closeable { /// {@macro aws_common.logging.aws_logger} @protected AWSLogger.protected(this._logger) { - _init(); + _init(this); } static bool _initialized = false; - static void _init() { + static void _init(AWSLogger rootLogger) { if (_initialized) return; _initialized = true; hierarchicalLoggingEnabled = true; - AWSLogger().registerPlugin(const SimpleLogPrinter()); + rootLogger.registerPlugin(const SimpleLogPrinter()); } /// The root namespace for all [AWSLogger] instances. From 7da0a505954bae8750d4c0c6e6e3db14c878c5d3 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Fri, 26 Aug 2022 21:40:40 -0700 Subject: [PATCH 04/40] fix(aft): Versioning algorithm and performance commit-id:c45852ad --- packages/aft/lib/src/changelog/changelog.dart | 12 +- packages/aft/lib/src/changelog/printer.dart | 10 +- .../lib/src/commands/changelog_command.dart | 13 +- .../aft/lib/src/commands/publish_command.dart | 21 +-- .../aft/lib/src/commands/version_command.dart | 142 +++++++++++------- packages/aft/lib/src/models.dart | 48 ++++-- packages/aft/lib/src/repo.dart | 100 +++++++++--- packages/aft/lib/src/util.dart | 27 ++++ packages/aft/test/e2e_test.dart | 21 ++- 9 files changed, 267 insertions(+), 127 deletions(-) diff --git a/packages/aft/lib/src/changelog/changelog.dart b/packages/aft/lib/src/changelog/changelog.dart index de151407f8..a19cf60062 100644 --- a/packages/aft/lib/src/changelog/changelog.dart +++ b/packages/aft/lib/src/changelog/changelog.dart @@ -93,7 +93,7 @@ abstract class Changelog implements Built { if (commits.isEmpty) { // If there are no commits worth including, add a generic message about // bug fixes/improvements. - nodes.add(Element.text('li', 'Minor bug fixes and improvements')); + nodes.add(Element.text('li', 'Minor bug fixes and improvements\n')); } else { for (final typedCommits in commitsByType.entries) { nodes.add(Element.text('h3', typedCommits.key.header)); @@ -116,16 +116,13 @@ abstract class Changelog implements Built { required Iterable commits, Version? version, }) { - if (commits.isEmpty) { - return ChangelogUpdate(originalText, commits: commits); - } final nodes = makeVersionEntry( commits: commits, version: version, ); // Replace the text in changelogMd so that the latest version matches // `version`, if given, else `NEXT`. - final String keepText; + String keepText; if (hasNextEntry || (version != null && latestVersion == version)) { // Update latest entry, either to `version` or as a new `NEXT` entry. keepText = LineSplitter.split(originalText) @@ -138,6 +135,9 @@ abstract class Changelog implements Built { // No `NEXT` or `version` entry exists yet. keepText = originalText; } + if (!keepText.endsWith('\n')) { + keepText = '$keepText\n'; + } return ChangelogUpdate(keepText, commits: commits, newText: render(nodes)); } @@ -161,5 +161,5 @@ class ChangelogUpdate { bool get hasUpdate => newText != null; @override - String toString() => newText == null ? keepText : '$newText\n\n$keepText'; + String toString() => newText == null ? keepText : '$newText$keepText'; } diff --git a/packages/aft/lib/src/changelog/printer.dart b/packages/aft/lib/src/changelog/printer.dart index f0db6dac81..672f35f465 100644 --- a/packages/aft/lib/src/changelog/printer.dart +++ b/packages/aft/lib/src/changelog/printer.dart @@ -28,7 +28,15 @@ class _MarkdownRenderer implements NodeVisitor { String get output => _builder.toString(); @override - void visitElementAfter(Element element) {} + void visitElementAfter(Element element) { + switch (element.type) { + case ElementType.ul: + _builder.writeln(); + break; + default: + break; + } + } @override bool visitElementBefore(Element element) { diff --git a/packages/aft/lib/src/commands/changelog_command.dart b/packages/aft/lib/src/commands/changelog_command.dart index fe7b73c669..0687663106 100644 --- a/packages/aft/lib/src/commands/changelog_command.dart +++ b/packages/aft/lib/src/commands/changelog_command.dart @@ -38,15 +38,12 @@ class ChangelogCommand extends AmplifyCommand { abstract class _ChangelogBaseCommand extends AmplifyCommand with GitRefOptions, GlobOptions { - @override - String get baseRef => super.baseRef ?? repo.latestTag('amplify_flutter')!; - - late final changes = repo.changes(baseRef, headRef); - Future _updateChangelogs({required bool preview}) async { - final publishablePackages = - allPackages.values.where((pkg) => !pkg.isExample); - for (final package in publishablePackages) { + for (final package in repo.publishablePackages) { + final baseRef = this.baseRef ?? + repo.latestTag(package.name) ?? + repo.latestTag('amplify_flutter')!; + final changes = repo.changes(baseRef, headRef); final commits = changes.commitsByPackage[package.name]?.toSet() ?? const {}; final changelogUpdate = package.changelog.update(commits: commits); diff --git a/packages/aft/lib/src/commands/publish_command.dart b/packages/aft/lib/src/commands/publish_command.dart index 65105441f0..51ea8ce0da 100644 --- a/packages/aft/lib/src/commands/publish_command.dart +++ b/packages/aft/lib/src/commands/publish_command.dart @@ -146,7 +146,7 @@ class PublishCommand extends AmplifyCommand { @override Future run() async { // Gather packages which can be published. - final publishablePackages = (await Future.wait([ + var publishablePackages = (await Future.wait([ for (final package in allPackages.values) _checkPublishable(package), ])) .whereType() @@ -165,7 +165,7 @@ class PublishCommand extends AmplifyCommand { } try { - sortPackagesTopologically( + publishablePackages = sortPackagesTopologically( publishablePackages, (pkg) => pkg.pubspecInfo.pubspec, ); @@ -260,8 +260,8 @@ Future runBuildRunner( /// /// Packages with inter-dependencies cannot be topologically sorted and will /// throw a [CycleException]. -void sortPackagesTopologically( - List packages, +List sortPackagesTopologically( + Iterable packages, Pubspec Function(T) getPubspec, ) { final pubspecs = packages.map(getPubspec); @@ -271,10 +271,11 @@ void sortPackagesTopologically( package.name: package.dependencies.keys.where(packageNames.contains), }; final ordered = topologicalSort(graph.keys, (key) => graph[key]!); - packages.sort((a, b) { - // `ordered` is in reverse ordering to our desired publish precedence. - return ordered - .indexOf(getPubspec(b).name) - .compareTo(ordered.indexOf(getPubspec(a).name)); - }); + return packages.toList() + ..sort((a, b) { + // `ordered` is in reverse ordering to our desired publish precedence. + return ordered + .indexOf(getPubspec(b).name) + .compareTo(ordered.indexOf(getPubspec(a).name)); + }); } diff --git a/packages/aft/lib/src/commands/version_command.dart b/packages/aft/lib/src/commands/version_command.dart index 1a5dccc997..1f7ebb99dd 100644 --- a/packages/aft/lib/src/commands/version_command.dart +++ b/packages/aft/lib/src/commands/version_command.dart @@ -16,9 +16,11 @@ import 'dart:io'; import 'package:aft/aft.dart'; import 'package:aft/src/changelog/changelog.dart'; +import 'package:aft/src/changelog/commit_message.dart'; import 'package:aft/src/options/git_ref_options.dart'; import 'package:aft/src/options/glob_options.dart'; import 'package:aft/src/repo.dart'; +import 'package:aft/src/util.dart'; import 'package:aws_common/aws_common.dart'; import 'package:collection/collection.dart'; import 'package:path/path.dart' as p; @@ -52,7 +54,7 @@ abstract class _VersionBaseCommand extends AmplifyCommand final baseRef = this.baseRef ?? repo.latestTag(package.name); if (baseRef == null) { exitError( - 'No tag exists for package. ' + 'No tag exists for package (${package.name}). ' 'Supply a base ref manually using --base-ref', ); } @@ -126,19 +128,36 @@ Future bumpVersions({ required Repo repo, required GitChanges Function(PackageInfo) changesForPackage, }) async { - final logger = AWSLogger('updateVersions'); - // Version updates, by component. final versionUpdates = {}; // Changelog updates. by package. final changelogUpdates = {}; - Version? bumpVersion(PackageInfo package, {required bool isBreakingChange}) { - final component = - repo.aftConfig.componentForPackage(package) ?? package.name; + /// Bumps the dependency for [package] in [dependent]. + void bumpDependency(PackageInfo package, PackageInfo dependent) { + final component = repo.aftConfig.componentForPackage(package.name); + final newVersion = versionUpdates[component] ?? package.version; + if (dependent.pubspecInfo.pubspec.dependencies.containsKey(package.name)) { + dependent.pubspecInfo.pubspecYamlEditor.update( + ['dependencies', package.name], + newVersion.amplifyConstraint(minVersion: newVersion), + ); + } + } + + /// Bumps the version and changelog in [package] using [commit]. + /// + /// Returns the new version. + Version bumpVersion( + PackageInfo package, { + bool propogate = false, + CommitMessage? commit, + }) { + final component = repo.aftConfig.componentForPackage(package.name); final currentVersion = package.version; final currentProposedVersion = versionUpdates[component] ?? currentVersion; + final isBreakingChange = commit?.isBreakingChange ?? false; final newProposedVersion = currentVersion.nextAmplifyVersion( isBreakingChange: isBreakingChange, ); @@ -146,67 +165,80 @@ Future bumpVersions({ [currentProposedVersion, newProposedVersion], (version) => version, )!; - if (newVersion > currentProposedVersion) { + + if (newVersion > currentVersion) { + repo.logger.debug( + 'Bumping ${package.name} from $currentProposedVersion to $newVersion: ' + '${commit?.summary}', + ); + package.pubspecInfo.pubspecYamlEditor.update( + ['version'], + newVersion.toString(), + ); final currentChangelogUpdate = changelogUpdates[package]; changelogUpdates[package] = package.changelog.update( - commits: currentChangelogUpdate?.commits ?? const Iterable.empty(), + commits: { + ...?currentChangelogUpdate?.commits, + if (commit != null) commit, + }, version: newVersion, ); - return newVersion; - } - return null; - } - for (final package in repo.publishablePackages) { - final changes = changesForPackage(package); - final commits = changes.commitsByPackage[package.name]?.toSet() ?? const {}; + if (propogate) { + // Propogate to all dependent packages. + dfs( + repo.reversedPackageGraph, + root: package, + (dependent) { + if (!dependent.isExample) { + bumpVersion(dependent, commit: commit); + bumpDependency(package, dependent); + } + }, + ); - var isBreakingChange = false; - final affectedPackages = {}; - for (final commit in commits) { - if (commit.isBreakingChange) { - isBreakingChange = true; + // Propogate to all component packages. + repo.components[component]?.forEach((componentPackage) { + bumpVersion(componentPackage, commit: commit); + dfs( + repo.reversedPackageGraph, + root: componentPackage, + (dependent) { + bumpDependency(componentPackage, dependent); + }, + ); + }); } - // Packages affected by the same commit. - affectedPackages.addAll( - changes.packagesByCommit[commit]?.where((pkg) => pkg != package.name) ?? - const {}, - ); } - final newVersion = bumpVersion(package, isBreakingChange: isBreakingChange); - final allAffectedPackages = - affectedPackages.map((packageName) => repo.allPackages[packageName]!); - for (final affectedPackage in allAffectedPackages) { - if (!affectedPackage.pubspecInfo.pubspec.dependencies - .containsKey(package.name)) { - continue; - } - affectedPackage.pubspecInfo.pubspecYamlEditor.update( - ['dependencies', package.name], - '^$newVersion', - ); - // Do a patch bump of the affected package and update its changelog. - bumpVersion(affectedPackage, isBreakingChange: false); - } - changelogUpdates[package] = package.changelog.update( - commits: commits, - version: newVersion, - ); + return newVersion; } - // Update pubspecs - for (final package in repo.publishablePackages) { - logger.info(package.name); - final newVersion = versionUpdates[package]; - if (newVersion != null) { - package.pubspecInfo.pubspecYamlEditor.update( - ['version'], - newVersion.toString(), + final sortedPackages = repo.publishablePackages; + sortPackagesTopologically( + sortedPackages, + (PackageInfo pkg) => pkg.pubspecInfo.pubspec, + ); + for (final package in sortedPackages) { + final changes = changesForPackage(package); + final commits = changes.commitsByPackage[package]?.toSet() ?? const {}; + for (final commit in commits) { + // TODO(dnys1): Define full set of commit types which should be ignored + // when considering version changes. + if (commit.isVersionBump || + commit.type == CommitType.merge && commit.taggedPr == null) { + continue; + } + bumpVersion( + package, + commit: commit, + propogate: commit.isBreakingChange, ); - logger - ..info('Version') - ..info('${package.version} --> $newVersion'); + // Propogate the version change to all packages affected by the same + // commit. + changes.packagesByCommit[commit]?.forEach((commitPackage) { + bumpDependency(package, commitPackage); + }); } } diff --git a/packages/aft/lib/src/models.dart b/packages/aft/lib/src/models.dart index ad79413ad8..70e1e62d3e 100644 --- a/packages/aft/lib/src/models.dart +++ b/packages/aft/lib/src/models.dart @@ -194,22 +194,23 @@ class PubspecInfo { } extension AmplifyVersion on Version { + Version get nextPreRelease => Version( + major, + minor, + patch, + pre: preRelease.map((el) { + if (el is! int) return el; + return el + 1; + }).join('.'), + ); + /// The next version according to Amplify rules for incrementing. Version nextAmplifyVersion({bool isBreakingChange = false}) { if (preRelease.isEmpty) { return isBreakingChange ? nextMinor : nextPatch; } if (isBreakingChange) { - final newPrelease = preRelease.map((el) { - if (el is! int) return el; - return el + 1; - }).join('.'); - return Version( - major, - minor, - patch, - pre: newPrelease, - ); + return nextPreRelease; } final newBuild = (build.singleOrNull as int? ?? 0) + 1; return Version( @@ -220,6 +221,26 @@ extension AmplifyVersion on Version { build: '$newBuild', ); } + + /// The constraint to use for this version in pubspecs. + String amplifyConstraint({Version? minVersion}) { + final Version maxVersion; + if (preRelease.isEmpty) { + final currentMinor = Version(major, minor, 0); + minVersion ??= currentMinor; + maxVersion = Version(major, minor + 1, 0); + } else { + final currentPreRelease = Version( + major, + minor, + patch, + pre: preRelease.join('.'), + ); + minVersion ??= currentPreRelease; + maxVersion = nextPreRelease; + } + return '>=$minVersion <$maxVersion'; + } } enum DependencyType { @@ -308,10 +329,11 @@ class AftConfig { final Map> components; /// Retrieves the component for [package], if any. - String? componentForPackage(PackageInfo package) { + String componentForPackage(String package) { return components.entries.firstWhereOrNull((component) { - return component.value.contains(package.name); - })?.key; + return component.value.contains(package); + })?.key ?? + package; } Map toJson() => _$AftConfigToJson(this); diff --git a/packages/aft/lib/src/repo.dart b/packages/aft/lib/src/repo.dart index 301a115ced..2ac2403f25 100644 --- a/packages/aft/lib/src/repo.dart +++ b/packages/aft/lib/src/repo.dart @@ -83,6 +83,14 @@ class Repo { return checkedYamlDecode(configYaml, AftConfig.fromJson); }(); + late final Map> components = + aftConfig.components.map((component, packages) { + return MapEntry( + component, + packages.map((name) => allPackages[name]!).toList(), + ); + }); + /// The libgit repository. late final Repository repo = Repository.open(rootDir.path); @@ -99,15 +107,11 @@ class Repo { if (packageRegex.hasMatch(tag)) { return true; } - final componentForPackage = aftConfig.components.entries - .singleWhereOrNull((entry) => entry.value.contains(packageName)) - ?.key; - if (componentForPackage != null) { - final componentRegex = - RegExp('${componentForPackage}_v${semverRegex.pattern}'); - return componentRegex.hasMatch(tag); - } - return false; + final componentForPackage = + aftConfig.componentForPackage(packageName); + final componentRegex = + RegExp('${componentForPackage}_v${semverRegex.pattern}'); + return componentRegex.hasMatch(tag); }), (t) { final version = semverRegex.firstMatch(t)!.group(0)!; @@ -115,6 +119,32 @@ class Repo { }, ); + /// The directed graph of packages to the packages it depends on. + late final Map> packageGraph = { + for (final package in allPackages.values) + package: package.pubspecInfo.pubspec.dependencies.keys + .map((packageName) => allPackages[packageName]) + .whereType() + .toList(), + }; + + /// The reversed (transposed) [packageGraph]. + /// + /// Provides a mapping from each packages to the packages which directly + /// depend on it. + late final Map> reversedPackageGraph = () { + final packageGraph = this.packageGraph; + final reversedPackageGraph = >{ + for (final package in allPackages.values) package: [], + }; + for (final entry in packageGraph.entries) { + for (final dep in entry.value) { + reversedPackageGraph[dep]!.add(entry.key); + } + } + return reversedPackageGraph; + }(); + /// The git diff between [baseRef] and [headRef]. Diff diffRefs(String baseRef, String headRef) => diffTrees( RevParse.single(repo: repo, spec: '$baseRef^{tree}') as Tree, @@ -128,10 +158,24 @@ class Repo { newTree: newTree, ); + final _changesCache = <_DiffMarker, GitChanges>{}; + /// Collect all the packages which have changed between [baseRef]..[headRef] /// and the commits which changed them. GitChanges changes(String baseRef, String headRef) { - final diff = diffRefs(baseRef, headRef); + final baseTree = RevParse.single( + repo: repo, + spec: '$baseRef^{tree}', + ) as Tree; + final headTree = RevParse.single( + repo: repo, + spec: '$headRef^{tree}', + ) as Tree; + final diffMarker = _DiffMarker(baseTree, headTree); + if (_changesCache.containsKey(diffMarker)) { + return _changesCache[diffMarker]!; + } + final diff = diffTrees(baseTree, headTree); final changedPaths = diff.deltas.expand( (delta) => [delta.oldFile.path, delta.newFile.path], ); @@ -146,17 +190,14 @@ class Repo { if (changedPackage != null && // Do not track example packages for git ops !changedPackage.isExample) { - logger.verbose( - 'Package ${changedPackage.name} changed by $changedPath', - ); changedPackages.add(changedPackage); } } // For each package, gather all the commits between baseRef..headRef which // affected the package. - final commitsByPackage = SetMultimapBuilder(); - final packagesByCommit = SetMultimapBuilder(); + final commitsByPackage = SetMultimapBuilder(); + final packagesByCommit = SetMultimapBuilder(); for (final package in changedPackages) { final walker = RevWalk(repo)..pushRange('$baseRef..$headRef'); for (final commit in walker.walk()) { @@ -167,9 +208,10 @@ class Repo { (delta) => [delta.oldFile.path, delta.newFile.path], ); final relativePath = p.relative(package.path, from: rootDir.path); - if (commitPaths.any( + final changedPath = commitPaths.firstWhereOrNull( (path) => path.contains('$relativePath/'), - )) { + ); + if (changedPath != null) { final commitMessage = CommitMessage.parse( commit.oid.sha, commit.summary, @@ -177,14 +219,18 @@ class Repo { commit.time * 1000, ).toUtc(), ); - commitsByPackage.add(package.name, commitMessage); - packagesByCommit.add(commitMessage, package.name); + logger.verbose( + 'Package ${package.name} changed by $changedPath ' + '(${commitMessage.summary})', + ); + commitsByPackage.add(package, commitMessage); + packagesByCommit.add(commitMessage, package); } } } } - return GitChanges( + return _changesCache[diffMarker] = GitChanges( commitsByPackage: commitsByPackage.build(), packagesByCommit: packagesByCommit.build(), ); @@ -197,6 +243,16 @@ class GitChanges { required this.packagesByCommit, }); - final BuiltSetMultimap commitsByPackage; - final BuiltSetMultimap packagesByCommit; + final BuiltSetMultimap commitsByPackage; + final BuiltSetMultimap packagesByCommit; +} + +class _DiffMarker with AWSEquatable<_DiffMarker> { + const _DiffMarker(this.baseTree, this.headTree); + + final Tree baseTree; + final Tree headTree; + + @override + List get props => [baseTree, headTree]; } diff --git a/packages/aft/lib/src/util.dart b/packages/aft/lib/src/util.dart index c77d161e2d..8151878cd9 100644 --- a/packages/aft/lib/src/util.dart +++ b/packages/aft/lib/src/util.dart @@ -47,3 +47,30 @@ extension ProcessUtil on Process { .listen((line) => sink('$prefix$line')); } } + +/// Performs a depth-first search on [graph] calling [visit] for every node. +/// +/// If [root] is specified, the search is started there. +void dfs( + Map> graph, + void Function(Node) visit, { + Node? root, +}) { + final visited = {}; + void search(Node node, List edges) { + visited.add(node); + visit(node); + for (final edge in edges) { + if (!visited.contains(edge)) { + search(edge, graph[edge]!); + } + } + } + + if (root != null) { + assert(graph.containsKey(root), 'Root is not in graph'); + search(root, graph[root]!); + } else { + graph.forEach(search); + } +} diff --git a/packages/aft/test/e2e_test.dart b/packages/aft/test/e2e_test.dart index be83402e4a..343fb3d8b1 100644 --- a/packages/aft/test/e2e_test.dart +++ b/packages/aft/test/e2e_test.dart @@ -73,7 +73,7 @@ environment: if (dependencies != null && dependencies.isNotEmpty) { pubspec.writeln('dependencies:'); for (final dependency in dependencies.entries) { - pubspec.writeln('${dependency.key}: ${dependency.value}'); + pubspec.writeln(' ${dependency.key}: ${dependency.value}'); } } final changelog = ''' @@ -272,7 +272,7 @@ Initial version. test(packageName, () { final package = repo.allPackages[packageName]!; final changes = changesForPackage(package); - final commits = changes.commitsByPackage[packageName]!; + final commits = changes.commitsByPackage[package]!; expect(commits, hasLength(check.value)); // Bump changelogs to NEXT @@ -280,7 +280,10 @@ Initial version. commits: commits, ); expect(updateChangelog.hasUpdate, true); - expect(updateChangelog.newText, changelogs[packageName]); + expect( + updateChangelog.newText, + equalsIgnoringWhitespace(changelogs[packageName]!), + ); }); } }); @@ -306,15 +309,9 @@ Initial version. final packageName = check.key; test(packageName, () { final package = repo.allPackages[packageName]!; - final component = repo.aftConfig.componentForPackage(package); - if (component != null) { - expect(updates.updatedVersions[package.name], isNull); - final newVersion = updates.updatedVersions[component]!; - expect(newVersion.toString(), finalVersions[packageName]); - } else { - final newVersion = updates.updatedVersions[package.name]!; - expect(newVersion.toString(), finalVersions[packageName]); - } + final component = repo.aftConfig.componentForPackage(package.name); + final newVersion = updates.updatedVersions[component]!; + expect(newVersion.toString(), finalVersions[packageName]); }); } }); From 47b97f89193aa7bf0b3376b2913044766c0d17e2 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sat, 27 Aug 2022 15:03:56 -0700 Subject: [PATCH 05/40] chore(aft): Clean up commit-id:de10a06e --- aft.yaml | 7 +- packages/aft/lib/src/changelog/changelog.dart | 24 +- .../aft/lib/src/changelog/commit_message.dart | 8 +- packages/aft/lib/src/changelog/parser.dart | 4 +- packages/aft/lib/src/changelog/printer.dart | 1 + .../aft/lib/src/commands/amplify_command.dart | 62 ++++- .../lib/src/commands/changelog_command.dart | 33 ++- .../aft/lib/src/commands/pub_command.dart | 1 - .../aft/lib/src/commands/publish_command.dart | 31 +-- .../aft/lib/src/commands/version_command.dart | 171 ++----------- packages/aft/lib/src/models.dart | 20 +- packages/aft/lib/src/models.g.dart | 22 +- .../aft/lib/src/options/git_ref_options.dart | 5 - .../aft/lib/src/options/glob_options.dart | 40 +-- packages/aft/lib/src/repo.dart | 227 +++++++++++++----- packages/aft/lib/src/util.dart | 26 ++ packages/aft/test/e2e_test.dart | 36 ++- ...blish_command_test.dart => util_test.dart} | 2 +- 18 files changed, 384 insertions(+), 336 deletions(-) rename packages/aft/test/{publish_command_test.dart => util_test.dart} (98%) diff --git a/aft.yaml b/aft.yaml index fcbd96d0cc..4ef7fc1503 100644 --- a/aft.yaml +++ b/aft.yaml @@ -18,12 +18,7 @@ dependencies: ignore: - synthetic_package -# Branch names which map to pub.dev stable and prerelease tracks. -branches: - stable: stable - prerelease: next - -# Strongly connected components which should have minor/major version bumps happen +# Strongly connected components which should have major version bumps happen # in unison, i.e. a version bump to one package cascades to all. components: amplify: diff --git a/packages/aft/lib/src/changelog/changelog.dart b/packages/aft/lib/src/changelog/changelog.dart index a19cf60062..317e3ac1dd 100644 --- a/packages/aft/lib/src/changelog/changelog.dart +++ b/packages/aft/lib/src/changelog/changelog.dart @@ -16,9 +16,9 @@ import 'dart:convert'; import 'package:aft/src/changelog/commit_message.dart'; import 'package:aft/src/changelog/printer.dart'; +import 'package:aws_common/aws_common.dart'; import 'package:built_collection/built_collection.dart'; import 'package:built_value/built_value.dart'; -import 'package:cli_util/cli_logging.dart'; import 'package:collection/collection.dart'; import 'package:markdown/markdown.dart'; import 'package:pub_semver/pub_semver.dart'; @@ -46,7 +46,7 @@ abstract class Changelog implements Built { /// changelog. factory Changelog.parse( String changelogMd, { - Logger? logger, + AWSLogger? logger, }) { final parser = Document(); final lines = LineSplitter.split(changelogMd).toList(); @@ -97,10 +97,24 @@ abstract class Changelog implements Built { } else { for (final typedCommits in commitsByType.entries) { nodes.add(Element.text('h3', typedCommits.key.header)); + + // Transform PR #'s into links to the main repo + const baseUrl = 'https://github.com/aws-amplify/amplify-flutter'; + final commits = typedCommits.value + .sortedBy((commit) => commit.summary) + .map((commit) { + final taggedPr = commit.taggedPr; + if (taggedPr == null) { + return commit.summary; + } + return commit.summary.replaceFirst( + '(#$taggedPr)', + '([#$taggedPr]($baseUrl/pull/$taggedPr))', + ); + }); + final list = Element('ul', [ - for (final commit - in typedCommits.value.sortedBy((commit) => commit.summary)) - Element.text('li', commit.summary), + for (final commit in commits) Element.text('li', commit), ]); nodes.add(list); } diff --git a/packages/aft/lib/src/changelog/commit_message.dart b/packages/aft/lib/src/changelog/commit_message.dart index e9bb4b1afe..1df5b3a36f 100644 --- a/packages/aft/lib/src/changelog/commit_message.dart +++ b/packages/aft/lib/src/changelog/commit_message.dart @@ -28,9 +28,9 @@ enum CommitTypeGroup { features('Features'), other('Other Changes'); - final String header; - const CommitTypeGroup(this.header); + + final String header; } enum CommitType { @@ -49,11 +49,11 @@ enum CommitType { style.other(), test.other(); - final CommitTypeGroup group; - const CommitType.fixes() : group = CommitTypeGroup.fixes; const CommitType.features() : group = CommitTypeGroup.features; const CommitType.other() : group = CommitTypeGroup.other; + + final CommitTypeGroup group; } /// {@template aft.changelog.commit_message} diff --git a/packages/aft/lib/src/changelog/parser.dart b/packages/aft/lib/src/changelog/parser.dart index 245a8465a6..77bae1b93c 100644 --- a/packages/aft/lib/src/changelog/parser.dart +++ b/packages/aft/lib/src/changelog/parser.dart @@ -20,7 +20,7 @@ final RegExp semverRegex = RegExp(r'\d+\.\d+\.\d+[\d\w\.\+\-]*'); class _ChangelogParser implements NodeVisitor { _ChangelogParser(this.logger); - final Logger? logger; + final AWSLogger? logger; final builder = ChangelogBuilder(); @@ -40,7 +40,7 @@ class _ChangelogParser implements NodeVisitor { } final versionMatch = semverRegex.firstMatch(versionText)?.group(0); if (versionMatch == null) { - logger?.trace('Could not parse version: $versionText'); + logger?.debug('Could not parse version: $versionText'); break; } _currentVersion = Version.parse(versionMatch); diff --git a/packages/aft/lib/src/changelog/printer.dart b/packages/aft/lib/src/changelog/printer.dart index 672f35f465..f5d65c0426 100644 --- a/packages/aft/lib/src/changelog/printer.dart +++ b/packages/aft/lib/src/changelog/printer.dart @@ -15,6 +15,7 @@ import 'package:aft/src/changelog/changelog.dart'; import 'package:markdown/markdown.dart'; +/// Renders the [markdown] AST as a string. String render(Iterable markdown) { final renderer = _MarkdownRenderer(); for (final node in markdown) { diff --git a/packages/aft/lib/src/commands/amplify_command.dart b/packages/aft/lib/src/commands/amplify_command.dart index 6f0b907f82..381c8e65ae 100644 --- a/packages/aft/lib/src/commands/amplify_command.dart +++ b/packages/aft/lib/src/commands/amplify_command.dart @@ -12,12 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'dart:collection'; import 'dart:io'; import 'package:aft/aft.dart'; import 'package:aft/src/repo.dart'; import 'package:args/command_runner.dart'; import 'package:aws_common/aws_common.dart'; +import 'package:checked_yaml/checked_yaml.dart'; +import 'package:git/git.dart' as git; import 'package:http/http.dart' as http; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; @@ -105,12 +108,63 @@ abstract class AmplifyCommand extends Command ); }(); - late final Repo repo = Repo(rootDir, logger: logger); + /// All packages in the Amplify Flutter repo. + late final Map allPackages = () { + final allDirs = rootDir + .listSync(recursive: true, followLinks: false) + .whereType(); + final allPackages = []; + for (final dir in allDirs) { + final pubspecInfo = dir.pubspec; + if (pubspecInfo == null) { + continue; + } + final pubspec = pubspecInfo.pubspec; + if (aftConfig.ignore.contains(pubspec.name)) { + continue; + } + allPackages.add( + PackageInfo( + name: pubspec.name, + path: dir.path, + usesMonoRepo: dir.usesMonoRepo, + pubspecInfo: pubspecInfo, + flavor: pubspec.flavor, + ), + ); + } + return UnmodifiableMapView({ + for (final package in allPackages..sort()) package.name: package, + }); + }(); - Map get allPackages => repo.allPackages; + /// The absolute path to the `aft.yaml` document. + late final String aftConfigPath = () { + final rootDir = this.rootDir; + return p.join(rootDir.path, 'aft.yaml'); + }(); - String get aftConfigPath => repo.aftConfigPath; - AftConfig get aftConfig => repo.aftConfig; + /// The global `aft` configuration for the repo. + late final AftConfig aftConfig = () { + final configFile = File(p.join(rootDir.path, 'aft.yaml')); + assert(configFile.existsSync(), 'Could not find aft.yaml'); + final configYaml = configFile.readAsStringSync(); + return checkedYamlDecode(configYaml, AftConfig.fromJson); + }(); + + late final Repo repo = Repo( + rootDir, + allPackages: allPackages, + aftConfig: aftConfig, + logger: logger, + ); + + /// Runs `git` with the given [args] from the repo's root directory. + Future runGit(List args) => git.runGit( + args, + processWorkingDir: rootDir.path, + throwOnError: true, + ); /// The `aft.yaml` document. String get aftConfigYaml { diff --git a/packages/aft/lib/src/commands/changelog_command.dart b/packages/aft/lib/src/commands/changelog_command.dart index 0687663106..c0e7046aa5 100644 --- a/packages/aft/lib/src/commands/changelog_command.dart +++ b/packages/aft/lib/src/commands/changelog_command.dart @@ -38,14 +38,29 @@ class ChangelogCommand extends AmplifyCommand { abstract class _ChangelogBaseCommand extends AmplifyCommand with GitRefOptions, GlobOptions { + _ChangelogBaseCommand() { + argParser.addFlag( + 'yes', + abbr: 'y', + help: 'Responds "yes" to all prompts', + defaultsTo: false, + negatable: false, + ); + } + + late final bool yes = argResults!['yes'] as bool; + Future _updateChangelogs({required bool preview}) async { for (final package in repo.publishablePackages) { - final baseRef = this.baseRef ?? - repo.latestTag(package.name) ?? - repo.latestTag('amplify_flutter')!; + final baseRef = this.baseRef ?? repo.latestTag(package.name); + if (baseRef == null) { + exitError( + 'No tag exists for package (${package.name}). ' + 'Supply a base ref manually using --base-ref', + ); + } final changes = repo.changes(baseRef, headRef); - final commits = - changes.commitsByPackage[package.name]?.toSet() ?? const {}; + final commits = changes.commitsByPackage[package]?.toSet() ?? const {}; final changelogUpdate = package.changelog.update(commits: commits); if (preview) { if (changelogUpdate.hasUpdate) { @@ -83,6 +98,12 @@ class _ChangelogUpdateCommand extends _ChangelogBaseCommand { @override Future run() async { - return _updateChangelogs(preview: false); + await _updateChangelogs(preview: false); + + logger.info('Changelogs successfully updated'); + if (yes || prompt('Commit changes? (y/N) ').toLowerCase() == 'y') { + await runGit(['add', '.']); + await runGit(['commit', '-m', 'chore(version): Update changelogs']); + } } } diff --git a/packages/aft/lib/src/commands/pub_command.dart b/packages/aft/lib/src/commands/pub_command.dart index 4ae623d8ad..7f0fb19cc4 100644 --- a/packages/aft/lib/src/commands/pub_command.dart +++ b/packages/aft/lib/src/commands/pub_command.dart @@ -18,7 +18,6 @@ import 'dart:io'; import 'package:aft/aft.dart'; import 'package:async/async.dart'; import 'package:aws_common/aws_common.dart'; -import 'package:cli_util/cli_logging.dart'; import 'package:http/http.dart' as http; import 'package:pub/src/http.dart' as pub_http; diff --git a/packages/aft/lib/src/commands/publish_command.dart b/packages/aft/lib/src/commands/publish_command.dart index 51ea8ce0da..79b3a01e53 100644 --- a/packages/aft/lib/src/commands/publish_command.dart +++ b/packages/aft/lib/src/commands/publish_command.dart @@ -17,9 +17,7 @@ import 'dart:io'; import 'package:aft/aft.dart'; import 'package:aft/src/util.dart'; import 'package:aws_common/aws_common.dart'; -import 'package:cli_util/cli_logging.dart'; import 'package:graphs/graphs.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; /// Command to publish all Dart/Flutter packages in the repo. class PublishCommand extends AmplifyCommand { @@ -146,7 +144,7 @@ class PublishCommand extends AmplifyCommand { @override Future run() async { // Gather packages which can be published. - var publishablePackages = (await Future.wait([ + final publishablePackages = (await Future.wait([ for (final package in allPackages.values) _checkPublishable(package), ])) .whereType() @@ -165,7 +163,7 @@ class PublishCommand extends AmplifyCommand { } try { - publishablePackages = sortPackagesTopologically( + sortPackagesTopologically( publishablePackages, (pkg) => pkg.pubspecInfo.pubspec, ); @@ -254,28 +252,3 @@ Future runBuildRunner( exit(1); } } - -/// Sorts packages in topological order so they may be published in the order -/// they're sorted. -/// -/// Packages with inter-dependencies cannot be topologically sorted and will -/// throw a [CycleException]. -List sortPackagesTopologically( - Iterable packages, - Pubspec Function(T) getPubspec, -) { - final pubspecs = packages.map(getPubspec); - final packageNames = pubspecs.map((el) => el.name).toList(); - final graph = >{ - for (var package in pubspecs) - package.name: package.dependencies.keys.where(packageNames.contains), - }; - final ordered = topologicalSort(graph.keys, (key) => graph[key]!); - return packages.toList() - ..sort((a, b) { - // `ordered` is in reverse ordering to our desired publish precedence. - return ordered - .indexOf(getPubspec(b).name) - .compareTo(ordered.indexOf(getPubspec(a).name)); - }); -} diff --git a/packages/aft/lib/src/commands/version_command.dart b/packages/aft/lib/src/commands/version_command.dart index 1f7ebb99dd..9efe4c1ac1 100644 --- a/packages/aft/lib/src/commands/version_command.dart +++ b/packages/aft/lib/src/commands/version_command.dart @@ -15,16 +15,10 @@ import 'dart:io'; import 'package:aft/aft.dart'; -import 'package:aft/src/changelog/changelog.dart'; -import 'package:aft/src/changelog/commit_message.dart'; import 'package:aft/src/options/git_ref_options.dart'; import 'package:aft/src/options/glob_options.dart'; import 'package:aft/src/repo.dart'; -import 'package:aft/src/util.dart'; -import 'package:aws_common/aws_common.dart'; -import 'package:collection/collection.dart'; import 'package:path/path.dart' as p; -import 'package:pub_semver/pub_semver.dart'; /// Command for bumping package versions across the repo. class VersionCommand extends AmplifyCommand { @@ -43,13 +37,18 @@ class VersionCommand extends AmplifyCommand { abstract class _VersionBaseCommand extends AmplifyCommand with GitRefOptions, GlobOptions { - @override - Map get allPackages { - return Map.fromEntries( - super.allPackages.entries.where((entry) => !entry.value.isExample), + _VersionBaseCommand() { + argParser.addFlag( + 'yes', + abbr: 'y', + help: 'Responds "yes" to all prompts', + defaultsTo: false, + negatable: false, ); } + late final bool yes = argResults!['yes'] as bool; + GitChanges _changesForPackage(PackageInfo package) { final baseRef = this.baseRef ?? repo.latestTag(package.name); if (baseRef == null) { @@ -62,13 +61,12 @@ abstract class _VersionBaseCommand extends AmplifyCommand } Future _updateVersions({required bool preview}) async { - final updates = await bumpVersions( - repo: repo, + repo.bumpAllVersions( changesForPackage: _changesForPackage, ); - final changelogUpdates = updates.updatedChangelogs; + final changelogUpdates = repo.changelogUpdates; - for (final package in allPackages.values) { + for (final package in repo.publishablePackages) { final edits = package.pubspecInfo.pubspecYamlEditor.edits; if (edits.isNotEmpty) { if (preview) { @@ -120,140 +118,17 @@ class _VersionUpdateCommand extends _VersionBaseCommand { @override Future run() async { - return _updateVersions(preview: false); - } -} - -Future bumpVersions({ - required Repo repo, - required GitChanges Function(PackageInfo) changesForPackage, -}) async { - // Version updates, by component. - final versionUpdates = {}; - - // Changelog updates. by package. - final changelogUpdates = {}; - - /// Bumps the dependency for [package] in [dependent]. - void bumpDependency(PackageInfo package, PackageInfo dependent) { - final component = repo.aftConfig.componentForPackage(package.name); - final newVersion = versionUpdates[component] ?? package.version; - if (dependent.pubspecInfo.pubspec.dependencies.containsKey(package.name)) { - dependent.pubspecInfo.pubspecYamlEditor.update( - ['dependencies', package.name], - newVersion.amplifyConstraint(minVersion: newVersion), - ); + await _updateVersions(preview: false); + + logger.info('Version successfully bumped'); + if (yes || prompt('Commit changes? (y/N) ').toLowerCase() == 'y') { + // Commit and tag changes + await runGit(['add', '.']); + await runGit(['commit', '-m', 'chore(version): Bump version']); + await Future.wait([ + for (final changeEntry in repo.versionChanges.entries) + runGit(['tag', '${changeEntry.key}_v${changeEntry.value}']), + ]); } } - - /// Bumps the version and changelog in [package] using [commit]. - /// - /// Returns the new version. - Version bumpVersion( - PackageInfo package, { - bool propogate = false, - CommitMessage? commit, - }) { - final component = repo.aftConfig.componentForPackage(package.name); - final currentVersion = package.version; - final currentProposedVersion = versionUpdates[component] ?? currentVersion; - final isBreakingChange = commit?.isBreakingChange ?? false; - final newProposedVersion = currentVersion.nextAmplifyVersion( - isBreakingChange: isBreakingChange, - ); - final newVersion = versionUpdates[component] = maxBy( - [currentProposedVersion, newProposedVersion], - (version) => version, - )!; - - if (newVersion > currentVersion) { - repo.logger.debug( - 'Bumping ${package.name} from $currentProposedVersion to $newVersion: ' - '${commit?.summary}', - ); - package.pubspecInfo.pubspecYamlEditor.update( - ['version'], - newVersion.toString(), - ); - final currentChangelogUpdate = changelogUpdates[package]; - changelogUpdates[package] = package.changelog.update( - commits: { - ...?currentChangelogUpdate?.commits, - if (commit != null) commit, - }, - version: newVersion, - ); - - if (propogate) { - // Propogate to all dependent packages. - dfs( - repo.reversedPackageGraph, - root: package, - (dependent) { - if (!dependent.isExample) { - bumpVersion(dependent, commit: commit); - bumpDependency(package, dependent); - } - }, - ); - - // Propogate to all component packages. - repo.components[component]?.forEach((componentPackage) { - bumpVersion(componentPackage, commit: commit); - dfs( - repo.reversedPackageGraph, - root: componentPackage, - (dependent) { - bumpDependency(componentPackage, dependent); - }, - ); - }); - } - } - - return newVersion; - } - - final sortedPackages = repo.publishablePackages; - sortPackagesTopologically( - sortedPackages, - (PackageInfo pkg) => pkg.pubspecInfo.pubspec, - ); - for (final package in sortedPackages) { - final changes = changesForPackage(package); - final commits = changes.commitsByPackage[package]?.toSet() ?? const {}; - for (final commit in commits) { - // TODO(dnys1): Define full set of commit types which should be ignored - // when considering version changes. - if (commit.isVersionBump || - commit.type == CommitType.merge && commit.taggedPr == null) { - continue; - } - bumpVersion( - package, - commit: commit, - propogate: commit.isBreakingChange, - ); - // Propogate the version change to all packages affected by the same - // commit. - changes.packagesByCommit[commit]?.forEach((commitPackage) { - bumpDependency(package, commitPackage); - }); - } - } - - return VersionChanges( - updatedChangelogs: changelogUpdates, - updatedVersions: versionUpdates, - ); -} - -class VersionChanges { - const VersionChanges({ - required this.updatedChangelogs, - required this.updatedVersions, - }); - - final Map updatedVersions; - final Map updatedChangelogs; } diff --git a/packages/aft/lib/src/models.dart b/packages/aft/lib/src/models.dart index 70e1e62d3e..f60b6353d6 100644 --- a/packages/aft/lib/src/models.dart +++ b/packages/aft/lib/src/models.dart @@ -297,8 +297,6 @@ const yamlSerializable = JsonSerializable( disallowUnrecognizedKeys: true, ); -enum PubTrack { stable, prerelease } - /// The typed representation of the `aft.yaml` file. @yamlSerializable @_VersionConstraintConverter() @@ -306,7 +304,6 @@ class AftConfig { const AftConfig({ this.dependencies = const {}, this.ignore = const [], - this.branches = const {PubTrack.stable: 'main'}, this.components = const {}, }); @@ -321,19 +318,18 @@ class AftConfig { /// Packages to ignore in all repo operations. final List ignore; - /// Branch names which map to pub.dev stable and prerelease tracks. - final Map branches; - /// Strongly connected components which should have minor/major version bumps /// happen in unison, i.e. a version bump to one package cascades to all. final Map> components; - /// Retrieves the component for [package], if any. - String componentForPackage(String package) { - return components.entries.firstWhereOrNull((component) { - return component.value.contains(package); - })?.key ?? - package; + /// Retrieves the component for [packageName], if any. + String componentForPackage(String packageName) { + return components.entries + .firstWhereOrNull( + (component) => component.value.contains(packageName), + ) + ?.key ?? + packageName; } Map toJson() => _$AftConfigToJson(this); diff --git a/packages/aft/lib/src/models.g.dart b/packages/aft/lib/src/models.g.dart index 2f0d573fa1..f97ade3a7b 100644 --- a/packages/aft/lib/src/models.g.dart +++ b/packages/aft/lib/src/models.g.dart @@ -12,12 +12,7 @@ AftConfig _$AftConfigFromJson(Map json) => $checkedCreate( ($checkedConvert) { $checkKeys( json, - allowedKeys: const [ - 'dependencies', - 'ignore', - 'branches', - 'components' - ], + allowedKeys: const ['dependencies', 'ignore', 'components'], ); final val = AftConfig( dependencies: $checkedConvert( @@ -35,14 +30,6 @@ AftConfig _$AftConfigFromJson(Map json) => $checkedCreate( (v) => (v as List?)?.map((e) => e as String).toList() ?? const []), - branches: $checkedConvert( - 'branches', - (v) => - (v as Map?)?.map( - (k, e) => MapEntry( - $enumDecode(_$PubTrackEnumMap, k), e as String), - ) ?? - const {PubTrack.stable: 'main'}), components: $checkedConvert( 'components', (v) => @@ -60,16 +47,9 @@ Map _$AftConfigToJson(AftConfig instance) => { 'dependencies': instance.dependencies.map( (k, e) => MapEntry(k, const _VersionConstraintConverter().toJson(e))), 'ignore': instance.ignore, - 'branches': - instance.branches.map((k, e) => MapEntry(_$PubTrackEnumMap[k]!, e)), 'components': instance.components, }; -const _$PubTrackEnumMap = { - PubTrack.stable: 'stable', - PubTrack.prerelease: 'prerelease', -}; - SdkConfig _$SdkConfigFromJson(Map json) => $checkedCreate( 'SdkConfig', json, diff --git a/packages/aft/lib/src/options/git_ref_options.dart b/packages/aft/lib/src/options/git_ref_options.dart index c7a8c3b665..fd6cd9879d 100644 --- a/packages/aft/lib/src/options/git_ref_options.dart +++ b/packages/aft/lib/src/options/git_ref_options.dart @@ -12,14 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:convert'; import 'dart:io'; import 'package:aft/aft.dart'; -import 'package:aft/src/changelog/changelog.dart'; -import 'package:aft/src/changelog/commit_message.dart'; -import 'package:aft/src/changelog/printer.dart'; -import 'package:pub_semver/pub_semver.dart'; /// Adds git ref options and functionality to a command. mixin GitRefOptions on AmplifyCommand { diff --git a/packages/aft/lib/src/options/glob_options.dart b/packages/aft/lib/src/options/glob_options.dart index 09386ae5bb..98168bd2b1 100644 --- a/packages/aft/lib/src/options/glob_options.dart +++ b/packages/aft/lib/src/options/glob_options.dart @@ -13,7 +13,6 @@ // limitations under the License. import 'package:aft/aft.dart'; -import 'package:glob/glob.dart'; /// Adds globbing options to a command. mixin GlobOptions on AmplifyCommand { @@ -21,32 +20,41 @@ mixin GlobOptions on AmplifyCommand { void init() { super.init(); argParser - ..addMultiOption('include', help: 'Glob of packages to include') - ..addMultiOption('exclude', help: 'Glob of packages to exclude'); + ..addMultiOption( + 'include', + help: 'Package or component names to include', + ) + ..addMultiOption( + 'exclude', + help: 'Package or component names to exclude', + ); } - /// Globs of packages which should be included in versioning. - late final include = (argResults?['include'] as List? ?? const []) - .map(Glob.new) - .toList(); + /// List of packages or components which should be included in versioning. + late final include = argResults?['include'] as List? ?? const []; - /// Globs of packages which should be excluded from versioning. - late final exclude = (argResults?['exclude'] as List? ?? const []) - .map(Glob.new) - .toList(); + /// List of packages or components which should be excluded from versioning. + late final exclude = argResults?['exclude'] as List? ?? const []; @override Map get allPackages { return Map.fromEntries( super.allPackages.entries.where((entry) { final package = entry.value; - for (final glob in include) { - if (glob.matches(package.path)) { - return true; + if (include.isNotEmpty) { + for (final packageOrComponent in include) { + if (package.name == packageOrComponent || + aftConfig.componentForPackage(package.name) == + packageOrComponent) { + return true; + } } + return false; } - for (final glob in exclude) { - if (glob.matches(package.path)) { + for (final packageOrComponent in exclude) { + if (package.name == packageOrComponent || + aftConfig.componentForPackage(package.name) == + packageOrComponent) { return false; } } diff --git a/packages/aft/lib/src/repo.dart b/packages/aft/lib/src/repo.dart index 2ac2403f25..2c3dfa116a 100644 --- a/packages/aft/lib/src/repo.dart +++ b/packages/aft/lib/src/repo.dart @@ -12,14 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'dart:collection'; import 'dart:io'; import 'package:aft/aft.dart'; import 'package:aft/src/changelog/changelog.dart'; import 'package:aft/src/changelog/commit_message.dart'; +import 'package:aft/src/util.dart'; import 'package:aws_common/aws_common.dart'; import 'package:built_collection/built_collection.dart'; -import 'package:checked_yaml/checked_yaml.dart'; import 'package:collection/collection.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:path/path.dart' as p; @@ -28,61 +29,27 @@ import 'package:pub_semver/pub_semver.dart'; /// Encapsulates all repository functionality including package and Git /// management. class Repo { - Repo(this.rootDir, {AWSLogger? logger}) - : logger = logger ?? AWSLogger('Repo'); + Repo( + this.rootDir, { + required this.allPackages, + required this.aftConfig, + AWSLogger? logger, + }) : logger = logger ?? AWSLogger('Repo'); /// The root directory of the repository. final Directory rootDir; final AWSLogger logger; - /// All packages in the Amplify Flutter repo. - late final Map allPackages = () { - final allDirs = rootDir - .listSync(recursive: true, followLinks: false) - .whereType(); - final allPackages = []; - for (final dir in allDirs) { - final pubspecInfo = dir.pubspec; - if (pubspecInfo == null) { - continue; - } - final pubspec = pubspecInfo.pubspec; - if (aftConfig.ignore.contains(pubspec.name)) { - continue; - } - allPackages.add( - PackageInfo( - name: pubspec.name, - path: dir.path, - usesMonoRepo: dir.usesMonoRepo, - pubspecInfo: pubspecInfo, - flavor: pubspec.flavor, - ), - ); - } - return UnmodifiableMapView({ - for (final package in allPackages..sort()) package.name: package, - }); - }(); + final Map allPackages; - late final List publishablePackages = - allPackages.values.where((pkg) => !pkg.isExample).toList(); + final AftConfig aftConfig; - /// The absolute path to the `aft.yaml` document. - late final String aftConfigPath = () { - final rootDir = this.rootDir; - return p.join(rootDir.path, 'aft.yaml'); - }(); - - /// The global `aft` configuration for the repo. - late final AftConfig aftConfig = () { - final configFile = File(p.join(rootDir.path, 'aft.yaml')); - assert(configFile.existsSync(), 'Could not find aft.yaml'); - final configYaml = configFile.readAsStringSync(); - return checkedYamlDecode(configYaml, AftConfig.fromJson); - }(); + late final List publishablePackages = UnmodifiableListView( + allPackages.values.where((pkg) => !pkg.isExample).toList(), + ); + /// The components of the late final Map> components = aftConfig.components.map((component, packages) { return MapEntry( @@ -120,13 +87,14 @@ class Repo { ); /// The directed graph of packages to the packages it depends on. - late final Map> packageGraph = { + late final Map> packageGraph = + UnmodifiableMapView({ for (final package in allPackages.values) package: package.pubspecInfo.pubspec.dependencies.keys .map((packageName) => allPackages[packageName]) .whereType() .toList(), - }; + }); /// The reversed (transposed) [packageGraph]. /// @@ -142,15 +110,9 @@ class Repo { reversedPackageGraph[dep]!.add(entry.key); } } - return reversedPackageGraph; + return UnmodifiableMapView(reversedPackageGraph); }(); - /// The git diff between [baseRef] and [headRef]. - Diff diffRefs(String baseRef, String headRef) => diffTrees( - RevParse.single(repo: repo, spec: '$baseRef^{tree}') as Tree, - RevParse.single(repo: repo, spec: '$headRef^{tree}') as Tree, - ); - /// The git diff between [oldTree] and [newTree]. Diff diffTrees(Tree oldTree, Tree newTree) => Diff.treeToTree( repo: repo, @@ -163,6 +125,8 @@ class Repo { /// Collect all the packages which have changed between [baseRef]..[headRef] /// and the commits which changed them. GitChanges changes(String baseRef, String headRef) { + // TODO(dnys1): Diff with index if headRef is null to include uncommitted + // changes? final baseTree = RevParse.single( repo: repo, spec: '$baseRef^{tree}', @@ -235,6 +199,125 @@ class Repo { packagesByCommit: packagesByCommit.build(), ); } + + late final versionChanges = VersionChanges(this); + + /// Changelog updates. by package. + final Map changelogUpdates = {}; + + /// Bumps the version for all packages in the repo. + void bumpAllVersions({ + required GitChanges Function(PackageInfo) changesForPackage, + }) { + final sortedPackages = List.of(publishablePackages); + sortPackagesTopologically( + sortedPackages, + (PackageInfo pkg) => pkg.pubspecInfo.pubspec, + ); + for (final package in sortedPackages) { + final changes = changesForPackage(package); + changes.commitsByPackage[package]?.forEach((commit) { + // TODO(dnys1): Define full set of commit types which should be ignored + // when considering version changes. + if (commit.isVersionBump || + commit.type == CommitType.merge && commit.taggedPr == null) { + return; + } + bumpVersion( + package, + commit: commit, + propagate: commit.isBreakingChange, + ); + // Propagate the version change to all packages affected by the same + // commit as if they were a component. + changes.packagesByCommit[commit]?.forEach((commitPackage) { + bumpDependency(package, commitPackage); + }); + }); + } + } + + /// Bumps the version and changelog in [package] using [commit] and returns + /// the new version. + /// + /// If [propagate] is `true`, the version change is propagated to all packages + /// which depend on [package]. + Version bumpVersion( + PackageInfo package, { + bool propagate = false, + CommitMessage? commit, + }) { + final component = aftConfig.componentForPackage(package.name); + final currentVersion = package.version; + final currentProposedVersion = versionChanges.newVersion(package); + final isBreakingChange = commit?.isBreakingChange ?? false; + final newProposedVersion = currentVersion.nextAmplifyVersion( + isBreakingChange: isBreakingChange, + ); + final newVersion = maxBy( + [currentProposedVersion, newProposedVersion], + (version) => version, + )!; + versionChanges.updateVersion(package, newVersion); + + if (newVersion > currentVersion) { + logger.debug( + 'Bumping ${package.name} from $currentProposedVersion to $newVersion: ' + '${commit?.summary}', + ); + package.pubspecInfo.pubspecYamlEditor.update( + ['version'], + newVersion.toString(), + ); + final currentChangelogUpdate = changelogUpdates[package]; + changelogUpdates[package] = package.changelog.update( + commits: { + ...?currentChangelogUpdate?.commits, + if (commit != null) commit, + }, + version: newVersion, + ); + + if (propagate) { + // Propagate to all dependent packages. + dfs( + reversedPackageGraph, + root: package, + (dependent) { + if (!dependent.isExample) { + bumpVersion(dependent, commit: commit); + } + bumpDependency(package, dependent); + }, + ); + + // Propagate to all component packages. + components[component]?.forEach((componentPackage) { + bumpVersion(componentPackage, commit: commit); + dfs( + reversedPackageGraph, + root: componentPackage, + (dependent) { + bumpDependency(componentPackage, dependent); + }, + ); + }); + } + } + + return newVersion; + } + + /// Bumps the dependency for [package] in [dependent]. + void bumpDependency(PackageInfo package, PackageInfo dependent) { + final newVersion = versionChanges.newVersion(package); + if (dependent.pubspecInfo.pubspec.dependencies.containsKey(package.name)) { + dependent.pubspecInfo.pubspecYamlEditor.update( + ['dependencies', package.name], + newVersion.amplifyConstraint(minVersion: newVersion), + ); + } + } } class GitChanges { @@ -256,3 +339,35 @@ class _DiffMarker with AWSEquatable<_DiffMarker> { @override List get props => [baseTree, headTree]; } + +class VersionChanges extends MapBase + with UnmodifiableMapMixin { + VersionChanges(this._repo); + + final Repo _repo; + + /// Version updates, by component. + final Map _versionUpdates = {}; + + /// The latest proposed version for [package]. + Version newVersion(PackageInfo package) { + final component = _repo.aftConfig.componentForPackage(package.name); + return _versionUpdates[component] ?? package.version; + } + + /// Updates the proposed version for [package]. + void updateVersion(PackageInfo package, Version version) { + final currentVersion = newVersion(package); + if (version <= currentVersion) { + return; + } + final component = _repo.aftConfig.componentForPackage(package.name); + _versionUpdates[component] = version; + } + + @override + Version? operator [](Object? key) => _versionUpdates[key]; + + @override + Iterable get keys => _versionUpdates.keys; +} diff --git a/packages/aft/lib/src/util.dart b/packages/aft/lib/src/util.dart index 8151878cd9..efd65c7ba7 100644 --- a/packages/aft/lib/src/util.dart +++ b/packages/aft/lib/src/util.dart @@ -16,6 +16,8 @@ import 'dart:convert'; import 'dart:io'; import 'package:async/async.dart'; +import 'package:graphs/graphs.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; typedef ProcessSink = void Function(String); @@ -74,3 +76,27 @@ void dfs( graph.forEach(search); } } + +/// Sorts packages in topological order so they may be published in the order +/// they're sorted. +/// +/// Packages with inter-dependencies cannot be topologically sorted and will +/// throw a [CycleException]. +void sortPackagesTopologically( + List packages, + Pubspec Function(T) getPubspec, +) { + final pubspecs = packages.map(getPubspec); + final packageNames = pubspecs.map((el) => el.name).toList(); + final graph = >{ + for (var package in pubspecs) + package.name: package.dependencies.keys.where(packageNames.contains), + }; + final ordered = topologicalSort(graph.keys, (key) => graph[key]!); + packages.sort((a, b) { + // `ordered` is in reverse ordering to our desired publish precedence. + return ordered + .indexOf(getPubspec(b).name) + .compareTo(ordered.indexOf(getPubspec(a).name)); + }); +} diff --git a/packages/aft/test/e2e_test.dart b/packages/aft/test/e2e_test.dart index 343fb3d8b1..f1d91fd859 100644 --- a/packages/aft/test/e2e_test.dart +++ b/packages/aft/test/e2e_test.dart @@ -25,23 +25,15 @@ import 'package:pub_semver/pub_semver.dart'; import 'package:test/test.dart'; class MockRepo extends Repo { - MockRepo(super.rootDir, {required this.repo, super.logger}); + MockRepo( + super.rootDir, { + required this.repo, + required super.aftConfig, + super.logger, + }) : super(allPackages: {}); @override final Repository repo; - - @override - final AftConfig aftConfig = const AftConfig( - components: { - 'amplify': [ - 'amplify_auth_cognito', - 'amplify_auth_cognito_ios', - ], - }, - ); - - @override - final Map allPackages = {}; } void main() { @@ -51,7 +43,6 @@ void main() { Future runGit(List args) => git.runGit( args, processWorkingDir: repo.rootDir.path, - throwOnError: true, ); PackageInfo createPackage( @@ -120,6 +111,14 @@ Initial version. repo = MockRepo( gitDir, repo: Repository.init(path: gitDir.path), + aftConfig: const AftConfig( + components: { + 'amplify': [ + 'amplify_auth_cognito', + 'amplify_auth_cognito_ios', + ], + }, + ), ); await runGit(['commit', '--allow-empty', '-m', 'Initial commit']); }); @@ -297,10 +296,8 @@ Initial version. 'amplify_auth_cognito_ios': '1.0.0-next.1', }; - late VersionChanges updates; setUp(() async { - updates = await bumpVersions( - repo: repo, + repo.bumpAllVersions( changesForPackage: changesForPackage, ); }); @@ -309,8 +306,7 @@ Initial version. final packageName = check.key; test(packageName, () { final package = repo.allPackages[packageName]!; - final component = repo.aftConfig.componentForPackage(package.name); - final newVersion = updates.updatedVersions[component]!; + final newVersion = repo.versionChanges.newVersion(package); expect(newVersion.toString(), finalVersions[packageName]); }); } diff --git a/packages/aft/test/publish_command_test.dart b/packages/aft/test/util_test.dart similarity index 98% rename from packages/aft/test/publish_command_test.dart rename to packages/aft/test/util_test.dart index bc808687f4..b180467c31 100644 --- a/packages/aft/test/publish_command_test.dart +++ b/packages/aft/test/util_test.dart @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:aft/aft.dart'; +import 'package:aft/src/util.dart'; import 'package:graphs/graphs.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; From 4c1b36d9aa8fc09117482a597683ca219538021b Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Tue, 30 Aug 2022 08:27:15 -0700 Subject: [PATCH 06/40] chore(aft): Update README commit-id:8261f867 --- packages/aft/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/aft/README.md b/packages/aft/README.md index dedac9ce94..c06a94eff0 100644 --- a/packages/aft/README.md +++ b/packages/aft/README.md @@ -5,6 +5,7 @@ A CLI tool for managing the Amplify Flutter repository. ## Commands - `bootstrap`/`bs`: Sets up repo for development work +- `changelog`: Updates changelogs without affecting version - `clean`: Cleans temporary files and build artifacts for all packages - `deps`: Manages dependencies of all packages in the repo - `check`: Checks dependencies against `aft.yaml`, for use in CI @@ -17,3 +18,24 @@ A CLI tool for managing the Amplify Flutter repository. - `get`: Runs `dart pub get`/`flutter pub get` for all packages - `upgrade`: Runs `dart pub upgrade`/`flutter pub upgrade` for all packages - `publish`: Runs `dart pub publish`/`flutter pub publish` for all packages which need publishing +- `version`: Bumps version using git history + +## Setup + +To run some commands, `libgit2` is required and can be installed with the following commands: + +```sh +$ brew install libgit2 +``` + +```sh +$ sudo apt-get install libgit2-dev +``` + +To activate `aft`, run: + +```sh +$ dart pub global activate -spath packages/aft +``` + +A full list of available commands and options can be found by running `aft --help`. From a4eb22bdf4062a474c94e4785586a47690bb8b3c Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Tue, 30 Aug 2022 08:45:11 -0700 Subject: [PATCH 07/40] chore(aft): Update tests Expand e2e tests to incude changelog/pubspec changes commit-id:e9757240 --- packages/aft/test/e2e_test.dart | 98 +++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/packages/aft/test/e2e_test.dart b/packages/aft/test/e2e_test.dart index f1d91fd859..e86e90f261 100644 --- a/packages/aft/test/e2e_test.dart +++ b/packages/aft/test/e2e_test.dart @@ -295,6 +295,92 @@ Initial version. 'amplify_auth_cognito': '1.0.0-next.1', 'amplify_auth_cognito_ios': '1.0.0-next.1', }; + final updatedChangelogs = { + 'aws_common': ''' +## 0.1.1 + +### Fixes +- fix(aws_common): Fix type +''', + 'amplify_core': ''' +## 1.0.0-next.0+1 + +### Features +- feat(amplify_core): New hub events +- feat(auth): New feature +''', + 'amplify_auth_cognito_dart': ''' +## 0.1.1 + +### Features +- feat(auth): New feature +''', + 'amplify_auth_cognito': ''' +## 1.0.0-next.1 + +### Breaking Changes +- fix(amplify_auth_cognito_ios)!: Change iOS dependency + +### Features +- feat(auth): New feature +''', + 'amplify_auth_cognito_ios': ''' +## 1.0.0-next.1 + +### Breaking Changes +- fix(amplify_auth_cognito_ios)!: Change iOS dependency +''', + }; + final updatedPubspecs = { + 'aws_common': ''' +name: aws_common +version: 0.1.1 + +environment: + sdk: '>=2.17.0 <3.0.0' +''', + 'amplify_core': ''' +name: amplify_core +version: 1.0.0-next.0+1 + +environment: + sdk: '>=2.17.0 <3.0.0' + +dependencies: + aws_common: ^0.1.0 +''', + 'amplify_auth_cognito_dart': ''' +name: amplify_auth_cognito_dart +version: 0.1.1 + +environment: + sdk: '>=2.17.0 <3.0.0' + +dependencies: + amplify_core: ">=1.0.0-next.0+1 <1.0.0-next.1" + aws_common: ^0.1.0 +''', + 'amplify_auth_cognito': ''' +name: amplify_auth_cognito +version: 1.0.0-next.1 + +environment: + sdk: '>=2.17.0 <3.0.0' + +dependencies: + amplify_auth_cognito_ios: ">=1.0.0-next.1 <1.0.0-next.2" + amplify_auth_cognito_dart: ">=0.1.1 <0.2.0" + amplify_core: ">=1.0.0-next.0+1 <1.0.0-next.1" + aws_common: ^0.1.0 +''', + 'amplify_auth_cognito_ios': ''' +name: amplify_auth_cognito_ios +version: 1.0.0-next.1 + +environment: + sdk: '>=2.17.0 <3.0.0' +''', + }; setUp(() async { repo.bumpAllVersions( @@ -308,6 +394,18 @@ Initial version. final package = repo.allPackages[packageName]!; final newVersion = repo.versionChanges.newVersion(package); expect(newVersion.toString(), finalVersions[packageName]); + + final changelog = repo.changelogUpdates[package]!.newText; + expect( + changelog, + equalsIgnoringWhitespace(updatedChangelogs[packageName]!), + ); + + final pubspec = package.pubspecInfo.pubspecYamlEditor.toString(); + expect( + pubspec, + equalsIgnoringWhitespace(updatedPubspecs[packageName]!), + ); }); } }); From 005559f0d23bb2d148da0b9a0302a32078ba6a02 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Mon, 12 Sep 2022 18:11:22 -0700 Subject: [PATCH 08/40] chore(aft): Update dependencies commit-id:4509edf2 --- packages/aft/pubspec.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/aft/pubspec.yaml b/packages/aft/pubspec.yaml index af979cd6b9..f3551231f1 100644 --- a/packages/aft/pubspec.yaml +++ b/packages/aft/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: async: ^2.8.0 aws_common: ">=0.3.0 <0.4.0" built_collection: ^5.0.0 - built_value: ^8.0.0 + built_value: ">=8.4.0 <8.5.0" checked_yaml: ^2.0.0 cli_util: ^0.3.5 collection: ^1.16.0 @@ -44,7 +44,7 @@ dependencies: yaml: ^3.1.0 yaml_edit: ^2.0.3 -# These need to match `flutter_tools` +# These are needed to override `flutter_tools` dependency_overrides: built_value: ">=8.4.0 <8.5.0" frontend_server_client: ^3.0.0 @@ -53,7 +53,7 @@ dev_dependencies: amplify_lints: path: ../amplify_lints build_runner: ^2.0.0 - built_value_generator: ^8.0.0 + built_value_generator: 8.4.1 json_serializable: 6.5.4 test: ^1.16.0 From 4f364f2d7a565d9de804a893ebe17b6ecd64e4b6 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Thu, 15 Sep 2022 15:22:23 -0700 Subject: [PATCH 09/40] chore(aft): Bump dependencies commit-id:14f44d17 --- packages/aft/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aft/pubspec.yaml b/packages/aft/pubspec.yaml index f3551231f1..6da5a937d0 100644 --- a/packages/aft/pubspec.yaml +++ b/packages/aft/pubspec.yaml @@ -52,7 +52,7 @@ dependency_overrides: dev_dependencies: amplify_lints: path: ../amplify_lints - build_runner: ^2.0.0 + build_runner: ^2.2.1 built_value_generator: 8.4.1 json_serializable: 6.5.4 test: ^1.16.0 From a6a35799f50a601a427ce1f0e1e0661b1484608e Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Thu, 15 Sep 2022 15:23:00 -0700 Subject: [PATCH 10/40] chore(aft): Enforce changelog update includes commits Changelog updates should only be made with a non-empty list of commits. commit-id:4eccafce --- packages/aft/lib/src/repo.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/aft/lib/src/repo.dart b/packages/aft/lib/src/repo.dart index 2c3dfa116a..d0891101f5 100644 --- a/packages/aft/lib/src/repo.dart +++ b/packages/aft/lib/src/repo.dart @@ -245,12 +245,12 @@ class Repo { Version bumpVersion( PackageInfo package, { bool propagate = false, - CommitMessage? commit, + required CommitMessage commit, }) { final component = aftConfig.componentForPackage(package.name); final currentVersion = package.version; final currentProposedVersion = versionChanges.newVersion(package); - final isBreakingChange = commit?.isBreakingChange ?? false; + final isBreakingChange = commit.isBreakingChange; final newProposedVersion = currentVersion.nextAmplifyVersion( isBreakingChange: isBreakingChange, ); @@ -263,7 +263,7 @@ class Repo { if (newVersion > currentVersion) { logger.debug( 'Bumping ${package.name} from $currentProposedVersion to $newVersion: ' - '${commit?.summary}', + '${commit.summary}', ); package.pubspecInfo.pubspecYamlEditor.update( ['version'], @@ -273,7 +273,7 @@ class Repo { changelogUpdates[package] = package.changelog.update( commits: { ...?currentChangelogUpdate?.commits, - if (commit != null) commit, + commit, }, version: newVersion, ); From 1f011c5ab2edae99bcd1c60ac7d587e1dd57ee95 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Thu, 15 Sep 2022 15:28:42 -0700 Subject: [PATCH 11/40] chore(aft): Add `promptYesNo` helper commit-id:8544885f --- packages/aft/lib/src/commands/amplify_command.dart | 6 ++++++ packages/aft/lib/src/commands/changelog_command.dart | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/aft/lib/src/commands/amplify_command.dart b/packages/aft/lib/src/commands/amplify_command.dart index 381c8e65ae..4603ecd668 100644 --- a/packages/aft/lib/src/commands/amplify_command.dart +++ b/packages/aft/lib/src/commands/amplify_command.dart @@ -188,6 +188,12 @@ abstract class AmplifyCommand extends Command return response; } + /// Displays a yes/no prompt and returns whether the answer was positive. + bool promptYesNo(String message) { + final answer = prompt(message).toLowerCase(); + return answer == 'y' || answer == 'yes'; + } + @override @mustCallSuper void close() { diff --git a/packages/aft/lib/src/commands/changelog_command.dart b/packages/aft/lib/src/commands/changelog_command.dart index c0e7046aa5..d560a23f43 100644 --- a/packages/aft/lib/src/commands/changelog_command.dart +++ b/packages/aft/lib/src/commands/changelog_command.dart @@ -101,7 +101,7 @@ class _ChangelogUpdateCommand extends _ChangelogBaseCommand { await _updateChangelogs(preview: false); logger.info('Changelogs successfully updated'); - if (yes || prompt('Commit changes? (y/N) ').toLowerCase() == 'y') { + if (yes || promptYesNo('Commit changes? (y/N) ')) { await runGit(['add', '.']); await runGit(['commit', '-m', 'chore(version): Update changelogs']); } From a2c747006604266710b11a6eced6525e4402d8ec Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Thu, 15 Sep 2022 15:44:29 -0700 Subject: [PATCH 12/40] chore(aft): Refine `isExample` Refine the definition of `isExample` to be more precise and not require workarounds. commit-id:51cc0ac0 --- .../lib/src/commands/bootstrap_command.dart | 3 +-- .../lib/src/commands/changelog_command.dart | 2 +- .../aft/lib/src/commands/version_command.dart | 2 +- packages/aft/lib/src/models.dart | 21 ++++++++++++------- packages/aft/lib/src/repo.dart | 10 ++++----- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/packages/aft/lib/src/commands/bootstrap_command.dart b/packages/aft/lib/src/commands/bootstrap_command.dart index 89382e8060..e94aa40bd1 100644 --- a/packages/aft/lib/src/commands/bootstrap_command.dart +++ b/packages/aft/lib/src/commands/bootstrap_command.dart @@ -52,8 +52,7 @@ class BootstrapCommand extends AmplifyCommand { /// Creates an empty `amplifyconfiguration.dart` file. Future _createEmptyConfig(PackageInfo package) async { // Only create for example apps. - if (package.pubspecInfo.pubspec.publishTo == null || - falsePositiveExamples.contains(package.name)) { + if (!package.isExample) { return; } final file = File( diff --git a/packages/aft/lib/src/commands/changelog_command.dart b/packages/aft/lib/src/commands/changelog_command.dart index d560a23f43..a279c0de81 100644 --- a/packages/aft/lib/src/commands/changelog_command.dart +++ b/packages/aft/lib/src/commands/changelog_command.dart @@ -51,7 +51,7 @@ abstract class _ChangelogBaseCommand extends AmplifyCommand late final bool yes = argResults!['yes'] as bool; Future _updateChangelogs({required bool preview}) async { - for (final package in repo.publishablePackages) { + for (final package in repo.developmentPackages) { final baseRef = this.baseRef ?? repo.latestTag(package.name); if (baseRef == null) { exitError( diff --git a/packages/aft/lib/src/commands/version_command.dart b/packages/aft/lib/src/commands/version_command.dart index 9efe4c1ac1..1c61baf9de 100644 --- a/packages/aft/lib/src/commands/version_command.dart +++ b/packages/aft/lib/src/commands/version_command.dart @@ -66,7 +66,7 @@ abstract class _VersionBaseCommand extends AmplifyCommand ); final changelogUpdates = repo.changelogUpdates; - for (final package in repo.publishablePackages) { + for (final package in repo.developmentPackages) { final edits = package.pubspecInfo.pubspecYamlEditor.edits; if (edits.isNotEmpty) { if (preview) { diff --git a/packages/aft/lib/src/models.dart b/packages/aft/lib/src/models.dart index f60b6353d6..50a5af94b0 100644 --- a/packages/aft/lib/src/models.dart +++ b/packages/aft/lib/src/models.dart @@ -28,10 +28,6 @@ import 'package:yaml_edit/yaml_edit.dart'; part 'models.g.dart'; -/// Packages which report as an example app, but should be considered as -/// publishable for some purposes. -const falsePositiveExamples = ['aft', 'smithy_codegen']; - /// The flavor of a package, e.g. Dart/Flutter. enum PackageFlavor { flutter('Flutter', 'flutter'), @@ -95,10 +91,19 @@ class PackageInfo !isExample; } + /// Whether the package is used in development. + bool get isDevelopmentPackage => !isExample && !isTestPackage; + /// Whether the package is an example package. bool get isExample { - return pubspecInfo.pubspec.publishTo == 'none' || - falsePositiveExamples.contains(name); + return p.basename(path) == 'example'; + } + + /// Whether the package is a test package. + bool get isTestPackage { + return p.basename(path).endsWith('_test') || + path.contains('goldens') || + p.basename(path).contains('e2e'); } /// The parsed `CHANGELOG.md`. @@ -108,7 +113,9 @@ class PackageInfo } /// The current version in `pubspec.yaml`. - Version get version => pubspecInfo.pubspec.version!; + Version get version => + pubspecInfo.pubspec.version ?? + (throw StateError('No version for package: $name')); /// Resolves the latest version information from `pub.dev`. Future resolveVersionInfo(http.Client client) async { diff --git a/packages/aft/lib/src/repo.dart b/packages/aft/lib/src/repo.dart index d0891101f5..67d3527cc9 100644 --- a/packages/aft/lib/src/repo.dart +++ b/packages/aft/lib/src/repo.dart @@ -45,8 +45,8 @@ class Repo { final AftConfig aftConfig; - late final List publishablePackages = UnmodifiableListView( - allPackages.values.where((pkg) => !pkg.isExample).toList(), + late final List developmentPackages = UnmodifiableListView( + allPackages.values.where((pkg) => pkg.isDevelopmentPackage).toList(), ); /// The components of the @@ -153,7 +153,7 @@ class Repo { ); if (changedPackage != null && // Do not track example packages for git ops - !changedPackage.isExample) { + changedPackage.isDevelopmentPackage) { changedPackages.add(changedPackage); } } @@ -209,7 +209,7 @@ class Repo { void bumpAllVersions({ required GitChanges Function(PackageInfo) changesForPackage, }) { - final sortedPackages = List.of(publishablePackages); + final sortedPackages = List.of(developmentPackages); sortPackagesTopologically( sortedPackages, (PackageInfo pkg) => pkg.pubspecInfo.pubspec, @@ -284,7 +284,7 @@ class Repo { reversedPackageGraph, root: package, (dependent) { - if (!dependent.isExample) { + if (dependent.isDevelopmentPackage) { bumpVersion(dependent, commit: commit); } bumpDependency(package, dependent); From 0dbd9f8576cb64a5705c0bdfb5b5b5d159a9b314 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Thu, 15 Sep 2022 15:51:32 -0700 Subject: [PATCH 13/40] chore(aft): Change `aft version` to `aft version-bump` commit-id:0526b477 --- packages/aft/README.md | 2 +- .../aft/lib/src/commands/version_command.dart | 82 ++++++++----------- 2 files changed, 33 insertions(+), 51 deletions(-) diff --git a/packages/aft/README.md b/packages/aft/README.md index c06a94eff0..1f41ca6db4 100644 --- a/packages/aft/README.md +++ b/packages/aft/README.md @@ -18,7 +18,7 @@ A CLI tool for managing the Amplify Flutter repository. - `get`: Runs `dart pub get`/`flutter pub get` for all packages - `upgrade`: Runs `dart pub upgrade`/`flutter pub upgrade` for all packages - `publish`: Runs `dart pub publish`/`flutter pub publish` for all packages which need publishing -- `version`: Bumps version using git history +- `version-bump`: Bumps version using git history ## Setup diff --git a/packages/aft/lib/src/commands/version_command.dart b/packages/aft/lib/src/commands/version_command.dart index 1c61baf9de..fa8ec0d47d 100644 --- a/packages/aft/lib/src/commands/version_command.dart +++ b/packages/aft/lib/src/commands/version_command.dart @@ -21,10 +21,22 @@ import 'package:aft/src/repo.dart'; import 'package:path/path.dart' as p; /// Command for bumping package versions across the repo. -class VersionCommand extends AmplifyCommand { +class VersionCommand extends AmplifyCommand with GitRefOptions, GlobOptions { VersionCommand() { - addSubcommand(_VersionUpdateCommand()); - addSubcommand(_VersionPreviewCommand()); + argParser + ..addFlag( + 'preview', + help: 'Preview version changes without applying', + defaultsTo: false, + negatable: false, + ) + ..addFlag( + 'yes', + abbr: 'y', + help: 'Responds "yes" to all prompts', + defaultsTo: false, + negatable: false, + ); } @override @@ -32,23 +44,12 @@ class VersionCommand extends AmplifyCommand { 'Bump package versions automatically using git magic'; @override - String get name => 'version'; -} - -abstract class _VersionBaseCommand extends AmplifyCommand - with GitRefOptions, GlobOptions { - _VersionBaseCommand() { - argParser.addFlag( - 'yes', - abbr: 'y', - help: 'Responds "yes" to all prompts', - defaultsTo: false, - negatable: false, - ); - } + String get name => 'version-bump'; late final bool yes = argResults!['yes'] as bool; + late final bool preview = argResults!['preview'] as bool; + GitChanges _changesForPackage(PackageInfo package) { final baseRef = this.baseRef ?? repo.latestTag(package.name); if (baseRef == null) { @@ -60,7 +61,7 @@ abstract class _VersionBaseCommand extends AmplifyCommand return repo.changes(baseRef, headRef); } - Future _updateVersions({required bool preview}) async { + Future _updateVersions() async { repo.bumpAllVersions( changesForPackage: _changesForPackage, ); @@ -94,41 +95,22 @@ abstract class _VersionBaseCommand extends AmplifyCommand } } } -} - -class _VersionPreviewCommand extends _VersionBaseCommand { - @override - String get description => 'Previews changes to package versions'; - - @override - String get name => 'preview'; - - @override - Future run() async { - return _updateVersions(preview: true); - } -} - -class _VersionUpdateCommand extends _VersionBaseCommand { - @override - String get description => 'Updates package versions'; - - @override - String get name => 'update'; @override Future run() async { - await _updateVersions(preview: false); - - logger.info('Version successfully bumped'); - if (yes || prompt('Commit changes? (y/N) ').toLowerCase() == 'y') { - // Commit and tag changes - await runGit(['add', '.']); - await runGit(['commit', '-m', 'chore(version): Bump version']); - await Future.wait([ - for (final changeEntry in repo.versionChanges.entries) - runGit(['tag', '${changeEntry.key}_v${changeEntry.value}']), - ]); + await _updateVersions(); + + if (!preview) { + logger.info('Version successfully bumped'); + if (yes || prompt('Commit changes? (y/N) ').toLowerCase() == 'y') { + // Commit and tag changes + await runGit(['add', '.']); + await runGit(['commit', '-m', 'chore(version): Bump version']); + await Future.wait([ + for (final changeEntry in repo.versionChanges.entries) + runGit(['tag', '${changeEntry.key}_v${changeEntry.value}']), + ]); + } } } } From 8196961415fea2cc722474ee129dd142c45ee55e Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Fri, 16 Sep 2022 09:05:35 -0700 Subject: [PATCH 14/40] chore(aft): Remove from mono_repo The package libgit2dart, while it can be used in Dart-only packages tricks pub into thinking it has a dependency on Flutter. mono_repo cannot handle this discrepancy. commit-id:ec27a8d2 --- .github/workflows/lint.yml | 19 +- .github/workflows/native_test.yml | 10 +- .github/workflows/test.yml | 323 +++++++++++++++--------------- packages/aft/mono_pkg.yaml | 10 - tool/ci.sh | 16 +- 5 files changed, 173 insertions(+), 205 deletions(-) delete mode 100644 packages/aft/mono_pkg.yaml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index cba6bfd064..9be787d2b3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -652,16 +652,16 @@ jobs: working-directory: packages/secure_storage/amplify_secure_storage_test run: dart analyze --fatal-infos lib test job_009: - name: "analyze_and_format; Dart stable; PKGS: packages/aft, packages/amplify_core, packages/auth/amplify_auth_cognito_test, packages/aws_common, packages/aws_signature_v4/example, packages/example_common, packages/secure_storage/amplify_secure_storage_dart/example, packages/smithy/goldens/lib/awsJson1_0, packages/smithy/goldens/lib/awsJson1_1, packages/smithy/goldens/lib/restJson1, packages/smithy/goldens/lib/restXml, packages/smithy/goldens/lib/restXmlWithNamespace, packages/smithy/goldens/lib2/awsJson1_0, packages/smithy/goldens/lib2/awsJson1_1, packages/smithy/goldens/lib2/custom, packages/smithy/goldens/lib2/restJson1, packages/smithy/goldens/lib2/restXml, packages/smithy/goldens/lib2/restXmlWithNamespace, packages/smithy/smithy, packages/smithy/smithy_aws, packages/smithy/smithy_codegen, packages/smithy/smithy_test, packages/worker_bee/e2e, packages/worker_bee/e2e_test, packages/worker_bee/worker_bee, packages/worker_bee/worker_bee_builder; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos .`" + name: "analyze_and_format; Dart stable; PKGS: packages/amplify_core, packages/auth/amplify_auth_cognito_test, packages/aws_common, packages/aws_signature_v4/example, packages/example_common, packages/secure_storage/amplify_secure_storage_dart/example, packages/smithy/goldens/lib/awsJson1_0, packages/smithy/goldens/lib/awsJson1_1, packages/smithy/goldens/lib/restJson1, packages/smithy/goldens/lib/restXml, packages/smithy/goldens/lib/restXmlWithNamespace, packages/smithy/goldens/lib2/awsJson1_0, packages/smithy/goldens/lib2/awsJson1_1, packages/smithy/goldens/lib2/custom, packages/smithy/goldens/lib2/restJson1, packages/smithy/goldens/lib2/restXml, packages/smithy/goldens/lib2/restXmlWithNamespace, packages/smithy/smithy, packages/smithy/smithy_aws, packages/smithy/smithy_codegen, packages/smithy/smithy_test, packages/worker_bee/e2e, packages/worker_bee/e2e_test, packages/worker_bee/worker_bee, packages/worker_bee/worker_bee_builder; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos .`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/aft-packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/aws_signature_v4/example-packages/example_common-packages/secure_storage/amplify_secure_storage_dart/example-packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/go-!!too_long!!-967-64838588" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/aws_signature_v4/example-packages/example_common-packages/secure_storage/amplify_secure_storage_dart/example-packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/a-!!too_long!!-954-610427231" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/aft-packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/aws_signature_v4/example-packages/example_common-packages/secure_storage/amplify_secure_storage_dart/example-packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/g-!!too_long!!-941-210486779 + os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/aws_signature_v4/example-packages/example_common-packages/secure_storage/amplify_secure_storage_dart/example-packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/a-!!too_long!!-928-892745537 os:ubuntu-latest;pub-cache-hosted;sdk:stable os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -670,19 +670,6 @@ jobs: sdk: stable - id: checkout uses: actions/checkout@d0651293c4a5a52e711f25b41b05b2212f385d28 - - id: packages_aft_pub_upgrade - name: packages/aft; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: packages/aft - run: dart pub upgrade - - name: "packages/aft; dart format --output=none --set-exit-if-changed ." - if: "always() && steps.packages_aft_pub_upgrade.conclusion == 'success'" - working-directory: packages/aft - run: "dart format --output=none --set-exit-if-changed ." - - name: "packages/aft; dart analyze --fatal-infos ." - if: "always() && steps.packages_aft_pub_upgrade.conclusion == 'success'" - working-directory: packages/aft - run: dart analyze --fatal-infos . - id: packages_amplify_core_pub_upgrade name: packages/amplify_core; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" diff --git a/.github/workflows/native_test.yml b/.github/workflows/native_test.yml index 2c25be02d7..5f2589e8b2 100644 --- a/.github/workflows/native_test.yml +++ b/.github/workflows/native_test.yml @@ -24,7 +24,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.17.0;packages:packages/aws_signature_v4;commands:command_3-command_4" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.17.0;packages:packages/aws_signature_v4;commands:command_3-test_0" restore-keys: | os:ubuntu-latest;pub-cache-hosted;sdk:2.17.0;packages:packages/aws_signature_v4 os:ubuntu-latest;pub-cache-hosted;sdk:2.17.0 @@ -57,7 +57,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:packages/aws_signature_v4;commands:command_3-command_4" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:packages/aws_signature_v4;commands:command_3-test_0" restore-keys: | os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:packages/aws_signature_v4 os:ubuntu-latest;pub-cache-hosted;sdk:dev @@ -265,7 +265,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/aws_signature_v4;commands:command_3-command_4" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/aws_signature_v4;commands:command_3-test_0" restore-keys: | os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/aws_signature_v4 os:ubuntu-latest;pub-cache-hosted;sdk:stable @@ -444,7 +444,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:macos-latest;pub-cache-hosted;sdk:dev;packages:packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/goldens/lib2/custom-packages/smithy/goldens/lib2/restJson1-packages/smithy/goldens/lib2/restXml-packages/smithy/goldens/lib2/restXmlWithNa-!!too_long!!-592-845066415" + key: "os:macos-latest;pub-cache-hosted;sdk:dev;packages:packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/goldens/lib2/custom-packages/smithy/goldens/lib2/restJson1-packages/smithy/goldens/lib2/restXml-packages/smithy/goldens/lib2/restXmlWithNa-!!too_long!!-589-940612423" restore-keys: | os:macos-latest;pub-cache-hosted;sdk:dev;packages:packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/goldens/lib2/custom-packages/smithy/goldens/lib2/restJson1-packages/smithy/goldens/lib2/restXml-packages/smithy/goldens/lib2/restXmlWithNa-!!too_long!!-573-897748075 os:macos-latest;pub-cache-hosted;sdk:dev @@ -590,7 +590,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:macos-latest;pub-cache-hosted;sdk:stable;packages:packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/goldens/lib2/custom-packages/smithy/goldens/lib2/restJson1-packages/smithy/goldens/lib2/restXml-packages/smithy/goldens/lib2/restXmlWit-!!too_long!!-595-353962632" + key: "os:macos-latest;pub-cache-hosted;sdk:stable;packages:packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/goldens/lib2/custom-packages/smithy/goldens/lib2/restJson1-packages/smithy/goldens/lib2/restXml-packages/smithy/goldens/lib2/restXmlWit-!!too_long!!-592-846050532" restore-keys: | os:macos-latest;pub-cache-hosted;sdk:stable;packages:packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/goldens/lib2/custom-packages/smithy/goldens/lib2/restJson1-packages/smithy/goldens/lib2/restXml-packages/smithy/goldens/lib2/restXmlWit-!!too_long!!-576-415910943 os:macos-latest;pub-cache-hosted;sdk:stable diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0774b0d95b..d851046c85 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -80,7 +80,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.17.0;packages:packages/aws_common;commands:command_4" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.17.0;packages:packages/aws_common;commands:test_0" restore-keys: | os:ubuntu-latest;pub-cache-hosted;sdk:2.17.0;packages:packages/aws_common os:ubuntu-latest;pub-cache-hosted;sdk:2.17.0 @@ -108,7 +108,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.17.0;packages:packages/aws_signature_v4;commands:command_3-command_4-command_5" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.17.0;packages:packages/aws_signature_v4;commands:command_3-test_0-test_2" restore-keys: | os:ubuntu-latest;pub-cache-hosted;sdk:2.17.0;packages:packages/aws_signature_v4 os:ubuntu-latest;pub-cache-hosted;sdk:2.17.0 @@ -291,7 +291,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/common/amplify_db_common_dart-packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/goldens/lib2/cus-!!too_long!!-743-907954463" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/common/amplify_db_common_dart-packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/goldens/lib2/cust-!!too_long!!-740-39904077" restore-keys: | os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/common/amplify_db_common_dart-packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/goldens/lib2/cus-!!too_long!!-724-123559909 os:ubuntu-latest;pub-cache-hosted;sdk:dev @@ -481,7 +481,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:packages/aws_signature_v4;commands:command_3-command_4-command_5" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:packages/aws_signature_v4;commands:command_3-test_0-test_2" restore-keys: | os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:packages/aws_signature_v4 os:ubuntu-latest;pub-cache-hosted;sdk:dev @@ -517,7 +517,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:packages/common/amplify_db_common_dart;commands:command_5" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:packages/common/amplify_db_common_dart;commands:test_2" restore-keys: | os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:packages/common/amplify_db_common_dart os:ubuntu-latest;pub-cache-hosted;sdk:dev @@ -776,16 +776,16 @@ jobs: working-directory: packages/worker_bee/e2e_test run: "dart run build_runner test --release --delete-conflicting-outputs --verbose -- -p chrome,firefox" job_015: - name: "unit_test; linux; Dart stable; PKGS: packages/aft, packages/amplify_core, packages/auth/amplify_auth_cognito_test, packages/aws_common, packages/common/amplify_db_common_dart, packages/smithy/goldens/lib/awsJson1_0, packages/smithy/goldens/lib/awsJson1_1, packages/smithy/goldens/lib/restJson1, packages/smithy/goldens/lib/restXml, packages/smithy/goldens/lib/restXmlWithNamespace, packages/smithy/goldens/lib2/awsJson1_0, packages/smithy/goldens/lib2/awsJson1_1, packages/smithy/goldens/lib2/custom, packages/smithy/goldens/lib2/restJson1, packages/smithy/goldens/lib2/restXml, packages/smithy/goldens/lib2/restXmlWithNamespace, packages/smithy/smithy, packages/smithy/smithy_aws, packages/smithy/smithy_codegen, packages/worker_bee/e2e_test; `dart test`" + name: "unit_test; linux; Dart stable; PKGS: packages/amplify_core, packages/auth/amplify_auth_cognito_test, packages/common/amplify_db_common_dart, packages/secure_storage/amplify_secure_storage_test; `dart --version`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/aft-packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/common/amplify_db_common_dart-packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/-!!too_long!!-759-798352901" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/common/amplify_db_common_dart-packages/secure_storage/amplify_secure_storage_test;commands:command_0" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/aft-packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/common/amplify_db_common_dart-packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/g-!!too_long!!-740-33487636 + os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/common/amplify_db_common_dart-packages/secure_storage/amplify_secure_storage_test os:ubuntu-latest;pub-cache-hosted;sdk:stable os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -794,15 +794,153 @@ jobs: sdk: stable - id: checkout uses: actions/checkout@d0651293c4a5a52e711f25b41b05b2212f385d28 - - id: packages_aft_pub_upgrade - name: packages/aft; dart pub upgrade + - id: packages_amplify_core_pub_upgrade + name: packages/amplify_core; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" - working-directory: packages/aft + working-directory: packages/amplify_core run: dart pub upgrade - - name: packages/aft; dart test - if: "always() && steps.packages_aft_pub_upgrade.conclusion == 'success'" - working-directory: packages/aft - run: dart test + - name: "packages/amplify_core; dart --version" + if: "always() && steps.packages_amplify_core_pub_upgrade.conclusion == 'success'" + working-directory: packages/amplify_core + run: dart --version + - id: packages_auth_amplify_auth_cognito_test_pub_upgrade + name: packages/auth/amplify_auth_cognito_test; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: packages/auth/amplify_auth_cognito_test + run: dart pub upgrade + - name: "packages/auth/amplify_auth_cognito_test; dart --version" + if: "always() && steps.packages_auth_amplify_auth_cognito_test_pub_upgrade.conclusion == 'success'" + working-directory: packages/auth/amplify_auth_cognito_test + run: dart --version + - id: packages_common_amplify_db_common_dart_pub_upgrade + name: packages/common/amplify_db_common_dart; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: packages/common/amplify_db_common_dart + run: dart pub upgrade + - name: "packages/common/amplify_db_common_dart; dart --version" + if: "always() && steps.packages_common_amplify_db_common_dart_pub_upgrade.conclusion == 'success'" + working-directory: packages/common/amplify_db_common_dart + run: dart --version + - id: packages_secure_storage_amplify_secure_storage_test_pub_upgrade + name: packages/secure_storage/amplify_secure_storage_test; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: packages/secure_storage/amplify_secure_storage_test + run: dart pub upgrade + - name: "packages/secure_storage/amplify_secure_storage_test; dart --version" + if: "always() && steps.packages_secure_storage_amplify_secure_storage_test_pub_upgrade.conclusion == 'success'" + working-directory: packages/secure_storage/amplify_secure_storage_test + run: dart --version + job_016: + name: "unit_test; linux; Dart stable; PKGS: packages/amplify_core, packages/aws_common; `dart run build_runner test --delete-conflicting-outputs -- -p chrome,firefox`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/aws_common;commands:command_1" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/aws_common + os:ubuntu-latest;pub-cache-hosted;sdk:stable + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d + with: + sdk: stable + - id: checkout + uses: actions/checkout@d0651293c4a5a52e711f25b41b05b2212f385d28 + - id: packages_amplify_core_pub_upgrade + name: packages/amplify_core; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: packages/amplify_core + run: dart pub upgrade + - name: "packages/amplify_core; dart run build_runner test --delete-conflicting-outputs -- -p chrome,firefox" + if: "always() && steps.packages_amplify_core_pub_upgrade.conclusion == 'success'" + working-directory: packages/amplify_core + run: "dart run build_runner test --delete-conflicting-outputs -- -p chrome,firefox" + - id: packages_aws_common_pub_upgrade + name: packages/aws_common; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: packages/aws_common + run: dart pub upgrade + - name: "packages/aws_common; dart run build_runner test --delete-conflicting-outputs -- -p chrome,firefox" + if: "always() && steps.packages_aws_common_pub_upgrade.conclusion == 'success'" + working-directory: packages/aws_common + run: "dart run build_runner test --delete-conflicting-outputs -- -p chrome,firefox" + job_017: + name: "unit_test; linux; Dart stable; PKGS: packages/amplify_core, packages/auth/amplify_auth_cognito_test, packages/aws_common, packages/secure_storage/amplify_secure_storage_test; `dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/secure_storage/amplify_secure_storage_test;commands:command_2" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/secure_storage/amplify_secure_storage_test + os:ubuntu-latest;pub-cache-hosted;sdk:stable + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d + with: + sdk: stable + - id: checkout + uses: actions/checkout@d0651293c4a5a52e711f25b41b05b2212f385d28 + - id: packages_amplify_core_pub_upgrade + name: packages/amplify_core; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: packages/amplify_core + run: dart pub upgrade + - name: "packages/amplify_core; dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" + if: "always() && steps.packages_amplify_core_pub_upgrade.conclusion == 'success'" + working-directory: packages/amplify_core + run: "dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" + - id: packages_auth_amplify_auth_cognito_test_pub_upgrade + name: packages/auth/amplify_auth_cognito_test; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: packages/auth/amplify_auth_cognito_test + run: dart pub upgrade + - name: "packages/auth/amplify_auth_cognito_test; dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" + if: "always() && steps.packages_auth_amplify_auth_cognito_test_pub_upgrade.conclusion == 'success'" + working-directory: packages/auth/amplify_auth_cognito_test + run: "dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" + - id: packages_aws_common_pub_upgrade + name: packages/aws_common; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: packages/aws_common + run: dart pub upgrade + - name: "packages/aws_common; dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" + if: "always() && steps.packages_aws_common_pub_upgrade.conclusion == 'success'" + working-directory: packages/aws_common + run: "dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" + - id: packages_secure_storage_amplify_secure_storage_test_pub_upgrade + name: packages/secure_storage/amplify_secure_storage_test; dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: packages/secure_storage/amplify_secure_storage_test + run: dart pub upgrade + - name: "packages/secure_storage/amplify_secure_storage_test; dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" + if: "always() && steps.packages_secure_storage_amplify_secure_storage_test_pub_upgrade.conclusion == 'success'" + working-directory: packages/secure_storage/amplify_secure_storage_test + run: "dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" + job_018: + name: "unit_test; linux; Dart stable; PKGS: packages/amplify_core, packages/auth/amplify_auth_cognito_test, packages/aws_common, packages/common/amplify_db_common_dart, packages/smithy/goldens/lib/awsJson1_0, packages/smithy/goldens/lib/awsJson1_1, packages/smithy/goldens/lib/restJson1, packages/smithy/goldens/lib/restXml, packages/smithy/goldens/lib/restXmlWithNamespace, packages/smithy/goldens/lib2/awsJson1_0, packages/smithy/goldens/lib2/awsJson1_1, packages/smithy/goldens/lib2/custom, packages/smithy/goldens/lib2/restJson1, packages/smithy/goldens/lib2/restXml, packages/smithy/goldens/lib2/restXmlWithNamespace, packages/smithy/smithy, packages/smithy/smithy_aws, packages/smithy/smithy_codegen, packages/worker_bee/e2e_test; `dart test`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/common/amplify_db_common_dart-packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/goldens/lib2/-!!too_long!!-743-107607119" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/common/amplify_db_common_dart-packages/smithy/goldens/lib/awsJson1_0-packages/smithy/goldens/lib/awsJson1_1-packages/smithy/goldens/lib/restJson1-packages/smithy/goldens/lib/restXml-packages/smithy/goldens/lib/restXmlWithNamespace-packages/smithy/goldens/lib2/awsJson1_0-packages/smithy/goldens/lib2/awsJson1_1-packages/smithy/goldens/lib2/-!!too_long!!-727-583550216 + os:ubuntu-latest;pub-cache-hosted;sdk:stable + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d + with: + sdk: stable + - id: checkout + uses: actions/checkout@d0651293c4a5a52e711f25b41b05b2212f385d28 - id: packages_amplify_core_pub_upgrade name: packages/amplify_core; dart pub upgrade if: "always() && steps.checkout.conclusion == 'success'" @@ -974,153 +1112,6 @@ jobs: if: "always() && steps.packages_worker_bee_e2e_test_pub_upgrade.conclusion == 'success'" working-directory: packages/worker_bee/e2e_test run: dart test - job_016: - name: "unit_test; linux; Dart stable; PKGS: packages/amplify_core, packages/auth/amplify_auth_cognito_test, packages/common/amplify_db_common_dart, packages/secure_storage/amplify_secure_storage_test; `dart --version`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/common/amplify_db_common_dart-packages/secure_storage/amplify_secure_storage_test;commands:command_0" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/common/amplify_db_common_dart-packages/secure_storage/amplify_secure_storage_test - os:ubuntu-latest;pub-cache-hosted;sdk:stable - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d - with: - sdk: stable - - id: checkout - uses: actions/checkout@d0651293c4a5a52e711f25b41b05b2212f385d28 - - id: packages_amplify_core_pub_upgrade - name: packages/amplify_core; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: packages/amplify_core - run: dart pub upgrade - - name: "packages/amplify_core; dart --version" - if: "always() && steps.packages_amplify_core_pub_upgrade.conclusion == 'success'" - working-directory: packages/amplify_core - run: dart --version - - id: packages_auth_amplify_auth_cognito_test_pub_upgrade - name: packages/auth/amplify_auth_cognito_test; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: packages/auth/amplify_auth_cognito_test - run: dart pub upgrade - - name: "packages/auth/amplify_auth_cognito_test; dart --version" - if: "always() && steps.packages_auth_amplify_auth_cognito_test_pub_upgrade.conclusion == 'success'" - working-directory: packages/auth/amplify_auth_cognito_test - run: dart --version - - id: packages_common_amplify_db_common_dart_pub_upgrade - name: packages/common/amplify_db_common_dart; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: packages/common/amplify_db_common_dart - run: dart pub upgrade - - name: "packages/common/amplify_db_common_dart; dart --version" - if: "always() && steps.packages_common_amplify_db_common_dart_pub_upgrade.conclusion == 'success'" - working-directory: packages/common/amplify_db_common_dart - run: dart --version - - id: packages_secure_storage_amplify_secure_storage_test_pub_upgrade - name: packages/secure_storage/amplify_secure_storage_test; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: packages/secure_storage/amplify_secure_storage_test - run: dart pub upgrade - - name: "packages/secure_storage/amplify_secure_storage_test; dart --version" - if: "always() && steps.packages_secure_storage_amplify_secure_storage_test_pub_upgrade.conclusion == 'success'" - working-directory: packages/secure_storage/amplify_secure_storage_test - run: dart --version - job_017: - name: "unit_test; linux; Dart stable; PKGS: packages/amplify_core, packages/aws_common; `dart run build_runner test --delete-conflicting-outputs -- -p chrome,firefox`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/aws_common;commands:command_1" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/aws_common - os:ubuntu-latest;pub-cache-hosted;sdk:stable - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d - with: - sdk: stable - - id: checkout - uses: actions/checkout@d0651293c4a5a52e711f25b41b05b2212f385d28 - - id: packages_amplify_core_pub_upgrade - name: packages/amplify_core; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: packages/amplify_core - run: dart pub upgrade - - name: "packages/amplify_core; dart run build_runner test --delete-conflicting-outputs -- -p chrome,firefox" - if: "always() && steps.packages_amplify_core_pub_upgrade.conclusion == 'success'" - working-directory: packages/amplify_core - run: "dart run build_runner test --delete-conflicting-outputs -- -p chrome,firefox" - - id: packages_aws_common_pub_upgrade - name: packages/aws_common; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: packages/aws_common - run: dart pub upgrade - - name: "packages/aws_common; dart run build_runner test --delete-conflicting-outputs -- -p chrome,firefox" - if: "always() && steps.packages_aws_common_pub_upgrade.conclusion == 'success'" - working-directory: packages/aws_common - run: "dart run build_runner test --delete-conflicting-outputs -- -p chrome,firefox" - job_018: - name: "unit_test; linux; Dart stable; PKGS: packages/amplify_core, packages/auth/amplify_auth_cognito_test, packages/aws_common, packages/secure_storage/amplify_secure_storage_test; `dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/secure_storage/amplify_secure_storage_test;commands:command_2" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/aws_common-packages/secure_storage/amplify_secure_storage_test - os:ubuntu-latest;pub-cache-hosted;sdk:stable - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d - with: - sdk: stable - - id: checkout - uses: actions/checkout@d0651293c4a5a52e711f25b41b05b2212f385d28 - - id: packages_amplify_core_pub_upgrade - name: packages/amplify_core; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: packages/amplify_core - run: dart pub upgrade - - name: "packages/amplify_core; dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" - if: "always() && steps.packages_amplify_core_pub_upgrade.conclusion == 'success'" - working-directory: packages/amplify_core - run: "dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" - - id: packages_auth_amplify_auth_cognito_test_pub_upgrade - name: packages/auth/amplify_auth_cognito_test; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: packages/auth/amplify_auth_cognito_test - run: dart pub upgrade - - name: "packages/auth/amplify_auth_cognito_test; dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" - if: "always() && steps.packages_auth_amplify_auth_cognito_test_pub_upgrade.conclusion == 'success'" - working-directory: packages/auth/amplify_auth_cognito_test - run: "dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" - - id: packages_aws_common_pub_upgrade - name: packages/aws_common; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: packages/aws_common - run: dart pub upgrade - - name: "packages/aws_common; dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" - if: "always() && steps.packages_aws_common_pub_upgrade.conclusion == 'success'" - working-directory: packages/aws_common - run: "dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" - - id: packages_secure_storage_amplify_secure_storage_test_pub_upgrade - name: packages/secure_storage/amplify_secure_storage_test; dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: packages/secure_storage/amplify_secure_storage_test - run: dart pub upgrade - - name: "packages/secure_storage/amplify_secure_storage_test; dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" - if: "always() && steps.packages_secure_storage_amplify_secure_storage_test_pub_upgrade.conclusion == 'success'" - working-directory: packages/secure_storage/amplify_secure_storage_test - run: "dart run build_runner test --release --delete-conflicting-outputs -- -p chrome,firefox" job_019: name: "unit_test; linux; Dart stable; PKG: packages/aws_signature_v4; `git submodule update --init`, `dart test`, `dart test -p chrome,firefox`" runs-on: ubuntu-latest @@ -1129,7 +1120,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/aws_signature_v4;commands:command_3-command_4-command_5" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/aws_signature_v4;commands:command_3-test_0-test_2" restore-keys: | os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/aws_signature_v4 os:ubuntu-latest;pub-cache-hosted;sdk:stable @@ -1165,7 +1156,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/common/amplify_db_common_dart;commands:command_5" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/common/amplify_db_common_dart;commands:test_2" restore-keys: | os:ubuntu-latest;pub-cache-hosted;sdk:stable;packages:packages/common/amplify_db_common_dart os:ubuntu-latest;pub-cache-hosted;sdk:stable @@ -1403,7 +1394,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:macos-latest;pub-cache-hosted;sdk:dev;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/common/amplify_db_common_dart-packages/worker_bee/e2e_test;commands:command_4" + key: "os:macos-latest;pub-cache-hosted;sdk:dev;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/common/amplify_db_common_dart-packages/worker_bee/e2e_test;commands:test_0" restore-keys: | os:macos-latest;pub-cache-hosted;sdk:dev;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/common/amplify_db_common_dart-packages/worker_bee/e2e_test os:macos-latest;pub-cache-hosted;sdk:dev @@ -1486,7 +1477,7 @@ jobs: uses: actions/cache@4504faf7e9bcf8f3ed0bc863c4e1d21499ab8ef8 with: path: "~/.pub-cache/hosted" - key: "os:macos-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/common/amplify_db_common_dart-packages/worker_bee/e2e_test;commands:command_4" + key: "os:macos-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/common/amplify_db_common_dart-packages/worker_bee/e2e_test;commands:test_0" restore-keys: | os:macos-latest;pub-cache-hosted;sdk:stable;packages:packages/amplify_core-packages/auth/amplify_auth_cognito_test-packages/common/amplify_db_common_dart-packages/worker_bee/e2e_test os:macos-latest;pub-cache-hosted;sdk:stable diff --git a/packages/aft/mono_pkg.yaml b/packages/aft/mono_pkg.yaml deleted file mode 100644 index 812312a82f..0000000000 --- a/packages/aft/mono_pkg.yaml +++ /dev/null @@ -1,10 +0,0 @@ -sdk: - - stable - -stages: - - analyze_and_format: - - group: - - format - - analyze: --fatal-infos . - - unit_test: - - test: diff --git a/tool/ci.sh b/tool/ci.sh index 0c70747f10..781f7964bf 100755 --- a/tool/ci.sh +++ b/tool/ci.sh @@ -103,14 +103,6 @@ for PKG in ${PKGS}; do echo 'git submodule update --init' git submodule update --init || EXIT_CODE=$? ;; - command_4) - echo 'dart test' - dart test || EXIT_CODE=$? - ;; - command_5) - echo 'dart test -p chrome,firefox' - dart test -p chrome,firefox || EXIT_CODE=$? - ;; command_6) echo 'tool/test-desktop.sh' tool/test-desktop.sh || EXIT_CODE=$? @@ -127,10 +119,18 @@ for PKG in ${PKGS}; do echo 'dart format --output=none --set-exit-if-changed .' dart format --output=none --set-exit-if-changed . || EXIT_CODE=$? ;; + test_0) + echo 'dart test' + dart test || EXIT_CODE=$? + ;; test_1) echo 'dart test --tags=build' dart test --tags=build || EXIT_CODE=$? ;; + test_2) + echo 'dart test -p chrome,firefox' + dart test -p chrome,firefox || EXIT_CODE=$? + ;; test_3) echo 'dart test -p chrome' dart test -p chrome || EXIT_CODE=$? From 1f6981aca6890ee9ea48b1bfac91a497a8a8ea72 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Wed, 21 Sep 2022 15:16:17 -0700 Subject: [PATCH 15/40] fix(aft): Publish constraints Fixes constraints around publishing checks and which packages to consider for publishing. commit-id:937ee042 --- .../aft/lib/src/commands/publish_command.dart | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/packages/aft/lib/src/commands/publish_command.dart b/packages/aft/lib/src/commands/publish_command.dart index 79b3a01e53..e600810d52 100644 --- a/packages/aft/lib/src/commands/publish_command.dart +++ b/packages/aft/lib/src/commands/publish_command.dart @@ -61,8 +61,9 @@ class PublishCommand extends AmplifyCommand { try { final versionInfo = await package.resolveVersionInfo(httpClient); - final latestVersion = versionInfo?.latestVersion; - if (latestVersion == null) { + final publishedVersion = + versionInfo?.latestPrerelease ?? versionInfo?.latestVersion; + if (publishedVersion == null) { if (force) { return package; } else { @@ -70,9 +71,8 @@ class PublishCommand extends AmplifyCommand { } } - return latestVersion < package.pubspecInfo.pubspec.version! - ? package - : null; + final currentVersion = package.pubspecInfo.pubspec.version!; + return currentVersion > publishedVersion ? package : null; } on Exception catch (e) { if (force) { return null; @@ -93,7 +93,15 @@ class PublishCommand extends AmplifyCommand { r'Package validation found the following', ); static final _validationConstraintRegex = RegExp( - r'\* Your dependency on ".+" should allow more than one version.', + r'\* Your dependency on ".+" should allow more than one version\.', + ); + static final _validationCheckedInFilesRegex = RegExp( + r'\* \d+ checked-in files? (is|are) ignored by a `\.gitignore`\.', + ); + // TODO(dnys1): Remove when we hit 1.0. For now this is appropriate since + // we have 0.x versions depending on 1.x-pre versions. + static final _validationPreReleaseRegex = RegExp( + r'\* Packages dependent on a pre-release of another package should themselves be published as a pre-release version\.', ); static final _validationErrorRegex = RegExp(r'^\s*\*'); @@ -128,7 +136,9 @@ class PublishCommand extends AmplifyCommand { .split('\n') .skipWhile((line) => !_validationStartRegex.hasMatch(line)) .where(_validationErrorRegex.hasMatch) - .where((line) => !_validationConstraintRegex.hasMatch(line)); + .where((line) => !_validationConstraintRegex.hasMatch(line)) + .where((line) => !_validationPreReleaseRegex.hasMatch(line)) + .where((line) => !_validationCheckedInFilesRegex.hasMatch(line)); if (failures.isNotEmpty) { logger ..error( @@ -144,27 +154,29 @@ class PublishCommand extends AmplifyCommand { @override Future run() async { // Gather packages which can be published. - final publishablePackages = (await Future.wait([ - for (final package in allPackages.values) _checkPublishable(package), + final publishablePackages = repo.developmentPackages + .where((pkg) => pkg.pubspecInfo.pubspec.publishTo != 'none'); + + // Gather packages which need to be published. + final packagesNeedingPublish = (await Future.wait([ + for (final package in publishablePackages) _checkPublishable(package), ])) .whereType() .toList(); - // Non-example packages which are being held back - final unpublishablePackages = allPackages.values.where( - (pkg) => - pkg.pubspecInfo.pubspec.publishTo == null && - !publishablePackages.contains(pkg), + // Publishable packages which are being held back. + final unpublishablePackages = publishablePackages.where( + (pkg) => !packagesNeedingPublish.contains(pkg), ); - if (publishablePackages.isEmpty) { + if (packagesNeedingPublish.isEmpty) { logger.info('No packages need publishing!'); return; } try { sortPackagesTopologically( - publishablePackages, + packagesNeedingPublish, (pkg) => pkg.pubspecInfo.pubspec, ); } on CycleException { @@ -176,7 +188,7 @@ class PublishCommand extends AmplifyCommand { stdout ..writeln('Preparing to publish${dryRun ? ' (dry run)' : ''}: ') ..writeln( - publishablePackages + packagesNeedingPublish .map((pkg) => '${pkg.pubspecInfo.pubspec.version} ${pkg.name}') .join('\n'), ) @@ -196,12 +208,12 @@ class PublishCommand extends AmplifyCommand { } // Run pre-publish checks before publishing any packages. - for (final package in publishablePackages) { + for (final package in packagesNeedingPublish) { await _prePublish(package); } // Publish each package sequentially. - for (final package in publishablePackages) { + for (final package in packagesNeedingPublish) { await _publish(package); } From 340e3351d8e0d8e29a9fe0d73564df804317e894 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Wed, 16 Nov 2022 12:17:12 -0700 Subject: [PATCH 16/40] chore(aft): Publish command checks Improves publish command checks by removing `pubspec_overrides` and not splitting the pre-publish and publish commands for a package. --- .../aft/lib/src/commands/publish_command.dart | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/packages/aft/lib/src/commands/publish_command.dart b/packages/aft/lib/src/commands/publish_command.dart index e600810d52..33fdd85541 100644 --- a/packages/aft/lib/src/commands/publish_command.dart +++ b/packages/aft/lib/src/commands/publish_command.dart @@ -12,12 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'dart:convert'; import 'dart:io'; import 'package:aft/aft.dart'; import 'package:aft/src/util.dart'; import 'package:aws_common/aws_common.dart'; import 'package:graphs/graphs.dart'; +import 'package:path/path.dart' as p; /// Command to publish all Dart/Flutter packages in the repo. class PublishCommand extends AmplifyCommand { @@ -64,18 +66,16 @@ class PublishCommand extends AmplifyCommand { final publishedVersion = versionInfo?.latestPrerelease ?? versionInfo?.latestVersion; if (publishedVersion == null) { - if (force) { - return package; - } else { - fail('Could not determine latest version'); - } + logger.info('No published info for package ${package.name}'); + return package; } final currentVersion = package.pubspecInfo.pubspec.version!; return currentVersion > publishedVersion ? package : null; } on Exception catch (e) { + logger.error('Error retrieving info for package ${package.name}', e); if (force) { - return null; + return package; } else { fail(e.toString()); } @@ -86,6 +86,27 @@ class PublishCommand extends AmplifyCommand { /// `build_runner` tasks. Future _prePublish(PackageInfo package) async { logger.info('Running pre-publish checks for ${package.name}...'); + // Remove any overrides so that `pub` commands resolve against + // `pubspec.yaml`, allowing us to verify we've set our constraints + // correctly. + final pubspecOverrideFile = File( + p.join(package.path, 'pubspec_overrides.yaml'), + ); + if (pubspecOverrideFile.existsSync()) { + pubspecOverrideFile.deleteSync(); + } + final res = await Process.run( + package.flavor.name, + ['pub', 'upgrade'], + stdoutEncoding: utf8, + stderrEncoding: utf8, + workingDirectory: package.path, + ); + if (res.exitCode != 0) { + stdout.write(res.stdout); + stderr.write(res.stderr); + exit(res.exitCode); + } await runBuildRunner(package, logger: logger, verbose: verbose); } @@ -207,13 +228,14 @@ class PublishCommand extends AmplifyCommand { } } - // Run pre-publish checks before publishing any packages. + // Run pre-publish checks then publish package. + // + // Do not split up this step. Since packages are iterated in topological + // ordering, it is okay for later packages to fail. While this means that + // some packages will not be published, it also means that the command + // can be re-run to pick up where it left off. for (final package in packagesNeedingPublish) { await _prePublish(package); - } - - // Publish each package sequentially. - for (final package in packagesNeedingPublish) { await _publish(package); } From 57b4b030bfa177b443e10af4c6113c992e36bab7 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Wed, 16 Nov 2022 12:20:42 -0700 Subject: [PATCH 17/40] chore(aft): Add components and define version bump types --- aft.yaml | 19 +- packages/aft/lib/aft.dart | 2 +- .../aft/lib/src/changelog/commit_message.dart | 75 +++++++- packages/aft/lib/src/changelog/parser.dart | 2 +- .../aft/lib/src/commands/amplify_command.dart | 4 +- ...command.dart => version_bump_command.dart} | 0 packages/aft/lib/src/models.dart | 166 ++++++++++++++-- packages/aft/lib/src/models.g.dart | 35 +++- packages/aft/lib/src/repo.dart | 180 +++++++++++++----- packages/aft/test/e2e_test.dart | 15 +- packages/aft/test/model_test.dart | 84 +++++--- 11 files changed, 472 insertions(+), 110 deletions(-) rename packages/aft/lib/src/commands/{version_command.dart => version_bump_command.dart} (100%) diff --git a/aft.yaml b/aft.yaml index 4ef7fc1503..0422a3f6a8 100644 --- a/aft.yaml +++ b/aft.yaml @@ -21,7 +21,9 @@ ignore: # Strongly connected components which should have major version bumps happen # in unison, i.e. a version bump to one package cascades to all. components: - amplify: + - name: amplify_flutter + summary: amplify_flutter + packages: - amplify_flutter - amplify_flutter_ios - amplify_flutter_android @@ -40,10 +42,21 @@ components: - amplify_storage_s3 - amplify_storage_s3_android - amplify_storage_s3_ios - smithy: + - name: amplify_dart + summary: amplify_core + packages: + - amplify_auth_cognito_dart + - name: amplify_ui + packages: + - amplify_authenticator + - name: smithy + summary: smithy + packages: - smithy - smithy_aws - smithy_codegen - worker_bee: + - name: worker_bee + summary: worker_bee + packages: - worker_bee - worker_bee_builder diff --git a/packages/aft/lib/aft.dart b/packages/aft/lib/aft.dart index 8dc889a1f2..e5127d8bfa 100644 --- a/packages/aft/lib/aft.dart +++ b/packages/aft/lib/aft.dart @@ -24,6 +24,6 @@ export 'src/commands/link_command.dart'; export 'src/commands/list_packages_command.dart'; export 'src/commands/pub_command.dart'; export 'src/commands/publish_command.dart'; -export 'src/commands/version_command.dart'; +export 'src/commands/version_bump_command.dart'; export 'src/models.dart'; export 'src/pub/pub_runner.dart'; diff --git a/packages/aft/lib/src/changelog/commit_message.dart b/packages/aft/lib/src/changelog/commit_message.dart index 1df5b3a36f..f0b1a62ad9 100644 --- a/packages/aft/lib/src/changelog/commit_message.dart +++ b/packages/aft/lib/src/changelog/commit_message.dart @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'package:aft/aft.dart'; import 'package:aws_common/aws_common.dart'; import 'package:collection/collection.dart'; @@ -47,7 +48,8 @@ enum CommitType { refactor.other(), revert.other(), style.other(), - test.other(); + test.other(), + version.other(); const CommitType.fixes() : group = CommitTypeGroup.fixes; const CommitType.features() : group = CommitTypeGroup.features; @@ -71,6 +73,7 @@ abstract class CommitMessage with AWSEquatable { factory CommitMessage.parse( String sha, String summary, { + required String body, required DateTime dateTime, }) { final commitMessage = _commitRegex.firstMatch(summary); @@ -101,10 +104,22 @@ abstract class CommitMessage with AWSEquatable { .toList() ?? const []; final description = commitMessage - .namedGroup('description')! - .replaceAll(RegExp(r'^:\s'), '') + .namedGroup('description') + ?.replaceAll(RegExp(r'^:\s'), '') .trim(); + // Fall back for malformed messages. + if (description == null) { + return UnconventionalCommitMessage(sha, summary, dateTime: dateTime); + } + if (type == CommitType.version) { + return VersionCommitMessage( + sha, + summary, + body: body, + dateTime: dateTime, + ); + } return ConventionalCommitMessage( sha, summary, @@ -150,6 +165,9 @@ abstract class CommitMessage with AWSEquatable { /// `fix(auth)!`. bool get isBreakingChange => false; + /// How to bump the package's version based off this commit. + VersionBumpType? get bumpType => null; + /// The PR tagged in this commit, e.g. `(#2012)`. int? get taggedPr { final match = RegExp(r'#(\d+)').firstMatch(summary)?.group(1); @@ -212,6 +230,33 @@ class ConventionalCommitMessage extends CommitMessage { bool get isVersionBump => type == CommitType.chore && scopes.singleOrNull == 'version'; + @override + VersionBumpType get bumpType { + switch (type) { + case CommitType.unconventional: + case CommitType.merge: + case CommitType.build: + case CommitType.chore: + case CommitType.ci: + case CommitType.docs: + case CommitType.refactor: + case CommitType.style: + case CommitType.test: + case CommitType.version: + case CommitType.fix: + case CommitType.bug: + case CommitType.perf: + case CommitType.revert: + return isBreakingChange + ? VersionBumpType.nonBreaking + : VersionBumpType.patch; + case CommitType.feat: + return isBreakingChange + ? VersionBumpType.breaking + : VersionBumpType.nonBreaking; + } + } + @override bool get includeInChangelog { if (isBreakingChange) { @@ -227,6 +272,7 @@ class ConventionalCommitMessage extends CommitMessage { case CommitType.refactor: case CommitType.style: case CommitType.test: + case CommitType.version: return false; case CommitType.feat: case CommitType.fix: @@ -252,4 +298,27 @@ class UnconventionalCommitMessage extends CommitMessage { @override CommitType get type => CommitType.unconventional; + + @override + VersionBumpType get bumpType => VersionBumpType.patch; +} + +class VersionCommitMessage extends CommitMessage { + factory VersionCommitMessage( + String sha, + String summary, { + required String body, + required DateTime dateTime, + }) { + throw UnimplementedError(); + } + + const VersionCommitMessage._( + super.sha, + super.summary, { + required super.dateTime, + }); + + @override + CommitType get type => CommitType.version; } diff --git a/packages/aft/lib/src/changelog/parser.dart b/packages/aft/lib/src/changelog/parser.dart index 77bae1b93c..ff9adafe6e 100644 --- a/packages/aft/lib/src/changelog/parser.dart +++ b/packages/aft/lib/src/changelog/parser.dart @@ -34,7 +34,7 @@ class _ChangelogParser implements NodeVisitor { switch (element.type) { case ElementType.h2: final versionText = element.textContent; - if (versionText.toLowerCase() == 'next') { + if (equalsIgnoreAsciiCase(versionText, nextVersionTag)) { _currentVersion = nextVersion; break; } diff --git a/packages/aft/lib/src/commands/amplify_command.dart b/packages/aft/lib/src/commands/amplify_command.dart index 4603ecd668..cd09b2a9fa 100644 --- a/packages/aft/lib/src/commands/amplify_command.dart +++ b/packages/aft/lib/src/commands/amplify_command.dart @@ -149,7 +149,9 @@ abstract class AmplifyCommand extends Command final configFile = File(p.join(rootDir.path, 'aft.yaml')); assert(configFile.existsSync(), 'Could not find aft.yaml'); final configYaml = configFile.readAsStringSync(); - return checkedYamlDecode(configYaml, AftConfig.fromJson); + final config = checkedYamlDecode(configYaml, AftConfig.fromJson); + logger.verbose('$config'); + return config; }(); late final Repo repo = Repo( diff --git a/packages/aft/lib/src/commands/version_command.dart b/packages/aft/lib/src/commands/version_bump_command.dart similarity index 100% rename from packages/aft/lib/src/commands/version_command.dart rename to packages/aft/lib/src/commands/version_bump_command.dart diff --git a/packages/aft/lib/src/models.dart b/packages/aft/lib/src/models.dart index 50a5af94b0..91445dd9b3 100644 --- a/packages/aft/lib/src/models.dart +++ b/packages/aft/lib/src/models.dart @@ -55,7 +55,7 @@ class PubVersionInfo { /// Information about a Dart/Flutter package in the repo. /// {@endtemplate} class PackageInfo - with AWSEquatable + with AWSEquatable, AWSDebuggable implements Comparable { /// {@macro amplify_tools.package_info} const PackageInfo({ @@ -156,13 +156,10 @@ class PackageInfo } @override - List get props => [ - name, - path, - usesMonoRepo, - pubspecInfo, - flavor, - ]; + List get props => [name, path]; + + @override + String get runtimeTypeName => 'PackageInfo'; @override int compareTo(PackageInfo other) { @@ -212,21 +209,31 @@ extension AmplifyVersion on Version { ); /// The next version according to Amplify rules for incrementing. - Version nextAmplifyVersion({bool isBreakingChange = false}) { + NextVersion nextAmplifyVersion(VersionBumpType type) { if (preRelease.isEmpty) { - return isBreakingChange ? nextMinor : nextPatch; + switch (type) { + case VersionBumpType.patch: + return NextVersion(nextPatch); + case VersionBumpType.nonBreaking: + return major == 0 + ? NextVersion(nextPatch) + : NextVersion.propogate(nextMinor); + case VersionBumpType.breaking: + return NextVersion.propogate(major == 0 ? nextMinor : nextMajor); + } } - if (isBreakingChange) { - return nextPreRelease; + if (type == VersionBumpType.breaking) { + return NextVersion.propogate(nextPreRelease); } final newBuild = (build.singleOrNull as int? ?? 0) + 1; - return Version( + final newVersion = Version( major, minor, patch, pre: preRelease.join('.'), build: '$newBuild', ); + return NextVersion(newVersion); } /// The constraint to use for this version in pubspecs. @@ -250,6 +257,14 @@ extension AmplifyVersion on Version { } } +class NextVersion { + const NextVersion(this.version) : propogateToComponent = false; + const NextVersion.propogate(this.version) : propogateToComponent = true; + + final Version version; + final bool propogateToComponent; +} + enum DependencyType { dependency('dependencies', 'dependency'), devDependency('dev_dependencies', 'dev dependency'), @@ -307,11 +322,11 @@ const yamlSerializable = JsonSerializable( /// The typed representation of the `aft.yaml` file. @yamlSerializable @_VersionConstraintConverter() -class AftConfig { +class AftConfig with AWSSerializable>, AWSDebuggable { const AftConfig({ this.dependencies = const {}, this.ignore = const [], - this.components = const {}, + this.components = const [], }); factory AftConfig.fromJson(Map? json) => @@ -325,23 +340,78 @@ class AftConfig { /// Packages to ignore in all repo operations. final List ignore; - /// Strongly connected components which should have minor/major version bumps - /// happen in unison, i.e. a version bump to one package cascades to all. - final Map> components; + /// {@macro aft.models.aft_component} + final List components; /// Retrieves the component for [packageName], if any. String componentForPackage(String packageName) { - return components.entries + return components .firstWhereOrNull( - (component) => component.value.contains(packageName), + (component) => component.packages.contains(packageName), ) - ?.key ?? + ?.name ?? packageName; } + @override + String get runtimeTypeName => 'AftConfig'; + + @override Map toJson() => _$AftConfigToJson(this); } +/// {@template aft.models.aft_component} +/// Strongly connected components which should have minor/major version bumps +/// happen in unison, i.e. a version bump to one package cascades to all. +/// {@endtemplate} +@yamlSerializable +class AftComponent with AWSSerializable>, AWSDebuggable { + const AftComponent({ + required this.name, + this.summary, + required this.packages, + }); + + factory AftComponent.fromJson(Map json) => + _$AftComponentFromJson(json); + + /// The name of the component. + final String name; + + /// The package name which summarizes all component changes in its changleog. + final String? summary; + + /// The list of packages in the component. + final List packages; + + @override + String get runtimeTypeName => 'AftComponent'; + + @override + Map toJson() => _$AftComponentToJson(this); +} + +class AftRepoComponent { + const AftRepoComponent({ + required this.name, + this.summary, + required this.packages, + required this.packageGraph, + }); + + /// The name of the component. + final String name; + + /// The package name which summarizes all component changes in its changleog. + final PackageInfo? summary; + + /// The list of packages in the component. + final List packages; + + /// The graph of packages to their dependencies. + final Map> packageGraph; +} + /// Typed representation of the `sdk.yaml` file. @yamlSerializable @ShapeIdConverter() @@ -384,3 +454,57 @@ class _VersionConstraintConverter @override String toJson(VersionConstraint object) => object.toString(); } + +/// The type of version change to perform. +enum VersionBumpType { + /// Library packages are allowed to vary in their version, meaning a small + /// change to one package (e.g. Update README) should be isolated to the + /// affected package. + /// + /// Examples: + /// * If the current version of a 0-based `aws_common` is `0.1.0` and its + /// README is updated, it and it alone should be bumped to `0.1.1`. + /// Note: a bump to `0.1.1` is technically a “minor” version bump in + /// 0-based SemVer, but for consistency we can choose not to use build + /// numbers (+). + /// * If the current version of a 1-based `amplify_flutter` is `1.0.0` and its + /// README is updated, it and it alone should be bumped to `1.0.1`. + /// + /// This version change is reserved for chores and bug fixes as denoted by + /// the following conventional commit tags: `fix`, `bug`, `perf`, `chore`, + /// `build`, `ci`, `docs`, `refactor`, `revert`, `style`, `test`. + patch, + + /// A non-breaking version bump for a package represents a divergence from + /// the previous version in terms of behavior or functionality in the form of + /// a new feature. + /// + /// Examples: + /// * If the current version of a 0-based aws_common is `0.1.0` and it is part + /// of a feature change, it is bumped to `0.1.1` alongside all other package + /// bumps. + /// * If the current version of a 1-based amplify_flutter is `1.0.0` and it is + /// part of a feature change, it is bumped to `1.1.0` alongside all other + /// package bumps. + /// + /// This version change is reserved for new features denoted by the `feat` + /// conventional commit tag. + nonBreaking, + + /// A breaking version bump is reserved for breaking API changes. **These are + /// rarely done.** + /// + /// * 0-based packages are allowed to break their APIs while 0-based and + /// follow the non-breaking version scheme described above, e.g. + /// `0.1.0` → `0.2.0`. + /// + /// * Stable packages (>1.0.0) bump to the next SemVer major version, e.g. + /// `1.0.0` → `2.0.0`. + /// + /// Packages opt in to this behavior by suffixing an exclamation point to + /// their commit message title tag, e.g. + /// + /// - `feat(auth): A new feature` would be a non-breaking feature change. + /// - `feat(auth)!: A new feature` would be a breaking feature change. + breaking, +} diff --git a/packages/aft/lib/src/models.g.dart b/packages/aft/lib/src/models.g.dart index f97ade3a7b..6cd59621bf 100644 --- a/packages/aft/lib/src/models.g.dart +++ b/packages/aft/lib/src/models.g.dart @@ -33,11 +33,11 @@ AftConfig _$AftConfigFromJson(Map json) => $checkedCreate( components: $checkedConvert( 'components', (v) => - (v as Map?)?.map( - (k, e) => MapEntry(k as String, - (e as List).map((e) => e as String).toList()), - ) ?? - const {}), + (v as List?) + ?.map((e) => AftComponent.fromJson( + Map.from(e as Map))) + .toList() ?? + const []), ); return val; }, @@ -50,6 +50,31 @@ Map _$AftConfigToJson(AftConfig instance) => { 'components': instance.components, }; +AftComponent _$AftComponentFromJson(Map json) => $checkedCreate( + 'AftComponent', + json, + ($checkedConvert) { + $checkKeys( + json, + allowedKeys: const ['name', 'summary', 'packages'], + ); + final val = AftComponent( + name: $checkedConvert('name', (v) => v as String), + summary: $checkedConvert('summary', (v) => v as String?), + packages: $checkedConvert('packages', + (v) => (v as List).map((e) => e as String).toList()), + ); + return val; + }, + ); + +Map _$AftComponentToJson(AftComponent instance) => + { + 'name': instance.name, + 'summary': instance.summary, + 'packages': instance.packages, + }; + SdkConfig _$SdkConfigFromJson(Map json) => $checkedCreate( 'SdkConfig', json, diff --git a/packages/aft/lib/src/repo.dart b/packages/aft/lib/src/repo.dart index 67d3527cc9..aa9b6efe36 100644 --- a/packages/aft/lib/src/repo.dart +++ b/packages/aft/lib/src/repo.dart @@ -49,14 +49,39 @@ class Repo { allPackages.values.where((pkg) => pkg.isDevelopmentPackage).toList(), ); - /// The components of the - late final Map> components = - aftConfig.components.map((component, packages) { - return MapEntry( - component, - packages.map((name) => allPackages[name]!).toList(), + /// The components of the repository. + late final Map components = () { + final components = Map.fromEntries( + aftConfig.components.map((component) { + final summaryPackage = + component.summary == null ? null : allPackages[component.summary]!; + final packages = + component.packages.map((name) => allPackages[name]!).toList(); + final packageGraph = UnmodifiableMapView({ + for (final package in packages) + package: package.pubspecInfo.pubspec.dependencies.keys + .map( + (packageName) => packages.firstWhereOrNull( + (pkg) => pkg.name == packageName, + ), + ) + .whereType() + .toList(), + }); + return MapEntry( + component.name, + AftRepoComponent( + name: component.name, + summary: summaryPackage, + packages: packages, + packageGraph: packageGraph, + ), + ); + }), ); - }); + logger.verbose('Components: $components'); + return components; + }(); /// The libgit repository. late final Repository repo = Repository.open(rootDir.path); @@ -114,6 +139,8 @@ class Repo { }(); /// The git diff between [oldTree] and [newTree]. + /// + /// **NOTE**: This is an expensive operation and its result should be cached. Diff diffTrees(Tree oldTree, Tree newTree) => Diff.treeToTree( repo: repo, oldTree: oldTree, @@ -179,6 +206,7 @@ class Repo { final commitMessage = CommitMessage.parse( commit.oid.sha, commit.summary, + body: commit.body, dateTime: DateTime.fromMillisecondsSinceEpoch( commit.time * 1000, ).toUtc(), @@ -219,14 +247,15 @@ class Repo { changes.commitsByPackage[package]?.forEach((commit) { // TODO(dnys1): Define full set of commit types which should be ignored // when considering version changes. - if (commit.isVersionBump || - commit.type == CommitType.merge && commit.taggedPr == null) { + final bumpType = commit.bumpType; + if (bumpType == null) { return; } bumpVersion( package, commit: commit, - propagate: commit.isBreakingChange, + type: bumpType, + includeInChangelog: commit.includeInChangelog, ); // Propagate the version change to all packages affected by the same // commit as if they were a component. @@ -237,74 +266,126 @@ class Repo { } } - /// Bumps the version and changelog in [package] using [commit] and returns - /// the new version. + /// Bumps the version and changelog in [package] and its component packages + /// using [commit] and returns the new version. /// - /// If [propagate] is `true`, the version change is propagated to all packages - /// which depend on [package]. + /// If [type] is [VersionBumpType.breaking], the version change is propagated + /// to all packages which depend on [package]. + /// + /// If [propogateToComponent] is `true`, all component packages are bumped to + /// the same version. + /// [VersionBumpType.nonBreaking] or [VersionBumpType.breaking]. Version bumpVersion( PackageInfo package, { - bool propagate = false, required CommitMessage commit, + required VersionBumpType type, + required bool includeInChangelog, + bool? propogateToComponent, }) { - final component = aftConfig.componentForPackage(package.name); + logger.verbose('bumpVersion ${package.name} $commit'); + final componentName = aftConfig.componentForPackage(package.name); + final component = components[componentName]; final currentVersion = package.version; final currentProposedVersion = versionChanges.newVersion(package); - final isBreakingChange = commit.isBreakingChange; - final newProposedVersion = currentVersion.nextAmplifyVersion( - isBreakingChange: isBreakingChange, - ); + final newProposedVersion = currentVersion.nextAmplifyVersion(type); final newVersion = maxBy( - [currentProposedVersion, newProposedVersion], + [currentProposedVersion, newProposedVersion.version], (version) => version, )!; versionChanges.updateVersion(package, newVersion); + propogateToComponent ??= newProposedVersion.propogateToComponent; + + final currentChangelogUpdate = changelogUpdates[package]; + changelogUpdates[package] = package.changelog.update( + commits: { + ...?currentChangelogUpdate?.commits, + if (includeInChangelog) commit, + }, + version: newVersion, + ); + logger + ..verbose(' component: $componentName') + ..verbose(' currentVersion: $currentVersion') + ..verbose(' currentProposedVersion: $currentProposedVersion') + ..verbose(' newProposedVersion: $newProposedVersion') + ..verbose(' newVersion: $newVersion'); if (newVersion > currentVersion) { logger.debug( - 'Bumping ${package.name} from $currentProposedVersion to $newVersion: ' + 'Bumping ${package.name} from $currentVersion to $newVersion: ' '${commit.summary}', ); package.pubspecInfo.pubspecYamlEditor.update( ['version'], newVersion.toString(), ); - final currentChangelogUpdate = changelogUpdates[package]; - changelogUpdates[package] = package.changelog.update( - commits: { - ...?currentChangelogUpdate?.commits, - commit, - }, - version: newVersion, - ); - - if (propagate) { - // Propagate to all dependent packages. + if (type == VersionBumpType.breaking) { + // Back-propogate to all dependent packages for breaking changes. + // + // Since we set semantic version constraints, only a breaking change + // in a direct dependency necessitates a version bump. + logger.verbose('Performing dfs on dependent packages...'); dfs( reversedPackageGraph, root: package, (dependent) { + if (dependent == package) return; + logger.verbose('dfs found dependent package ${dependent.name}'); if (dependent.isDevelopmentPackage) { - bumpVersion(dependent, commit: commit); + bumpVersion( + dependent, + commit: commit, + // Do not consider it a breaking change in dependent packages + // even if it was a breaking change in the main package. + type: VersionBumpType.nonBreaking, + includeInChangelog: false, + ); } bumpDependency(package, dependent); }, ); + } - // Propagate to all component packages. - components[component]?.forEach((componentPackage) { - bumpVersion(componentPackage, commit: commit); - dfs( - reversedPackageGraph, - root: componentPackage, - (dependent) { - bumpDependency(componentPackage, dependent); - }, - ); - }); + // Propagate to all component packages. + final componentPackages = component?.packageGraph; + if (propogateToComponent && componentPackages != null) { + dfs( + componentPackages, + (componentPackage) { + if (componentPackage == package) return; + logger.verbose( + 'Bumping component package ${componentPackage.name}', + ); + bumpVersion( + componentPackage, + commit: commit, + type: type, + includeInChangelog: false, + propogateToComponent: false, + ); + }, + ); } } + // Update summary package's changelog if it exists. + final summaryPackage = component?.summary; + if (summaryPackage != null) { + logger.debug( + 'Updating summary package `${summaryPackage.name}` ' + 'with commit: $commit', + ); + final packageVersion = versionChanges[summaryPackage.name]; + final currentChangelogUpdate = changelogUpdates[summaryPackage]; + changelogUpdates[summaryPackage] = summaryPackage.changelog.update( + commits: { + ...?currentChangelogUpdate?.commits, + commit, + }, + version: packageVersion, + ); + } + return newVersion; } @@ -352,7 +433,11 @@ class VersionChanges extends MapBase /// The latest proposed version for [package]. Version newVersion(PackageInfo package) { final component = _repo.aftConfig.componentForPackage(package.name); - return _versionUpdates[component] ?? package.version; + final componentVersion = _versionUpdates['component_$component']; + if (componentVersion != null) { + return componentVersion; + } + return _versionUpdates[package.name] ??= package.version; } /// Updates the proposed version for [package]. @@ -362,7 +447,8 @@ class VersionChanges extends MapBase return; } final component = _repo.aftConfig.componentForPackage(package.name); - _versionUpdates[component] = version; + _versionUpdates['component_$component'] = version; + _versionUpdates[package.name] = version; } @override diff --git a/packages/aft/test/e2e_test.dart b/packages/aft/test/e2e_test.dart index e86e90f261..0f78878bec 100644 --- a/packages/aft/test/e2e_test.dart +++ b/packages/aft/test/e2e_test.dart @@ -112,12 +112,15 @@ Initial version. gitDir, repo: Repository.init(path: gitDir.path), aftConfig: const AftConfig( - components: { - 'amplify': [ - 'amplify_auth_cognito', - 'amplify_auth_cognito_ios', - ], - }, + components: [ + AftComponent( + name: 'amplify', + packages: [ + 'amplify_auth_cognito', + 'amplify_auth_cognito_ios', + ], + ), + ], ), ); await runGit(['commit', '--allow-empty', '-m', 'Initial commit']); diff --git a/packages/aft/test/model_test.dart b/packages/aft/test/model_test.dart index 6e62789bc4..4c7f2afc95 100644 --- a/packages/aft/test/model_test.dart +++ b/packages/aft/test/model_test.dart @@ -20,48 +20,88 @@ void main() { group('AmplifyVersion', () { test('0-version', () { final version = Version(0, 1, 0); + + final patch = version.nextAmplifyVersion(VersionBumpType.patch); + expect(patch.version, Version(0, 1, 1)); + expect(patch.propogateToComponent, false); + + final nonBreaking = + version.nextAmplifyVersion(VersionBumpType.nonBreaking); + expect(nonBreaking.version, Version(0, 1, 1)); + expect(nonBreaking.propogateToComponent, false); + + final breaking = version.nextAmplifyVersion(VersionBumpType.breaking); + expect(breaking.version, Version(0, 2, 0)); + expect(breaking.propogateToComponent, true); + }); + + test('pre-release (untagged)', () { + final version = Version(1, 0, 0, pre: 'next.0'); + + final patch = version.nextAmplifyVersion(VersionBumpType.patch); expect( - version.nextAmplifyVersion(isBreakingChange: false), - Version(0, 1, 1), - ); - expect( - version.nextAmplifyVersion(isBreakingChange: true), - Version(0, 2, 0), + patch.version, + Version(1, 0, 0, pre: 'next.0', build: '1'), ); - }); + expect(patch.propogateToComponent, false); - test('pre-release', () { - var version = Version(1, 0, 0, pre: 'next.0'); + final nonBreaking = + version.nextAmplifyVersion(VersionBumpType.nonBreaking); expect( - version.nextAmplifyVersion(isBreakingChange: false), + nonBreaking.version, Version(1, 0, 0, pre: 'next.0', build: '1'), ); + expect(nonBreaking.propogateToComponent, false); + + final breaking = version.nextAmplifyVersion(VersionBumpType.breaking); expect( - version.nextAmplifyVersion(isBreakingChange: true), + breaking.version, Version(1, 0, 0, pre: 'next.1'), ); + expect(breaking.propogateToComponent, true); + }); + + test('pre-release (tagged)', () { + final version = Version(1, 0, 0, pre: 'next.0', build: '1'); - version = Version(1, 0, 0, pre: 'next.0', build: '1'); + final patch = version.nextAmplifyVersion(VersionBumpType.patch); expect( - version.nextAmplifyVersion(isBreakingChange: false), + patch.version, Version(1, 0, 0, pre: 'next.0', build: '2'), ); + expect(patch.propogateToComponent, false); + + final nonBreaking = + version.nextAmplifyVersion(VersionBumpType.nonBreaking); + expect( + nonBreaking.version, + Version(1, 0, 0, pre: 'next.0', build: '2'), + ); + expect(nonBreaking.propogateToComponent, false); + + final breaking = version.nextAmplifyVersion(VersionBumpType.breaking); expect( - version.nextAmplifyVersion(isBreakingChange: true), + breaking.version, Version(1, 0, 0, pre: 'next.1'), ); + expect(breaking.propogateToComponent, true); }); test('release', () { final version = Version(1, 0, 0); - expect( - version.nextAmplifyVersion(isBreakingChange: false), - Version(1, 0, 1), - ); - expect( - version.nextAmplifyVersion(isBreakingChange: true), - Version(1, 1, 0), - ); + + final patch = version.nextAmplifyVersion(VersionBumpType.patch); + expect(patch.version, Version(1, 0, 1)); + expect(patch.propogateToComponent, false); + + final nonBreaking = + version.nextAmplifyVersion(VersionBumpType.nonBreaking); + expect(nonBreaking.version, Version(1, 1, 0)); + expect(nonBreaking.propogateToComponent, true); + + final breaking = version.nextAmplifyVersion(VersionBumpType.breaking); + expect(breaking.version, Version(2, 0, 0)); + expect(breaking.propogateToComponent, true); }); }); } From 9417e009328724e55d4a6cc7f89373c02cef45fc Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Wed, 16 Nov 2022 13:12:25 -0700 Subject: [PATCH 18/40] chore(aft): Clean up `deps` command Fix logging and merge `pub.dev` logic with `publish` --- .../aft/lib/src/commands/amplify_command.dart | 34 ++++++++++++++++ .../aft/lib/src/commands/deps_command.dart | 29 ++++---------- .../aft/lib/src/commands/publish_command.dart | 7 +++- packages/aft/lib/src/models.dart | 40 ------------------- 4 files changed, 47 insertions(+), 63 deletions(-) diff --git a/packages/aft/lib/src/commands/amplify_command.dart b/packages/aft/lib/src/commands/amplify_command.dart index cd09b2a9fa..9cb67e4c3c 100644 --- a/packages/aft/lib/src/commands/amplify_command.dart +++ b/packages/aft/lib/src/commands/amplify_command.dart @@ -13,6 +13,7 @@ // limitations under the License. import 'dart:collection'; +import 'dart:convert'; import 'dart:io'; import 'package:aft/aft.dart'; @@ -25,6 +26,7 @@ import 'package:http/http.dart' as http; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:pub/pub.dart'; +import 'package:pub_semver/pub_semver.dart'; /// Base class for all commands in this package providing common functionality. abstract class AmplifyCommand extends Command @@ -196,6 +198,38 @@ abstract class AmplifyCommand extends Command return answer == 'y' || answer == 'yes'; } + /// Resolves the latest version information from `pub.dev`. + Future resolveVersionInfo(String package) async { + // Get the currently published version of the package. + final uri = Uri.parse('https://pub.dev/api/packages/$package'); + final resp = await httpClient.get( + uri, + headers: {AWSHeaders.accept: 'application/vnd.pub.v2+json'}, + ); + + // Package is unpublished + if (resp.statusCode == 404) { + return null; + } + if (resp.statusCode != 200) { + throw http.ClientException(resp.body, uri); + } + + final respJson = jsonDecode(resp.body) as Map; + final versions = (respJson['versions'] as List?) ?? []; + final semvers = []; + for (final version in versions) { + final map = (version as Map).cast(); + final semver = map['version'] as String?; + if (semver == null) { + continue; + } + semvers.add(Version.parse(semver)); + } + + return PubVersionInfo(semvers..sort()); + } + @override @mustCallSuper void close() { diff --git a/packages/aft/lib/src/commands/deps_command.dart b/packages/aft/lib/src/commands/deps_command.dart index 88cba2a69c..647af4c4c6 100644 --- a/packages/aft/lib/src/commands/deps_command.dart +++ b/packages/aft/lib/src/commands/deps_command.dart @@ -12,11 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:convert'; import 'dart:io'; import 'package:aft/aft.dart'; -import 'package:aws_common/aws_common.dart'; import 'package:collection/collection.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; @@ -175,27 +173,14 @@ class _DepsUpdateCommand extends _DepsSubcommand { final versionConstraint = entry.value; VersionConstraint? newVersionConstraint; - // TODO(dnys1): Merge with publish logic // Get the currently published version of the package. - final uri = Uri.parse('https://pub.dev/api/packages/$package'); - logger.trace('GET $uri'); try { - final resp = await httpClient.get( - uri, - headers: {AWSHeaders.accept: 'application/vnd.pub.v2+json'}, - ); - if (resp.statusCode != 200) { - failedUpdates.add('$package: Could not reach server'); - continue; - } - final respJson = jsonDecode(resp.body) as Map; - final latestVersionStr = - (respJson['latest'] as Map?)?['version'] as String?; - if (latestVersionStr == null) { - failedUpdates.add('$package: No versions found for package'); + final versionInfo = await resolveVersionInfo(package); + final latestVersion = versionInfo?.latestVersion; + if (latestVersion == null) { + failedUpdates.add('No versions found for package: $package'); continue; } - final latestVersion = Version.parse(latestVersionStr); // Update the constraint to include `latestVersion` as its new upper // bound. @@ -249,13 +234,13 @@ class _DepsUpdateCommand extends _DepsSubcommand { aftEditor.toString(), flush: true, ); - logger.stdout(action.successMessage); + logger.info(action.successMessage); } else { - logger.stderr('No dependencies updated'); + logger.info('No dependencies updated'); } for (final failedUpdate in failedUpdates) { - logger.stderr('Could not update $failedUpdate'); + logger.error('Could not update $failedUpdate'); exitCode = 1; } diff --git a/packages/aft/lib/src/commands/publish_command.dart b/packages/aft/lib/src/commands/publish_command.dart index 33fdd85541..3cde33c6a3 100644 --- a/packages/aft/lib/src/commands/publish_command.dart +++ b/packages/aft/lib/src/commands/publish_command.dart @@ -53,6 +53,11 @@ class PublishCommand extends AmplifyCommand { /// Checks if [package] can be published based on whether the local version /// is newer than the one published to `pub.dev`. Future _checkPublishable(PackageInfo package) async { + final publishTo = package.pubspecInfo.pubspec.publishTo; + if (publishTo == 'none') { + return null; + } + Never fail(String error) { logger ..error('Could not retrieve package info for ${package.name}: ') @@ -62,7 +67,7 @@ class PublishCommand extends AmplifyCommand { } try { - final versionInfo = await package.resolveVersionInfo(httpClient); + final versionInfo = await resolveVersionInfo(package.name); final publishedVersion = versionInfo?.latestPrerelease ?? versionInfo?.latestVersion; if (publishedVersion == null) { diff --git a/packages/aft/lib/src/models.dart b/packages/aft/lib/src/models.dart index 91445dd9b3..d401a0603f 100644 --- a/packages/aft/lib/src/models.dart +++ b/packages/aft/lib/src/models.dart @@ -12,13 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:convert'; import 'dart:io'; import 'package:aft/src/changelog/changelog.dart'; import 'package:aws_common/aws_common.dart'; import 'package:collection/collection.dart'; -import 'package:http/http.dart' as http; import 'package:json_annotation/json_annotation.dart'; import 'package:path/path.dart' as p; import 'package:pub_semver/pub_semver.dart'; @@ -117,44 +115,6 @@ class PackageInfo pubspecInfo.pubspec.version ?? (throw StateError('No version for package: $name')); - /// Resolves the latest version information from `pub.dev`. - Future resolveVersionInfo(http.Client client) async { - final publishTo = pubspecInfo.pubspec.publishTo; - if (publishTo == 'none') { - return null; - } - - // Get the currently published version of the package. - final uri = Uri.parse(publishTo ?? 'https://pub.dev') - .replace(path: '/api/packages/$name'); - final resp = await client.get( - uri, - headers: {AWSHeaders.accept: 'application/vnd.pub.v2+json'}, - ); - - // Package is unpublished - if (resp.statusCode == 404) { - return null; - } - if (resp.statusCode != 200) { - throw http.ClientException(resp.body, uri); - } - - final respJson = jsonDecode(resp.body) as Map; - final versions = (respJson['versions'] as List?) ?? []; - final semvers = []; - for (final version in versions) { - final map = (version as Map).cast(); - final semver = map['version'] as String?; - if (semver == null) { - continue; - } - semvers.add(Version.parse(semver)); - } - - return PubVersionInfo(semvers..sort()); - } - @override List get props => [name, path]; From 759f60f2b428675c88d3235ccdfac4e887b61658 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Wed, 16 Nov 2022 13:17:50 -0700 Subject: [PATCH 19/40] chore(aft): Add placeholders for version bump commit --- .../aft/lib/src/changelog/commit_message.dart | 23 +++++++++++-------- .../src/commands/version_bump_command.dart | 19 +++++++-------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/packages/aft/lib/src/changelog/commit_message.dart b/packages/aft/lib/src/changelog/commit_message.dart index f0b1a62ad9..501f3313a0 100644 --- a/packages/aft/lib/src/changelog/commit_message.dart +++ b/packages/aft/lib/src/changelog/commit_message.dart @@ -303,22 +303,25 @@ class UnconventionalCommitMessage extends CommitMessage { VersionBumpType get bumpType => VersionBumpType.patch; } +/// {@template aft.changelog.version_commit_message} +/// A commit message which identifies a version bump performed by `aft`. +/// +/// These messages take the form `chore(version): ...` and list out the +/// relevant version bump information including a complete feature log. +/// {@endtemplate} class VersionCommitMessage extends CommitMessage { - factory VersionCommitMessage( - String sha, - String summary, { - required String body, - required DateTime dateTime, - }) { - throw UnimplementedError(); - } - - const VersionCommitMessage._( + /// {@macro aft.changelog.version_commit_message} + const VersionCommitMessage( super.sha, super.summary, { + required this.body, required super.dateTime, }); + /// The body of the commit message. + // TODO(dnys1): Parse + final String body; + @override CommitType get type => CommitType.version; } diff --git a/packages/aft/lib/src/commands/version_bump_command.dart b/packages/aft/lib/src/commands/version_bump_command.dart index fa8ec0d47d..5636566191 100644 --- a/packages/aft/lib/src/commands/version_bump_command.dart +++ b/packages/aft/lib/src/commands/version_bump_command.dart @@ -102,15 +102,16 @@ class VersionCommand extends AmplifyCommand with GitRefOptions, GlobOptions { if (!preview) { logger.info('Version successfully bumped'); - if (yes || prompt('Commit changes? (y/N) ').toLowerCase() == 'y') { - // Commit and tag changes - await runGit(['add', '.']); - await runGit(['commit', '-m', 'chore(version): Bump version']); - await Future.wait([ - for (final changeEntry in repo.versionChanges.entries) - runGit(['tag', '${changeEntry.key}_v${changeEntry.value}']), - ]); - } + // TODO(dnys1): Use version bump commit instead of tags + // if (yes || prompt('Commit changes? (y/N) ').toLowerCase() == 'y') { + // // Commit and tag changes + // await runGit(['add', '.']); + // await runGit(['commit', '-m', 'chore(version): Bump version']); + // await Future.wait([ + // for (final changeEntry in repo.versionChanges.entries) + // runGit(['tag', '${changeEntry.key}_v${changeEntry.value}']), + // ]); + // } } } } From 2257ddac321aa2fced9267fdb48af7afec7f365c Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Tue, 29 Nov 2022 12:23:39 -0700 Subject: [PATCH 20/40] chore(aft): Update logging settings --- .../aft/lib/src/commands/amplify_command.dart | 9 ++++++--- packages/aft/lib/src/models.dart | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/aft/lib/src/commands/amplify_command.dart b/packages/aft/lib/src/commands/amplify_command.dart index 9cb67e4c3c..576e34a0b0 100644 --- a/packages/aft/lib/src/commands/amplify_command.dart +++ b/packages/aft/lib/src/commands/amplify_command.dart @@ -44,6 +44,10 @@ abstract class AmplifyCommand extends Command AWSLogger() ..unregisterAllPlugins() ..registerPlugin(this); + + if (globalResults?['verbose'] as bool? ?? false) { + AWSLogger().logLevel = LogLevel.verbose; + } } late final AWSLogger logger = () { @@ -51,8 +55,7 @@ abstract class AmplifyCommand extends Command for (Command? cmd = this; cmd != null; cmd = cmd.parent) { allCommands.add(cmd.name); } - return AWSLogger().createChild(allCommands.reversed.join('.')) - ..logLevel = verbose ? LogLevel.verbose : LogLevel.info; + return AWSLogger().createChild(allCommands.reversed.join('.')); }(); @override @@ -76,7 +79,7 @@ abstract class AmplifyCommand extends Command } /// Whether verbose logging is enabled. - bool get verbose => globalResults?['verbose'] as bool? ?? false; + bool get verbose => AWSLogger().logLevel == LogLevel.verbose; /// The current working directory. late final Directory workingDirectory = () { diff --git a/packages/aft/lib/src/models.dart b/packages/aft/lib/src/models.dart index d401a0603f..303efa83bb 100644 --- a/packages/aft/lib/src/models.dart +++ b/packages/aft/lib/src/models.dart @@ -217,12 +217,18 @@ extension AmplifyVersion on Version { } } -class NextVersion { +class NextVersion with AWSEquatable, AWSDebuggable { const NextVersion(this.version) : propogateToComponent = false; const NextVersion.propogate(this.version) : propogateToComponent = true; final Version version; final bool propogateToComponent; + + @override + List get props => [version, propogateToComponent]; + + @override + String get runtimeTypeName => 'NextVersion'; } enum DependencyType { @@ -351,7 +357,7 @@ class AftComponent with AWSSerializable>, AWSDebuggable { Map toJson() => _$AftComponentToJson(this); } -class AftRepoComponent { +class AftRepoComponent with AWSEquatable, AWSDebuggable { const AftRepoComponent({ required this.name, this.summary, @@ -370,6 +376,12 @@ class AftRepoComponent { /// The graph of packages to their dependencies. final Map> packageGraph; + + @override + List get props => [name]; + + @override + String get runtimeTypeName => 'AftRepoComponent'; } /// Typed representation of the `sdk.yaml` file. From e32a2dce4fc7cb5f214aa334a8344dcf8045bb40 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Tue, 29 Nov 2022 12:24:29 -0700 Subject: [PATCH 21/40] test(aft): Update e2e tests --- packages/aft/test/e2e_test.dart | 36 +++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/packages/aft/test/e2e_test.dart b/packages/aft/test/e2e_test.dart index 0f78878bec..d8d3c3ae64 100644 --- a/packages/aft/test/e2e_test.dart +++ b/packages/aft/test/e2e_test.dart @@ -18,6 +18,7 @@ import 'dart:io'; import 'package:aft/aft.dart'; import 'package:aft/src/repo.dart'; +import 'package:aws_common/aws_common.dart'; import 'package:git/git.dart' as git; import 'package:libgit2dart/libgit2dart.dart'; import 'package:path/path.dart' as p; @@ -37,6 +38,8 @@ class MockRepo extends Repo { } void main() { + final logger = AWSLogger()..logLevel = LogLevel.verbose; + group('Repo', () { late Repo repo; @@ -64,7 +67,7 @@ environment: if (dependencies != null && dependencies.isNotEmpty) { pubspec.writeln('dependencies:'); for (final dependency in dependencies.entries) { - pubspec.writeln(' ${dependency.key}: ${dependency.value}'); + pubspec.writeln(' ${dependency.key}: "${dependency.value}"'); } } final changelog = ''' @@ -111,6 +114,7 @@ Initial version. repo = MockRepo( gitDir, repo: Repository.init(path: gitDir.path), + logger: logger, aftConfig: const AftConfig( components: [ AftComponent( @@ -155,7 +159,12 @@ Initial version. group('E2E', () { final nextVersion = Version(1, 0, 0, pre: 'next.0'); final coreVersion = Version(0, 1, 0); - final nextConstraint = nextVersion; + final nextConstraint = VersionRange( + min: nextVersion, + max: Version(1, 0, 0, pre: 'next.1'), + includeMin: true, + includeMax: false, + ); final coreConstraint = VersionConstraint.compatibleWith(coreVersion); setUp(() async { @@ -295,8 +304,8 @@ Initial version. 'aws_common': '0.1.1', 'amplify_core': '1.0.0-next.0+1', 'amplify_auth_cognito_dart': '0.1.1', - 'amplify_auth_cognito': '1.0.0-next.1', - 'amplify_auth_cognito_ios': '1.0.0-next.1', + 'amplify_auth_cognito': '1.0.0-next.0+1', + 'amplify_auth_cognito_ios': '1.0.0-next.0+1', }; final updatedChangelogs = { 'aws_common': ''' @@ -319,16 +328,13 @@ Initial version. - feat(auth): New feature ''', 'amplify_auth_cognito': ''' -## 1.0.0-next.1 - -### Breaking Changes -- fix(amplify_auth_cognito_ios)!: Change iOS dependency +## 1.0.0-next.0+1 ### Features - feat(auth): New feature ''', 'amplify_auth_cognito_ios': ''' -## 1.0.0-next.1 +## 1.0.0-next.0+1 ### Breaking Changes - fix(amplify_auth_cognito_ios)!: Change iOS dependency @@ -350,7 +356,7 @@ environment: sdk: '>=2.17.0 <3.0.0' dependencies: - aws_common: ^0.1.0 + aws_common: "^0.1.0" ''', 'amplify_auth_cognito_dart': ''' name: amplify_auth_cognito_dart @@ -361,24 +367,24 @@ environment: dependencies: amplify_core: ">=1.0.0-next.0+1 <1.0.0-next.1" - aws_common: ^0.1.0 + aws_common: "^0.1.0" ''', 'amplify_auth_cognito': ''' name: amplify_auth_cognito -version: 1.0.0-next.1 +version: 1.0.0-next.0+1 environment: sdk: '>=2.17.0 <3.0.0' dependencies: - amplify_auth_cognito_ios: ">=1.0.0-next.1 <1.0.0-next.2" + amplify_auth_cognito_ios: ">=1.0.0-next.0+1 <1.0.0-next.1" amplify_auth_cognito_dart: ">=0.1.1 <0.2.0" amplify_core: ">=1.0.0-next.0+1 <1.0.0-next.1" - aws_common: ^0.1.0 + aws_common: "^0.1.0" ''', 'amplify_auth_cognito_ios': ''' name: amplify_auth_cognito_ios -version: 1.0.0-next.1 +version: 1.0.0-next.0+1 environment: sdk: '>=2.17.0 <3.0.0' From 6fd0b4d7a95dc75cbfb6daf473b7b5ce273e4b9f Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Thu, 8 Dec 2022 17:59:54 -0700 Subject: [PATCH 22/40] More updates --- aft.yaml | 7 +- packages/aft/bin/aft.dart | 2 +- packages/aft/doc/versioning.md | 15 +++++ .../aft/lib/src/changelog/commit_message.dart | 22 +++--- .../aft/lib/src/commands/amplify_command.dart | 18 +++-- .../lib/src/commands/bootstrap_command.dart | 1 + .../lib/src/commands/changelog_command.dart | 4 +- .../aft/lib/src/commands/clean_command.dart | 1 + .../aft/lib/src/commands/deps_command.dart | 2 + .../src/commands/generate_sdk_command.dart | 1 + .../aft/lib/src/commands/link_command.dart | 1 + .../src/commands/list_packages_command.dart | 1 + .../aft/lib/src/commands/pub_command.dart | 5 ++ .../aft/lib/src/commands/publish_command.dart | 28 ++++---- .../src/commands/version_bump_command.dart | 30 +++++++-- packages/aft/lib/src/models.dart | 3 + packages/aft/lib/src/pub/pub_runner.dart | 7 ++ packages/aft/lib/src/repo.dart | 67 +++++++++---------- packages/aft/pubspec.yaml | 6 +- packages/aft/test/commit_message_test.dart | 28 ++++++++ .../smithy/goldens/lib/restXml/pubspec.yaml | 2 +- .../lib/restXmlWithNamespace/pubspec.yaml | 2 +- .../smithy/goldens/lib2/restXml/pubspec.yaml | 2 +- .../lib2/restXmlWithNamespace/pubspec.yaml | 2 +- packages/smithy/smithy/pubspec.yaml | 2 +- packages/smithy/smithy_aws/pubspec.yaml | 2 +- packages/smithy/smithy_codegen/pubspec.yaml | 2 +- packages/smithy/smithy_test/pubspec.yaml | 2 +- 28 files changed, 179 insertions(+), 86 deletions(-) create mode 100644 packages/aft/doc/versioning.md create mode 100644 packages/aft/test/commit_message_test.dart diff --git a/aft.yaml b/aft.yaml index 0422a3f6a8..27e28443f0 100644 --- a/aft.yaml +++ b/aft.yaml @@ -12,6 +12,7 @@ dependencies: code_builder: 4.3.0 json_annotation: ^4.7.0 json_serializable: 6.5.4 + xml: ">=6.1.0 <=6.2.2" uuid: ">=3.0.6 <=3.0.7" # Packages to ignore in all repo operations. @@ -31,8 +32,6 @@ components: - amplify_datastore - amplify_datastore_plugin_interface - amplify_analytics_pinpoint - - amplify_analytics_pinpoint_android - - amplify_analytics_pinpoint_ios - amplify_api - amplify_api_android - amplify_api_ios @@ -40,12 +39,12 @@ components: - amplify_auth_cognito_android - amplify_auth_cognito_ios - amplify_storage_s3 - - amplify_storage_s3_android - - amplify_storage_s3_ios - name: amplify_dart summary: amplify_core packages: - amplify_auth_cognito_dart + - amplify_analytics_pinpoint_dart + - amplify_storage_s3_dart - name: amplify_ui packages: - amplify_authenticator diff --git a/packages/aft/bin/aft.dart b/packages/aft/bin/aft.dart index f8774241c7..0f7c00c353 100644 --- a/packages/aft/bin/aft.dart +++ b/packages/aft/bin/aft.dart @@ -38,7 +38,7 @@ Future main(List args) async { ..addCommand(PubCommand()) ..addCommand(BootstrapCommand()) ..addCommand(ChangelogCommand()) - ..addCommand(VersionCommand()); + ..addCommand(VersionBumpCommand()); try { await runner.run(args); } on UsageException catch (e) { diff --git a/packages/aft/doc/versioning.md b/packages/aft/doc/versioning.md new file mode 100644 index 0000000000..7ad6893e93 --- /dev/null +++ b/packages/aft/doc/versioning.md @@ -0,0 +1,15 @@ +# Versioning Algorithm + +The `aft version` command uses Git history + [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) formatting to determine a suitable next version for a package along with the required changes for depending packages. + +1. Let `packages` be the set of all packages in the repo which are publishable to `pub.dev`. +2. For every package `P` in `packages`: + 1. Let `baseRef` be the commit of the last release of `P` (defaults to the latest tag). + 2. Let `headRef` be the releaseable commit of `P` (defaults to `HEAD`). + 3. Let `history` be the list of git commits in the range `baseRef..headRef` which affected `P`, i.e. those commits which included changes to files in `P`. + 4. Let `nextVersion = currentVersion`. + 5. For each `commit` in `history`: + 1. If `commit` is a version bump (i.e. `chore(version)`) or a merge commit, ignore it. + 2. If `commit` is a breaking change (i.e. `feat(auth)!`), set `isBreakingChange = true`. + 3. If `commit` is a noteworthy change (scope of `feat`/`fix` or breaking change), set `includeInChangelog = true`. + 4. diff --git a/packages/aft/lib/src/changelog/commit_message.dart b/packages/aft/lib/src/changelog/commit_message.dart index 501f3313a0..a8c51ae6e6 100644 --- a/packages/aft/lib/src/changelog/commit_message.dart +++ b/packages/aft/lib/src/changelog/commit_message.dart @@ -16,11 +16,12 @@ import 'package:aft/aft.dart'; import 'package:aws_common/aws_common.dart'; import 'package:collection/collection.dart'; +final RegExp _mergeCommitRegex = RegExp(r'^Merge .+$'); final RegExp _commitRegex = RegExp( r'(?build|chore|ci|docs|feat|fix|bug|perf|refactor|revert|style|test)?' r'(?\([a-zA-Z0-9_,\s\*]+\)?((?=:\s?)|(?=!:\s?)))?' r'(?!)?' - r'(?:\s?.*)?|^(?Merge .+)', + r'(?:\s?.*)?', ); enum CommitTypeGroup { @@ -76,6 +77,15 @@ abstract class CommitMessage with AWSEquatable { required String body, required DateTime dateTime, }) { + final mergeCommit = _mergeCommitRegex.firstMatch(summary); + if (mergeCommit != null) { + return MergeCommitMessage( + sha, + mergeCommit.group(0)!, + dateTime: dateTime, + ); + } + final commitMessage = _commitRegex.firstMatch(summary); if (commitMessage == null) { throw ArgumentError.value( @@ -85,11 +95,6 @@ abstract class CommitMessage with AWSEquatable { ); } - final mergeCommit = commitMessage.namedGroup('merge'); - if (mergeCommit != null) { - return MergeCommitMessage(sha, mergeCommit, dateTime: dateTime); - } - final typeStr = commitMessage.namedGroup('type'); if (typeStr == null) { return UnconventionalCommitMessage(sha, summary, dateTime: dateTime); @@ -99,7 +104,8 @@ abstract class CommitMessage with AWSEquatable { final isBreakingChange = commitMessage.namedGroup('breaking') != null; final scopes = commitMessage .namedGroup('scope') - ?.split(',') + ?.replaceAll(RegExp(r'[\(\)]'), '') + .split(',') .map((scope) => scope.trim()) .toList() ?? const []; @@ -112,7 +118,7 @@ abstract class CommitMessage with AWSEquatable { return UnconventionalCommitMessage(sha, summary, dateTime: dateTime); } - if (type == CommitType.version) { + if (type == CommitType.chore && scopes.singleOrNull == 'version') { return VersionCommitMessage( sha, summary, diff --git a/packages/aft/lib/src/commands/amplify_command.dart b/packages/aft/lib/src/commands/amplify_command.dart index 576e34a0b0..e7508c3531 100644 --- a/packages/aft/lib/src/commands/amplify_command.dart +++ b/packages/aft/lib/src/commands/amplify_command.dart @@ -44,10 +44,6 @@ abstract class AmplifyCommand extends Command AWSLogger() ..unregisterAllPlugins() ..registerPlugin(this); - - if (globalResults?['verbose'] as bool? ?? false) { - AWSLogger().logLevel = LogLevel.verbose; - } } late final AWSLogger logger = () { @@ -79,7 +75,9 @@ abstract class AmplifyCommand extends Command } /// Whether verbose logging is enabled. - bool get verbose => AWSLogger().logLevel == LogLevel.verbose; + bool get verbose => + globalResults?['verbose'] as bool? ?? + AWSLogger().logLevel == LogLevel.verbose; /// The current working directory. late final Directory workingDirectory = () { @@ -101,7 +99,7 @@ abstract class AmplifyCommand extends Command while (dir.parent != dir) { final files = dir.listSync(followLinks: false).whereType(); for (final file in files) { - if (p.basename(file.path) == 'mono_repo.yaml') { + if (p.basename(file.path) == 'aft.yaml') { return dir; } } @@ -233,6 +231,14 @@ abstract class AmplifyCommand extends Command return PubVersionInfo(semvers..sort()); } + @override + @mustCallSuper + Future run() async { + if (globalResults?['verbose'] as bool? ?? false) { + AWSLogger().logLevel = LogLevel.verbose; + } + } + @override @mustCallSuper void close() { diff --git a/packages/aft/lib/src/commands/bootstrap_command.dart b/packages/aft/lib/src/commands/bootstrap_command.dart index e94aa40bd1..cf95f901f2 100644 --- a/packages/aft/lib/src/commands/bootstrap_command.dart +++ b/packages/aft/lib/src/commands/bootstrap_command.dart @@ -77,6 +77,7 @@ const amplifyEnvironments = {}; @override Future run() async { + await super.run(); await linkPackages(allPackages); await pubAction( action: upgrade ? PubAction.upgrade : PubAction.get, diff --git a/packages/aft/lib/src/commands/changelog_command.dart b/packages/aft/lib/src/commands/changelog_command.dart index a279c0de81..4a1719f15b 100644 --- a/packages/aft/lib/src/commands/changelog_command.dart +++ b/packages/aft/lib/src/commands/changelog_command.dart @@ -51,7 +51,7 @@ abstract class _ChangelogBaseCommand extends AmplifyCommand late final bool yes = argResults!['yes'] as bool; Future _updateChangelogs({required bool preview}) async { - for (final package in repo.developmentPackages) { + for (final package in repo.publishablePackages) { final baseRef = this.baseRef ?? repo.latestTag(package.name); if (baseRef == null) { exitError( @@ -85,6 +85,7 @@ class _ChangelogPreviewCommand extends _ChangelogBaseCommand { @override Future run() async { + await super.run(); return _updateChangelogs(preview: true); } } @@ -98,6 +99,7 @@ class _ChangelogUpdateCommand extends _ChangelogBaseCommand { @override Future run() async { + await super.run(); await _updateChangelogs(preview: false); logger.info('Changelogs successfully updated'); diff --git a/packages/aft/lib/src/commands/clean_command.dart b/packages/aft/lib/src/commands/clean_command.dart index 05bb3ce195..03414f83a3 100644 --- a/packages/aft/lib/src/commands/clean_command.dart +++ b/packages/aft/lib/src/commands/clean_command.dart @@ -55,6 +55,7 @@ class CleanCommand extends AmplifyCommand { @override Future run() async { + await super.run(); await Future.wait([ for (final package in allPackages.values) _cleanBuildFolder(package), ]); diff --git a/packages/aft/lib/src/commands/deps_command.dart b/packages/aft/lib/src/commands/deps_command.dart index 647af4c4c6..8d518710f3 100644 --- a/packages/aft/lib/src/commands/deps_command.dart +++ b/packages/aft/lib/src/commands/deps_command.dart @@ -155,6 +155,7 @@ class _DepsSubcommand extends AmplifyCommand { @override Future run() async { + await super.run(); return _run(action); } } @@ -164,6 +165,7 @@ class _DepsUpdateCommand extends _DepsSubcommand { @override Future run() async { + await super.run(); final globalDependencyConfig = aftConfig.dependencies; final aftEditor = YamlEditor(aftConfigYaml); diff --git a/packages/aft/lib/src/commands/generate_sdk_command.dart b/packages/aft/lib/src/commands/generate_sdk_command.dart index f6f4bf85c4..808aa539f5 100644 --- a/packages/aft/lib/src/commands/generate_sdk_command.dart +++ b/packages/aft/lib/src/commands/generate_sdk_command.dart @@ -100,6 +100,7 @@ class GenerateSdkCommand extends AmplifyCommand { @override Future run() async { + await super.run(); final args = argResults!; final configFilepath = args['config'] as String; final configFile = File(configFilepath); diff --git a/packages/aft/lib/src/commands/link_command.dart b/packages/aft/lib/src/commands/link_command.dart index 80a66ca671..95b229417d 100644 --- a/packages/aft/lib/src/commands/link_command.dart +++ b/packages/aft/lib/src/commands/link_command.dart @@ -32,6 +32,7 @@ class LinkCommand extends AmplifyCommand { @override Future run() async { + await super.run(); await linkPackages(allPackages); stdout.writeln('Packages successfully linked!'); } diff --git a/packages/aft/lib/src/commands/list_packages_command.dart b/packages/aft/lib/src/commands/list_packages_command.dart index e62165d6af..5108ee2374 100644 --- a/packages/aft/lib/src/commands/list_packages_command.dart +++ b/packages/aft/lib/src/commands/list_packages_command.dart @@ -24,6 +24,7 @@ class ListPackagesCommand extends AmplifyCommand { @override Future run() async { + await super.run(); for (final package in allPackages.keys) { logger.info(package); } diff --git a/packages/aft/lib/src/commands/pub_command.dart b/packages/aft/lib/src/commands/pub_command.dart index 7f0fb19cc4..9b0f80d47e 100644 --- a/packages/aft/lib/src/commands/pub_command.dart +++ b/packages/aft/lib/src/commands/pub_command.dart @@ -29,6 +29,10 @@ enum PubAction { upgrade( 'Upgrades packages and the pubspec.lock file to their latest versions', 'Successfully upgraded packages', + ), + publish( + 'Publishes package to pub.dev', + 'Successfully published package', ); const PubAction(this.description, this.successMessage); @@ -75,6 +79,7 @@ class PubSubcommand extends AmplifyCommand { @override Future run() async { + await super.run(); await pubAction( action: action, allPackages: allPackages, diff --git a/packages/aft/lib/src/commands/publish_command.dart b/packages/aft/lib/src/commands/publish_command.dart index 3cde33c6a3..41d7e6794d 100644 --- a/packages/aft/lib/src/commands/publish_command.dart +++ b/packages/aft/lib/src/commands/publish_command.dart @@ -91,17 +91,19 @@ class PublishCommand extends AmplifyCommand { /// `build_runner` tasks. Future _prePublish(PackageInfo package) async { logger.info('Running pre-publish checks for ${package.name}...'); - // Remove any overrides so that `pub` commands resolve against - // `pubspec.yaml`, allowing us to verify we've set our constraints - // correctly. - final pubspecOverrideFile = File( - p.join(package.path, 'pubspec_overrides.yaml'), - ); - if (pubspecOverrideFile.existsSync()) { - pubspecOverrideFile.deleteSync(); + if (!dryRun) { + // Remove any overrides so that `pub` commands resolve against + // `pubspec.yaml`, allowing us to verify we've set our constraints + // correctly. + final pubspecOverrideFile = File( + p.join(package.path, 'pubspec_overrides.yaml'), + ); + if (pubspecOverrideFile.existsSync()) { + pubspecOverrideFile.deleteSync(); + } } final res = await Process.run( - package.flavor.name, + package.flavor.entrypoint, ['pub', 'upgrade'], stdoutEncoding: utf8, stderrEncoding: utf8, @@ -142,6 +144,7 @@ class PublishCommand extends AmplifyCommand { if (dryRun) '--dry-run', ], workingDirectory: package.path, + runInShell: true, ); final output = StringBuffer(); publishCmd @@ -157,9 +160,7 @@ class PublishCommand extends AmplifyCommand { final exitCode = await publishCmd.exitCode; if (exitCode != 0) { // Find any error lines which are not dependency constraint-related. - final failures = output - .toString() - .split('\n') + final failures = LineSplitter.split(output.toString()) .skipWhile((line) => !_validationStartRegex.hasMatch(line)) .where(_validationErrorRegex.hasMatch) .where((line) => !_validationConstraintRegex.hasMatch(line)) @@ -179,8 +180,9 @@ class PublishCommand extends AmplifyCommand { @override Future run() async { + await super.run(); // Gather packages which can be published. - final publishablePackages = repo.developmentPackages + final publishablePackages = repo.publishablePackages .where((pkg) => pkg.pubspecInfo.pubspec.publishTo != 'none'); // Gather packages which need to be published. diff --git a/packages/aft/lib/src/commands/version_bump_command.dart b/packages/aft/lib/src/commands/version_bump_command.dart index 5636566191..742f1483b5 100644 --- a/packages/aft/lib/src/commands/version_bump_command.dart +++ b/packages/aft/lib/src/commands/version_bump_command.dart @@ -21,8 +21,9 @@ import 'package:aft/src/repo.dart'; import 'package:path/path.dart' as p; /// Command for bumping package versions across the repo. -class VersionCommand extends AmplifyCommand with GitRefOptions, GlobOptions { - VersionCommand() { +class VersionBumpCommand extends AmplifyCommand + with GitRefOptions, GlobOptions { + VersionBumpCommand() { argParser ..addFlag( 'preview', @@ -61,15 +62,17 @@ class VersionCommand extends AmplifyCommand with GitRefOptions, GlobOptions { return repo.changes(baseRef, headRef); } - Future _updateVersions() async { + Future> _updateVersions() async { repo.bumpAllVersions( changesForPackage: _changesForPackage, ); final changelogUpdates = repo.changelogUpdates; - for (final package in repo.developmentPackages) { + final bumpedPackages = []; + for (final package in repo.publishablePackages) { final edits = package.pubspecInfo.pubspecYamlEditor.edits; if (edits.isNotEmpty) { + bumpedPackages.add(package); if (preview) { logger.info('pubspec.yaml'); for (final edit in edits) { @@ -94,13 +97,30 @@ class VersionCommand extends AmplifyCommand with GitRefOptions, GlobOptions { } } } + + return bumpedPackages; } @override Future run() async { - await _updateVersions(); + await super.run(); + final bumpedPackages = await _updateVersions(); if (!preview) { + for (final package in bumpedPackages) { + // Run build_runner for packages which generate their version number. + final needsBuildRunner = package.pubspecInfo.pubspec.devDependencies + .containsKey('build_version'); + if (!needsBuildRunner) { + continue; + } + await runBuildRunner( + package, + logger: logger, + verbose: verbose, + ); + } + logger.info('Version successfully bumped'); // TODO(dnys1): Use version bump commit instead of tags // if (yes || prompt('Commit changes? (y/N) ').toLowerCase() == 'y') { diff --git a/packages/aft/lib/src/models.dart b/packages/aft/lib/src/models.dart index 303efa83bb..4b77f4f70f 100644 --- a/packages/aft/lib/src/models.dart +++ b/packages/aft/lib/src/models.dart @@ -89,6 +89,9 @@ class PackageInfo !isExample; } + /// Whether the package is publishable. + bool get isPublishable => pubspecInfo.pubspec.publishTo == null; + /// Whether the package is used in development. bool get isDevelopmentPackage => !isExample && !isTestPackage; diff --git a/packages/aft/lib/src/pub/pub_runner.dart b/packages/aft/lib/src/pub/pub_runner.dart index 43e93b27c6..aa8b4cd4ec 100644 --- a/packages/aft/lib/src/pub/pub_runner.dart +++ b/packages/aft/lib/src/pub/pub_runner.dart @@ -87,6 +87,13 @@ Future runFlutterPub( final runner = FlutterCommandRunner() ..addCommand( PackagesGetCommand(action.name, action == PubAction.upgrade), + ) + ..addCommand( + PackagesForwardCommand( + 'publish', + 'Publish the current package to pub.dartlang.org.', + requiresPubspec: true, + ), ); await runner.run([action.name, package.path]); }, diff --git a/packages/aft/lib/src/repo.dart b/packages/aft/lib/src/repo.dart index aa9b6efe36..c31cba7666 100644 --- a/packages/aft/lib/src/repo.dart +++ b/packages/aft/lib/src/repo.dart @@ -34,7 +34,7 @@ class Repo { required this.allPackages, required this.aftConfig, AWSLogger? logger, - }) : logger = logger ?? AWSLogger('Repo'); + }) : logger = logger ?? AWSLogger().createChild('Repo'); /// The root directory of the repository. final Directory rootDir; @@ -45,8 +45,9 @@ class Repo { final AftConfig aftConfig; - late final List developmentPackages = UnmodifiableListView( - allPackages.values.where((pkg) => pkg.isDevelopmentPackage).toList(), + /// All packages which can be published to `pub.dev`. + late final List publishablePackages = UnmodifiableListView( + allPackages.values.where((pkg) => pkg.isPublishable).toList(), ); /// The components of the repository. @@ -237,7 +238,7 @@ class Repo { void bumpAllVersions({ required GitChanges Function(PackageInfo) changesForPackage, }) { - final sortedPackages = List.of(developmentPackages); + final sortedPackages = List.of(publishablePackages); sortPackagesTopologically( sortedPackages, (PackageInfo pkg) => pkg.pubspecInfo.pubspec, @@ -269,12 +270,12 @@ class Repo { /// Bumps the version and changelog in [package] and its component packages /// using [commit] and returns the new version. /// - /// If [type] is [VersionBumpType.breaking], the version change is propagated - /// to all packages which depend on [package]. + /// If [type] is [VersionBumpType.nonBreaking] or [VersionBumpType.breaking], + /// the version change is propagated to all packages which depend on + /// [package] at the type which is next least severe. /// /// If [propogateToComponent] is `true`, all component packages are bumped to /// the same version. - /// [VersionBumpType.nonBreaking] or [VersionBumpType.breaking]. Version bumpVersion( PackageInfo package, { required CommitMessage commit, @@ -319,31 +320,30 @@ class Repo { ['version'], newVersion.toString(), ); - if (type == VersionBumpType.breaking) { + if (commit.isBreakingChange && type != VersionBumpType.patch) { // Back-propogate to all dependent packages for breaking changes. // // Since we set semantic version constraints, only a breaking change // in a direct dependency necessitates a version bump. - logger.verbose('Performing dfs on dependent packages...'); - dfs( - reversedPackageGraph, - root: package, - (dependent) { - if (dependent == package) return; - logger.verbose('dfs found dependent package ${dependent.name}'); - if (dependent.isDevelopmentPackage) { - bumpVersion( - dependent, - commit: commit, - // Do not consider it a breaking change in dependent packages - // even if it was a breaking change in the main package. - type: VersionBumpType.nonBreaking, - includeInChangelog: false, - ); - } - bumpDependency(package, dependent); - }, + logger.verbose( + 'Breaking change. Performing dfs on dependent packages...', ); + for (final dependent in allPackages.values.where( + (pkg) => pkg.pubspecInfo.pubspec.dependencies.keys.contains( + package.name, + ), + )) { + logger.verbose('found dependent package ${dependent.name}'); + if (dependent.isPublishable) { + bumpVersion( + dependent, + commit: commit, + type: VersionBumpType.patch, + includeInChangelog: false, + ); + } + bumpDependency(package, dependent); + } } // Propagate to all component packages. @@ -370,12 +370,12 @@ class Repo { // Update summary package's changelog if it exists. final summaryPackage = component?.summary; - if (summaryPackage != null) { + if (summaryPackage != null && includeInChangelog) { logger.debug( 'Updating summary package `${summaryPackage.name}` ' 'with commit: $commit', ); - final packageVersion = versionChanges[summaryPackage.name]; + final packageVersion = versionChanges.newVersion(summaryPackage); final currentChangelogUpdate = changelogUpdates[summaryPackage]; changelogUpdates[summaryPackage] = summaryPackage.changelog.update( commits: { @@ -421,8 +421,7 @@ class _DiffMarker with AWSEquatable<_DiffMarker> { List get props => [baseTree, headTree]; } -class VersionChanges extends MapBase - with UnmodifiableMapMixin { +class VersionChanges { VersionChanges(this._repo); final Repo _repo; @@ -450,10 +449,4 @@ class VersionChanges extends MapBase _versionUpdates['component_$component'] = version; _versionUpdates[package.name] = version; } - - @override - Version? operator [](Object? key) => _versionUpdates[key]; - - @override - Iterable get keys => _versionUpdates.keys; } diff --git a/packages/aft/pubspec.yaml b/packages/aft/pubspec.yaml index 6da5a937d0..4aabb11cd1 100644 --- a/packages/aft/pubspec.yaml +++ b/packages/aft/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: flutter_tools: git: url: https://github.com/flutter/flutter.git - ref: 0df0e2ea1f7fe5021c3593d61903e70bdbda8107 + ref: 117a83a4a7178c328d296748bd93ff388724cb67 path: packages/flutter_tools git: ^2.0.0 glob: ^2.1.0 @@ -35,7 +35,7 @@ dependencies: pub: git: url: https://github.com/dart-lang/pub.git - ref: 0a48753420cbd03eb72a92d38ef06fe3164dc335 + ref: 6cbbec2abbf6a54074ae1005c06a26dfb14a86c8 pub_semver: ^2.1.1 pubspec_parse: ^1.2.0 smithy: ">=0.3.0 <0.4.0" @@ -53,7 +53,7 @@ dev_dependencies: amplify_lints: path: ../amplify_lints build_runner: ^2.2.1 - built_value_generator: 8.4.1 + built_value_generator: 8.4.2 json_serializable: 6.5.4 test: ^1.16.0 diff --git a/packages/aft/test/commit_message_test.dart b/packages/aft/test/commit_message_test.dart new file mode 100644 index 0000000000..61d7f0afe3 --- /dev/null +++ b/packages/aft/test/commit_message_test.dart @@ -0,0 +1,28 @@ +import 'package:aft/src/changelog/commit_message.dart'; +import 'package:test/test.dart'; + +void main() { + group('CommitMessage', () { + test('parses merge commits', () { + final parsed = CommitMessage.parse( + '', + 'Merge pull request #2407 from NikaHsn/chore/contributing-doc-fix', + body: '', + dateTime: DateTime.now(), + ); + expect(parsed.type, CommitType.merge); + expect(parsed.bumpType, isNull); + }); + + test('parses version commit', () { + final parsed = CommitMessage.parse( + '', + 'chore(version): Next version', + body: '', + dateTime: DateTime.now(), + ); + expect(parsed.type, CommitType.version); + expect(parsed.bumpType, isNull); + }); + }); +} diff --git a/packages/smithy/goldens/lib/restXml/pubspec.yaml b/packages/smithy/goldens/lib/restXml/pubspec.yaml index 44f0a578d7..37e5887876 100644 --- a/packages/smithy/goldens/lib/restXml/pubspec.yaml +++ b/packages/smithy/goldens/lib/restXml/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: shelf_router: ^1.1.0 shelf: ^1.1.0 fixnum: ^1.0.0 - xml: 6.1.0 + xml: ">=6.1.0 <=6.2.2" dependency_overrides: smithy: diff --git a/packages/smithy/goldens/lib/restXmlWithNamespace/pubspec.yaml b/packages/smithy/goldens/lib/restXmlWithNamespace/pubspec.yaml index 9c404ffa12..0615acc369 100644 --- a/packages/smithy/goldens/lib/restXmlWithNamespace/pubspec.yaml +++ b/packages/smithy/goldens/lib/restXmlWithNamespace/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: aws_common: path: ../../../../aws_common built_value: ">=8.4.0 <8.5.0" - xml: 6.1.0 + xml: ">=6.1.0 <=6.2.2" fixnum: ^1.0.0 meta: ^1.7.0 built_collection: ^5.0.0 diff --git a/packages/smithy/goldens/lib2/restXml/pubspec.yaml b/packages/smithy/goldens/lib2/restXml/pubspec.yaml index 26da58d5ea..1ce18387df 100644 --- a/packages/smithy/goldens/lib2/restXml/pubspec.yaml +++ b/packages/smithy/goldens/lib2/restXml/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: fixnum: ^1.0.0 built_collection: ^5.0.0 meta: ^1.7.0 - xml: 6.1.0 + xml: ">=6.1.0 <=6.2.2" shelf_router: ^1.1.0 shelf: ^1.1.0 aws_signature_v4: diff --git a/packages/smithy/goldens/lib2/restXmlWithNamespace/pubspec.yaml b/packages/smithy/goldens/lib2/restXmlWithNamespace/pubspec.yaml index 6d2ed3224c..605f0c4304 100644 --- a/packages/smithy/goldens/lib2/restXmlWithNamespace/pubspec.yaml +++ b/packages/smithy/goldens/lib2/restXmlWithNamespace/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: aws_common: path: ../../../../aws_common built_value: ">=8.4.0 <8.5.0" - xml: 6.1.0 + xml: ">=6.1.0 <=6.2.2" fixnum: ^1.0.0 meta: ^1.7.0 built_collection: ^5.0.0 diff --git a/packages/smithy/smithy/pubspec.yaml b/packages/smithy/smithy/pubspec.yaml index fb230f30ac..abba4dadd4 100644 --- a/packages/smithy/smithy/pubspec.yaml +++ b/packages/smithy/smithy/pubspec.yaml @@ -25,7 +25,7 @@ dependencies: retry: ^3.1.0 shelf: ^1.1.0 typed_data: ^1.3.0 - xml: 6.1.0 + xml: ">=6.1.0 <=6.2.2" dev_dependencies: amplify_lints: diff --git a/packages/smithy/smithy_aws/pubspec.yaml b/packages/smithy/smithy_aws/pubspec.yaml index 7ec2266867..05f17b15f1 100644 --- a/packages/smithy/smithy_aws/pubspec.yaml +++ b/packages/smithy/smithy_aws/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: meta: ^1.7.0 path: ^1.8.0 smithy: ">=0.3.0 <0.4.0" - xml: 6.1.0 + xml: ">=6.1.0 <=6.2.2" dev_dependencies: amplify_lints: diff --git a/packages/smithy/smithy_codegen/pubspec.yaml b/packages/smithy/smithy_codegen/pubspec.yaml index f455fa9ffe..ab8a7d2566 100644 --- a/packages/smithy/smithy_codegen/pubspec.yaml +++ b/packages/smithy/smithy_codegen/pubspec.yaml @@ -33,7 +33,7 @@ dependencies: smithy: ">=0.3.0 <0.4.0" smithy_aws: ">=0.3.0 <0.4.0" tuple: ^2.0.0 - xml: 6.1.0 + xml: ">=6.1.0 <=6.2.2" yaml_edit: ^2.0.1 dev_dependencies: diff --git a/packages/smithy/smithy_test/pubspec.yaml b/packages/smithy/smithy_test/pubspec.yaml index b125f609d7..13022937d3 100644 --- a/packages/smithy/smithy_test/pubspec.yaml +++ b/packages/smithy/smithy_test/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: smithy: ^0.1.0 test: ^1.16.0 tuple: ^2.0.0 - xml: 6.1.0 + xml: ">=6.1.0 <=6.2.2" dev_dependencies: amplify_lints: From ef26f28f9c76b601412fa2d6ad5e83612845e8ac Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Wed, 14 Dec 2022 16:00:50 -0700 Subject: [PATCH 23/40] chore(aft): More cleanup --- aft.yaml | 10 +- packages/aft/doc/versioning.md | 39 +++- .../aft/lib/src/changelog/commit_message.dart | 105 ++++++---- .../lib/src/commands/changelog_command.dart | 4 +- .../src/commands/version_bump_command.dart | 4 +- packages/aft/lib/src/repo.dart | 112 ++++++----- packages/aft/pubspec.yaml | 1 + packages/aft/test/commit_message_test.dart | 78 ++++++-- packages/aft/test/e2e_test.dart | 179 ++++++++++++------ 9 files changed, 366 insertions(+), 166 deletions(-) diff --git a/aft.yaml b/aft.yaml index 77f762a749..fb388820ed 100644 --- a/aft.yaml +++ b/aft.yaml @@ -24,7 +24,7 @@ ignore: # Strongly connected components which should have major version bumps happen # in unison, i.e. a version bump to one package cascades to all. components: - - name: amplify_flutter + - name: Amplify Flutter summary: amplify_flutter packages: - amplify_flutter @@ -41,22 +41,22 @@ components: - amplify_auth_cognito_android - amplify_auth_cognito_ios - amplify_storage_s3 - - name: amplify_dart + - name: Amplify Dart summary: amplify_core packages: - amplify_auth_cognito_dart - amplify_analytics_pinpoint_dart - amplify_storage_s3_dart - - name: amplify_ui + - name: Amplify UI packages: - amplify_authenticator - - name: smithy + - name: Smithy summary: smithy packages: - smithy - smithy_aws - smithy_codegen - - name: worker_bee + - name: Worker Bee summary: worker_bee packages: - worker_bee diff --git a/packages/aft/doc/versioning.md b/packages/aft/doc/versioning.md index 7ad6893e93..745f656076 100644 --- a/packages/aft/doc/versioning.md +++ b/packages/aft/doc/versioning.md @@ -4,12 +4,33 @@ The `aft version` command uses Git history + [conventional commit](https://www.c 1. Let `packages` be the set of all packages in the repo which are publishable to `pub.dev`. 2. For every package `P` in `packages`: - 1. Let `baseRef` be the commit of the last release of `P` (defaults to the latest tag). - 2. Let `headRef` be the releaseable commit of `P` (defaults to `HEAD`). - 3. Let `history` be the list of git commits in the range `baseRef..headRef` which affected `P`, i.e. those commits which included changes to files in `P`. - 4. Let `nextVersion = currentVersion`. - 5. For each `commit` in `history`: - 1. If `commit` is a version bump (i.e. `chore(version)`) or a merge commit, ignore it. - 2. If `commit` is a breaking change (i.e. `feat(auth)!`), set `isBreakingChange = true`. - 3. If `commit` is a noteworthy change (scope of `feat`/`fix` or breaking change), set `includeInChangelog = true`. - 4. + 1. Let `component` be the component of `P`, if any. + 2. Let `baseRef` be the commit of the last release of `P`. + 3. Let `headRef` be the releaseable commit of `P` (defaults to `HEAD`). + 4. Let `history` be the list of git commits in the range `baseRef..headRef` which affected `P`, i.e. those commits which included changes to files in `P`. + 5. Let `nextVersion = currentVersion`. + 6. For each `commit` in `history`: + 1. If `commit` is a version bump (i.e. `chore(version)`), ignore it. + 2. If `commit` is a merge commit, update dependencies based on the packages changed by the commit. + 1. The thinking here is that PRs should either be squashed into a single commit or merged as a set of independent commits capped off by a merge commit. The independent commits are isolated changes which are used to update changelogs and bump versions. The merge commit is then used solely for associating previous commits and updating constraints accordingly. + 3. If `commit` is a breaking change (i.e. `feat(auth)!`), set `bumpType = breaking`. + 1. else if `commit`'s type is `feat`, set `bumpType = nonBreaking`. + 2. else, set `bumpType = patch`. + 4. If `commit` is a noteworthy change (scope is one of `feat`, `fix`, `bug`, `perf`, or `revert` or it's a breaking change), set `includeInChangelog = true`. + 5. Let `proposedVersion = currentVersion.bump(bumpType)` + 6. Let `nextVersion = max(nextVersion, proposedVersion)` + 7. If `nextVersion > currentVersion`: + 1. Update `pubspec.yaml`, set `version = nextVersion` + 2. If `includeInChangelog`: + 1. Update `CHANGELOG.md` with an entry for `commit`. + 3. If `bumpType == breaking`: + 1. For every package `Q` which directly depends on `P`: + 1. Bump the version of `Q` with `bumpType = patch` and `includeInChangelog = false`. + 2. Update `Q`'s constraint on `P`. + 4. If `bumpType == breaking` or `bumpType == nonBreaking` and `component != null`: + 1. For every package `Q` in `component`: + 1. Bump the version of `Q` with the same `bumpType` as `P` and `includeInChangelog = false`. + 8. If `component` has a summary package: + 1. Update `CHANGELOG.md` in the summary package with `commit`. + 9. For every package `Q` which was affected by `commit`: + 1. Update `Q`'s constraint on `P` using `nextVersion`. diff --git a/packages/aft/lib/src/changelog/commit_message.dart b/packages/aft/lib/src/changelog/commit_message.dart index a8c51ae6e6..b24e96cfa3 100644 --- a/packages/aft/lib/src/changelog/commit_message.dart +++ b/packages/aft/lib/src/changelog/commit_message.dart @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'dart:convert'; + import 'package:aft/aft.dart'; import 'package:aws_common/aws_common.dart'; import 'package:collection/collection.dart'; @@ -23,6 +25,7 @@ final RegExp _commitRegex = RegExp( r'(?!)?' r'(?:\s?.*)?', ); +final RegExp _trailerRegex = RegExp(r'^[^:\s]+:[^:]+$'); enum CommitTypeGroup { breaking('Breaking Changes'), @@ -64,9 +67,9 @@ enum CommitType { /// {@endtemplate} abstract class CommitMessage with AWSEquatable { /// {@macro aft.changelog.commit_message} - const CommitMessage( - this.sha, - this.summary, { + const CommitMessage({ + required this.sha, + required this.summary, required this.dateTime, }); @@ -75,13 +78,18 @@ abstract class CommitMessage with AWSEquatable { String sha, String summary, { required String body, - required DateTime dateTime, + int? commitTimeSecs, }) { + final dateTime = commitTimeSecs == null + ? DateTime.now().toUtc() + : DateTime.fromMillisecondsSinceEpoch( + commitTimeSecs * 1000, + ).toUtc(); final mergeCommit = _mergeCommitRegex.firstMatch(summary); if (mergeCommit != null) { return MergeCommitMessage( - sha, - mergeCommit.group(0)!, + sha: sha, + summary: mergeCommit.group(0)!, dateTime: dateTime, ); } @@ -97,7 +105,11 @@ abstract class CommitMessage with AWSEquatable { final typeStr = commitMessage.namedGroup('type'); if (typeStr == null) { - return UnconventionalCommitMessage(sha, summary, dateTime: dateTime); + return UnconventionalCommitMessage( + sha: sha, + summary: summary, + dateTime: dateTime, + ); } final type = CommitType.values.byName(typeStr); @@ -115,20 +127,24 @@ abstract class CommitMessage with AWSEquatable { .trim(); // Fall back for malformed messages. if (description == null) { - return UnconventionalCommitMessage(sha, summary, dateTime: dateTime); + return UnconventionalCommitMessage( + sha: sha, + summary: summary, + dateTime: dateTime, + ); } if (type == CommitType.chore && scopes.singleOrNull == 'version') { - return VersionCommitMessage( - sha, - summary, + return VersionCommitMessage.parse( + sha: sha, + summary: summary, body: body, dateTime: dateTime, ); } return ConventionalCommitMessage( - sha, - summary, + sha: sha, + summary: summary, description: description, type: type, isBreakingChange: isBreakingChange, @@ -184,7 +200,7 @@ abstract class CommitMessage with AWSEquatable { } @override - List get props => [sha]; + List get props => [summary, dateTime]; @override String toString() => summary; @@ -195,9 +211,9 @@ abstract class CommitMessage with AWSEquatable { /// {@endtemplate} class MergeCommitMessage extends CommitMessage { /// {@macro aft.changelog.merge_commit_message} - const MergeCommitMessage( - super.sha, - super.summary, { + const MergeCommitMessage({ + required super.sha, + required super.summary, required super.dateTime, }); @@ -210,9 +226,9 @@ class MergeCommitMessage extends CommitMessage { /// {@endtemplate} class ConventionalCommitMessage extends CommitMessage { /// {@macro aft.changelog.conventional_commit_message} - const ConventionalCommitMessage( - super.sha, - super.summary, { + const ConventionalCommitMessage({ + required super.sha, + required super.summary, required this.description, required this.type, required this.isBreakingChange, @@ -239,6 +255,7 @@ class ConventionalCommitMessage extends CommitMessage { @override VersionBumpType get bumpType { switch (type) { + case CommitType.version: case CommitType.unconventional: case CommitType.merge: case CommitType.build: @@ -248,13 +265,12 @@ class ConventionalCommitMessage extends CommitMessage { case CommitType.refactor: case CommitType.style: case CommitType.test: - case CommitType.version: case CommitType.fix: case CommitType.bug: case CommitType.perf: case CommitType.revert: return isBreakingChange - ? VersionBumpType.nonBreaking + ? VersionBumpType.breaking : VersionBumpType.patch; case CommitType.feat: return isBreakingChange @@ -296,9 +312,9 @@ class ConventionalCommitMessage extends CommitMessage { /// {@endtemplate} class UnconventionalCommitMessage extends CommitMessage { /// {@macro aft.changelog.unconventional_commit_message} - const UnconventionalCommitMessage( - super.sha, - super.summary, { + const UnconventionalCommitMessage({ + required super.sha, + required super.summary, required super.dateTime, }); @@ -317,16 +333,41 @@ class UnconventionalCommitMessage extends CommitMessage { /// {@endtemplate} class VersionCommitMessage extends CommitMessage { /// {@macro aft.changelog.version_commit_message} - const VersionCommitMessage( - super.sha, - super.summary, { - required this.body, + factory VersionCommitMessage.parse({ + required String sha, + required String summary, + required String body, + required DateTime dateTime, + }) { + final trailers = Map.fromEntries( + LineSplitter.split(body).where(_trailerRegex.hasMatch).map( + (line) => MapEntry( + line.split(':')[0], + line.split(':')[1].trim(), + ), + ), + ); + final updatedComponentsStr = trailers['Updated-Components']; + final updatedComponents = updatedComponentsStr == null + ? const [] + : updatedComponentsStr.split(',').map((el) => el.trim()).toList(); + return VersionCommitMessage._( + sha: sha, + summary: summary, + updatedComponents: updatedComponents, + dateTime: dateTime, + ); + } + + const VersionCommitMessage._({ + required super.sha, + required super.summary, + required this.updatedComponents, required super.dateTime, }); - /// The body of the commit message. - // TODO(dnys1): Parse - final String body; + /// The list of updated components or packages. + final List updatedComponents; @override CommitType get type => CommitType.version; diff --git a/packages/aft/lib/src/commands/changelog_command.dart b/packages/aft/lib/src/commands/changelog_command.dart index 4a1719f15b..e6caeb70ab 100644 --- a/packages/aft/lib/src/commands/changelog_command.dart +++ b/packages/aft/lib/src/commands/changelog_command.dart @@ -52,10 +52,10 @@ abstract class _ChangelogBaseCommand extends AmplifyCommand Future _updateChangelogs({required bool preview}) async { for (final package in repo.publishablePackages) { - final baseRef = this.baseRef ?? repo.latestTag(package.name); + final baseRef = this.baseRef ?? repo.latestBumpRef(package.name); if (baseRef == null) { exitError( - 'No tag exists for package (${package.name}). ' + 'No previous version bumps for package (${package.name}). ' 'Supply a base ref manually using --base-ref', ); } diff --git a/packages/aft/lib/src/commands/version_bump_command.dart b/packages/aft/lib/src/commands/version_bump_command.dart index 742f1483b5..70db1d74e8 100644 --- a/packages/aft/lib/src/commands/version_bump_command.dart +++ b/packages/aft/lib/src/commands/version_bump_command.dart @@ -52,10 +52,10 @@ class VersionBumpCommand extends AmplifyCommand late final bool preview = argResults!['preview'] as bool; GitChanges _changesForPackage(PackageInfo package) { - final baseRef = this.baseRef ?? repo.latestTag(package.name); + final baseRef = this.baseRef ?? repo.latestBumpRef(package.name); if (baseRef == null) { exitError( - 'No tag exists for package (${package.name}). ' + 'No previous version bumps for package (${package.name}). ' 'Supply a base ref manually using --base-ref', ); } diff --git a/packages/aft/lib/src/repo.dart b/packages/aft/lib/src/repo.dart index c31cba7666..e3ea3f0c55 100644 --- a/packages/aft/lib/src/repo.dart +++ b/packages/aft/lib/src/repo.dart @@ -12,13 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:collection'; import 'dart:io'; import 'package:aft/aft.dart'; import 'package:aft/src/changelog/changelog.dart'; import 'package:aft/src/changelog/commit_message.dart'; -import 'package:aft/src/util.dart'; import 'package:aws_common/aws_common.dart'; import 'package:built_collection/built_collection.dart'; import 'package:collection/collection.dart'; @@ -87,30 +85,39 @@ class Repo { /// The libgit repository. late final Repository repo = Repository.open(rootDir.path); - /// The latest tag in the [repo], i.e. the last checkpoint for versioning. - String? latestTag([String? packageName]) => maxBy( - repo.tags.where((tag) { - if (!semverRegex.hasMatch(tag)) { - return false; - } - if (packageName == null) { - return true; - } - final packageRegex = RegExp('${packageName}_v${semverRegex.pattern}'); - if (packageRegex.hasMatch(tag)) { - return true; - } - final componentForPackage = - aftConfig.componentForPackage(packageName); - final componentRegex = - RegExp('${componentForPackage}_v${semverRegex.pattern}'); - return componentRegex.hasMatch(tag); - }), - (t) { - final version = semverRegex.firstMatch(t)!.group(0)!; - return Version.parse(version); - }, + /// Returns the latest version bump commit for [packageOrComponent], or `null` + /// if no such commit exists. + /// + /// This is the marker of the last time [packageOrComponent] was released and + /// is used as the base git reference for calculating changes relevant to this + /// version bump. + String? latestBumpRef(String packageOrComponent) { + final component = components[packageOrComponent]?.name ?? + components.values + .firstWhereOrNull( + (component) => component.packages + .map((pkg) => pkg.name) + .contains(packageOrComponent), + ) + ?.name ?? + packageOrComponent; + var commit = Commit.lookup(repo: repo, oid: repo.head.target); + while (commit.parents.isNotEmpty) { + final commitMessage = CommitMessage.parse( + commit.oid.sha, + commit.summary, + body: commit.body, + commitTimeSecs: commit.time, ); + if (commitMessage is VersionCommitMessage && + (commitMessage.updatedComponents.contains(component) || + commitMessage.updatedComponents.isEmpty)) { + return commitMessage.sha; + } + commit = commit.parent(0); + } + return null; + } /// The directed graph of packages to the packages it depends on. late final Map> packageGraph = @@ -208,9 +215,7 @@ class Repo { commit.oid.sha, commit.summary, body: commit.body, - dateTime: DateTime.fromMillisecondsSinceEpoch( - commit.time * 1000, - ).toUtc(), + commitTimeSecs: commit.time, ); logger.verbose( 'Package ${package.name} changed by $changedPath ' @@ -245,25 +250,31 @@ class Repo { ); for (final package in sortedPackages) { final changes = changesForPackage(package); - changes.commitsByPackage[package]?.forEach((commit) { - // TODO(dnys1): Define full set of commit types which should be ignored - // when considering version changes. + final commits = (changes.commitsByPackage[package]?.toList() ?? const []) + ..sort((a, b) => a.dateTime.compareTo(b.dateTime)); + for (final commit in commits) { + if (commit.type == CommitType.version) { + continue; + } final bumpType = commit.bumpType; - if (bumpType == null) { - return; + if (bumpType != null) { + bumpVersion( + package, + commit: commit, + type: bumpType, + includeInChangelog: commit.includeInChangelog, + ); } - bumpVersion( - package, - commit: commit, - type: bumpType, - includeInChangelog: commit.includeInChangelog, - ); // Propagate the version change to all packages affected by the same // commit as if they were a component. - changes.packagesByCommit[commit]?.forEach((commitPackage) { - bumpDependency(package, commitPackage); - }); - }); + // + // Even if _this_ commit didn't trigger a version bump, it may be a + // merge commit, in which case, it's important to propagate the changes + // of previous commits. + for (final commitPackage in changes.packagesByCommit[commit]!) { + updateConstraint(package, commitPackage); + } + } } } @@ -342,7 +353,7 @@ class Repo { includeInChangelog: false, ); } - bumpDependency(package, dependent); + updateConstraint(package, dependent); } } @@ -389,13 +400,20 @@ class Repo { return newVersion; } - /// Bumps the dependency for [package] in [dependent]. - void bumpDependency(PackageInfo package, PackageInfo dependent) { + /// Updates the constraint for [package] in [dependent]. + void updateConstraint(PackageInfo package, PackageInfo dependent) { final newVersion = versionChanges.newVersion(package); if (dependent.pubspecInfo.pubspec.dependencies.containsKey(package.name)) { + final newConstraint = newVersion.amplifyConstraint( + minVersion: newVersion, + ); + logger.verbose( + 'Bumping dependency for ${dependent.name} in ${package.name} ' + 'to $newConstraint', + ); dependent.pubspecInfo.pubspecYamlEditor.update( ['dependencies', package.name], - newVersion.amplifyConstraint(minVersion: newVersion), + newConstraint, ); } } diff --git a/packages/aft/pubspec.yaml b/packages/aft/pubspec.yaml index ca6a46a027..b302a73322 100644 --- a/packages/aft/pubspec.yaml +++ b/packages/aft/pubspec.yaml @@ -53,6 +53,7 @@ dependency_overrides: aws_signature_v4: path: ../aws_signature_v4 built_value: ">=8.4.0 <8.5.0" + frontend_server_client: ^3.2.0 smithy: path: ../smithy/smithy smithy_aws: diff --git a/packages/aft/test/commit_message_test.dart b/packages/aft/test/commit_message_test.dart index 61d7f0afe3..12f072875a 100644 --- a/packages/aft/test/commit_message_test.dart +++ b/packages/aft/test/commit_message_test.dart @@ -1,3 +1,17 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import 'package:aft/src/changelog/commit_message.dart'; import 'package:test/test.dart'; @@ -5,24 +19,62 @@ void main() { group('CommitMessage', () { test('parses merge commits', () { final parsed = CommitMessage.parse( - '', - 'Merge pull request #2407 from NikaHsn/chore/contributing-doc-fix', + 'sha', + 'Merge pull request #1234 from user/chore/some-fix', body: '', - dateTime: DateTime.now(), ); - expect(parsed.type, CommitType.merge); + expect( + parsed, + isA().having( + (msg) => msg.taggedPr, + 'taggedPr', + 1234, + ), + ); expect(parsed.bumpType, isNull); }); - test('parses version commit', () { - final parsed = CommitMessage.parse( - '', - 'chore(version): Next version', - body: '', - dateTime: DateTime.now(), - ); - expect(parsed.type, CommitType.version); - expect(parsed.bumpType, isNull); + group('VersionCommitMessage', () { + test('parses version commit', () { + final parsed = CommitMessage.parse( + 'sha', + 'chore(version): Next version', + body: '', + ); + expect( + parsed, + isA().having( + (msg) => msg.updatedComponents, + 'updatedComponents', + isEmpty, + ), + ); + expect(parsed.bumpType, isNull); + }); + + test('parses updated components', () { + final parsed = CommitMessage.parse( + 'sha', + 'chore(version): Next version', + body: ''' +New features +- a +- b +- c + +Test-Trailer:123 +Updated-Components:component_1,component_2 +''', + ); + expect( + parsed, + isA().having( + (msg) => msg.updatedComponents, + 'updatedComponents', + unorderedEquals(['component_1', 'component_2']), + ), + ); + }); }); }); } diff --git a/packages/aft/test/e2e_test.dart b/packages/aft/test/e2e_test.dart index d8d3c3ae64..36821eff84 100644 --- a/packages/aft/test/e2e_test.dart +++ b/packages/aft/test/e2e_test.dart @@ -42,11 +42,16 @@ void main() { group('Repo', () { late Repo repo; + late String baseRef; + final packageBumps = {}; - Future runGit(List args) => git.runGit( - args, - processWorkingDir: repo.rootDir.path, - ); + Future runGit(List args) async { + final result = await git.runGit( + args, + processWorkingDir: repo.rootDir.path, + ); + return (result.stdout as String).trim(); + } PackageInfo createPackage( String packageName, { @@ -86,7 +91,6 @@ Initial version. final package = PackageInfo( name: packageName, path: packagePath, - usesMonoRepo: false, pubspecInfo: PubspecInfo.fromUri(pubspecUri), flavor: PackageFlavor.dart, ); @@ -95,10 +99,11 @@ Initial version. return package; } - Future makeChange( + Future makeChange( String commitTitle, - List packages, - ) async { + List packages, { + Map? trailers, + }) async { for (final package in packages) { final newDir = Directory(p.join(repo.rootDir.path, 'packages', package)) .createTempSync(); @@ -106,7 +111,15 @@ Initial version. } await runGit(['add', '.']); - await runGit(['commit', '-m', commitTitle]); + await runGit([ + 'commit', + '-m', + commitTitle, + if (trailers != null) + ...trailers.entries + .expand((e) => ['--trailer', '${e.key}:${e.value}']), + ]); + return runGit(['rev-parse', 'HEAD']); } setUp(() async { @@ -118,7 +131,7 @@ Initial version. aftConfig: const AftConfig( components: [ AftComponent( - name: 'amplify', + name: 'Amplify Flutter', packages: [ 'amplify_auth_cognito', 'amplify_auth_cognito_ios', @@ -127,33 +140,10 @@ Initial version. ], ), ); - await runGit(['commit', '--allow-empty', '-m', 'Initial commit']); - }); - - test('latestTag', () async { - // Create tags for packages + components - await runGit(['tag', 'amplify_v1.0.0-next.0+2']); - await runGit(['tag', 'amplify_v1.0.0-next.0+1']); - await runGit(['tag', 'amplify_auth_cognito_v0.6.6']); - await runGit(['tag', 'amplify_auth_cognito_dart_v0.1.0']); - await runGit(['tag', 'amplify_auth_cognito_dart_v0.1.1']); - await runGit(['tag', 'amplify_auth_cognito_dart_v0.2.0']); - - expect( - repo.latestTag('amplify_auth_cognito'), - 'amplify_v1.0.0-next.0+2', - reason: 'Package should follow components', - ); - expect( - repo.latestTag('amplify'), - 'amplify_v1.0.0-next.0+2', - reason: 'latestTag should allow component lookups directly', - ); - expect( - repo.latestTag('amplify_auth_cognito_dart'), - 'amplify_auth_cognito_dart_v0.2.0', + await runGit( + ['commit', '--allow-empty', '-m', 'Initial commit'], ); - expect(repo.latestTag('smithy'), isNull); + await runGit(['rev-parse', 'HEAD']); }); group('E2E', () { @@ -198,10 +188,7 @@ Initial version. await runGit(['add', '.']); await runGit(['commit', '-m', 'Add packages']); - await runGit(['tag', 'amplify_v$nextVersion']); - await runGit(['tag', 'aws_common_v$coreVersion']); - await runGit(['tag', 'amplify_core_v$coreVersion']); - await runGit(['tag', 'amplify_auth_cognito_dart_v$coreVersion']); + baseRef = await runGit(['rev-parse', 'HEAD']); // Make changes that affect: // - Only a leaf package @@ -228,20 +215,97 @@ Initial version. 'amplify_auth_cognito_dart', ], ); + final coreBump = await makeChange( + 'chore(version): Release core', + [ + 'amplify_core', + 'amplify_auth_cognito_dart', + ], + trailers: { + 'Updated-Components': 'amplify_core, amplify_auth_cognito_dart', + }, + ); + packageBumps['amplify_core'] = coreBump; + packageBumps['amplify_auth_cognito_dart'] = coreBump; + final flutterBump = await makeChange( + 'chore(version): Release flutter', + [ + 'amplify_auth_cognito', + 'amplify_auth_cognito_ios', + ], + trailers: { + 'Updated-Components': 'Amplify Flutter', + }, + ); + packageBumps['amplify_auth_cognito'] = flutterBump; + packageBumps['amplify_auth_cognito_ios'] = flutterBump; }); - GitChanges changesForPackage(PackageInfo package) { - final latestTag = repo.latestTag(package.name)!; - return repo.changes(latestTag, 'HEAD'); + GitChanges changesForPackage( + String package, { + required String baseRef, + }) { + return repo.changes(baseRef, 'HEAD'); } + group('changesForPackage', () { + group('w/ no version bump', () { + final packages = { + 'aws_common': 1, + 'amplify_core': 3, + 'amplify_auth_cognito_dart': 2, + 'amplify_auth_cognito': 2, + 'amplify_auth_cognito_ios': 3, + }; + for (final entry in packages.entries) { + test(entry.key, () { + final numCommits = entry.value; + final package = repo.allPackages[entry.key]!; + expect( + changesForPackage( + package.name, + baseRef: baseRef, + ).commitsByPackage[package], + hasLength(numCommits), + ); + }); + } + }); + + group('w/ version bump', () { + final packages = { + 'aws_common': 1, // since it was never released + 'amplify_core': 0, + 'amplify_auth_cognito_dart': 0, + 'amplify_auth_cognito': 0, + 'amplify_auth_cognito_ios': 0, + }; + for (final entry in packages.entries) { + test(entry.key, () { + final package = repo.allPackages[entry.key]!; + final lastBump = repo.latestBumpRef(package.name); + expect(lastBump, packageBumps[package.name]); + + final numCommits = entry.value; + expect( + changesForPackage( + package.name, + baseRef: lastBump ?? baseRef, + ).commitsByPackage[package], + hasLength(numCommits), + ); + }); + } + }); + }); + group('calculates changes', () { final numCommits = { 'aws_common': 1, - 'amplify_core': 2, - 'amplify_auth_cognito_dart': 1, - 'amplify_auth_cognito': 1, - 'amplify_auth_cognito_ios': 2, + 'amplify_core': 3, + 'amplify_auth_cognito_dart': 2, + 'amplify_auth_cognito': 2, + 'amplify_auth_cognito_ios': 3, }; final changelogs = { 'aws_common': ''' @@ -282,7 +346,7 @@ Initial version. test(packageName, () { final package = repo.allPackages[packageName]!; - final changes = changesForPackage(package); + final changes = changesForPackage(package.name, baseRef: baseRef); final commits = changes.commitsByPackage[package]!; expect(commits, hasLength(check.value)); @@ -304,8 +368,8 @@ Initial version. 'aws_common': '0.1.1', 'amplify_core': '1.0.0-next.0+1', 'amplify_auth_cognito_dart': '0.1.1', - 'amplify_auth_cognito': '1.0.0-next.0+1', - 'amplify_auth_cognito_ios': '1.0.0-next.0+1', + 'amplify_auth_cognito': '1.0.0-next.1', + 'amplify_auth_cognito_ios': '1.0.0-next.1', }; final updatedChangelogs = { 'aws_common': ''' @@ -328,13 +392,13 @@ Initial version. - feat(auth): New feature ''', 'amplify_auth_cognito': ''' -## 1.0.0-next.0+1 +## 1.0.0-next.1 ### Features - feat(auth): New feature ''', 'amplify_auth_cognito_ios': ''' -## 1.0.0-next.0+1 +## 1.0.0-next.1 ### Breaking Changes - fix(amplify_auth_cognito_ios)!: Change iOS dependency @@ -371,20 +435,20 @@ dependencies: ''', 'amplify_auth_cognito': ''' name: amplify_auth_cognito -version: 1.0.0-next.0+1 +version: 1.0.0-next.1 environment: sdk: '>=2.17.0 <3.0.0' dependencies: - amplify_auth_cognito_ios: ">=1.0.0-next.0+1 <1.0.0-next.1" + amplify_auth_cognito_ios: ">=1.0.0-next.1 <1.0.0-next.2" amplify_auth_cognito_dart: ">=0.1.1 <0.2.0" amplify_core: ">=1.0.0-next.0+1 <1.0.0-next.1" aws_common: "^0.1.0" ''', 'amplify_auth_cognito_ios': ''' name: amplify_auth_cognito_ios -version: 1.0.0-next.0+1 +version: 1.0.0-next.1 environment: sdk: '>=2.17.0 <3.0.0' @@ -393,7 +457,10 @@ environment: setUp(() async { repo.bumpAllVersions( - changesForPackage: changesForPackage, + changesForPackage: (pkg) => changesForPackage( + pkg.name, + baseRef: baseRef, + ), ); }); From 364b7ff948a51189d1c0c61e37237feff2463653 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Wed, 14 Dec 2022 17:37:43 -0700 Subject: [PATCH 24/40] chore(aft): Add propagation option --- aft.yaml | 2 +- packages/aft/lib/src/changelog/changelog.dart | 5 +- .../aft/lib/src/commands/amplify_command.dart | 7 +- .../src/commands/version_bump_command.dart | 42 +++++++--- packages/aft/lib/src/models.dart | 82 +++++++++++++------ packages/aft/lib/src/models.g.dart | 15 +++- packages/aft/lib/src/repo.dart | 27 ++++-- packages/aft/test/model_test.dart | 50 +++++------ 8 files changed, 162 insertions(+), 68 deletions(-) diff --git a/aft.yaml b/aft.yaml index fb388820ed..a0e9ee3256 100644 --- a/aft.yaml +++ b/aft.yaml @@ -43,6 +43,7 @@ components: - amplify_storage_s3 - name: Amplify Dart summary: amplify_core + propagate: none packages: - amplify_auth_cognito_dart - amplify_analytics_pinpoint_dart @@ -55,7 +56,6 @@ components: packages: - smithy - smithy_aws - - smithy_codegen - name: Worker Bee summary: worker_bee packages: diff --git a/packages/aft/lib/src/changelog/changelog.dart b/packages/aft/lib/src/changelog/changelog.dart index 317e3ac1dd..16f4c4bd35 100644 --- a/packages/aft/lib/src/changelog/changelog.dart +++ b/packages/aft/lib/src/changelog/changelog.dart @@ -23,8 +23,8 @@ import 'package:collection/collection.dart'; import 'package:markdown/markdown.dart'; import 'package:pub_semver/pub_semver.dart'; -part 'parser.dart'; part 'changelog.g.dart'; +part 'parser.dart'; /// Marker version for the `NEXT` (unreleased) version. final Version nextVersion = Version(0, 0, 0, pre: nextVersionTag); @@ -40,6 +40,9 @@ abstract class Changelog implements Built { factory Changelog([void Function(ChangelogBuilder) updates]) = _$Changelog; Changelog._(); + /// Creates an empty changelog. + factory Changelog.empty() => Changelog((b) => b..originalText = ''); + /// Parses [changelogMd] for a list of the versions. /// /// Throws a [ChangelogParseException] if there are issues processing the diff --git a/packages/aft/lib/src/commands/amplify_command.dart b/packages/aft/lib/src/commands/amplify_command.dart index ddcffd27a8..90eeceaf34 100644 --- a/packages/aft/lib/src/commands/amplify_command.dart +++ b/packages/aft/lib/src/commands/amplify_command.dart @@ -164,10 +164,15 @@ abstract class AmplifyCommand extends Command ); /// Runs `git` with the given [args] from the repo's root directory. - Future runGit(List args) => git.runGit( + Future runGit( + List args, { + bool echoOutput = false, + }) => + git.runGit( args, processWorkingDir: rootDir.path, throwOnError: true, + echoOutput: echoOutput, ); /// The `aft.yaml` document. diff --git a/packages/aft/lib/src/commands/version_bump_command.dart b/packages/aft/lib/src/commands/version_bump_command.dart index 70db1d74e8..c4bd776a0e 100644 --- a/packages/aft/lib/src/commands/version_bump_command.dart +++ b/packages/aft/lib/src/commands/version_bump_command.dart @@ -12,9 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'dart:convert'; import 'dart:io'; import 'package:aft/aft.dart'; +import 'package:aft/src/changelog/changelog.dart'; +import 'package:aft/src/changelog/printer.dart'; import 'package:aft/src/options/git_ref_options.dart'; import 'package:aft/src/options/glob_options.dart'; import 'package:aft/src/repo.dart'; @@ -120,18 +123,35 @@ class VersionBumpCommand extends AmplifyCommand verbose: verbose, ); } + } - logger.info('Version successfully bumped'); - // TODO(dnys1): Use version bump commit instead of tags - // if (yes || prompt('Commit changes? (y/N) ').toLowerCase() == 'y') { - // // Commit and tag changes - // await runGit(['add', '.']); - // await runGit(['commit', '-m', 'chore(version): Bump version']); - // await Future.wait([ - // for (final changeEntry in repo.versionChanges.entries) - // runGit(['tag', '${changeEntry.key}_v${changeEntry.value}']), - // ]); - // } + logger.info('Version successfully bumped'); + // Stage changes + final mergedChangelog = Changelog.empty().makeVersionEntry( + commits: { + for (final package in bumpedPackages) + ...?repo.changelogUpdates[package]?.commits, + }, + ); + final updatedComponents = List.of(bumpedPackages.map((pkg) => pkg.name)); + for (final component in repo.components.values) { + final componentPackages = + component.packages.map((pkg) => pkg.name).toList(); + if (componentPackages.every(updatedComponents.contains)) { + updatedComponents + ..removeWhere(componentPackages.contains) + ..add(component.name); + } } + final changelog = + LineSplitter.split(render(mergedChangelog)).skip(2).join('\n'); + final commitMsg = ''' +chore(version): Bump version + +$changelog + +Updated-Components: ${updatedComponents.join(', ')} +'''; + stdout.writeln(commitMsg); } } diff --git a/packages/aft/lib/src/models.dart b/packages/aft/lib/src/models.dart index 34a72df493..b8de1fc7f8 100644 --- a/packages/aft/lib/src/models.dart +++ b/packages/aft/lib/src/models.dart @@ -226,31 +226,28 @@ extension AmplifyVersion on Version { ); /// The next version according to Amplify rules for incrementing. - NextVersion nextAmplifyVersion(VersionBumpType type) { + Version nextAmplifyVersion(VersionBumpType type) { if (preRelease.isEmpty) { switch (type) { case VersionBumpType.patch: - return NextVersion(nextPatch); + return nextPatch; case VersionBumpType.nonBreaking: - return major == 0 - ? NextVersion(nextPatch) - : NextVersion.propogate(nextMinor); + return major == 0 ? nextPatch : nextMinor; case VersionBumpType.breaking: - return NextVersion.propogate(major == 0 ? nextMinor : nextMajor); + return major == 0 ? nextMinor : nextMajor; } } if (type == VersionBumpType.breaking) { - return NextVersion.propogate(nextPreRelease); + return nextPreRelease; } final newBuild = (build.singleOrNull as int? ?? 0) + 1; - final newVersion = Version( + return Version( major, minor, patch, pre: preRelease.join('.'), build: '$newBuild', ); - return NextVersion(newVersion); } /// The constraint to use for this version in pubspecs. @@ -274,20 +271,6 @@ extension AmplifyVersion on Version { } } -class NextVersion with AWSEquatable, AWSDebuggable { - const NextVersion(this.version) : propogateToComponent = false; - const NextVersion.propogate(this.version) : propogateToComponent = true; - - final Version version; - final bool propogateToComponent; - - @override - List get props => [version, propogateToComponent]; - - @override - String get runtimeTypeName => 'NextVersion'; -} - enum DependencyType { dependency('dependencies', 'dependency'), devDependency('dev_dependencies', 'dev dependency'), @@ -377,6 +360,51 @@ class AftConfig with AWSSerializable>, AWSDebuggable { Map toJson() => _$AftConfigToJson(this); } +/// Specifies how to propagate version changes within a component. +enum VersionPropagation { + /// Propagates only major version changes. + major, + + /// Propagates only minor version changes. + minor, + + /// Propagates all version changes. + all, + + /// Propagates no version changes. + none; + + /// Whether to propagate a version change from [oldVersion] to [newVersion] + /// within its component. + bool propagateToComponent(Version oldVersion, Version newVersion) { + if (oldVersion == newVersion) { + return false; + } + final majorVersionChanged = () { + if (newVersion.isPreRelease) { + if (oldVersion.isPreRelease) { + return newVersion == oldVersion.nextPreRelease; + } + return true; + } + return newVersion.major > oldVersion.major; + }(); + switch (this) { + case VersionPropagation.major: + return majorVersionChanged; + case VersionPropagation.minor: + if (majorVersionChanged) { + return true; + } + return newVersion.minor > oldVersion.minor; + case VersionPropagation.all: + return true; + case VersionPropagation.none: + return false; + } + } +} + /// {@template aft.models.aft_component} /// Strongly connected components which should have minor/major version bumps /// happen in unison, i.e. a version bump to one package cascades to all. @@ -387,6 +415,7 @@ class AftComponent with AWSSerializable>, AWSDebuggable { required this.name, this.summary, required this.packages, + this.propagate = VersionPropagation.minor, }); factory AftComponent.fromJson(Map json) => @@ -401,6 +430,9 @@ class AftComponent with AWSSerializable>, AWSDebuggable { /// The list of packages in the component. final List packages; + /// How to align package versions in this component when one changes. + final VersionPropagation propagate; + @override String get runtimeTypeName => 'AftComponent'; @@ -414,6 +446,7 @@ class AftRepoComponent with AWSEquatable, AWSDebuggable { this.summary, required this.packages, required this.packageGraph, + required this.propagate, }); /// The name of the component. @@ -428,6 +461,9 @@ class AftRepoComponent with AWSEquatable, AWSDebuggable { /// The graph of packages to their dependencies. final Map> packageGraph; + /// How to align package versions in this component when one changes. + final VersionPropagation propagate; + @override List get props => [name]; diff --git a/packages/aft/lib/src/models.g.dart b/packages/aft/lib/src/models.g.dart index d5f49a56dc..4d9aaf5996 100644 --- a/packages/aft/lib/src/models.g.dart +++ b/packages/aft/lib/src/models.g.dart @@ -56,13 +56,18 @@ AftComponent _$AftComponentFromJson(Map json) => $checkedCreate( ($checkedConvert) { $checkKeys( json, - allowedKeys: const ['name', 'summary', 'packages'], + allowedKeys: const ['name', 'summary', 'packages', 'propagate'], ); final val = AftComponent( name: $checkedConvert('name', (v) => v as String), summary: $checkedConvert('summary', (v) => v as String?), packages: $checkedConvert('packages', (v) => (v as List).map((e) => e as String).toList()), + propagate: $checkedConvert( + 'propagate', + (v) => + $enumDecodeNullable(_$VersionPropagationEnumMap, v) ?? + VersionPropagation.minor), ); return val; }, @@ -73,8 +78,16 @@ Map _$AftComponentToJson(AftComponent instance) => 'name': instance.name, 'summary': instance.summary, 'packages': instance.packages, + 'propagate': _$VersionPropagationEnumMap[instance.propagate]!, }; +const _$VersionPropagationEnumMap = { + VersionPropagation.major: 'major', + VersionPropagation.minor: 'minor', + VersionPropagation.all: 'all', + VersionPropagation.none: 'none', +}; + SdkConfig _$SdkConfigFromJson(Map json) => $checkedCreate( 'SdkConfig', json, diff --git a/packages/aft/lib/src/repo.dart b/packages/aft/lib/src/repo.dart index e3ea3f0c55..f876431382 100644 --- a/packages/aft/lib/src/repo.dart +++ b/packages/aft/lib/src/repo.dart @@ -74,6 +74,7 @@ class Repo { summary: summaryPackage, packages: packages, packageGraph: packageGraph, + propagate: component.propagate, ), ); }), @@ -301,11 +302,19 @@ class Repo { final currentProposedVersion = versionChanges.newVersion(package); final newProposedVersion = currentVersion.nextAmplifyVersion(type); final newVersion = maxBy( - [currentProposedVersion, newProposedVersion.version], + [currentProposedVersion, newProposedVersion], (version) => version, )!; - versionChanges.updateVersion(package, newVersion); - propogateToComponent ??= newProposedVersion.propogateToComponent; + propogateToComponent ??= component != null && + component.propagate.propagateToComponent( + currentVersion, + newVersion, + ); + versionChanges.updateVersion( + package, + newVersion, + propagateToComponent: propogateToComponent, + ); final currentChangelogUpdate = changelogUpdates[package]; changelogUpdates[package] = package.changelog.update( @@ -458,13 +467,19 @@ class VersionChanges { } /// Updates the proposed version for [package]. - void updateVersion(PackageInfo package, Version version) { + void updateVersion( + PackageInfo package, + Version version, { + required bool propagateToComponent, + }) { final currentVersion = newVersion(package); if (version <= currentVersion) { return; } - final component = _repo.aftConfig.componentForPackage(package.name); - _versionUpdates['component_$component'] = version; + if (propagateToComponent) { + final component = _repo.aftConfig.componentForPackage(package.name); + _versionUpdates['component_$component'] = version; + } _versionUpdates[package.name] = version; } } diff --git a/packages/aft/test/model_test.dart b/packages/aft/test/model_test.dart index 4c7f2afc95..2b2059bb94 100644 --- a/packages/aft/test/model_test.dart +++ b/packages/aft/test/model_test.dart @@ -18,21 +18,23 @@ import 'package:test/test.dart'; void main() { group('AmplifyVersion', () { + const proagation = VersionPropagation.minor; + test('0-version', () { final version = Version(0, 1, 0); final patch = version.nextAmplifyVersion(VersionBumpType.patch); - expect(patch.version, Version(0, 1, 1)); - expect(patch.propogateToComponent, false); + expect(patch, Version(0, 1, 1)); + expect(proagation.propagateToComponent(version, patch), false); final nonBreaking = version.nextAmplifyVersion(VersionBumpType.nonBreaking); - expect(nonBreaking.version, Version(0, 1, 1)); - expect(nonBreaking.propogateToComponent, false); + expect(nonBreaking, Version(0, 1, 1)); + expect(proagation.propagateToComponent(version, nonBreaking), false); final breaking = version.nextAmplifyVersion(VersionBumpType.breaking); - expect(breaking.version, Version(0, 2, 0)); - expect(breaking.propogateToComponent, true); + expect(breaking, Version(0, 2, 0)); + expect(proagation.propagateToComponent(version, breaking), true); }); test('pre-release (untagged)', () { @@ -40,25 +42,25 @@ void main() { final patch = version.nextAmplifyVersion(VersionBumpType.patch); expect( - patch.version, + patch, Version(1, 0, 0, pre: 'next.0', build: '1'), ); - expect(patch.propogateToComponent, false); + expect(proagation.propagateToComponent(version, patch), false); final nonBreaking = version.nextAmplifyVersion(VersionBumpType.nonBreaking); expect( - nonBreaking.version, + nonBreaking, Version(1, 0, 0, pre: 'next.0', build: '1'), ); - expect(nonBreaking.propogateToComponent, false); + expect(proagation.propagateToComponent(version, nonBreaking), false); final breaking = version.nextAmplifyVersion(VersionBumpType.breaking); expect( - breaking.version, + breaking, Version(1, 0, 0, pre: 'next.1'), ); - expect(breaking.propogateToComponent, true); + expect(proagation.propagateToComponent(version, breaking), true); }); test('pre-release (tagged)', () { @@ -66,42 +68,42 @@ void main() { final patch = version.nextAmplifyVersion(VersionBumpType.patch); expect( - patch.version, + patch, Version(1, 0, 0, pre: 'next.0', build: '2'), ); - expect(patch.propogateToComponent, false); + expect(proagation.propagateToComponent(version, patch), false); final nonBreaking = version.nextAmplifyVersion(VersionBumpType.nonBreaking); expect( - nonBreaking.version, + nonBreaking, Version(1, 0, 0, pre: 'next.0', build: '2'), ); - expect(nonBreaking.propogateToComponent, false); + expect(proagation.propagateToComponent(version, nonBreaking), false); final breaking = version.nextAmplifyVersion(VersionBumpType.breaking); expect( - breaking.version, + breaking, Version(1, 0, 0, pre: 'next.1'), ); - expect(breaking.propogateToComponent, true); + expect(proagation.propagateToComponent(version, breaking), true); }); test('release', () { final version = Version(1, 0, 0); final patch = version.nextAmplifyVersion(VersionBumpType.patch); - expect(patch.version, Version(1, 0, 1)); - expect(patch.propogateToComponent, false); + expect(patch, Version(1, 0, 1)); + expect(proagation.propagateToComponent(version, patch), false); final nonBreaking = version.nextAmplifyVersion(VersionBumpType.nonBreaking); - expect(nonBreaking.version, Version(1, 1, 0)); - expect(nonBreaking.propogateToComponent, true); + expect(nonBreaking, Version(1, 1, 0)); + expect(proagation.propagateToComponent(version, nonBreaking), true); final breaking = version.nextAmplifyVersion(VersionBumpType.breaking); - expect(breaking.version, Version(2, 0, 0)); - expect(breaking.propogateToComponent, true); + expect(breaking, Version(2, 0, 0)); + expect(proagation.propagateToComponent(version, breaking), true); }); }); } From db77a1a5f73452a5202d92c8ad6bb6e8776e7e59 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Wed, 14 Dec 2022 18:23:45 -0700 Subject: [PATCH 25/40] chore(aft): Remove `changelog` command --- packages/aft/README.md | 1 - packages/aft/bin/aft.dart | 1 - packages/aft/lib/aft.dart | 1 - .../lib/src/commands/changelog_command.dart | 111 ------------------ packages/aft/lib/src/repo.dart | 49 +++++--- packages/aft/test/e2e_test.dart | 2 +- 6 files changed, 31 insertions(+), 134 deletions(-) delete mode 100644 packages/aft/lib/src/commands/changelog_command.dart diff --git a/packages/aft/README.md b/packages/aft/README.md index ddfd54b2ee..d617b597c2 100644 --- a/packages/aft/README.md +++ b/packages/aft/README.md @@ -5,7 +5,6 @@ A CLI tool for managing the Amplify Flutter repository. ## Commands - `bootstrap`/`bs`: Sets up repo for development work -- `changelog`: Updates changelogs without affecting version - `clean`: Cleans temporary files and build artifacts for all packages - `deps`: Manages dependencies of all packages in the repo - `check`: Checks dependencies against `aft.yaml`, for use in CI diff --git a/packages/aft/bin/aft.dart b/packages/aft/bin/aft.dart index 0e107f997d..bb9baed99a 100644 --- a/packages/aft/bin/aft.dart +++ b/packages/aft/bin/aft.dart @@ -37,7 +37,6 @@ Future main(List args) async { ..addCommand(CleanCommand()) ..addCommand(PubCommand()) ..addCommand(BootstrapCommand()) - ..addCommand(ChangelogCommand()) ..addCommand(VersionBumpCommand()); try { await runner.run(args); diff --git a/packages/aft/lib/aft.dart b/packages/aft/lib/aft.dart index c93c2034a3..527533cb1b 100644 --- a/packages/aft/lib/aft.dart +++ b/packages/aft/lib/aft.dart @@ -16,7 +16,6 @@ library aft; export 'src/commands/amplify_command.dart'; export 'src/commands/bootstrap_command.dart'; -export 'src/commands/changelog_command.dart'; export 'src/commands/clean_command.dart'; export 'src/commands/deps_command.dart'; export 'src/commands/generate/generate_command.dart'; diff --git a/packages/aft/lib/src/commands/changelog_command.dart b/packages/aft/lib/src/commands/changelog_command.dart deleted file mode 100644 index e6caeb70ab..0000000000 --- a/packages/aft/lib/src/commands/changelog_command.dart +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:io'; - -import 'package:aft/aft.dart'; -import 'package:aft/src/options/git_ref_options.dart'; -import 'package:aft/src/options/glob_options.dart'; -import 'package:path/path.dart' as p; - -/// Command for manipulating changelogs. -class ChangelogCommand extends AmplifyCommand { - ChangelogCommand() { - addSubcommand(_ChangelogUpdateCommand()); - addSubcommand(_ChangelogPreviewCommand()); - } - - @override - String get description => 'Update changelog entries automatically'; - - @override - String get name => 'changelog'; - - @override - List get aliases => ['cl']; -} - -abstract class _ChangelogBaseCommand extends AmplifyCommand - with GitRefOptions, GlobOptions { - _ChangelogBaseCommand() { - argParser.addFlag( - 'yes', - abbr: 'y', - help: 'Responds "yes" to all prompts', - defaultsTo: false, - negatable: false, - ); - } - - late final bool yes = argResults!['yes'] as bool; - - Future _updateChangelogs({required bool preview}) async { - for (final package in repo.publishablePackages) { - final baseRef = this.baseRef ?? repo.latestBumpRef(package.name); - if (baseRef == null) { - exitError( - 'No previous version bumps for package (${package.name}). ' - 'Supply a base ref manually using --base-ref', - ); - } - final changes = repo.changes(baseRef, headRef); - final commits = changes.commitsByPackage[package]?.toSet() ?? const {}; - final changelogUpdate = package.changelog.update(commits: commits); - if (preview) { - if (changelogUpdate.hasUpdate) { - logger - ..info('${package.name}\n') - ..info(changelogUpdate.newText!); - } - } else { - await File(p.join(package.path, 'CHANGELOG.md')) - .writeAsString(changelogUpdate.toString()); - } - } - } -} - -class _ChangelogPreviewCommand extends _ChangelogBaseCommand { - @override - String get description => 'Previews changelogs with the latest commit info'; - - @override - String get name => 'preview'; - - @override - Future run() async { - await super.run(); - return _updateChangelogs(preview: true); - } -} - -class _ChangelogUpdateCommand extends _ChangelogBaseCommand { - @override - String get description => 'Updates changelogs with the latest commit info'; - - @override - String get name => 'update'; - - @override - Future run() async { - await super.run(); - await _updateChangelogs(preview: false); - - logger.info('Changelogs successfully updated'); - if (yes || promptYesNo('Commit changes? (y/N) ')) { - await runGit(['add', '.']); - await runGit(['commit', '-m', 'chore(version): Update changelogs']); - } - } -} diff --git a/packages/aft/lib/src/repo.dart b/packages/aft/lib/src/repo.dart index f876431382..fc673da40e 100644 --- a/packages/aft/lib/src/repo.dart +++ b/packages/aft/lib/src/repo.dart @@ -286,26 +286,32 @@ class Repo { /// the version change is propagated to all packages which depend on /// [package] at the type which is next least severe. /// - /// If [propogateToComponent] is `true`, all component packages are bumped to + /// If [propagateToComponent] is `true`, all component packages are bumped to /// the same version. Version bumpVersion( PackageInfo package, { required CommitMessage commit, required VersionBumpType type, required bool includeInChangelog, - bool? propogateToComponent, + bool? propagateToComponent, }) { logger.verbose('bumpVersion ${package.name} $commit'); final componentName = aftConfig.componentForPackage(package.name); final component = components[componentName]; final currentVersion = package.version; - final currentProposedVersion = versionChanges.newVersion(package); + final proposedPackageVersion = + versionChanges.newVersion(package.name) ?? currentVersion; + final proposedComponentVersion = versionChanges.newVersion(componentName); final newProposedVersion = currentVersion.nextAmplifyVersion(type); final newVersion = maxBy( - [currentProposedVersion, newProposedVersion], + [ + proposedPackageVersion, + if (proposedComponentVersion != null) proposedComponentVersion, + newProposedVersion, + ], (version) => version, )!; - propogateToComponent ??= component != null && + propagateToComponent ??= component != null && component.propagate.propagateToComponent( currentVersion, newVersion, @@ -313,7 +319,7 @@ class Repo { versionChanges.updateVersion( package, newVersion, - propagateToComponent: propogateToComponent, + propagateToComponent: propagateToComponent, ); final currentChangelogUpdate = changelogUpdates[package]; @@ -327,7 +333,8 @@ class Repo { logger ..verbose(' component: $componentName') ..verbose(' currentVersion: $currentVersion') - ..verbose(' currentProposedVersion: $currentProposedVersion') + ..verbose(' proposedPackageVersion: $proposedPackageVersion') + ..verbose(' proposedComponentVersion: $proposedComponentVersion') ..verbose(' newProposedVersion: $newProposedVersion') ..verbose(' newVersion: $newVersion'); @@ -341,7 +348,7 @@ class Repo { newVersion.toString(), ); if (commit.isBreakingChange && type != VersionBumpType.patch) { - // Back-propogate to all dependent packages for breaking changes. + // Back-propagate to all dependent packages for breaking changes. // // Since we set semantic version constraints, only a breaking change // in a direct dependency necessitates a version bump. @@ -368,7 +375,7 @@ class Repo { // Propagate to all component packages. final componentPackages = component?.packageGraph; - if (propogateToComponent && componentPackages != null) { + if (propagateToComponent && componentPackages != null) { dfs( componentPackages, (componentPackage) { @@ -381,7 +388,7 @@ class Repo { commit: commit, type: type, includeInChangelog: false, - propogateToComponent: false, + propagateToComponent: false, ); }, ); @@ -395,7 +402,8 @@ class Repo { 'Updating summary package `${summaryPackage.name}` ' 'with commit: $commit', ); - final packageVersion = versionChanges.newVersion(summaryPackage); + final packageVersion = versionChanges.newVersion(summaryPackage.name) ?? + versionChanges.newVersion(componentName); final currentChangelogUpdate = changelogUpdates[summaryPackage]; changelogUpdates[summaryPackage] = summaryPackage.changelog.update( commits: { @@ -411,13 +419,13 @@ class Repo { /// Updates the constraint for [package] in [dependent]. void updateConstraint(PackageInfo package, PackageInfo dependent) { - final newVersion = versionChanges.newVersion(package); + final newVersion = versionChanges.newVersion(package.name)!; if (dependent.pubspecInfo.pubspec.dependencies.containsKey(package.name)) { final newConstraint = newVersion.amplifyConstraint( minVersion: newVersion, ); logger.verbose( - 'Bumping dependency for ${dependent.name} in ${package.name} ' + 'Bumping dependency on ${dependent.name} in ${package.name} ' 'to $newConstraint', ); dependent.pubspecInfo.pubspecYamlEditor.update( @@ -456,14 +464,17 @@ class VersionChanges { /// Version updates, by component. final Map _versionUpdates = {}; - /// The latest proposed version for [package]. - Version newVersion(PackageInfo package) { - final component = _repo.aftConfig.componentForPackage(package.name); + /// The latest proposed version for [packageOrComponent]. + Version? newVersion(String packageOrComponent) { + final component = _repo.aftConfig.componentForPackage(packageOrComponent); final componentVersion = _versionUpdates['component_$component']; if (componentVersion != null) { return componentVersion; } - return _versionUpdates[package.name] ??= package.version; + if (_repo.components.containsKey(packageOrComponent)) { + return null; + } + return _versionUpdates[packageOrComponent]; } /// Updates the proposed version for [package]. @@ -472,8 +483,8 @@ class VersionChanges { Version version, { required bool propagateToComponent, }) { - final currentVersion = newVersion(package); - if (version <= currentVersion) { + final currentVersion = newVersion(package.name); + if (currentVersion != null && version <= currentVersion) { return; } if (propagateToComponent) { diff --git a/packages/aft/test/e2e_test.dart b/packages/aft/test/e2e_test.dart index 36821eff84..238e9237f7 100644 --- a/packages/aft/test/e2e_test.dart +++ b/packages/aft/test/e2e_test.dart @@ -468,7 +468,7 @@ environment: final packageName = check.key; test(packageName, () { final package = repo.allPackages[packageName]!; - final newVersion = repo.versionChanges.newVersion(package); + final newVersion = repo.versionChanges.newVersion(package.name); expect(newVersion.toString(), finalVersions[packageName]); final changelog = repo.changelogUpdates[package]!.newText; From 7f71862814edc45dc4f34224cdde0dec9572db75 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Wed, 14 Dec 2022 19:06:32 -0700 Subject: [PATCH 26/40] chore(aft): Clean up --- packages/aft/doc/versioning.md | 2 +- packages/aft/lib/src/repo.dart | 31 +++++++++++++++---------------- packages/aft/test/e2e_test.dart | 3 ++- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/aft/doc/versioning.md b/packages/aft/doc/versioning.md index 745f656076..191d34ea6e 100644 --- a/packages/aft/doc/versioning.md +++ b/packages/aft/doc/versioning.md @@ -1,6 +1,6 @@ # Versioning Algorithm -The `aft version` command uses Git history + [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) formatting to determine a suitable next version for a package along with the required changes for depending packages. +The `aft version-bump` command uses Git history + [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) formatting to determine a suitable next version for a package along with the required changes for depending packages. 1. Let `packages` be the set of all packages in the repo which are publishable to `pub.dev`. 2. For every package `P` in `packages`: diff --git a/packages/aft/lib/src/repo.dart b/packages/aft/lib/src/repo.dart index fc673da40e..86cd7167c6 100644 --- a/packages/aft/lib/src/repo.dart +++ b/packages/aft/lib/src/repo.dart @@ -300,8 +300,9 @@ class Repo { final component = components[componentName]; final currentVersion = package.version; final proposedPackageVersion = - versionChanges.newVersion(package.name) ?? currentVersion; - final proposedComponentVersion = versionChanges.newVersion(componentName); + versionChanges.proposedVersion(package.name) ?? currentVersion; + final proposedComponentVersion = + versionChanges.proposedVersion(componentName); final newProposedVersion = currentVersion.nextAmplifyVersion(type); final newVersion = maxBy( [ @@ -316,7 +317,7 @@ class Repo { currentVersion, newVersion, ); - versionChanges.updateVersion( + versionChanges.updateProposedVersion( package, newVersion, propagateToComponent: propagateToComponent, @@ -338,7 +339,7 @@ class Repo { ..verbose(' newProposedVersion: $newProposedVersion') ..verbose(' newVersion: $newVersion'); - if (newVersion > currentVersion) { + if (newVersion > proposedPackageVersion) { logger.debug( 'Bumping ${package.name} from $currentVersion to $newVersion: ' '${commit.summary}', @@ -402,8 +403,9 @@ class Repo { 'Updating summary package `${summaryPackage.name}` ' 'with commit: $commit', ); - final packageVersion = versionChanges.newVersion(summaryPackage.name) ?? - versionChanges.newVersion(componentName); + final packageVersion = + versionChanges.proposedVersion(summaryPackage.name) ?? + versionChanges.proposedVersion(componentName); final currentChangelogUpdate = changelogUpdates[summaryPackage]; changelogUpdates[summaryPackage] = summaryPackage.changelog.update( commits: { @@ -419,7 +421,7 @@ class Repo { /// Updates the constraint for [package] in [dependent]. void updateConstraint(PackageInfo package, PackageInfo dependent) { - final newVersion = versionChanges.newVersion(package.name)!; + final newVersion = versionChanges.proposedVersion(package.name)!; if (dependent.pubspecInfo.pubspec.dependencies.containsKey(package.name)) { final newConstraint = newVersion.amplifyConstraint( minVersion: newVersion, @@ -465,25 +467,22 @@ class VersionChanges { final Map _versionUpdates = {}; /// The latest proposed version for [packageOrComponent]. - Version? newVersion(String packageOrComponent) { - final component = _repo.aftConfig.componentForPackage(packageOrComponent); - final componentVersion = _versionUpdates['component_$component']; - if (componentVersion != null) { + Version? proposedVersion(String packageOrComponent) { + final isComponent = _repo.components.containsKey(packageOrComponent); + final componentVersion = _versionUpdates['component_$packageOrComponent']; + if (isComponent) { return componentVersion; } - if (_repo.components.containsKey(packageOrComponent)) { - return null; - } return _versionUpdates[packageOrComponent]; } /// Updates the proposed version for [package]. - void updateVersion( + void updateProposedVersion( PackageInfo package, Version version, { required bool propagateToComponent, }) { - final currentVersion = newVersion(package.name); + final currentVersion = proposedVersion(package.name); if (currentVersion != null && version <= currentVersion) { return; } diff --git a/packages/aft/test/e2e_test.dart b/packages/aft/test/e2e_test.dart index 238e9237f7..321d592009 100644 --- a/packages/aft/test/e2e_test.dart +++ b/packages/aft/test/e2e_test.dart @@ -468,7 +468,8 @@ environment: final packageName = check.key; test(packageName, () { final package = repo.allPackages[packageName]!; - final newVersion = repo.versionChanges.newVersion(package.name); + final newVersion = + repo.versionChanges.proposedVersion(package.name); expect(newVersion.toString(), finalVersions[packageName]); final changelog = repo.changelogUpdates[package]!.newText; From b2fe8880aa85083243634d49163c49719f3b9aef Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Wed, 14 Dec 2022 19:10:46 -0700 Subject: [PATCH 27/40] chore(aft): Update wording in `aft.yaml` --- aft.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aft.yaml b/aft.yaml index a0e9ee3256..4d4f3dbf34 100644 --- a/aft.yaml +++ b/aft.yaml @@ -21,8 +21,11 @@ dependencies: ignore: - synthetic_package -# Strongly connected components which should have major version bumps happen +# Strongly connected components which should have version bumps happen # in unison, i.e. a version bump to one package cascades to all. +# +# By default, this happens only for minor version bumps. However, this +# can be modified on a per-component basis using the `propagate` flag. components: - name: Amplify Flutter summary: amplify_flutter From a48bab8730bfcb0fec855c38d4e58db509773bf7 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sun, 18 Dec 2022 10:35:37 -0700 Subject: [PATCH 28/40] chore(aft): Link packages before version bump --- packages/aft/lib/src/commands/version_bump_command.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/aft/lib/src/commands/version_bump_command.dart b/packages/aft/lib/src/commands/version_bump_command.dart index c4bd776a0e..f328c0f65e 100644 --- a/packages/aft/lib/src/commands/version_bump_command.dart +++ b/packages/aft/lib/src/commands/version_bump_command.dart @@ -107,6 +107,10 @@ class VersionBumpCommand extends AmplifyCommand @override Future run() async { await super.run(); + + // Link packages so that we can run build_runner + await linkPackages(repo.allPackages); + final bumpedPackages = await _updateVersions(); if (!preview) { From 80cd3837d74685cccae4ae2f28801830f5c9922a Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sun, 18 Dec 2022 10:42:35 -0700 Subject: [PATCH 29/40] chore(aft): Follow Dart SemVer strategy Use build tag (`+`) for patch releases. --- packages/aft/lib/src/models.dart | 41 ++++++++++++++++++++++++------- packages/aft/test/model_test.dart | 6 ++++- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/packages/aft/lib/src/models.dart b/packages/aft/lib/src/models.dart index 4df6270af2..44d8a33d04 100644 --- a/packages/aft/lib/src/models.dart +++ b/packages/aft/lib/src/models.dart @@ -227,10 +227,11 @@ extension AmplifyVersion on Version { /// The next version according to Amplify rules for incrementing. Version nextAmplifyVersion(VersionBumpType type) { + final newBuild = (build.singleOrNull as int? ?? 0) + 1; if (preRelease.isEmpty) { switch (type) { case VersionBumpType.patch: - return nextPatch; + return major == 0 ? replace(build: [newBuild]) : nextPatch; case VersionBumpType.nonBreaking: return major == 0 ? nextPatch : nextMinor; case VersionBumpType.breaking: @@ -240,14 +241,7 @@ extension AmplifyVersion on Version { if (type == VersionBumpType.breaking) { return nextPreRelease; } - final newBuild = (build.singleOrNull as int? ?? 0) + 1; - return Version( - major, - minor, - patch, - pre: preRelease.join('.'), - build: '$newBuild', - ); + return replace(build: [newBuild]); } /// The constraint to use for this version in pubspecs. @@ -269,6 +263,35 @@ extension AmplifyVersion on Version { } return '>=$minVersion <$maxVersion'; } + + /// Creates a copy of this version with the given fields replaced. + Version replace({ + int? major, + int? minor, + int? patch, + List? preRelease, + List? build, + }) { + String? pre; + if (preRelease != null) { + pre = preRelease.join('.'); + } else if (this.preRelease.isNotEmpty) { + pre = this.preRelease.join('.'); + } + String? buildString; + if (build != null) { + buildString = build.join('.'); + } else if (this.build.isNotEmpty) { + buildString = this.build.join('.'); + } + return Version( + major ?? this.major, + minor ?? this.minor, + patch ?? this.patch, + pre: pre, + build: buildString, + ); + } } enum DependencyType { diff --git a/packages/aft/test/model_test.dart b/packages/aft/test/model_test.dart index 2b2059bb94..01c039fa04 100644 --- a/packages/aft/test/model_test.dart +++ b/packages/aft/test/model_test.dart @@ -24,9 +24,13 @@ void main() { final version = Version(0, 1, 0); final patch = version.nextAmplifyVersion(VersionBumpType.patch); - expect(patch, Version(0, 1, 1)); + expect(patch, Version(0, 1, 0, build: '1')); expect(proagation.propagateToComponent(version, patch), false); + final nextPatch = patch.nextAmplifyVersion(VersionBumpType.patch); + expect(nextPatch, Version(0, 1, 0, build: '2')); + expect(proagation.propagateToComponent(version, nextPatch), false); + final nonBreaking = version.nextAmplifyVersion(VersionBumpType.nonBreaking); expect(nonBreaking, Version(0, 1, 1)); From ae1ff5cc529417ac7b1de4b4ee88c6fb1d72cb03 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sun, 18 Dec 2022 10:50:36 -0700 Subject: [PATCH 30/40] chore(aft): Fix analysis errors --- .../commands/generate/generate_workflows_command.dart | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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 588cfb6e44..9ef1300540 100644 --- a/packages/aft/lib/src/commands/generate/generate_workflows_command.dart +++ b/packages/aft/lib/src/commands/generate/generate_workflows_command.dart @@ -29,8 +29,7 @@ class GenerateWorkflowsCommand extends AmplifyCommand { @override Future run() async { - final allPackages = await this.allPackages; - final repoRoot = await rootDir; + await super.run(); for (final package in allPackages.values) { if (package.pubspecInfo.pubspec.publishTo == 'none' && !falsePositiveExamples.contains(package.name)) { @@ -43,13 +42,13 @@ class GenerateWorkflowsCommand extends AmplifyCommand { continue; } final workflowFilepath = p.join( - repoRoot.path, + rootDir.path, '.github', 'workflows', '${package.name}.yaml', ); final workflowFile = File(workflowFilepath); - final repoRelativePath = p.relative(package.path, from: repoRoot.path); + final repoRelativePath = p.relative(package.path, from: rootDir.path); final customWorkflow = File(p.join(package.path, 'workflow.yaml')); if (customWorkflow.existsSync()) { customWorkflow.copySync(workflowFilepath); @@ -83,7 +82,7 @@ class GenerateWorkflowsCommand extends AmplifyCommand { ]; final workflowPaths = [ ...workflows.map((workflow) => '.github/workflows/$workflow'), - p.relative(workflowFilepath, from: repoRoot.path), + p.relative(workflowFilepath, from: rootDir.path), ].map((path) => " - '$path'").join('\n'); final workflowContents = StringBuffer( From 9c053cd9c72f1053b00fa6647fddfa873badbca7 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sun, 18 Dec 2022 11:28:02 -0700 Subject: [PATCH 31/40] fix(aft): Submodule `libgit2dart` Adds workaround for https://github.com/dart-lang/pub/issues/3563 which requires using Flutter's `dart` command if a package lists a Flutter SDK constraint even if the package does not depend on Flutter. --- .circleci/config.yml | 1 + .github/workflows/aft.yaml | 44 +++++++++++------- .github/workflows/amplify_authenticator.yaml | 2 +- .github/workflows/dart_dart2js.yaml | 5 ++- .github/workflows/dart_ddc.yaml | 5 ++- .github/workflows/dart_native.yaml | 5 ++- .github/workflows/dart_vm.yaml | 5 ++- .github/workflows/flutter_vm.yaml | 3 ++ .github/workflows/smoke_test.yaml | 7 ++- .gitmodules | 3 ++ aft.yaml | 1 + packages/aft/analysis_options.yaml | 1 + packages/aft/external/libgit2dart | 1 + packages/aft/external/libgit2dart.patch | 29 ++++++++++++ packages/aft/pubspec.yaml | 4 +- packages/aft/workflow.yaml | 47 ++++++++++++++++++++ packages/aws_sdk/smoke_test/workflow.yaml | 7 ++- 17 files changed, 144 insertions(+), 26 deletions(-) create mode 160000 packages/aft/external/libgit2dart create mode 100644 packages/aft/external/libgit2dart.patch create mode 100644 packages/aft/workflow.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml index 8bc65c237d..2f610e3583 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -42,6 +42,7 @@ commands: - run: name: Install and set up aft command: | + git submodule update --init flutter pub global activate -spath packages/aft aft bootstrap activate_pana: diff --git a/.github/workflows/aft.yaml b/.github/workflows/aft.yaml index c8f04cdab4..05501989c1 100644 --- a/.github/workflows/aft.yaml +++ b/.github/workflows/aft.yaml @@ -1,4 +1,3 @@ -# Generated with aft. To update, run: `aft generate workflows` name: aft on: push: @@ -6,15 +5,11 @@ on: - main - stable - next + paths: + - 'packages/aft/**/*.dart' pull_request: paths: - 'packages/aft/**/*.dart' - - 'packages/aft/**/*.yaml' - - 'packages/aft/lib/**/*' - - 'packages/aft/test/**/*' - - '.github/workflows/dart_vm.yaml' - - '.github/workflows/dart_native.yaml' - - '.github/workflows/aft.yaml' schedule: - cron: "0 0 * * 0" # Every Sunday at 00:00 defaults: @@ -24,12 +19,29 @@ permissions: read-all jobs: test: - uses: ./.github/workflows/dart_vm.yaml - with: - working-directory: packages/aft - native_test: - if: ${{ github.event_name == 'push' }} - needs: test - uses: ./.github/workflows/dart_native.yaml - with: - working-directory: packages/aft + name: Test + runs-on: ubuntu-latest + steps: + - name: Git Checkout + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # 3.1.0 + + - name: Git Submodules + run: git submodule update --init + + - name: Setup Dart + uses: dart-lang/setup-dart@196f54580e9eee2797c57e85e289339f85e6779d # main + with: + sdk: stable + + - name: Get Packages + working-directory: packages/aft + run: | + # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) + ( cd external/libgit2dart; git apply ../libgit2dart.patch ) + dart pub upgrade + sudo apt-get install libgit2-dev + dart run libgit2dart:setup + + - name: Run Tests + working-directory: packages/aft + run: dart test diff --git a/.github/workflows/amplify_authenticator.yaml b/.github/workflows/amplify_authenticator.yaml index 4be8e8d839..0e43cb0334 100644 --- a/.github/workflows/amplify_authenticator.yaml +++ b/.github/workflows/amplify_authenticator.yaml @@ -15,7 +15,7 @@ on: - '.github/workflows/flutter_vm.yaml' - '.github/workflows/amplify_authenticator.yaml' schedule: - - cron: '0 0 * * 0' # Every Sunday at 00:00 + - cron: "0 0 * * 0" # Every Sunday at 00:00 defaults: run: shell: bash diff --git a/.github/workflows/dart_dart2js.yaml b/.github/workflows/dart_dart2js.yaml index fa8799c400..523e7df923 100644 --- a/.github/workflows/dart_dart2js.yaml +++ b/.github/workflows/dart_dart2js.yaml @@ -50,7 +50,10 @@ jobs: sdk: ${{ matrix.sdk }} - name: Setup aft - run: dart pub global activate -spath packages/aft + run: | + # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) + ( cd packages/aft/external/libgit2dart; git apply ../libgit2dart.patch ) + dart pub global activate -spath packages/aft - name: Setup Firefox if: ${{ matrix.browser == 'firefox' }} diff --git a/.github/workflows/dart_ddc.yaml b/.github/workflows/dart_ddc.yaml index e471e0b643..1ade4fddcc 100644 --- a/.github/workflows/dart_ddc.yaml +++ b/.github/workflows/dart_ddc.yaml @@ -54,7 +54,10 @@ jobs: sdk: ${{ matrix.sdk }} - name: Setup aft - run: dart pub global activate -spath packages/aft + run: | + # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) + ( cd packages/aft/external/libgit2dart; git apply ../libgit2dart.patch ) + dart pub global activate -spath packages/aft - name: Setup Firefox if: ${{ matrix.browser == 'firefox' }} diff --git a/.github/workflows/dart_native.yaml b/.github/workflows/dart_native.yaml index 6b1ebd8744..4a4da4c5c4 100644 --- a/.github/workflows/dart_native.yaml +++ b/.github/workflows/dart_native.yaml @@ -51,7 +51,10 @@ jobs: sdk: stable - name: Setup aft - run: dart pub global activate -spath packages/aft + run: | + # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) + ( cd packages/aft/external/libgit2dart; git apply ../libgit2dart.patch ) + dart pub global activate -spath packages/aft - name: Bootstrap id: bootstrap diff --git a/.github/workflows/dart_vm.yaml b/.github/workflows/dart_vm.yaml index 582ec320ea..1187d5cba7 100644 --- a/.github/workflows/dart_vm.yaml +++ b/.github/workflows/dart_vm.yaml @@ -47,7 +47,10 @@ jobs: sdk: ${{ matrix.sdk }} - name: Setup aft - run: dart pub global activate -spath packages/aft + run: | + # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) + ( cd packages/aft/external/libgit2dart; git apply ../libgit2dart.patch ) + dart pub global activate -spath packages/aft - name: Bootstrap id: bootstrap diff --git a/.github/workflows/flutter_vm.yaml b/.github/workflows/flutter_vm.yaml index b5dd0f60cd..049f10d7be 100644 --- a/.github/workflows/flutter_vm.yaml +++ b/.github/workflows/flutter_vm.yaml @@ -23,6 +23,9 @@ jobs: - name: Git Checkout uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # 3.1.0 + - name: Git Submodules + run: git submodule update --init + - name: Setup Flutter uses: subosito/flutter-action@dbf1fa04f4d2e52c33185153d06cdb5443aa189d # 2.8.0 with: diff --git a/.github/workflows/smoke_test.yaml b/.github/workflows/smoke_test.yaml index d487fb483f..14dd5f7ec3 100644 --- a/.github/workflows/smoke_test.yaml +++ b/.github/workflows/smoke_test.yaml @@ -25,7 +25,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Git Checkout - uses: actions/checkout@e2f20e631ae6d7dd3b768f56a5d2af784dd54791 # v2.5.0 + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # 3.1.0 + + - name: Git Submodules + run: git submodule update --init - name: Setup Dart uses: dart-lang/setup-dart@196f54580e9eee2797c57e85e289339f85e6779d # main @@ -34,6 +37,8 @@ jobs: - name: Link Packages run: | + # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) + ( cd packages/aft/external/libgit2dart; git apply ../libgit2dart.patch ) dart pub global activate -spath packages/aft aft link diff --git a/.gitmodules b/.gitmodules index 6e7923f264..81a2534ca5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,3 +11,6 @@ path = packages/smithy/goldens/smithy url = https://github.com/awslabs/smithy.git branch = main +[submodule "packages/aft/external/libgit2dart"] + path = packages/aft/external/libgit2dart + url = https://github.com/SkinnyMind/libgit2dart.git diff --git a/aft.yaml b/aft.yaml index 4d4f3dbf34..ebd3f305f8 100644 --- a/aft.yaml +++ b/aft.yaml @@ -20,6 +20,7 @@ dependencies: # Packages to ignore in all repo operations. ignore: - synthetic_package + - libgit2dart # Strongly connected components which should have version bumps happen # in unison, i.e. a version bump to one package cascades to all. diff --git a/packages/aft/analysis_options.yaml b/packages/aft/analysis_options.yaml index 46eb63b7b0..7d9efef6a1 100644 --- a/packages/aft/analysis_options.yaml +++ b/packages/aft/analysis_options.yaml @@ -5,3 +5,4 @@ analyzer: public_member_api_docs: ignore exclude: - '**/*.g.dart' + - external/ diff --git a/packages/aft/external/libgit2dart b/packages/aft/external/libgit2dart new file mode 160000 index 0000000000..34d492a9b6 --- /dev/null +++ b/packages/aft/external/libgit2dart @@ -0,0 +1 @@ +Subproject commit 34d492a9b6704a5d5bad7ece8970109df0f05752 diff --git a/packages/aft/external/libgit2dart.patch b/packages/aft/external/libgit2dart.patch new file mode 100644 index 0000000000..b9465a2206 --- /dev/null +++ b/packages/aft/external/libgit2dart.patch @@ -0,0 +1,29 @@ +diff --git a/pubspec.yaml b/pubspec.yaml +index 5acda72..2831e58 100644 +--- a/pubspec.yaml ++++ b/pubspec.yaml +@@ -8,7 +8,6 @@ homepage: https://github.com/SkinnyMind/libgit2dart + + environment: + sdk: ">=2.18.0 <3.0.0" +- flutter: ">=3.3.0" + + dependencies: + args: ^2.3.0 +@@ -23,16 +22,6 @@ dev_dependencies: + lints: ^2.0.0 + test: ^1.20.0 + +-flutter: +- plugin: +- platforms: +- linux: +- pluginClass: Libgit2dartPlugin +- macos: +- pluginClass: Libgit2dartPlugin +- windows: +- pluginClass: Libgit2dartPlugin +- + ffigen: + output: "lib/src/bindings/libgit2_bindings.dart" + headers: diff --git a/packages/aft/pubspec.yaml b/packages/aft/pubspec.yaml index b302a73322..51c5c3f39f 100644 --- a/packages/aft/pubspec.yaml +++ b/packages/aft/pubspec.yaml @@ -27,9 +27,7 @@ dependencies: http: ^0.13.0 json_annotation: ^4.7.0 libgit2dart: - git: - url: https://github.com/SkinnyMind/libgit2dart - ref: d55742d6d7725fa190df0caf3c1cbe60b7b481aa + path: external/libgit2dart markdown: ^5.0.0 meta: ^1.7.0 path: any diff --git a/packages/aft/workflow.yaml b/packages/aft/workflow.yaml new file mode 100644 index 0000000000..05501989c1 --- /dev/null +++ b/packages/aft/workflow.yaml @@ -0,0 +1,47 @@ +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@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # 3.1.0 + + - name: Git Submodules + run: git submodule update --init + + - name: Setup Dart + uses: dart-lang/setup-dart@196f54580e9eee2797c57e85e289339f85e6779d # main + with: + sdk: stable + + - name: Get Packages + working-directory: packages/aft + run: | + # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) + ( cd external/libgit2dart; git apply ../libgit2dart.patch ) + dart pub upgrade + sudo apt-get install libgit2-dev + dart run libgit2dart:setup + + - name: Run Tests + working-directory: packages/aft + run: dart test diff --git a/packages/aws_sdk/smoke_test/workflow.yaml b/packages/aws_sdk/smoke_test/workflow.yaml index d487fb483f..14dd5f7ec3 100644 --- a/packages/aws_sdk/smoke_test/workflow.yaml +++ b/packages/aws_sdk/smoke_test/workflow.yaml @@ -25,7 +25,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Git Checkout - uses: actions/checkout@e2f20e631ae6d7dd3b768f56a5d2af784dd54791 # v2.5.0 + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # 3.1.0 + + - name: Git Submodules + run: git submodule update --init - name: Setup Dart uses: dart-lang/setup-dart@196f54580e9eee2797c57e85e289339f85e6779d # main @@ -34,6 +37,8 @@ jobs: - name: Link Packages run: | + # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) + ( cd packages/aft/external/libgit2dart; git apply ../libgit2dart.patch ) dart pub global activate -spath packages/aft aft link From 8d6234d5855f2b28b33113d54efcff5a16817ee3 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sun, 18 Dec 2022 12:23:56 -0700 Subject: [PATCH 32/40] fix(authenticator): ARB syntax Remove unncessary commas --- .../amplify_authenticator/example/lib/l10n/amplify_en.arb | 8 +++----- .../amplify_authenticator/example/lib/l10n/amplify_es.arb | 4 ++-- .../example/lib/resolvers/localized_button_resolver.dart | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/authenticator/amplify_authenticator/example/lib/l10n/amplify_en.arb b/packages/authenticator/amplify_authenticator/example/lib/l10n/amplify_en.arb index 3ae6122a7d..5f1779ba00 100644 --- a/packages/authenticator/amplify_authenticator/example/lib/l10n/amplify_en.arb +++ b/packages/authenticator/amplify_authenticator/example/lib/l10n/amplify_en.arb @@ -57,24 +57,22 @@ "@signOut": { "description": "Label of button to sign out the user" }, - "backTo": "Back to {previousStep, select, signUp{Sign Up}, signIn{Sign In}, confirmSignUp{Confirm Sign-up}, confirmSignInMfa{Confirm Sign-in} confirmSignInNewPassword{Confirm Sign-in} sendCode{Send Code} resetPassword{Reset Password} verifyUser{Verify User} confirmVerifyUser{Confirm Verify User}}", + "backTo": "Back to {previousStep, select, signUp{Sign Up} signIn{Sign In} confirmSignUp{Confirm Sign-up} confirmSignInMfa{Confirm Sign-in} confirmSignInNewPassword{Confirm Sign-in} sendCode{Send Code} resetPassword{Reset Password} verifyUser{Verify User} confirmVerifyUser{Confirm Verify User} other{ERROR}}", "@backTo": { "description": "Label of button to return to the previous step", "placeholders": { "previousStep": { - "type": "AuthenticatorStep", "example": "signIn", "description": "The title of the previous step." } } }, - "signInWith": "Sign In with {provider, select, google{Google} facebook{Facebook} amazon{Amazon} apple{Apple}}", + "signInWith": "Sign In with {provider, select, google{Google} facebook{Facebook} amazon{Amazon} apple{Apple} other{ERROR}}", "@signInWith": { "description": "Label of button to sign in with a social provider", "placeholders": { "provider": { - "type": "AuthProvider", - "example": "Google", + "example": "google", "description": "The social sign-in provider" } } diff --git a/packages/authenticator/amplify_authenticator/example/lib/l10n/amplify_es.arb b/packages/authenticator/amplify_authenticator/example/lib/l10n/amplify_es.arb index 7917ec4490..1b726119eb 100644 --- a/packages/authenticator/amplify_authenticator/example/lib/l10n/amplify_es.arb +++ b/packages/authenticator/amplify_authenticator/example/lib/l10n/amplify_es.arb @@ -13,8 +13,8 @@ "verify": "Verificar", "skip": "Omitir", "signOut": "Salir", - "backTo": "Regresar al {previousStep, select, signUp{registro}, signIn{inicio de sesión}, confirmSignUp{Confirmar registro}, confirmSignInMfa{Confirmar inicio de sesión} confirmSignInNewPassword{Confirmar inicio de sesión} sendCode{Enviar código} resetPassword{Restablecer contraseña} verifyUser{Verificar usuario} confirmVerifyUser{Confirmar verificación de usuario}}", - "signInWith": "Iniciar Sesión con {provider, select, google{Google} facebook{Facebook} amazon{Amazon} apple{Apple}}", + "backTo": "Regresar al {previousStep, select, signUp{registro} signIn{inicio de sesión} confirmSignUp{Confirmar registro} confirmSignInMfa{Confirmar inicio de sesión} confirmSignInNewPassword{Confirmar inicio de sesión} sendCode{Enviar código} resetPassword{Restablecer contraseña} verifyUser{Verificar usuario} confirmVerifyUser{Confirmar verificación de usuario} other{ERROR}}", + "signInWith": "Iniciar Sesión con {provider, select, google{Google} facebook{Facebook} amazon{Amazon} apple{Apple} other{ERROR}}", "us": "Estados Unidos", "selectDialCode": "seleccionar país", "confirmSignInCustomAuth": "Título de formulario personalizado" diff --git a/packages/authenticator/amplify_authenticator/example/lib/resolvers/localized_button_resolver.dart b/packages/authenticator/amplify_authenticator/example/lib/resolvers/localized_button_resolver.dart index 4bc69995ca..bab82b6f29 100644 --- a/packages/authenticator/amplify_authenticator/example/lib/resolvers/localized_button_resolver.dart +++ b/packages/authenticator/amplify_authenticator/example/lib/resolvers/localized_button_resolver.dart @@ -68,12 +68,12 @@ class LocalizedButtonResolver extends ButtonResolver { @override String signInWith(BuildContext context, AuthProvider provider) { - return AppLocalizations.of(context).signInWith(provider); + return AppLocalizations.of(context).signInWith(provider.name); } @override String backTo(BuildContext context, AuthenticatorStep previousStep) { - return AppLocalizations.of(context).backTo(previousStep); + return AppLocalizations.of(context).backTo(previousStep.name); } @override From 1415fb7cadf71d096ae7ebdbf1021ec86e9f4328 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sun, 18 Dec 2022 12:54:52 -0700 Subject: [PATCH 33/40] chore(aft): Remove setup step --- .github/workflows/aft.yaml | 1 - packages/aft/workflow.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/aft.yaml b/.github/workflows/aft.yaml index 05501989c1..118cb3499e 100644 --- a/.github/workflows/aft.yaml +++ b/.github/workflows/aft.yaml @@ -40,7 +40,6 @@ jobs: ( cd external/libgit2dart; git apply ../libgit2dart.patch ) dart pub upgrade sudo apt-get install libgit2-dev - dart run libgit2dart:setup - name: Run Tests working-directory: packages/aft diff --git a/packages/aft/workflow.yaml b/packages/aft/workflow.yaml index 05501989c1..118cb3499e 100644 --- a/packages/aft/workflow.yaml +++ b/packages/aft/workflow.yaml @@ -40,7 +40,6 @@ jobs: ( cd external/libgit2dart; git apply ../libgit2dart.patch ) dart pub upgrade sudo apt-get install libgit2-dev - dart run libgit2dart:setup - name: Run Tests working-directory: packages/aft From 297f7c3df5943409d199432bfc4e2634acb4b634 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sun, 18 Dec 2022 13:40:56 -0700 Subject: [PATCH 34/40] chore(aft): Copy libgit2 from Instead of trying to install it (since the latest version is not available in apt) --- .github/workflows/aft.yaml | 3 ++- packages/aft/workflow.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/aft.yaml b/.github/workflows/aft.yaml index 118cb3499e..7d7fec8aa8 100644 --- a/.github/workflows/aft.yaml +++ b/.github/workflows/aft.yaml @@ -39,7 +39,8 @@ jobs: # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) ( cd external/libgit2dart; git apply ../libgit2dart.patch ) dart pub upgrade - sudo apt-get install libgit2-dev + mkdir -p .dart_tool/libgit2 + cp external/libgit2dart/linux/*.so .dart_tool/libgit2 - name: Run Tests working-directory: packages/aft diff --git a/packages/aft/workflow.yaml b/packages/aft/workflow.yaml index 118cb3499e..7d7fec8aa8 100644 --- a/packages/aft/workflow.yaml +++ b/packages/aft/workflow.yaml @@ -39,7 +39,8 @@ jobs: # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) ( cd external/libgit2dart; git apply ../libgit2dart.patch ) dart pub upgrade - sudo apt-get install libgit2-dev + mkdir -p .dart_tool/libgit2 + cp external/libgit2dart/linux/*.so .dart_tool/libgit2 - name: Run Tests working-directory: packages/aft From 018acaaf53f19a7763481abbdf3b6b4699873098 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sun, 18 Dec 2022 13:41:49 -0700 Subject: [PATCH 35/40] chore(aft): Reset after patch --- .github/workflows/dart_dart2js.yaml | 1 + .github/workflows/dart_ddc.yaml | 1 + .github/workflows/dart_native.yaml | 1 + .github/workflows/dart_vm.yaml | 1 + 4 files changed, 4 insertions(+) diff --git a/.github/workflows/dart_dart2js.yaml b/.github/workflows/dart_dart2js.yaml index 523e7df923..f35190bef8 100644 --- a/.github/workflows/dart_dart2js.yaml +++ b/.github/workflows/dart_dart2js.yaml @@ -54,6 +54,7 @@ jobs: # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) ( cd packages/aft/external/libgit2dart; git apply ../libgit2dart.patch ) dart pub global activate -spath packages/aft + ( cd packages/aft/external/libgit2dart; git reset --hard HEAD ) - name: Setup Firefox if: ${{ matrix.browser == 'firefox' }} diff --git a/.github/workflows/dart_ddc.yaml b/.github/workflows/dart_ddc.yaml index 1ade4fddcc..48b6a849e4 100644 --- a/.github/workflows/dart_ddc.yaml +++ b/.github/workflows/dart_ddc.yaml @@ -58,6 +58,7 @@ jobs: # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) ( cd packages/aft/external/libgit2dart; git apply ../libgit2dart.patch ) dart pub global activate -spath packages/aft + ( cd packages/aft/external/libgit2dart; git reset --hard HEAD ) - name: Setup Firefox if: ${{ matrix.browser == 'firefox' }} diff --git a/.github/workflows/dart_native.yaml b/.github/workflows/dart_native.yaml index 4a4da4c5c4..ecdd637ea3 100644 --- a/.github/workflows/dart_native.yaml +++ b/.github/workflows/dart_native.yaml @@ -55,6 +55,7 @@ jobs: # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) ( cd packages/aft/external/libgit2dart; git apply ../libgit2dart.patch ) dart pub global activate -spath packages/aft + ( cd packages/aft/external/libgit2dart; git reset --hard HEAD ) - name: Bootstrap id: bootstrap diff --git a/.github/workflows/dart_vm.yaml b/.github/workflows/dart_vm.yaml index 1187d5cba7..33dc2e128f 100644 --- a/.github/workflows/dart_vm.yaml +++ b/.github/workflows/dart_vm.yaml @@ -51,6 +51,7 @@ jobs: # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) ( cd packages/aft/external/libgit2dart; git apply ../libgit2dart.patch ) dart pub global activate -spath packages/aft + ( cd packages/aft/external/libgit2dart; git reset --hard HEAD ) - name: Bootstrap id: bootstrap From bed7229feba1c760db7a1dc569d0733d603db957 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sun, 18 Dec 2022 17:24:03 -0700 Subject: [PATCH 36/40] chore(aft): Copy lib to /usr --- .github/workflows/aft.yaml | 3 +-- packages/aft/workflow.yaml | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/aft.yaml b/.github/workflows/aft.yaml index 7d7fec8aa8..7bb08071ed 100644 --- a/.github/workflows/aft.yaml +++ b/.github/workflows/aft.yaml @@ -39,8 +39,7 @@ jobs: # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) ( cd external/libgit2dart; git apply ../libgit2dart.patch ) dart pub upgrade - mkdir -p .dart_tool/libgit2 - cp external/libgit2dart/linux/*.so .dart_tool/libgit2 + sudo cp external/libgit2dart/linux/*.so /usr/local/lib - name: Run Tests working-directory: packages/aft diff --git a/packages/aft/workflow.yaml b/packages/aft/workflow.yaml index 7d7fec8aa8..7bb08071ed 100644 --- a/packages/aft/workflow.yaml +++ b/packages/aft/workflow.yaml @@ -39,8 +39,7 @@ jobs: # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) ( cd external/libgit2dart; git apply ../libgit2dart.patch ) dart pub upgrade - mkdir -p .dart_tool/libgit2 - cp external/libgit2dart/linux/*.so .dart_tool/libgit2 + sudo cp external/libgit2dart/linux/*.so /usr/local/lib - name: Run Tests working-directory: packages/aft From 0d5a35f959649d2d9464aca310bc6103ddd288e2 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Mon, 9 Jan 2023 16:49:53 -0700 Subject: [PATCH 37/40] fix(aft): `dev_dependency` conflicts When `dev_dependencies` contains versions of the published packages different than those on pub.dev (for example when using path deps or `any`), this causes issues during pre-publish verification. By simply keeping these constraints up-to-date as well, we lose nothing (since they are overridden in local development via linking), but gain stronger confidence in the pre-publish step. --- .../src/commands/version_bump_command.dart | 2 +- packages/aft/lib/src/repo.dart | 23 +++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/aft/lib/src/commands/version_bump_command.dart b/packages/aft/lib/src/commands/version_bump_command.dart index f328c0f65e..e6313436b7 100644 --- a/packages/aft/lib/src/commands/version_bump_command.dart +++ b/packages/aft/lib/src/commands/version_bump_command.dart @@ -72,7 +72,7 @@ class VersionBumpCommand extends AmplifyCommand final changelogUpdates = repo.changelogUpdates; final bumpedPackages = []; - for (final package in repo.publishablePackages) { + for (final package in repo.allPackages.values) { final edits = package.pubspecInfo.pubspecYamlEditor.edits; if (edits.isNotEmpty) { bumpedPackages.add(package); diff --git a/packages/aft/lib/src/repo.dart b/packages/aft/lib/src/repo.dart index 86cd7167c6..79b96ccc1f 100644 --- a/packages/aft/lib/src/repo.dart +++ b/packages/aft/lib/src/repo.dart @@ -348,7 +348,7 @@ class Repo { ['version'], newVersion.toString(), ); - if (commit.isBreakingChange && type != VersionBumpType.patch) { + if (commit.isBreakingChange) { // Back-propagate to all dependent packages for breaking changes. // // Since we set semantic version constraints, only a breaking change @@ -357,12 +357,14 @@ class Repo { 'Breaking change. Performing dfs on dependent packages...', ); for (final dependent in allPackages.values.where( - (pkg) => pkg.pubspecInfo.pubspec.dependencies.keys.contains( - package.name, - ), + (pkg) => + pkg.pubspecInfo.pubspec.dependencies.keys + .contains(package.name) || + pkg.pubspecInfo.pubspec.devDependencies.keys + .contains(package.name), )) { logger.verbose('found dependent package ${dependent.name}'); - if (dependent.isPublishable) { + if (dependent.isPublishable && type != VersionBumpType.patch) { bumpVersion( dependent, commit: commit, @@ -422,7 +424,11 @@ class Repo { /// Updates the constraint for [package] in [dependent]. void updateConstraint(PackageInfo package, PackageInfo dependent) { final newVersion = versionChanges.proposedVersion(package.name)!; - if (dependent.pubspecInfo.pubspec.dependencies.containsKey(package.name)) { + final hasDependency = + dependent.pubspecInfo.pubspec.dependencies.containsKey(package.name); + final hasDevDependency = + dependent.pubspecInfo.pubspec.devDependencies.containsKey(package.name); + if (hasDependency || hasDevDependency) { final newConstraint = newVersion.amplifyConstraint( minVersion: newVersion, ); @@ -431,7 +437,10 @@ class Repo { 'to $newConstraint', ); dependent.pubspecInfo.pubspecYamlEditor.update( - ['dependencies', package.name], + [ + if (hasDependency) 'dependencies' else 'dev_dependencies', + package.name + ], newConstraint, ); } From f5c7f6bba0c127a4a1d40ca9e231a008ce8c9de1 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Mon, 9 Jan 2023 17:16:48 -0700 Subject: [PATCH 38/40] chore(aft): Change workflow path for libgit2 --- .github/workflows/aft.yaml | 3 ++- packages/aft/workflow.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/aft.yaml b/.github/workflows/aft.yaml index 7bb08071ed..7d7fec8aa8 100644 --- a/.github/workflows/aft.yaml +++ b/.github/workflows/aft.yaml @@ -39,7 +39,8 @@ jobs: # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) ( cd external/libgit2dart; git apply ../libgit2dart.patch ) dart pub upgrade - sudo cp external/libgit2dart/linux/*.so /usr/local/lib + mkdir -p .dart_tool/libgit2 + cp external/libgit2dart/linux/*.so .dart_tool/libgit2 - name: Run Tests working-directory: packages/aft diff --git a/packages/aft/workflow.yaml b/packages/aft/workflow.yaml index 7bb08071ed..7d7fec8aa8 100644 --- a/packages/aft/workflow.yaml +++ b/packages/aft/workflow.yaml @@ -39,7 +39,8 @@ jobs: # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) ( cd external/libgit2dart; git apply ../libgit2dart.patch ) dart pub upgrade - sudo cp external/libgit2dart/linux/*.so /usr/local/lib + mkdir -p .dart_tool/libgit2 + cp external/libgit2dart/linux/*.so .dart_tool/libgit2 - name: Run Tests working-directory: packages/aft From c1de8010e27cf55b7f3b8b91edf6a7309c2960be Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Tue, 10 Jan 2023 09:11:36 -0700 Subject: [PATCH 39/40] chore(aft): Fix tests in CI --- .github/workflows/aft.yaml | 4 ++-- packages/aft/test/e2e_test.dart | 6 +++--- packages/aft/workflow.yaml | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/aft.yaml b/.github/workflows/aft.yaml index 7d7fec8aa8..288934a9dd 100644 --- a/.github/workflows/aft.yaml +++ b/.github/workflows/aft.yaml @@ -39,8 +39,8 @@ jobs: # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) ( cd external/libgit2dart; git apply ../libgit2dart.patch ) dart pub upgrade - mkdir -p .dart_tool/libgit2 - cp external/libgit2dart/linux/*.so .dart_tool/libgit2 + mkdir linux + cp external/libgit2dart/linux/*.so linux - name: Run Tests working-directory: packages/aft diff --git a/packages/aft/test/e2e_test.dart b/packages/aft/test/e2e_test.dart index 321d592009..e738db67bb 100644 --- a/packages/aft/test/e2e_test.dart +++ b/packages/aft/test/e2e_test.dart @@ -365,7 +365,7 @@ Initial version. group('bumps versions', () { final finalVersions = { - 'aws_common': '0.1.1', + 'aws_common': '0.1.0+1', 'amplify_core': '1.0.0-next.0+1', 'amplify_auth_cognito_dart': '0.1.1', 'amplify_auth_cognito': '1.0.0-next.1', @@ -373,7 +373,7 @@ Initial version. }; final updatedChangelogs = { 'aws_common': ''' -## 0.1.1 +## 0.1.0+1 ### Fixes - fix(aws_common): Fix type @@ -407,7 +407,7 @@ Initial version. final updatedPubspecs = { 'aws_common': ''' name: aws_common -version: 0.1.1 +version: 0.1.0+1 environment: sdk: '>=2.17.0 <3.0.0' diff --git a/packages/aft/workflow.yaml b/packages/aft/workflow.yaml index 7d7fec8aa8..288934a9dd 100644 --- a/packages/aft/workflow.yaml +++ b/packages/aft/workflow.yaml @@ -39,8 +39,8 @@ jobs: # Patch libgit2dart (see https://github.com/dart-lang/pub/issues/3563) ( cd external/libgit2dart; git apply ../libgit2dart.patch ) dart pub upgrade - mkdir -p .dart_tool/libgit2 - cp external/libgit2dart/linux/*.so .dart_tool/libgit2 + mkdir linux + cp external/libgit2dart/linux/*.so linux - name: Run Tests working-directory: packages/aft From 2cc6576b17ad9353b27d6d588c08dccc4e7cdaab Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Tue, 10 Jan 2023 09:34:09 -0700 Subject: [PATCH 40/40] chore(aft): Bump dependency --- packages/aft/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aft/pubspec.yaml b/packages/aft/pubspec.yaml index 51c5c3f39f..0fdca05b83 100644 --- a/packages/aft/pubspec.yaml +++ b/packages/aft/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: flutter_tools: git: url: https://github.com/flutter/flutter.git - ref: 117a83a4a7178c328d296748bd93ff388724cb67 + ref: 7a743c8816fd8ff7df99858c545c1dbe396d1103 path: packages/flutter_tools git: ^2.0.0 glob: ^2.1.0